ソースを参照

Merge https://github.com/BabylonJS/Babylon.js into dualVRControllers

Trevor Baron 7 年 前
コミット
e1666f320a
100 ファイル変更46542 行追加38347 行削除
  1. 16 0
      .github/ISSUE_TEMPLATE.md
  2. 11691 10951
      Playground/babylon.d.txt
  3. 1 0
      Playground/debug.html
  4. 1 0
      Playground/frame.html
  5. 1 0
      Playground/full.html
  6. 3 2
      Playground/index-local.html
  7. 1 0
      Playground/index.html
  8. 1 0
      Playground/indexStable.html
  9. 1 1
      Playground/scripts/instanced bones.js
  10. 1 0
      Playground/zipContent/index.html
  11. 13 1
      Tools/Gulp/config.json
  12. 1 1
      Tools/Gulp/package.json
  13. 3 2
      Viewer/src/configuration/configuration.ts
  14. 5 2
      Viewer/src/configuration/loader.ts
  15. 2 0
      Viewer/src/configuration/types/index.ts
  16. 79 29
      Viewer/src/viewer/viewer.ts
  17. 21 0
      dist/preview release/Oimo.license
  18. 11114 10389
      dist/preview release/babylon.d.ts
  19. 54 54
      dist/preview release/babylon.js
  20. 2039 806
      dist/preview release/babylon.max.js
  21. 55 55
      dist/preview release/babylon.worker.js
  22. 22 0
      dist/preview release/cannon.license
  23. 10990 10265
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  24. 54 54
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  25. 1981 877
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  26. 1983 879
      dist/preview release/customConfigurations/minimalGLTFViewer/es6.js
  27. 201 0
      dist/preview release/draco.license
  28. 31 0
      dist/preview release/draco_decoder.js
  29. 2041 808
      dist/preview release/es6.js
  30. 22 0
      dist/preview release/gui/babylon.gui.d.ts
  31. 76 2
      dist/preview release/gui/babylon.gui.js
  32. 3 3
      dist/preview release/gui/babylon.gui.min.js
  33. 22 0
      dist/preview release/gui/babylon.gui.module.d.ts
  34. 4 4
      dist/preview release/inspector/babylon.inspector.bundle.js
  35. 5 2
      dist/preview release/inspector/babylon.inspector.css
  36. 13 2
      dist/preview release/inspector/babylon.inspector.js
  37. 4 4
      dist/preview release/inspector/babylon.inspector.min.js
  38. 20 13
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  39. 24 13
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  40. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  41. 35 25
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  42. 157 121
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  43. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  44. 36 26
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  45. 158 122
      dist/preview release/loaders/babylon.glTFFileLoader.js
  46. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  47. 158 122
      dist/preview release/loaders/babylonjs.loaders.js
  48. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  49. 36 26
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  50. 1 0
      dist/preview release/materialsLibrary/babylon.fireMaterial.js
  51. 1 1
      dist/preview release/materialsLibrary/babylon.fireMaterial.min.js
  52. 1 0
      dist/preview release/materialsLibrary/babylonjs.materials.js
  53. 1 1
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  54. 65 22
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  55. 297 248
      dist/preview release/serializers/babylon.glTF2Serializer.js
  56. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  57. 297 248
      dist/preview release/serializers/babylonjs.serializers.js
  58. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  59. 65 22
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  60. 2 546
      dist/preview release/typedocValidationBaseline.json
  61. 67 66
      dist/preview release/viewer/babylon.viewer.js
  62. 15 5
      dist/preview release/what's new.md
  63. 16 9
      gui/src/advancedDynamicTexture.ts
  64. 6 6
      gui/src/controls/button.ts
  65. 2 2
      gui/src/controls/checkbox.ts
  66. 6 6
      gui/src/controls/colorpicker.ts
  67. 4 3
      gui/src/controls/container.ts
  68. 40 13
      gui/src/controls/control.ts
  69. 4 4
      gui/src/controls/inputText.ts
  70. 2 2
      gui/src/controls/radioButton.ts
  71. 6 6
      gui/src/controls/slider.ts
  72. 49 0
      gui/src/controls/textBlock.ts
  73. 3 2
      inspector/sass/_detailPanel.scss
  74. 3 0
      inspector/sass/_tree.scss
  75. 1 1
      inspector/src/properties.ts
  76. 12 1
      inspector/src/tree/TreeItem.ts
  77. 70 0
      loaders/src/glTF/2.0/Extensions/KHR_draco_mesh_compression.ts
  78. 47 17
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  79. 17 7
      loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts
  80. 12 3
      loaders/src/glTF/babylon.glTFFileLoader.ts
  81. 1 0
      localDev/index.html
  82. 2 0
      materialsLibrary/src/fire/babylon.fireMaterial.ts
  83. 1 1
      package.json
  84. 17 19
      readme.md
  85. 5 4
      sandbox/index-local.html
  86. 1 1
      sandbox/index.html
  87. 0 28
      sandbox/index.js
  88. 22 218
      serializers/src/glTF/2.0/babylon.glTFExporter.ts
  89. 328 113
      serializers/src/glTF/2.0/babylon.glTFMaterial.ts
  90. 1 1
      src/Animations/babylon.animatable.ts
  91. 1 1
      src/Audio/babylon.soundtrack.ts
  92. 5 0
      src/Bones/babylon.skeleton.ts
  93. 1 7
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  94. 106 43
      src/Engine/babylon.engine.ts
  95. 0 1
      src/Engine/babylon.nullEngine.ts
  96. 639 0
      src/Layer/babylon.effectLayer.ts
  97. 437 0
      src/Layer/babylon.glowLayer.ts
  98. 672 0
      src/Layer/babylon.highlightLayer.ts
  99. 0 996
      src/Layer/babylon.highlightlayer.ts
  100. 0 0
      src/Loading/Plugins/babylon.babylonFileLoader.ts

+ 16 - 0
.github/ISSUE_TEMPLATE.md

@@ -0,0 +1,16 @@
+Before creating an issue, please make sure to provide the following template (based on why you create the issue):
+(Please do not use Github issues for questions - We have a really active forum to help answering questions (http://www.html5gamedevs.com/forum/16-babylonjs/))
+
+# Bugs
+
+- Bug repro on [playground](https://playground.babylonjs.com):
+- Expected result:
+- Current result:
+
+# Feature request
+
+- Link to [forum](http://www.html5gamedevs.com/forum/16-babylonjs/) discussion about the feature:
+- Feature description
+- Additional links that could help implementing the feature:
+
+

ファイルの差分が大きいため隠しています
+ 11691 - 10951
Playground/babylon.d.txt


+ 1 - 0
Playground/debug.html

@@ -37,6 +37,7 @@
     <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
     <!-- Babylon.js -->
     <script src="https://preview.babylonjs.com/cannon.js"></script>
+    <script src="https://preview.babylonjs.com/draco_decoder.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/babylon.max.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

+ 1 - 0
Playground/frame.html

@@ -26,6 +26,7 @@
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <!-- Babylon.js -->
     <script src="https://preview.babylonjs.com/cannon.js"></script>
+    <script src="https://preview.babylonjs.com/draco_decoder.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/babylon.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

+ 1 - 0
Playground/full.html

@@ -26,6 +26,7 @@
         <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
         <!-- Babylon.js -->
         <script src="https://preview.babylonjs.com/cannon.js"></script>
+        <script src="https://preview.babylonjs.com/draco_decoder.js"></script>
         <script src="https://preview.babylonjs.com/Oimo.js"></script>
         <script src="https://preview.babylonjs.com/babylon.js"></script>
         <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

+ 3 - 2
Playground/index-local.html

@@ -16,8 +16,9 @@
     <!--Monaco-->
     <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
     <!-- Babylon.js -->
-    <script src="https://preview.babylonjs.com/cannon.js"></script>
-    <script src="https://preview.babylonjs.com/Oimo.js"></script>
+    <script src="../dist/preview%20release/cannon.js"></script>
+    <script src="../dist/preview%20release/draco_decoder.js"></script>
+    <script src="../dist/preview%20release/Oimo.js"></script>
     <script src="../tools/DevLoader/BabylonLoader.js"></script>
 
     <link href="css/index.css" rel="stylesheet" />

+ 1 - 0
Playground/index.html

@@ -37,6 +37,7 @@
     <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
     <!-- Babylon.js -->
     <script src="https://preview.babylonjs.com/cannon.js"></script>
+    <script src="https://preview.babylonjs.com/draco_decoder.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/babylon.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

+ 1 - 0
Playground/indexStable.html

@@ -37,6 +37,7 @@
     <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
     <!-- Babylon.js -->
     <script src="https://cdn.babylonjs.com/cannon.js"></script>
+    <script src="https://cdn.babylonjs.com/draco_decoder.js"></script>
     <script src="https://cdn.babylonjs.com/Oimo.js"></script>
     <script src="https://cdn.babylonjs.com/babylon.js"></script>
     <script src="https://cdn.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

+ 1 - 1
Playground/scripts/instanced bones.js

@@ -21,7 +21,7 @@ var createScene = function () {
 
     // Shadows
     var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
-    shadowGenerator.useBlurVarianceShadowMap = true;
+    shadowGenerator.useBlurExponentialShadowMap = true;
 
     // Dude
     BABYLON.SceneLoader.ImportMesh("him", "scenes/Dude/", "Dude.babylon", scene, function (newMeshes2, particleSystems2, skeletons2) {

+ 1 - 0
Playground/zipContent/index.html

@@ -9,6 +9,7 @@
         <script src="https://preview.babylonjs.com/babylon.js"></script>
         <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
         <script src="https://preview.babylonjs.com/cannon.js"></script>
+        <script src="https://preview.babylonjs.com/draco_decoder.js"></script>
         <script src="https://preview.babylonjs.com/oimo.js"></script>
         
         <style>

+ 13 - 1
Tools/Gulp/config.json

@@ -37,6 +37,7 @@
             "solidParticles",
             "additionalMeshes",
             "meshBuilder",
+            "meshCompression",
             "audio",
             "additionalTextures",
             "shadows",
@@ -115,6 +116,7 @@
             "solidParticles",
             "additionalMeshes",
             "meshBuilder",
+            "meshCompression",
             "audio",
             "additionalTextures",
             "shadows",
@@ -427,6 +429,12 @@
                 "additionalMeshes"
             ]
         },
+        "meshCompression" :
+        {
+            "files": [
+                "../../src/Mesh/Compression/babylon.dracoCompression.js"
+            ]
+        },
         "shaderMaterial": {
             "files": [
                 "../../src/Materials/babylon.shaderMaterial.js"
@@ -1043,7 +1051,9 @@
             "files": [
                 "../../src/Rendering/babylon.outlineRenderer.js",
                 "../../src/Rendering/babylon.edgesRenderer.js",
-                "../../src/Layer/babylon.highlightlayer.js"
+                "../../src/Layer/babylon.effectLayer.js",
+                "../../src/Layer/babylon.highlightLayer.js",
+                "../../src/Layer/babylon.glowLayer.js"
             ],
             "dependUpon": [
                 "shaderMaterial"
@@ -1477,6 +1487,7 @@
                     "../../loaders/src/glTF/2.0/babylon.glTFLoader.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts",
                     "../../loaders/src/glTF/2.0/Extensions/MSFT_lod.ts",
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_draco_mesh_compression.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_lights.ts"
                 ],
@@ -1497,6 +1508,7 @@
                     "../../loaders/src/glTF/2.0/babylon.glTFLoader.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts",
                     "../../loaders/src/glTF/2.0/Extensions/MSFT_lod.ts",
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_draco_mesh_compression.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_lights.ts"
                 ],

+ 1 - 1
Tools/Gulp/package.json

@@ -57,7 +57,7 @@
         "through2": "~0.6.5",
         "ts-loader": "^2.3.7",
         "typedoc": "^0.9.0",
-        "typescript": "^2.6.2",
+        "typescript": "~2.6.2",
         "webpack-stream": "^4.0.0"
     },
     "scripts": {

+ 3 - 2
Viewer/src/configuration/configuration.ts

@@ -67,7 +67,7 @@ export interface ViewerConfiguration {
 }
 
 export interface IModelConfiguration {
-    url: string;
+    url?: string;
     loader?: string; // obj, gltf?
     position?: { x: number, y: number, z: number };
     rotation?: { x: number, y: number, z: number, w?: number };
@@ -81,7 +81,7 @@ export interface IModelConfiguration {
         parentIndex?: number;
     }; // shoud the model be scaled to unit-size
 
-    title: string;
+    title?: string;
     subtitle?: string;
     thumbnail?: string; // URL or data-url
 
@@ -100,6 +100,7 @@ export interface ISkyboxConfiguration {
     blur?: number; // deprecated
     material?: {
         imageProcessingConfiguration?: IImageProcessingConfiguration;
+        [propName: string]: any;
     };
     infiniteDIstance?: boolean;
 

+ 5 - 2
Viewer/src/configuration/loader.ts

@@ -12,7 +12,7 @@ export class ConfigurationLoader {
         this.configurationCache = {};
     }
 
-    public loadConfiguration(initConfig: ViewerConfiguration = {}): Promise<ViewerConfiguration> {
+    public loadConfiguration(initConfig: ViewerConfiguration = {}, callback?: (config: ViewerConfiguration) => void): Promise<ViewerConfiguration> {
 
         let loadedConfig: ViewerConfiguration = deepmerge({}, initConfig);
 
@@ -55,9 +55,12 @@ export class ConfigurationLoader {
             }).then((data: any) => {
                 let mapper = mapperManager.getMapper(mapperType);
                 let parsed = mapper.map(data);
-                return deepmerge(loadedConfig, parsed);
+                let merged = deepmerge(loadedConfig, parsed);
+                if (callback) callback(merged);
+                return merged;
             });
         } else {
+            if (callback) callback(loadedConfig);
             return Promise.resolve(loadedConfig);
         }
     }

+ 2 - 0
Viewer/src/configuration/types/index.ts

@@ -8,6 +8,8 @@ let getConfigurationType = function (type: string): ViewerConfiguration {
             return defaultConfiguration;
         case 'minimal':
             return minimalConfiguration;
+        case 'none':
+            return {};
         default:
             return defaultConfiguration;
     }

+ 79 - 29
Viewer/src/viewer/viewer.ts

@@ -26,7 +26,7 @@ export abstract class AbstractViewer {
     public lastUsedLoader: ISceneLoaderPlugin | ISceneLoaderPluginAsync;
 
     protected configuration: ViewerConfiguration;
-    protected environmentHelper: EnvironmentHelper;
+    public environmentHelper: EnvironmentHelper;
 
     protected defaultHighpTextureType: number;
     protected shadowGeneratorBias: number;
@@ -43,7 +43,7 @@ export abstract class AbstractViewer {
     public onLoaderInitObservable: Observable<ISceneLoaderPlugin | ISceneLoaderPluginAsync>;
     public onInitDoneObservable: Observable<AbstractViewer>;
 
-    protected canvas: HTMLCanvasElement;
+    public canvas: HTMLCanvasElement;
 
     protected registeredOnBeforerenderFunctions: Array<() => void>;
 
@@ -74,8 +74,8 @@ export abstract class AbstractViewer {
         this.prepareContainerElement();
 
         // extend the configuration
-        configurationLoader.loadConfiguration(initialConfiguration).then((configuration) => {
-            this.configuration = configuration;
+        configurationLoader.loadConfiguration(initialConfiguration, (configuration) => {
+            this.configuration = deepmerge(this.configuration || {}, configuration);
             if (this.configuration.observers) {
                 this.configureObservers(this.configuration.observers);
             }
@@ -131,6 +131,9 @@ export abstract class AbstractViewer {
      * @param newConfiguration 
      */
     public updateConfiguration(newConfiguration: Partial<ViewerConfiguration> = this.configuration) {
+        // update this.configuration with the new data
+        this.configuration = deepmerge(this.configuration || {}, newConfiguration);
+
         // update scene configuration
         if (newConfiguration.scene) {
             this.configureScene(newConfiguration.scene);
@@ -160,14 +163,17 @@ export abstract class AbstractViewer {
             this.configureEnvironment(newConfiguration.skybox, newConfiguration.ground);
         }
 
-        // update this.configuration with the new data
-        this.configuration = deepmerge(this.configuration || {}, newConfiguration);
+        // camera
+        if (newConfiguration.camera) {
+            this.configureCamera(newConfiguration.camera);
+        }
     }
 
     protected configureEnvironment(skyboxConifguration?: ISkyboxConfiguration | boolean, groundConfiguration?: IGroundConfiguration | boolean) {
         if (!skyboxConifguration && !groundConfiguration) {
             if (this.environmentHelper) {
                 this.environmentHelper.dispose();
+                delete this.environmentHelper;
             };
             return Promise.resolve(this.scene);
         }
@@ -187,7 +193,7 @@ export abstract class AbstractViewer {
             }
 
             options.enableGroundShadow = groundConfig === true || groundConfig.receiveShadows;
-            if (groundConfig.shadowLevel) {
+            if (groundConfig.shadowLevel !== undefined) {
                 options.groundShadowLevel = groundConfig.shadowLevel;
             }
             options.enableGroundMirror = !!groundConfig.mirror;
@@ -198,19 +204,23 @@ export abstract class AbstractViewer {
                 options.groundColor = new Color3(groundConfig.color.r, groundConfig.color.g, groundConfig.color.b)
             }
 
+            if (groundConfig.opacity !== undefined) {
+                options.groundOpacity = groundConfig.opacity;
+            }
+
             if (groundConfig.mirror) {
                 options.enableGroundMirror = true;
                 // to prevent undefines
                 if (typeof groundConfig.mirror === "object") {
-                    if (groundConfig.mirror.amount)
+                    if (groundConfig.mirror.amount !== undefined)
                         options.groundMirrorAmount = groundConfig.mirror.amount;
-                    if (groundConfig.mirror.sizeRatio)
+                    if (groundConfig.mirror.sizeRatio !== undefined)
                         options.groundMirrorSizeRatio = groundConfig.mirror.sizeRatio;
-                    if (groundConfig.mirror.blurKernel)
+                    if (groundConfig.mirror.blurKernel !== undefined)
                         options.groundMirrorBlurKernel = groundConfig.mirror.blurKernel;
-                    if (groundConfig.mirror.fresnelWeight)
+                    if (groundConfig.mirror.fresnelWeight !== undefined)
                         options.groundMirrorFresnelWeight = groundConfig.mirror.fresnelWeight;
-                    if (groundConfig.mirror.fallOffDistance)
+                    if (groundConfig.mirror.fallOffDistance !== undefined)
                         options.groundMirrorFallOffDistance = groundConfig.mirror.fallOffDistance;
                     if (this.defaultHighpTextureType !== undefined)
                         options.groundMirrorTextureType = this.defaultHighpTextureType;
@@ -247,6 +257,8 @@ export abstract class AbstractViewer {
             }
         }
 
+        options.setupImageProcessing = false; // TMP
+
         if (!this.environmentHelper) {
             this.environmentHelper = this.scene.createDefaultEnvironment(options)!;
         } else {
@@ -393,6 +405,8 @@ export abstract class AbstractViewer {
             this.camera.rotationQuaternion = new Quaternion(cameraConfig.rotation.x || 0, cameraConfig.rotation.y || 0, cameraConfig.rotation.z || 0, cameraConfig.rotation.w || 0)
         }
 
+        this.extendClassWithConfig(this.camera, cameraConfig);
+
         this.camera.minZ = cameraConfig.minZ || this.camera.minZ;
         this.camera.maxZ = cameraConfig.maxZ || this.camera.maxZ;
 
@@ -405,7 +419,8 @@ export abstract class AbstractViewer {
         const sceneExtends = this.scene.getWorldExtends();
         const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
         const sceneDiagonalLenght = sceneDiagonal.length();
-        this.camera.upperRadiusLimit = sceneDiagonalLenght * 3;
+        if (isFinite(sceneDiagonalLenght))
+            this.camera.upperRadiusLimit = sceneDiagonalLenght * 3;
     }
 
     protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean } = {}, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
@@ -413,6 +428,15 @@ export abstract class AbstractViewer {
         if (!Object.keys(lightsConfiguration).length) return;
 
         let lightsAvailable: Array<string> = this.scene.lights.map(light => light.name);
+        // compare to the global (!) configuration object and dispose unneeded:
+        let lightsToConfigure = Object.keys(this.configuration.lights || []);
+        if (Object.keys(lightsToConfigure).length !== lightsAvailable.length) {
+            lightsAvailable.forEach(lName => {
+                if (lightsToConfigure.indexOf(lName) === -1) {
+                    this.scene.getLightByName(lName)!.dispose()
+                }
+            });
+        }
 
         Object.keys(lightsConfiguration).forEach((name, idx) => {
             let lightConfig: ILightConfiguration = { type: 0 };
@@ -422,7 +446,7 @@ export abstract class AbstractViewer {
 
             lightConfig.name = name;
 
-            let light;
+            let light: Light;
             // light is not already available
             if (lightsAvailable.indexOf(name) === -1) {
                 let constructor = Light.GetConstructorFromName(lightConfig.type, lightConfig.name, this.scene);
@@ -430,8 +454,14 @@ export abstract class AbstractViewer {
                 light = constructor();
             } else {
                 // available? get it from the scene
-                light = this.scene.getLightByName(name);
+                light = <Light>this.scene.getLightByName(name);
                 lightsAvailable = lightsAvailable.filter(ln => ln !== name);
+                if (lightConfig.type !== undefined && light.getTypeID() !== lightConfig.type) {
+                    light.dispose();
+                    let constructor = Light.GetConstructorFromName(lightConfig.type, lightConfig.name, this.scene);
+                    if (!constructor) return;
+                    light = constructor();
+                }
             }
 
             // if config set the light to false, dispose it.
@@ -441,18 +471,28 @@ export abstract class AbstractViewer {
             }
 
             //enabled
-            if (light.isEnabled() !== !lightConfig.disabled) {
-                light.setEnabled(!lightConfig.disabled);
-            }
+            var enabled = lightConfig.enabled !== undefined ? lightConfig.enabled : !lightConfig.disabled;
+            light.setEnabled(enabled);
+
 
             this.extendClassWithConfig(light, lightConfig);
 
             //position. Some lights don't support shadows
             if (light instanceof ShadowLight) {
+                if (lightConfig.target) {
+                    if (light.setDirectionToTarget) {
+                        let target = Vector3.Zero().copyFrom(lightConfig.target as Vector3);
+                        light.setDirectionToTarget(target);
+                    }
+                } else if (lightConfig.direction) {
+                    let direction = Vector3.Zero().copyFrom(lightConfig.direction as Vector3);
+                    light.direction = direction;
+                }
                 let shadowGenerator = light.getShadowGenerator();
                 if (lightConfig.shadowEnabled && this.maxShadows) {
                     if (!shadowGenerator) {
                         shadowGenerator = new ShadowGenerator(512, light);
+                        // TODO blur kernel definition
                     }
                     this.extendClassWithConfig(shadowGenerator, lightConfig.shadowConfig || {});
                     // add the focues meshes to the shadow list
@@ -461,7 +501,7 @@ export abstract class AbstractViewer {
                     let renderList = shadownMap.renderList;
                     for (var index = 0; index < focusMeshes.length; index++) {
                         if (Tags.MatchesQuery(focusMeshes[index], 'castShadow')) {
-                            // renderList && renderList.push(focusMeshes[index]);
+                            renderList && renderList.push(focusMeshes[index]);
                         }
                     }
                 } else if (shadowGenerator) {
@@ -473,7 +513,7 @@ export abstract class AbstractViewer {
         // remove the unneeded lights
         /*lightsAvailable.forEach(name => {
             let light = this.scene.getLightByName(name);
-            if (light) {
+            if (light && !Tags.MatchesQuery(light, "fixed")) {
                 light.dispose();
             }
         });*/
@@ -579,9 +619,10 @@ export abstract class AbstractViewer {
 
     public dispose() {
         window.removeEventListener('resize', this.resize);
-
-        this.sceneOptimizer.stop();
-        this.sceneOptimizer.dispose();
+        if (this.sceneOptimizer) {
+            this.sceneOptimizer.stop();
+            this.sceneOptimizer.dispose();
+        }
 
         if (this.scene.activeCamera) {
             this.scene.activeCamera.detachControl(this.canvas);
@@ -699,6 +740,10 @@ export abstract class AbstractViewer {
     }
 
     public loadModel(model: any = this.configuration.model, clearScene: boolean = true): Promise<Scene> {
+        // no model was provided? Do nothing!
+        if (!model.url) {
+            return Promise.resolve(this.scene);
+        }
         this.configuration.model = model;
         let modelUrl = (typeof model === 'string') ? model : model.url;
         let parts = modelUrl.split('/');
@@ -816,12 +861,14 @@ export abstract class AbstractViewer {
         if (!config) return;
         Object.keys(config).forEach(key => {
             if (key in object && typeof object[key] !== 'function') {
-                if (typeof object[key] === 'function') return;
+                // if (typeof object[key] === 'function') return;
                 // if it is an object, iterate internally until reaching basic types
                 if (typeof object[key] === 'object') {
                     this.extendClassWithConfig(object[key], config[key]);
                 } else {
-                    object[key] = config[key];
+                    if (config[key] !== undefined) {
+                        object[key] = config[key];
+                    }
                 }
             }
         });
@@ -840,13 +887,16 @@ export abstract class AbstractViewer {
         // constructing behavior
         switch (type) {
             case CameraBehavior.AUTOROTATION:
-                behavior = new AutoRotationBehavior();
+                this.camera.useAutoRotationBehavior = true;
+                behavior = this.camera.autoRotationBehavior;
                 break;
             case CameraBehavior.BOUNCING:
-                behavior = new BouncingBehavior();
+                this.camera.useBouncingBehavior = true;
+                behavior = this.camera.bouncingBehavior;
                 break;
             case CameraBehavior.FRAMING:
-                behavior = new FramingBehavior();
+                this.camera.useFramingBehavior = true;
+                behavior = this.camera.framingBehavior;
                 break;
             default:
                 behavior = null;
@@ -857,7 +907,7 @@ export abstract class AbstractViewer {
             if (typeof behaviorConfig === "object") {
                 this.extendClassWithConfig(behavior, behaviorConfig);
             }
-            this.camera.addBehavior(behavior);
+            //this.camera.addBehavior(behavior);
         }
 
         // post attach configuration. Some functionalities require the attached camera.

+ 21 - 0
dist/preview release/Oimo.license

@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright © 2010-2017 three.js authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

ファイルの差分が大きいため隠しています
+ 11114 - 10389
dist/preview release/babylon.d.ts


ファイルの差分が大きいため隠しています
+ 54 - 54
dist/preview release/babylon.js


ファイルの差分が大きいため隠しています
+ 2039 - 806
dist/preview release/babylon.max.js


ファイルの差分が大きいため隠しています
+ 55 - 55
dist/preview release/babylon.worker.js


+ 22 - 0
dist/preview release/cannon.license

@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 cannon.js Authors
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */

ファイルの差分が大きいため隠しています
+ 10990 - 10265
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


ファイルの差分が大きいため隠しています
+ 54 - 54
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


ファイルの差分が大きいため隠しています
+ 1981 - 877
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


ファイルの差分が大きいため隠しています
+ 1983 - 879
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js


+ 201 - 0
dist/preview release/draco.license

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

ファイルの差分が大きいため隠しています
+ 31 - 0
dist/preview release/draco_decoder.js


ファイルの差分が大きいため隠しています
+ 2041 - 808
dist/preview release/es6.js


+ 22 - 0
dist/preview release/gui/babylon.gui.d.ts

@@ -163,6 +163,7 @@ declare module BABYLON.GUI {
         protected _horizontalAlignment: number;
         protected _verticalAlignment: number;
         private _isDirty;
+        _tempParentMeasure: Measure;
         protected _cachedParentMeasure: Measure;
         private _paddingLeft;
         private _paddingRight;
@@ -247,6 +248,8 @@ declare module BABYLON.GUI {
         readonly heightInPixels: number;
         fontFamily: string;
         fontStyle: string;
+        /** @ignore */
+        readonly _isFontSizeInPercentage: boolean;
         readonly fontSizeInPixels: number;
         fontSize: string | number;
         color: string;
@@ -274,6 +277,8 @@ declare module BABYLON.GUI {
         readonly centerY: number;
         constructor(name?: string | undefined);
         protected _getTypeName(): string;
+        /** @ignore */
+        _resetFontCache(): void;
         getLocalCoordinates(globalCoordinates: Vector2): Vector2;
         getLocalCoordinatesToRef(globalCoordinates: Vector2, result: Vector2): Control;
         getParentLocalCoordinates(globalCoordinates: Vector2): Vector2;
@@ -543,6 +548,8 @@ declare module BABYLON.GUI {
         private _lines;
         private _resizeToFit;
         private _lineSpacing;
+        private _outlineWidth;
+        private _outlineColor;
         /**
         * An event triggered after the text is changed
         * @type {BABYLON.Observable}
@@ -600,6 +607,20 @@ declare module BABYLON.GUI {
          */
         lineSpacing: string | number;
         /**
+         * Gets or sets outlineWidth of the text to display
+         */
+        /**
+         * Gets or sets outlineWidth of the text to display
+         */
+        outlineWidth: number;
+        /**
+         * Gets or sets outlineColor of the text to display
+         */
+        /**
+         * Gets or sets outlineColor of the text to display
+         */
+        outlineColor: string;
+        /**
          * Creates a new TextBlock object
          * @param name defines the name of the control
          * @param text defines the text to display (emptry string by default)
@@ -613,6 +634,7 @@ declare module BABYLON.GUI {
         private _drawText(text, textWidth, y, context);
         /** @ignore */
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+        protected _applyStates(context: CanvasRenderingContext2D): void;
         protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         protected _parseLine(line: string | undefined, context: CanvasRenderingContext2D): object;
         protected _parseLineWithTextWrapping(line: string | undefined, context: CanvasRenderingContext2D): object;

+ 76 - 2
dist/preview release/gui/babylon.gui.js

@@ -203,6 +203,11 @@ var BABYLON;
             };
             AdvancedDynamicTexture.prototype.markAsDirty = function () {
                 this._isDirty = true;
+                this.executeOnAllControls(function (control) {
+                    if (control._isFontSizeInPercentage) {
+                        control._resetFontCache();
+                    }
+                });
             };
             AdvancedDynamicTexture.prototype.addControl = function (control) {
                 this._rootContainer.addControl(control);
@@ -826,6 +831,7 @@ var BABYLON;
                 this._horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
                 this._verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
                 this._isDirty = true;
+                this._tempParentMeasure = GUI.Measure.Empty();
                 this._cachedParentMeasure = GUI.Measure.Empty();
                 this._paddingLeft = new GUI.ValueAndUnit(0);
                 this._paddingRight = new GUI.ValueAndUnit(0);
@@ -1091,9 +1097,20 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
+            Object.defineProperty(Control.prototype, "_isFontSizeInPercentage", {
+                /** @ignore */
+                get: function () {
+                    return this._fontSize.isPercentage;
+                },
+                enumerable: true,
+                configurable: true
+            });
             Object.defineProperty(Control.prototype, "fontSizeInPixels", {
                 get: function () {
-                    return this._fontSize.getValueInPixel(this._host, 100);
+                    if (this._fontSize.isPixel) {
+                        return this._fontSize.getValue(this._host);
+                    }
+                    return this._fontSize.getValueInPixel(this._host, this._tempParentMeasure.height || this._cachedParentMeasure.height);
                 },
                 enumerable: true,
                 configurable: true
@@ -1348,6 +1365,10 @@ var BABYLON;
             Control.prototype._getTypeName = function () {
                 return "Control";
             };
+            /** @ignore */
+            Control.prototype._resetFontCache = function () {
+                this._fontSet = true;
+            };
             Control.prototype.getLocalCoordinates = function (globalCoordinates) {
                 var result = BABYLON.Vector2.Zero();
                 this.getLocalCoordinatesToRef(globalCoordinates, result);
@@ -1721,7 +1742,7 @@ var BABYLON;
                 if (!this._font && !this._fontSet) {
                     return;
                 }
-                this._font = this._fontStyle + " " + this._fontSize.getValue(this._host) + "px " + this._fontFamily;
+                this._font = this._fontStyle + " " + this.fontSizeInPixels + "px " + this._fontFamily;
                 this._fontOffset = Control._GetFontOffset(this._font);
             };
             Control.prototype.dispose = function () {
@@ -2009,6 +2030,7 @@ var BABYLON;
                     for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
                         var child = _a[_i];
                         if (child.isVisible && !child.notRenderable) {
+                            child._tempParentMeasure.copyFrom(this._measureForChildren);
                             child._draw(this._measureForChildren, context);
                             if (child.onAfterDrawObservable.hasObservers()) {
                                 child.onAfterDrawObservable.notifyObservers(child);
@@ -3241,6 +3263,8 @@ var BABYLON;
                 _this._textVerticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_CENTER;
                 _this._resizeToFit = false;
                 _this._lineSpacing = new GUI.ValueAndUnit(0);
+                _this._outlineWidth = 0;
+                _this._outlineColor = "white";
                 /**
                 * An event triggered after the text is changed
                 * @type {BABYLON.Observable}
@@ -3383,6 +3407,46 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
+            Object.defineProperty(TextBlock.prototype, "outlineWidth", {
+                /**
+                 * Gets or sets outlineWidth of the text to display
+                 */
+                get: function () {
+                    return this._outlineWidth;
+                },
+                /**
+                 * Gets or sets outlineWidth of the text to display
+                 */
+                set: function (value) {
+                    if (this._outlineWidth === value) {
+                        return;
+                    }
+                    this._outlineWidth = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(TextBlock.prototype, "outlineColor", {
+                /**
+                 * Gets or sets outlineColor of the text to display
+                 */
+                get: function () {
+                    return this._outlineColor;
+                },
+                /**
+                 * Gets or sets outlineColor of the text to display
+                 */
+                set: function (value) {
+                    if (this._outlineColor === value) {
+                        return;
+                    }
+                    this._outlineColor = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
             TextBlock.prototype._getTypeName = function () {
                 return "TextBlock";
             };
@@ -3406,6 +3470,9 @@ var BABYLON;
                     context.shadowOffsetX = this.shadowOffsetX;
                     context.shadowOffsetY = this.shadowOffsetY;
                 }
+                if (this.outlineWidth) {
+                    context.strokeText(text, this._currentMeasure.left + x, y);
+                }
                 context.fillText(text, this._currentMeasure.left + x, y);
             };
             /** @ignore */
@@ -3418,6 +3485,13 @@ var BABYLON;
                 }
                 context.restore();
             };
+            TextBlock.prototype._applyStates = function (context) {
+                _super.prototype._applyStates.call(this, context);
+                if (this.outlineWidth) {
+                    context.lineWidth = this.outlineWidth;
+                    context.strokeStyle = this.outlineColor;
+                }
+            };
             TextBlock.prototype._additionalProcessing = function (parentMeasure, context) {
                 this._lines = [];
                 var _lines = this.text.split("\n");

ファイルの差分が大きいため隠しています
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


+ 22 - 0
dist/preview release/gui/babylon.gui.module.d.ts

@@ -169,6 +169,7 @@ declare module BABYLON.GUI {
         protected _horizontalAlignment: number;
         protected _verticalAlignment: number;
         private _isDirty;
+        _tempParentMeasure: Measure;
         protected _cachedParentMeasure: Measure;
         private _paddingLeft;
         private _paddingRight;
@@ -253,6 +254,8 @@ declare module BABYLON.GUI {
         readonly heightInPixels: number;
         fontFamily: string;
         fontStyle: string;
+        /** @ignore */
+        readonly _isFontSizeInPercentage: boolean;
         readonly fontSizeInPixels: number;
         fontSize: string | number;
         color: string;
@@ -280,6 +283,8 @@ declare module BABYLON.GUI {
         readonly centerY: number;
         constructor(name?: string | undefined);
         protected _getTypeName(): string;
+        /** @ignore */
+        _resetFontCache(): void;
         getLocalCoordinates(globalCoordinates: Vector2): Vector2;
         getLocalCoordinatesToRef(globalCoordinates: Vector2, result: Vector2): Control;
         getParentLocalCoordinates(globalCoordinates: Vector2): Vector2;
@@ -549,6 +554,8 @@ declare module BABYLON.GUI {
         private _lines;
         private _resizeToFit;
         private _lineSpacing;
+        private _outlineWidth;
+        private _outlineColor;
         /**
         * An event triggered after the text is changed
         * @type {BABYLON.Observable}
@@ -606,6 +613,20 @@ declare module BABYLON.GUI {
          */
         lineSpacing: string | number;
         /**
+         * Gets or sets outlineWidth of the text to display
+         */
+        /**
+         * Gets or sets outlineWidth of the text to display
+         */
+        outlineWidth: number;
+        /**
+         * Gets or sets outlineColor of the text to display
+         */
+        /**
+         * Gets or sets outlineColor of the text to display
+         */
+        outlineColor: string;
+        /**
          * Creates a new TextBlock object
          * @param name defines the name of the control
          * @param text defines the text to display (emptry string by default)
@@ -619,6 +640,7 @@ declare module BABYLON.GUI {
         private _drawText(text, textWidth, y, context);
         /** @ignore */
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+        protected _applyStates(context: CanvasRenderingContext2D): void;
         protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         protected _parseLine(line: string | undefined, context: CanvasRenderingContext2D): object;
         protected _parseLineWithTextWrapping(line: string | undefined, context: CanvasRenderingContext2D): object;

ファイルの差分が大きいため隠しています
+ 4 - 4
dist/preview release/inspector/babylon.inspector.bundle.js


+ 5 - 2
dist/preview release/inspector/babylon.inspector.css

@@ -283,6 +283,8 @@
           background-color: #242424; }
         .insp-wrapper .insp-tree .line .line-content .line:hover:first-child {
           background-color: #383838; }
+    .insp-wrapper .insp-tree .line_invisible {
+      display: none; }
   .insp-wrapper .insp-details {
     background-color: #242424;
     overflow-y: auto;
@@ -303,7 +305,7 @@
         width: 35%; }
       .insp-wrapper .insp-details .base-row .prop-value, .insp-wrapper .insp-details .row .prop-value, .insp-wrapper .insp-details .header-row .prop-value {
         width: 59%;
-        padding-left: 10px; }
+        padding-left: 5px; }
         .insp-wrapper .insp-details .base-row .prop-value.clickable, .insp-wrapper .insp-details .row .prop-value.clickable, .insp-wrapper .insp-details .header-row .prop-value.clickable {
           cursor: pointer; }
           .insp-wrapper .insp-details .base-row .prop-value.clickable:hover, .insp-wrapper .insp-details .row .prop-value.clickable:hover, .insp-wrapper .insp-details .header-row .prop-value.clickable:hover {
@@ -340,7 +342,8 @@
       display: inline-block;
       margin-left: 5px; }
     .insp-wrapper .insp-details .color-element {
-      top: 2px; }
+      width: 20px;
+      height: 15px; }
     .insp-wrapper .insp-details .texture-element {
       color: #f29766;
       margin-left: 10px; }

+ 13 - 2
dist/preview release/inspector/babylon.inspector.js

@@ -383,7 +383,7 @@ var INSPECTOR;
         },
         'Color3': {
             type: BABYLON.Color3,
-            format: function (color) { return "R:" + color.r + ", G:" + color.g + ", B:" + color.b; },
+            format: function (color) { return "R:" + color.r.toPrecision(2) + ", G:" + color.g.toPrecision(2) + ", B:" + color.b.toPrecision(2); },
             slider: {
                 r: { min: 0, max: 1, step: 0.01 },
                 g: { min: 0, max: 1, step: 0.01 },
@@ -4568,7 +4568,18 @@ var INSPECTOR;
         };
         /** Build the HTML of this item */
         TreeItem.prototype._build = function () {
-            this._div.className = 'line';
+            /**
+             *  Hide the debug objects :
+             * - Axis : xline, yline, zline
+             * */
+            var adapterId = this._adapter.id();
+            if (adapterId == "xline"
+                || adapterId == "yline"
+                || adapterId == "zline") {
+                this._div.className = "line_invisible";
+            }
+            else
+                this._div.className = 'line';
             // special class for transform node ONLY
             if (this.adapter instanceof INSPECTOR.MeshAdapter) {
                 var obj = this.adapter.object;

ファイルの差分が大きいため隠しています
+ 4 - 4
dist/preview release/inspector/babylon.inspector.min.js


+ 20 - 13
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -28,30 +28,34 @@ declare module BABYLON {
         json: Object;
         bin: Nullable<ArrayBufferView>;
     }
+    interface IGLTFLoaderExtension {
+        /**
+         * The name of this extension.
+         */
+        readonly name: string;
+        /**
+         * Whether this extension is enabled.
+         */
+        enabled: boolean;
+    }
     enum GLTFLoaderState {
         Loading = 0,
         Ready = 1,
         Complete = 2,
     }
-    interface IGLTFLoaderExtension {
-        enabled: boolean;
-    }
-    interface IGLTFLoaderExtensions {
-        [name: string]: IGLTFLoaderExtension;
-    }
     interface IGLTFLoader extends IDisposable {
         coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
         animationStartMode: GLTFLoaderAnimationStartMode;
         compileMaterials: boolean;
         useClipPlane: boolean;
         compileShadowGenerators: boolean;
-        onDisposeObservable: Observable<IGLTFLoader>;
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
         onCompleteObservable: Observable<IGLTFLoader>;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
-        extensions: Nullable<IGLTFLoaderExtensions>;
         importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{
             meshes: AbstractMesh[];
             particleSystems: ParticleSystem[];
@@ -125,13 +129,16 @@ declare module BABYLON {
         private _onDisposeObserver;
         onDispose: () => void;
         /**
-         * The loader state or null if not active.
+         * Raised after a loader extension is created.
+         * Set additional options for a loader extension in this event.
          */
-        readonly loaderState: Nullable<GLTFLoaderState>;
+        readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
+        private _onExtensionLoadedObserver;
+        onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
-         * The loader extensions or null if not active.
+         * The loader state or null if not active.
          */
-        readonly loaderExtensions: Nullable<IGLTFLoaderExtensions>;
+        readonly loaderState: Nullable<GLTFLoaderState>;
         private _loader;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
@@ -563,8 +570,8 @@ declare module BABYLON.GLTF1 {
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
         onCompleteObservable: Observable<IGLTFLoader>;
+        onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
-        extensions: Nullable<IGLTFLoaderExtensions>;
         dispose(): void;
         private _importMeshAsync(meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError);
         importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress: (event: SceneLoaderProgressEvent) => void): Promise<{

+ 24 - 13
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -86,6 +86,11 @@ var BABYLON;
             * Raised when the loader is disposed.
             */
             this.onDisposeObservable = new BABYLON.Observable();
+            /**
+             * Raised after a loader extension is created.
+             * Set additional options for a loader extension in this event.
+             */
+            this.onExtensionLoadedObservable = new BABYLON.Observable();
             // #endregion
             this._loader = null;
             this.name = "gltf";
@@ -154,22 +159,22 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
-            /**
-             * The loader state or null if not active.
-             */
-            get: function () {
-                return this._loader ? this._loader.state : null;
+        Object.defineProperty(GLTFFileLoader.prototype, "onExtensionLoaded", {
+            set: function (callback) {
+                if (this._onExtensionLoadedObserver) {
+                    this.onExtensionLoadedObservable.remove(this._onExtensionLoadedObserver);
+                }
+                this._onExtensionLoadedObserver = this.onExtensionLoadedObservable.add(callback);
             },
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(GLTFFileLoader.prototype, "loaderExtensions", {
+        Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
-             * The loader extensions or null if not active.
+             * The loader state or null if not active.
              */
             get: function () {
-                return this._loader ? this._loader.extensions : null;
+                return this._loader ? this._loader.state : null;
             },
             enumerable: true,
             configurable: true
@@ -182,11 +187,9 @@ var BABYLON;
                 this._loader.dispose();
                 this._loader = null;
             }
-            this.onParsedObservable.clear();
             this.onMeshLoadedObservable.clear();
             this.onTextureLoadedObservable.clear();
             this.onMaterialLoadedObservable.clear();
-            this.onCompleteObservable.clear();
             this.onDisposeObservable.notifyObservers(this);
             this.onDisposeObservable.clear();
         };
@@ -239,6 +242,7 @@ var BABYLON;
                 };
             }
             this.onParsedObservable.notifyObservers(parsedData);
+            this.onParsedObservable.clear();
             return parsedData;
         };
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
@@ -275,7 +279,14 @@ var BABYLON;
             loader.onMeshLoadedObservable.add(function (mesh) { return _this.onMeshLoadedObservable.notifyObservers(mesh); });
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
             loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
-            loader.onCompleteObservable.add(function () { return _this.onCompleteObservable.notifyObservers(_this); });
+            loader.onExtensionLoadedObservable.add(function (extension) { return _this.onExtensionLoadedObservable.notifyObservers(extension); });
+            loader.onCompleteObservable.add(function () {
+                _this.onMeshLoadedObservable.clear();
+                _this.onTextureLoadedObservable.clear();
+                _this.onMaterialLoadedObservable.clear();
+                _this.onCompleteObservable.notifyObservers(_this);
+                _this.onCompleteObservable.clear();
+            });
             return loader;
         };
         GLTFFileLoader._parseBinary = function (data) {
@@ -1827,8 +1838,8 @@ var BABYLON;
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
+                this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;
-                this.extensions = null;
             }
             GLTFLoader.RegisterExtension = function (extension) {
                 if (GLTFLoader.Extensions[extension.name]) {

ファイルの差分が大きいため隠しています
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 35 - 25
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -28,30 +28,34 @@ declare module BABYLON {
         json: Object;
         bin: Nullable<ArrayBufferView>;
     }
+    interface IGLTFLoaderExtension {
+        /**
+         * The name of this extension.
+         */
+        readonly name: string;
+        /**
+         * Whether this extension is enabled.
+         */
+        enabled: boolean;
+    }
     enum GLTFLoaderState {
         Loading = 0,
         Ready = 1,
         Complete = 2,
     }
-    interface IGLTFLoaderExtension {
-        enabled: boolean;
-    }
-    interface IGLTFLoaderExtensions {
-        [name: string]: IGLTFLoaderExtension;
-    }
     interface IGLTFLoader extends IDisposable {
         coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
         animationStartMode: GLTFLoaderAnimationStartMode;
         compileMaterials: boolean;
         useClipPlane: boolean;
         compileShadowGenerators: boolean;
-        onDisposeObservable: Observable<IGLTFLoader>;
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
         onCompleteObservable: Observable<IGLTFLoader>;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
-        extensions: Nullable<IGLTFLoaderExtensions>;
         importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{
             meshes: AbstractMesh[];
             particleSystems: ParticleSystem[];
@@ -125,13 +129,16 @@ declare module BABYLON {
         private _onDisposeObserver;
         onDispose: () => void;
         /**
-         * The loader state or null if not active.
+         * Raised after a loader extension is created.
+         * Set additional options for a loader extension in this event.
          */
-        readonly loaderState: Nullable<GLTFLoaderState>;
+        readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
+        private _onExtensionLoadedObserver;
+        onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
-         * The loader extensions or null if not active.
+         * The loader state or null if not active.
          */
-        readonly loaderExtensions: Nullable<IGLTFLoaderExtensions>;
+        readonly loaderState: Nullable<GLTFLoaderState>;
         private _loader;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
@@ -283,10 +290,9 @@ declare module BABYLON.GLTF2 {
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         readonly onMaterialLoadedObservable: Observable<Material>;
+        readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         readonly onCompleteObservable: Observable<IGLTFLoader>;
         readonly state: Nullable<GLTFLoaderState>;
-        readonly extensions: IGLTFLoaderExtensions;
-        constructor();
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
             meshes: AbstractMesh[];
@@ -295,6 +301,7 @@ declare module BABYLON.GLTF2 {
         }>;
         loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
         private _loadAsync(nodes, scene, data, rootUrl, onProgress?);
+        private _loadExtensions();
         private _loadData(data);
         private _setupData();
         private _createRootNode();
@@ -345,19 +352,18 @@ declare module BABYLON.GLTF2 {
         private static _ValidateUri(uri);
         private _compileMaterialsAsync();
         private _compileShadowGeneratorsAsync();
-        private _abortRequests();
-        private _releaseResources();
+        private _clear();
         _applyExtensions<T>(actionAsync: (extension: GLTFLoaderExtension) => Nullable<Promise<T>>): Nullable<Promise<T>>;
     }
 }
 
 
 declare module BABYLON.GLTF2 {
-    abstract class GLTFLoaderExtension {
+    abstract class GLTFLoaderExtension implements IGLTFLoaderExtension {
         enabled: boolean;
+        readonly abstract name: string;
         protected _loader: GLTFLoader;
         constructor(loader: GLTFLoader);
-        protected readonly abstract _name: string;
         /** Override this method to modify the default behavior for loading scenes. */
         protected _loadSceneAsync(context: string, node: ILoaderScene): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading nodes. */
@@ -381,26 +387,30 @@ declare module BABYLON.GLTF2 {
 
 
 declare module BABYLON.GLTF2.Extensions {
-    class MSFTLOD extends GLTFLoaderExtension {
+    class MSFT_lod extends GLTFLoaderExtension {
+        readonly name: string;
+        /**
+         * Maximum number of LODs to load, starting from the lowest LOD.
+         */
+        maxLODsToLoad: number;
         private _loadingNodeLOD;
         private _loadNodeSignals;
         private _loadingMaterialLOD;
         private _loadMaterialSignals;
-        protected readonly _name: string;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
         protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /**
          * Gets an array of LOD properties from lowest to highest.
          */
-        private static _GetLODs<T>(context, property, array, ids);
+        private _getLODs<T>(context, property, array, ids);
     }
 }
 
 
 declare module BABYLON.GLTF2.Extensions {
-    class KHRMaterialsPbrSpecularGlossiness extends GLTFLoaderExtension {
-        protected readonly _name: string;
+    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);
     }
@@ -408,8 +418,8 @@ declare module BABYLON.GLTF2.Extensions {
 
 
 declare module BABYLON.GLTF2.Extensions {
-    class KHRLights extends GLTFLoaderExtension {
-        protected readonly _name: string;
+    class KHR_lights extends GLTFLoaderExtension {
+        readonly name: string;
         protected _loadSceneAsync(context: string, scene: ILoaderScene): Nullable<Promise<void>>;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
         private readonly _lights;

+ 157 - 121
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -86,6 +86,11 @@ var BABYLON;
             * Raised when the loader is disposed.
             */
             this.onDisposeObservable = new BABYLON.Observable();
+            /**
+             * Raised after a loader extension is created.
+             * Set additional options for a loader extension in this event.
+             */
+            this.onExtensionLoadedObservable = new BABYLON.Observable();
             // #endregion
             this._loader = null;
             this.name = "gltf";
@@ -154,22 +159,22 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
-            /**
-             * The loader state or null if not active.
-             */
-            get: function () {
-                return this._loader ? this._loader.state : null;
+        Object.defineProperty(GLTFFileLoader.prototype, "onExtensionLoaded", {
+            set: function (callback) {
+                if (this._onExtensionLoadedObserver) {
+                    this.onExtensionLoadedObservable.remove(this._onExtensionLoadedObserver);
+                }
+                this._onExtensionLoadedObserver = this.onExtensionLoadedObservable.add(callback);
             },
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(GLTFFileLoader.prototype, "loaderExtensions", {
+        Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
-             * The loader extensions or null if not active.
+             * The loader state or null if not active.
              */
             get: function () {
-                return this._loader ? this._loader.extensions : null;
+                return this._loader ? this._loader.state : null;
             },
             enumerable: true,
             configurable: true
@@ -182,11 +187,9 @@ var BABYLON;
                 this._loader.dispose();
                 this._loader = null;
             }
-            this.onParsedObservable.clear();
             this.onMeshLoadedObservable.clear();
             this.onTextureLoadedObservable.clear();
             this.onMaterialLoadedObservable.clear();
-            this.onCompleteObservable.clear();
             this.onDisposeObservable.notifyObservers(this);
             this.onDisposeObservable.clear();
         };
@@ -239,6 +242,7 @@ var BABYLON;
                 };
             }
             this.onParsedObservable.notifyObservers(parsedData);
+            this.onParsedObservable.clear();
             return parsedData;
         };
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
@@ -275,7 +279,14 @@ var BABYLON;
             loader.onMeshLoadedObservable.add(function (mesh) { return _this.onMeshLoadedObservable.notifyObservers(mesh); });
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
             loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
-            loader.onCompleteObservable.add(function () { return _this.onCompleteObservable.notifyObservers(_this); });
+            loader.onExtensionLoadedObservable.add(function (extension) { return _this.onExtensionLoadedObservable.notifyObservers(extension); });
+            loader.onCompleteObservable.add(function () {
+                _this.onMeshLoadedObservable.clear();
+                _this.onTextureLoadedObservable.clear();
+                _this.onMaterialLoadedObservable.clear();
+                _this.onCompleteObservable.notifyObservers(_this);
+                _this.onCompleteObservable.clear();
+            });
             return loader;
         };
         GLTFFileLoader._parseBinary = function (data) {
@@ -488,12 +499,8 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
-                for (var _i = 0, _a = GLTFLoader._Names; _i < _a.length; _i++) {
-                    var name_1 = _a[_i];
-                    var extension = GLTFLoader._Factories[name_1](this);
-                    this._extensions[name_1] = extension;
-                }
             }
             GLTFLoader._Register = function (name, factory) {
                 if (GLTFLoader._Factories[name]) {
@@ -511,21 +518,14 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(GLTFLoader.prototype, "extensions", {
-                get: function () {
-                    return this.extensions;
-                },
-                enumerable: true,
-                configurable: true
-            });
             GLTFLoader.prototype.dispose = function () {
                 if (this._disposed) {
                     return;
                 }
                 this._disposed = true;
-                this._abortRequests();
-                this._releaseResources();
                 this.onDisposeObservable.notifyObservers(this);
+                this.onDisposeObservable.clear();
+                this._clear();
             };
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
                 var _this = this;
@@ -565,6 +565,7 @@ var BABYLON;
             GLTFLoader.prototype._loadAsync = function (nodes, scene, data, rootUrl, onProgress) {
                 var _this = this;
                 return Promise.resolve().then(function () {
+                    _this._loadExtensions();
                     _this._babylonScene = scene;
                     _this._rootUrl = rootUrl;
                     _this._progressCallback = onProgress;
@@ -588,22 +589,34 @@ var BABYLON;
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
                         BABYLON.Tools.SetImmediate(function () {
-                            Promise.all(_this._completePromises).then(function () {
-                                _this._releaseResources();
-                                _this._state = BABYLON.GLTFLoaderState.Complete;
-                                _this.onCompleteObservable.notifyObservers(_this);
-                            }).catch(function (error) {
-                                BABYLON.Tools.Error("glTF Loader: " + error.message);
-                                _this.dispose();
-                            });
+                            if (!_this._disposed) {
+                                Promise.all(_this._completePromises).then(function () {
+                                    _this._state = BABYLON.GLTFLoaderState.Complete;
+                                    _this.onCompleteObservable.notifyObservers(_this);
+                                    _this.onCompleteObservable.clear();
+                                    _this._clear();
+                                }).catch(function (error) {
+                                    BABYLON.Tools.Error("glTF Loader: " + error.message);
+                                    _this._clear();
+                                });
+                            }
                         });
-                    }).catch(function (error) {
-                        BABYLON.Tools.Error("glTF Loader: " + error.message);
-                        _this.dispose();
-                        throw error;
                     });
+                }).catch(function (error) {
+                    BABYLON.Tools.Error("glTF Loader: " + error.message);
+                    _this._clear();
+                    throw error;
                 });
             };
+            GLTFLoader.prototype._loadExtensions = function () {
+                for (var _i = 0, _a = GLTFLoader._Names; _i < _a.length; _i++) {
+                    var name_1 = _a[_i];
+                    var extension = GLTFLoader._Factories[name_1](this);
+                    this._extensions[name_1] = extension;
+                    this.onExtensionLoadedObservable.notifyObservers(extension);
+                }
+                this.onExtensionLoadedObservable.clear();
+            };
             GLTFLoader.prototype._loadData = function (data) {
                 this._gltf = data.json;
                 this._setupData();
@@ -711,6 +724,12 @@ var BABYLON;
                         if (node._babylonMesh) {
                             meshes.push(node._babylonMesh);
                         }
+                        if (node._primitiveBabylonMeshes) {
+                            for (var _a = 0, _b = node._primitiveBabylonMeshes; _a < _b.length; _a++) {
+                                var babylonMesh = _b[_a];
+                                meshes.push(babylonMesh);
+                            }
+                        }
                     }
                 }
                 return meshes;
@@ -828,6 +847,7 @@ var BABYLON;
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
                     promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
                 }
+                this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 return Promise.all(promises).then(function () {
                     babylonMesh.setEnabled(true);
                 });
@@ -1542,6 +1562,7 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._loadTextureAsync = function (context, textureInfo, assign) {
+                var _this = this;
                 var texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
                 context = "#/textures/" + textureInfo.index;
                 var promises = new Array();
@@ -1549,9 +1570,13 @@ var BABYLON;
                 var samplerData = this._loadSampler("#/samplers/" + sampler._index, sampler);
                 var deferred = new BABYLON.Deferred();
                 var babylonTexture = new BABYLON.Texture(null, this._babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, function () {
-                    deferred.resolve();
+                    if (!_this._disposed) {
+                        deferred.resolve();
+                    }
                 }, function (message, exception) {
-                    deferred.reject(new Error(context + ": " + (exception && exception.message) ? exception.message : message || "Failed to load texture"));
+                    if (!_this._disposed) {
+                        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;
@@ -1609,21 +1634,27 @@ var BABYLON;
                 }
                 return new Promise(function (resolve, reject) {
                     var request = BABYLON.Tools.LoadFile(_this._rootUrl + uri, function (data) {
-                        resolve(new Uint8Array(data));
+                        if (!_this._disposed) {
+                            resolve(new Uint8Array(data));
+                        }
                     }, function (event) {
-                        try {
-                            if (request && _this._state === BABYLON.GLTFLoaderState.Loading) {
-                                request._lengthComputable = event.lengthComputable;
-                                request._loaded = event.loaded;
-                                request._total = event.total;
-                                _this._onProgress();
+                        if (!_this._disposed) {
+                            try {
+                                if (request && _this._state === BABYLON.GLTFLoaderState.Loading) {
+                                    request._lengthComputable = event.lengthComputable;
+                                    request._loaded = event.loaded;
+                                    request._total = event.total;
+                                    _this._onProgress();
+                                }
+                            }
+                            catch (e) {
+                                reject(e);
                             }
-                        }
-                        catch (e) {
-                            reject(e);
                         }
                     }, _this._babylonScene.database, true, function (request, exception) {
-                        reject(new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request));
+                        if (!_this._disposed) {
+                            reject(new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request));
+                        }
                     });
                     _this._requests.push(request);
                 });
@@ -1745,17 +1776,15 @@ var BABYLON;
                 }
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._abortRequests = function () {
+            GLTFLoader.prototype._clear = function () {
                 for (var _i = 0, _a = this._requests; _i < _a.length; _i++) {
                     var request = _a[_i];
                     request.abort();
                 }
                 this._requests.length = 0;
-            };
-            GLTFLoader.prototype._releaseResources = function () {
-                if (this._gltf.images) {
-                    for (var _i = 0, _a = this._gltf.images; _i < _a.length; _i++) {
-                        var image = _a[_i];
+                if (this._gltf && this._gltf.images) {
+                    for (var _b = 0, _c = this._gltf.images; _b < _c.length; _b++) {
+                        var image = _c[_b];
                         if (image._objectURL) {
                             image._objectURL.then(function (value) {
                                 URL.revokeObjectURL(value);
@@ -1764,6 +1793,15 @@ var BABYLON;
                         }
                     }
                 }
+                delete this._gltf;
+                delete this._babylonScene;
+                this._completePromises.length = 0;
+                this._extensions = {};
+                delete this._rootBabylonMesh;
+                delete this._progressCallback;
+                this.onMeshLoadedObservable.clear();
+                this.onTextureLoadedObservable.clear();
+                this.onMaterialLoadedObservable.clear();
             };
             GLTFLoader.prototype._applyExtensions = function (actionAsync) {
                 for (var _i = 0, _a = GLTFLoader._Names; _i < _a.length; _i++) {
@@ -1816,15 +1854,15 @@ var BABYLON;
                     return null;
                 }
                 var extensions = property.extensions;
-                var extension = extensions[this._name];
+                var extension = extensions[this.name];
                 if (!extension) {
                     return null;
                 }
                 // Clear out the extension before executing the action to avoid recursing into the same property.
-                delete extensions[this._name];
-                return actionAsync(context + "extensions/" + this._name, extension).then(function () {
+                delete extensions[this.name];
+                return actionAsync(context + "/extensions/" + this.name, extension).then(function () {
                     // Restore the extension after completing the action.
-                    extensions[_this._name] = extension;
+                    extensions[_this.name] = extension;
                 });
             };
             /** Helper method called by the loader to allow extensions to override loading scenes. */
@@ -1870,28 +1908,26 @@ var BABYLON;
         (function (Extensions) {
             // https://github.com/sbtron/glTF/tree/MSFT_lod/extensions/Vendor/MSFT_lod
             var NAME = "MSFT_lod";
-            var MSFTLOD = /** @class */ (function (_super) {
-                __extends(MSFTLOD, _super);
-                function MSFTLOD() {
+            var MSFT_lod = /** @class */ (function (_super) {
+                __extends(MSFT_lod, _super);
+                function MSFT_lod() {
                     var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    /**
+                     * Maximum number of LODs to load, starting from the lowest LOD.
+                     */
+                    _this.maxLODsToLoad = Number.MAX_VALUE;
                     _this._loadingNodeLOD = null;
                     _this._loadNodeSignals = {};
                     _this._loadingMaterialLOD = null;
                     _this._loadMaterialSignals = {};
                     return _this;
                 }
-                Object.defineProperty(MSFTLOD.prototype, "_name", {
-                    get: function () {
-                        return NAME;
-                    },
-                    enumerable: true,
-                    configurable: true
-                });
-                MSFTLOD.prototype._loadNodeAsync = function (context, node) {
+                MSFT_lod.prototype._loadNodeAsync = function (context, node) {
                     var _this = this;
                     return this._loadExtensionAsync(context, node, function (context, extension) {
                         var firstPromise;
-                        var nodeLODs = MSFTLOD._GetLODs(context, node, _this._loader._gltf.nodes, extension.ids);
+                        var nodeLODs = _this._getLODs(context, node, _this._loader._gltf.nodes, extension.ids);
                         var _loop_1 = function (indexLOD) {
                             var nodeLOD = nodeLODs[indexLOD];
                             if (indexLOD !== 0) {
@@ -1923,11 +1959,15 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFTLOD.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
                     var _this = this;
+                    // Don't load material LODs if already loading a node LOD.
+                    if (this._loadingNodeLOD) {
+                        return null;
+                    }
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         var firstPromise;
-                        var materialLODs = MSFTLOD._GetLODs(context, material, _this._loader._gltf.materials, extension.ids);
+                        var materialLODs = _this._getLODs(context, material, _this._loader._gltf.materials, extension.ids);
                         var _loop_2 = function (indexLOD) {
                             var materialLOD = materialLODs[indexLOD];
                             if (indexLOD !== 0) {
@@ -1955,7 +1995,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFTLOD.prototype._loadUriAsync = function (context, uri) {
+                MSFT_lod.prototype._loadUriAsync = function (context, uri) {
                     var _this = this;
                     // Defer the loading of uris if loading a material or node LOD.
                     if (this._loadingMaterialLOD) {
@@ -1975,18 +2015,24 @@ var BABYLON;
                 /**
                  * Gets an array of LOD properties from lowest to highest.
                  */
-                MSFTLOD._GetLODs = function (context, property, array, ids) {
-                    var properties = [property];
-                    for (var _i = 0, ids_1 = ids; _i < ids_1.length; _i++) {
-                        var id = ids_1[_i];
-                        properties.push(GLTF2.GLTFLoader._GetProperty(context + "/ids/" + id, array, id));
+                MSFT_lod.prototype._getLODs = function (context, property, array, ids) {
+                    if (this.maxLODsToLoad <= 0) {
+                        throw new Error("maxLODsToLoad must be greater than zero");
+                    }
+                    var properties = new Array();
+                    for (var i = ids.length - 1; i >= 0; i--) {
+                        properties.push(GLTF2.GLTFLoader._GetProperty(context + "/ids/" + ids[i], array, ids[i]));
+                        if (properties.length === this.maxLODsToLoad) {
+                            return properties;
+                        }
                     }
-                    return properties.reverse();
+                    properties.push(property);
+                    return properties;
                 };
-                return MSFTLOD;
+                return MSFT_lod;
             }(GLTF2.GLTFLoaderExtension));
-            Extensions.MSFTLOD = MSFTLOD;
-            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new MSFTLOD(loader); });
+            Extensions.MSFT_lod = MSFT_lod;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new MSFT_lod(loader); });
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
@@ -2012,19 +2058,14 @@ var BABYLON;
         (function (Extensions) {
             // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
             var NAME = "KHR_materials_pbrSpecularGlossiness";
-            var KHRMaterialsPbrSpecularGlossiness = /** @class */ (function (_super) {
-                __extends(KHRMaterialsPbrSpecularGlossiness, _super);
-                function KHRMaterialsPbrSpecularGlossiness() {
-                    return _super !== null && _super.apply(this, arguments) || this;
+            var KHR_materials_pbrSpecularGlossiness = /** @class */ (function (_super) {
+                __extends(KHR_materials_pbrSpecularGlossiness, _super);
+                function KHR_materials_pbrSpecularGlossiness() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
                 }
-                Object.defineProperty(KHRMaterialsPbrSpecularGlossiness.prototype, "_name", {
-                    get: function () {
-                        return NAME;
-                    },
-                    enumerable: true,
-                    configurable: true
-                });
-                KHRMaterialsPbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
@@ -2043,7 +2084,7 @@ var BABYLON;
                         return (material._loaded = Promise.all(promises).then(function () { }));
                     });
                 };
-                KHRMaterialsPbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -2070,10 +2111,10 @@ var BABYLON;
                     loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
-                return KHRMaterialsPbrSpecularGlossiness;
+                return KHR_materials_pbrSpecularGlossiness;
             }(GLTF2.GLTFLoaderExtension));
-            Extensions.KHRMaterialsPbrSpecularGlossiness = KHRMaterialsPbrSpecularGlossiness;
-            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHRMaterialsPbrSpecularGlossiness(loader); });
+            Extensions.KHR_materials_pbrSpecularGlossiness = KHR_materials_pbrSpecularGlossiness;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHR_materials_pbrSpecularGlossiness(loader); });
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
@@ -2106,19 +2147,14 @@ var BABYLON;
                 LightType["POINT"] = "point";
                 LightType["SPOT"] = "spot";
             })(LightType || (LightType = {}));
-            var KHRLights = /** @class */ (function (_super) {
-                __extends(KHRLights, _super);
-                function KHRLights() {
-                    return _super !== null && _super.apply(this, arguments) || this;
+            var KHR_lights = /** @class */ (function (_super) {
+                __extends(KHR_lights, _super);
+                function KHR_lights() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
                 }
-                Object.defineProperty(KHRLights.prototype, "_name", {
-                    get: function () {
-                        return NAME;
-                    },
-                    enumerable: true,
-                    configurable: true
-                });
-                KHRLights.prototype._loadSceneAsync = function (context, scene) {
+                KHR_lights.prototype._loadSceneAsync = function (context, scene) {
                     var _this = this;
                     return this._loadExtensionAsync(context, scene, function (context, extension) {
                         var promise = _this._loader._loadSceneAsync(context, scene);
@@ -2130,7 +2166,7 @@ var BABYLON;
                         return promise;
                     });
                 };
-                KHRLights.prototype._loadNodeAsync = function (context, node) {
+                KHR_lights.prototype._loadNodeAsync = function (context, node) {
                     var _this = this;
                     return this._loadExtensionAsync(context, node, function (context, extension) {
                         var promise = _this._loader._loadNodeAsync(context, node);
@@ -2167,22 +2203,22 @@ var BABYLON;
                         return promise;
                     });
                 };
-                Object.defineProperty(KHRLights.prototype, "_lights", {
+                Object.defineProperty(KHR_lights.prototype, "_lights", {
                     get: function () {
                         var extensions = this._loader._gltf.extensions;
-                        if (!extensions || !extensions[this._name]) {
-                            throw new Error("#/extensions: " + this._name + " not found");
+                        if (!extensions || !extensions[this.name]) {
+                            throw new Error("#/extensions: " + this.name + " not found");
                         }
-                        var extension = extensions[this._name];
+                        var extension = extensions[this.name];
                         return extension.lights;
                     },
                     enumerable: true,
                     configurable: true
                 });
-                return KHRLights;
+                return KHR_lights;
             }(GLTF2.GLTFLoaderExtension));
-            Extensions.KHRLights = KHRLights;
-            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHRLights(loader); });
+            Extensions.KHR_lights = KHR_lights;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHR_lights(loader); });
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));

ファイルの差分が大きいため隠しています
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 36 - 26
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -28,30 +28,34 @@ declare module BABYLON {
         json: Object;
         bin: Nullable<ArrayBufferView>;
     }
+    interface IGLTFLoaderExtension {
+        /**
+         * The name of this extension.
+         */
+        readonly name: string;
+        /**
+         * Whether this extension is enabled.
+         */
+        enabled: boolean;
+    }
     enum GLTFLoaderState {
         Loading = 0,
         Ready = 1,
         Complete = 2,
     }
-    interface IGLTFLoaderExtension {
-        enabled: boolean;
-    }
-    interface IGLTFLoaderExtensions {
-        [name: string]: IGLTFLoaderExtension;
-    }
     interface IGLTFLoader extends IDisposable {
         coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
         animationStartMode: GLTFLoaderAnimationStartMode;
         compileMaterials: boolean;
         useClipPlane: boolean;
         compileShadowGenerators: boolean;
-        onDisposeObservable: Observable<IGLTFLoader>;
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
         onCompleteObservable: Observable<IGLTFLoader>;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
-        extensions: Nullable<IGLTFLoaderExtensions>;
         importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{
             meshes: AbstractMesh[];
             particleSystems: ParticleSystem[];
@@ -125,13 +129,16 @@ declare module BABYLON {
         private _onDisposeObserver;
         onDispose: () => void;
         /**
-         * The loader state or null if not active.
+         * Raised after a loader extension is created.
+         * Set additional options for a loader extension in this event.
          */
-        readonly loaderState: Nullable<GLTFLoaderState>;
+        readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
+        private _onExtensionLoadedObserver;
+        onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
-         * The loader extensions or null if not active.
+         * The loader state or null if not active.
          */
-        readonly loaderExtensions: Nullable<IGLTFLoaderExtensions>;
+        readonly loaderState: Nullable<GLTFLoaderState>;
         private _loader;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
@@ -563,8 +570,8 @@ declare module BABYLON.GLTF1 {
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
         onCompleteObservable: Observable<IGLTFLoader>;
+        onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
-        extensions: Nullable<IGLTFLoaderExtensions>;
         dispose(): void;
         private _importMeshAsync(meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError);
         importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress: (event: SceneLoaderProgressEvent) => void): Promise<{
@@ -838,10 +845,9 @@ declare module BABYLON.GLTF2 {
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         readonly onMaterialLoadedObservable: Observable<Material>;
+        readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         readonly onCompleteObservable: Observable<IGLTFLoader>;
         readonly state: Nullable<GLTFLoaderState>;
-        readonly extensions: IGLTFLoaderExtensions;
-        constructor();
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
             meshes: AbstractMesh[];
@@ -850,6 +856,7 @@ declare module BABYLON.GLTF2 {
         }>;
         loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
         private _loadAsync(nodes, scene, data, rootUrl, onProgress?);
+        private _loadExtensions();
         private _loadData(data);
         private _setupData();
         private _createRootNode();
@@ -900,19 +907,18 @@ declare module BABYLON.GLTF2 {
         private static _ValidateUri(uri);
         private _compileMaterialsAsync();
         private _compileShadowGeneratorsAsync();
-        private _abortRequests();
-        private _releaseResources();
+        private _clear();
         _applyExtensions<T>(actionAsync: (extension: GLTFLoaderExtension) => Nullable<Promise<T>>): Nullable<Promise<T>>;
     }
 }
 
 
 declare module BABYLON.GLTF2 {
-    abstract class GLTFLoaderExtension {
+    abstract class GLTFLoaderExtension implements IGLTFLoaderExtension {
         enabled: boolean;
+        readonly abstract name: string;
         protected _loader: GLTFLoader;
         constructor(loader: GLTFLoader);
-        protected readonly abstract _name: string;
         /** Override this method to modify the default behavior for loading scenes. */
         protected _loadSceneAsync(context: string, node: ILoaderScene): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading nodes. */
@@ -936,26 +942,30 @@ declare module BABYLON.GLTF2 {
 
 
 declare module BABYLON.GLTF2.Extensions {
-    class MSFTLOD extends GLTFLoaderExtension {
+    class MSFT_lod extends GLTFLoaderExtension {
+        readonly name: string;
+        /**
+         * Maximum number of LODs to load, starting from the lowest LOD.
+         */
+        maxLODsToLoad: number;
         private _loadingNodeLOD;
         private _loadNodeSignals;
         private _loadingMaterialLOD;
         private _loadMaterialSignals;
-        protected readonly _name: string;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
         protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /**
          * Gets an array of LOD properties from lowest to highest.
          */
-        private static _GetLODs<T>(context, property, array, ids);
+        private _getLODs<T>(context, property, array, ids);
     }
 }
 
 
 declare module BABYLON.GLTF2.Extensions {
-    class KHRMaterialsPbrSpecularGlossiness extends GLTFLoaderExtension {
-        protected readonly _name: string;
+    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);
     }
@@ -963,8 +973,8 @@ declare module BABYLON.GLTF2.Extensions {
 
 
 declare module BABYLON.GLTF2.Extensions {
-    class KHRLights extends GLTFLoaderExtension {
-        protected readonly _name: string;
+    class KHR_lights extends GLTFLoaderExtension {
+        readonly name: string;
         protected _loadSceneAsync(context: string, scene: ILoaderScene): Nullable<Promise<void>>;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
         private readonly _lights;

+ 158 - 122
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -86,6 +86,11 @@ var BABYLON;
             * Raised when the loader is disposed.
             */
             this.onDisposeObservable = new BABYLON.Observable();
+            /**
+             * Raised after a loader extension is created.
+             * Set additional options for a loader extension in this event.
+             */
+            this.onExtensionLoadedObservable = new BABYLON.Observable();
             // #endregion
             this._loader = null;
             this.name = "gltf";
@@ -154,22 +159,22 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
-            /**
-             * The loader state or null if not active.
-             */
-            get: function () {
-                return this._loader ? this._loader.state : null;
+        Object.defineProperty(GLTFFileLoader.prototype, "onExtensionLoaded", {
+            set: function (callback) {
+                if (this._onExtensionLoadedObserver) {
+                    this.onExtensionLoadedObservable.remove(this._onExtensionLoadedObserver);
+                }
+                this._onExtensionLoadedObserver = this.onExtensionLoadedObservable.add(callback);
             },
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(GLTFFileLoader.prototype, "loaderExtensions", {
+        Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
-             * The loader extensions or null if not active.
+             * The loader state or null if not active.
              */
             get: function () {
-                return this._loader ? this._loader.extensions : null;
+                return this._loader ? this._loader.state : null;
             },
             enumerable: true,
             configurable: true
@@ -182,11 +187,9 @@ var BABYLON;
                 this._loader.dispose();
                 this._loader = null;
             }
-            this.onParsedObservable.clear();
             this.onMeshLoadedObservable.clear();
             this.onTextureLoadedObservable.clear();
             this.onMaterialLoadedObservable.clear();
-            this.onCompleteObservable.clear();
             this.onDisposeObservable.notifyObservers(this);
             this.onDisposeObservable.clear();
         };
@@ -239,6 +242,7 @@ var BABYLON;
                 };
             }
             this.onParsedObservable.notifyObservers(parsedData);
+            this.onParsedObservable.clear();
             return parsedData;
         };
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
@@ -275,7 +279,14 @@ var BABYLON;
             loader.onMeshLoadedObservable.add(function (mesh) { return _this.onMeshLoadedObservable.notifyObservers(mesh); });
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
             loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
-            loader.onCompleteObservable.add(function () { return _this.onCompleteObservable.notifyObservers(_this); });
+            loader.onExtensionLoadedObservable.add(function (extension) { return _this.onExtensionLoadedObservable.notifyObservers(extension); });
+            loader.onCompleteObservable.add(function () {
+                _this.onMeshLoadedObservable.clear();
+                _this.onTextureLoadedObservable.clear();
+                _this.onMaterialLoadedObservable.clear();
+                _this.onCompleteObservable.notifyObservers(_this);
+                _this.onCompleteObservable.clear();
+            });
             return loader;
         };
         GLTFFileLoader._parseBinary = function (data) {
@@ -1827,8 +1838,8 @@ var BABYLON;
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
+                this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;
-                this.extensions = null;
             }
             GLTFLoader.RegisterExtension = function (extension) {
                 if (GLTFLoader.Extensions[extension.name]) {
@@ -2664,12 +2675,8 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
-                for (var _i = 0, _a = GLTFLoader._Names; _i < _a.length; _i++) {
-                    var name_1 = _a[_i];
-                    var extension = GLTFLoader._Factories[name_1](this);
-                    this._extensions[name_1] = extension;
-                }
             }
             GLTFLoader._Register = function (name, factory) {
                 if (GLTFLoader._Factories[name]) {
@@ -2687,21 +2694,14 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(GLTFLoader.prototype, "extensions", {
-                get: function () {
-                    return this.extensions;
-                },
-                enumerable: true,
-                configurable: true
-            });
             GLTFLoader.prototype.dispose = function () {
                 if (this._disposed) {
                     return;
                 }
                 this._disposed = true;
-                this._abortRequests();
-                this._releaseResources();
                 this.onDisposeObservable.notifyObservers(this);
+                this.onDisposeObservable.clear();
+                this._clear();
             };
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
                 var _this = this;
@@ -2741,6 +2741,7 @@ var BABYLON;
             GLTFLoader.prototype._loadAsync = function (nodes, scene, data, rootUrl, onProgress) {
                 var _this = this;
                 return Promise.resolve().then(function () {
+                    _this._loadExtensions();
                     _this._babylonScene = scene;
                     _this._rootUrl = rootUrl;
                     _this._progressCallback = onProgress;
@@ -2764,22 +2765,34 @@ var BABYLON;
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
                         BABYLON.Tools.SetImmediate(function () {
-                            Promise.all(_this._completePromises).then(function () {
-                                _this._releaseResources();
-                                _this._state = BABYLON.GLTFLoaderState.Complete;
-                                _this.onCompleteObservable.notifyObservers(_this);
-                            }).catch(function (error) {
-                                BABYLON.Tools.Error("glTF Loader: " + error.message);
-                                _this.dispose();
-                            });
+                            if (!_this._disposed) {
+                                Promise.all(_this._completePromises).then(function () {
+                                    _this._state = BABYLON.GLTFLoaderState.Complete;
+                                    _this.onCompleteObservable.notifyObservers(_this);
+                                    _this.onCompleteObservable.clear();
+                                    _this._clear();
+                                }).catch(function (error) {
+                                    BABYLON.Tools.Error("glTF Loader: " + error.message);
+                                    _this._clear();
+                                });
+                            }
                         });
-                    }).catch(function (error) {
-                        BABYLON.Tools.Error("glTF Loader: " + error.message);
-                        _this.dispose();
-                        throw error;
                     });
+                }).catch(function (error) {
+                    BABYLON.Tools.Error("glTF Loader: " + error.message);
+                    _this._clear();
+                    throw error;
                 });
             };
+            GLTFLoader.prototype._loadExtensions = function () {
+                for (var _i = 0, _a = GLTFLoader._Names; _i < _a.length; _i++) {
+                    var name_1 = _a[_i];
+                    var extension = GLTFLoader._Factories[name_1](this);
+                    this._extensions[name_1] = extension;
+                    this.onExtensionLoadedObservable.notifyObservers(extension);
+                }
+                this.onExtensionLoadedObservable.clear();
+            };
             GLTFLoader.prototype._loadData = function (data) {
                 this._gltf = data.json;
                 this._setupData();
@@ -2887,6 +2900,12 @@ var BABYLON;
                         if (node._babylonMesh) {
                             meshes.push(node._babylonMesh);
                         }
+                        if (node._primitiveBabylonMeshes) {
+                            for (var _a = 0, _b = node._primitiveBabylonMeshes; _a < _b.length; _a++) {
+                                var babylonMesh = _b[_a];
+                                meshes.push(babylonMesh);
+                            }
+                        }
                     }
                 }
                 return meshes;
@@ -3004,6 +3023,7 @@ var BABYLON;
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
                     promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
                 }
+                this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 return Promise.all(promises).then(function () {
                     babylonMesh.setEnabled(true);
                 });
@@ -3718,6 +3738,7 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._loadTextureAsync = function (context, textureInfo, assign) {
+                var _this = this;
                 var texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
                 context = "#/textures/" + textureInfo.index;
                 var promises = new Array();
@@ -3725,9 +3746,13 @@ var BABYLON;
                 var samplerData = this._loadSampler("#/samplers/" + sampler._index, sampler);
                 var deferred = new BABYLON.Deferred();
                 var babylonTexture = new BABYLON.Texture(null, this._babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, function () {
-                    deferred.resolve();
+                    if (!_this._disposed) {
+                        deferred.resolve();
+                    }
                 }, function (message, exception) {
-                    deferred.reject(new Error(context + ": " + (exception && exception.message) ? exception.message : message || "Failed to load texture"));
+                    if (!_this._disposed) {
+                        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;
@@ -3785,21 +3810,27 @@ var BABYLON;
                 }
                 return new Promise(function (resolve, reject) {
                     var request = BABYLON.Tools.LoadFile(_this._rootUrl + uri, function (data) {
-                        resolve(new Uint8Array(data));
+                        if (!_this._disposed) {
+                            resolve(new Uint8Array(data));
+                        }
                     }, function (event) {
-                        try {
-                            if (request && _this._state === BABYLON.GLTFLoaderState.Loading) {
-                                request._lengthComputable = event.lengthComputable;
-                                request._loaded = event.loaded;
-                                request._total = event.total;
-                                _this._onProgress();
+                        if (!_this._disposed) {
+                            try {
+                                if (request && _this._state === BABYLON.GLTFLoaderState.Loading) {
+                                    request._lengthComputable = event.lengthComputable;
+                                    request._loaded = event.loaded;
+                                    request._total = event.total;
+                                    _this._onProgress();
+                                }
+                            }
+                            catch (e) {
+                                reject(e);
                             }
-                        }
-                        catch (e) {
-                            reject(e);
                         }
                     }, _this._babylonScene.database, true, function (request, exception) {
-                        reject(new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request));
+                        if (!_this._disposed) {
+                            reject(new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request));
+                        }
                     });
                     _this._requests.push(request);
                 });
@@ -3921,17 +3952,15 @@ var BABYLON;
                 }
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._abortRequests = function () {
+            GLTFLoader.prototype._clear = function () {
                 for (var _i = 0, _a = this._requests; _i < _a.length; _i++) {
                     var request = _a[_i];
                     request.abort();
                 }
                 this._requests.length = 0;
-            };
-            GLTFLoader.prototype._releaseResources = function () {
-                if (this._gltf.images) {
-                    for (var _i = 0, _a = this._gltf.images; _i < _a.length; _i++) {
-                        var image = _a[_i];
+                if (this._gltf && this._gltf.images) {
+                    for (var _b = 0, _c = this._gltf.images; _b < _c.length; _b++) {
+                        var image = _c[_b];
                         if (image._objectURL) {
                             image._objectURL.then(function (value) {
                                 URL.revokeObjectURL(value);
@@ -3940,6 +3969,15 @@ var BABYLON;
                         }
                     }
                 }
+                delete this._gltf;
+                delete this._babylonScene;
+                this._completePromises.length = 0;
+                this._extensions = {};
+                delete this._rootBabylonMesh;
+                delete this._progressCallback;
+                this.onMeshLoadedObservable.clear();
+                this.onTextureLoadedObservable.clear();
+                this.onMaterialLoadedObservable.clear();
             };
             GLTFLoader.prototype._applyExtensions = function (actionAsync) {
                 for (var _i = 0, _a = GLTFLoader._Names; _i < _a.length; _i++) {
@@ -3992,15 +4030,15 @@ var BABYLON;
                     return null;
                 }
                 var extensions = property.extensions;
-                var extension = extensions[this._name];
+                var extension = extensions[this.name];
                 if (!extension) {
                     return null;
                 }
                 // Clear out the extension before executing the action to avoid recursing into the same property.
-                delete extensions[this._name];
-                return actionAsync(context + "extensions/" + this._name, extension).then(function () {
+                delete extensions[this.name];
+                return actionAsync(context + "/extensions/" + this.name, extension).then(function () {
                     // Restore the extension after completing the action.
-                    extensions[_this._name] = extension;
+                    extensions[_this.name] = extension;
                 });
             };
             /** Helper method called by the loader to allow extensions to override loading scenes. */
@@ -4046,28 +4084,26 @@ var BABYLON;
         (function (Extensions) {
             // https://github.com/sbtron/glTF/tree/MSFT_lod/extensions/Vendor/MSFT_lod
             var NAME = "MSFT_lod";
-            var MSFTLOD = /** @class */ (function (_super) {
-                __extends(MSFTLOD, _super);
-                function MSFTLOD() {
+            var MSFT_lod = /** @class */ (function (_super) {
+                __extends(MSFT_lod, _super);
+                function MSFT_lod() {
                     var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    /**
+                     * Maximum number of LODs to load, starting from the lowest LOD.
+                     */
+                    _this.maxLODsToLoad = Number.MAX_VALUE;
                     _this._loadingNodeLOD = null;
                     _this._loadNodeSignals = {};
                     _this._loadingMaterialLOD = null;
                     _this._loadMaterialSignals = {};
                     return _this;
                 }
-                Object.defineProperty(MSFTLOD.prototype, "_name", {
-                    get: function () {
-                        return NAME;
-                    },
-                    enumerable: true,
-                    configurable: true
-                });
-                MSFTLOD.prototype._loadNodeAsync = function (context, node) {
+                MSFT_lod.prototype._loadNodeAsync = function (context, node) {
                     var _this = this;
                     return this._loadExtensionAsync(context, node, function (context, extension) {
                         var firstPromise;
-                        var nodeLODs = MSFTLOD._GetLODs(context, node, _this._loader._gltf.nodes, extension.ids);
+                        var nodeLODs = _this._getLODs(context, node, _this._loader._gltf.nodes, extension.ids);
                         var _loop_1 = function (indexLOD) {
                             var nodeLOD = nodeLODs[indexLOD];
                             if (indexLOD !== 0) {
@@ -4099,11 +4135,15 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFTLOD.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
                     var _this = this;
+                    // Don't load material LODs if already loading a node LOD.
+                    if (this._loadingNodeLOD) {
+                        return null;
+                    }
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         var firstPromise;
-                        var materialLODs = MSFTLOD._GetLODs(context, material, _this._loader._gltf.materials, extension.ids);
+                        var materialLODs = _this._getLODs(context, material, _this._loader._gltf.materials, extension.ids);
                         var _loop_2 = function (indexLOD) {
                             var materialLOD = materialLODs[indexLOD];
                             if (indexLOD !== 0) {
@@ -4131,7 +4171,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFTLOD.prototype._loadUriAsync = function (context, uri) {
+                MSFT_lod.prototype._loadUriAsync = function (context, uri) {
                     var _this = this;
                     // Defer the loading of uris if loading a material or node LOD.
                     if (this._loadingMaterialLOD) {
@@ -4151,18 +4191,24 @@ var BABYLON;
                 /**
                  * Gets an array of LOD properties from lowest to highest.
                  */
-                MSFTLOD._GetLODs = function (context, property, array, ids) {
-                    var properties = [property];
-                    for (var _i = 0, ids_1 = ids; _i < ids_1.length; _i++) {
-                        var id = ids_1[_i];
-                        properties.push(GLTF2.GLTFLoader._GetProperty(context + "/ids/" + id, array, id));
+                MSFT_lod.prototype._getLODs = function (context, property, array, ids) {
+                    if (this.maxLODsToLoad <= 0) {
+                        throw new Error("maxLODsToLoad must be greater than zero");
+                    }
+                    var properties = new Array();
+                    for (var i = ids.length - 1; i >= 0; i--) {
+                        properties.push(GLTF2.GLTFLoader._GetProperty(context + "/ids/" + ids[i], array, ids[i]));
+                        if (properties.length === this.maxLODsToLoad) {
+                            return properties;
+                        }
                     }
-                    return properties.reverse();
+                    properties.push(property);
+                    return properties;
                 };
-                return MSFTLOD;
+                return MSFT_lod;
             }(GLTF2.GLTFLoaderExtension));
-            Extensions.MSFTLOD = MSFTLOD;
-            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new MSFTLOD(loader); });
+            Extensions.MSFT_lod = MSFT_lod;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new MSFT_lod(loader); });
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
@@ -4188,19 +4234,14 @@ var BABYLON;
         (function (Extensions) {
             // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
             var NAME = "KHR_materials_pbrSpecularGlossiness";
-            var KHRMaterialsPbrSpecularGlossiness = /** @class */ (function (_super) {
-                __extends(KHRMaterialsPbrSpecularGlossiness, _super);
-                function KHRMaterialsPbrSpecularGlossiness() {
-                    return _super !== null && _super.apply(this, arguments) || this;
+            var KHR_materials_pbrSpecularGlossiness = /** @class */ (function (_super) {
+                __extends(KHR_materials_pbrSpecularGlossiness, _super);
+                function KHR_materials_pbrSpecularGlossiness() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
                 }
-                Object.defineProperty(KHRMaterialsPbrSpecularGlossiness.prototype, "_name", {
-                    get: function () {
-                        return NAME;
-                    },
-                    enumerable: true,
-                    configurable: true
-                });
-                KHRMaterialsPbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
@@ -4219,7 +4260,7 @@ var BABYLON;
                         return (material._loaded = Promise.all(promises).then(function () { }));
                     });
                 };
-                KHRMaterialsPbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -4246,10 +4287,10 @@ var BABYLON;
                     loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
-                return KHRMaterialsPbrSpecularGlossiness;
+                return KHR_materials_pbrSpecularGlossiness;
             }(GLTF2.GLTFLoaderExtension));
-            Extensions.KHRMaterialsPbrSpecularGlossiness = KHRMaterialsPbrSpecularGlossiness;
-            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHRMaterialsPbrSpecularGlossiness(loader); });
+            Extensions.KHR_materials_pbrSpecularGlossiness = KHR_materials_pbrSpecularGlossiness;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHR_materials_pbrSpecularGlossiness(loader); });
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
@@ -4282,19 +4323,14 @@ var BABYLON;
                 LightType["POINT"] = "point";
                 LightType["SPOT"] = "spot";
             })(LightType || (LightType = {}));
-            var KHRLights = /** @class */ (function (_super) {
-                __extends(KHRLights, _super);
-                function KHRLights() {
-                    return _super !== null && _super.apply(this, arguments) || this;
+            var KHR_lights = /** @class */ (function (_super) {
+                __extends(KHR_lights, _super);
+                function KHR_lights() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
                 }
-                Object.defineProperty(KHRLights.prototype, "_name", {
-                    get: function () {
-                        return NAME;
-                    },
-                    enumerable: true,
-                    configurable: true
-                });
-                KHRLights.prototype._loadSceneAsync = function (context, scene) {
+                KHR_lights.prototype._loadSceneAsync = function (context, scene) {
                     var _this = this;
                     return this._loadExtensionAsync(context, scene, function (context, extension) {
                         var promise = _this._loader._loadSceneAsync(context, scene);
@@ -4306,7 +4342,7 @@ var BABYLON;
                         return promise;
                     });
                 };
-                KHRLights.prototype._loadNodeAsync = function (context, node) {
+                KHR_lights.prototype._loadNodeAsync = function (context, node) {
                     var _this = this;
                     return this._loadExtensionAsync(context, node, function (context, extension) {
                         var promise = _this._loader._loadNodeAsync(context, node);
@@ -4343,22 +4379,22 @@ var BABYLON;
                         return promise;
                     });
                 };
-                Object.defineProperty(KHRLights.prototype, "_lights", {
+                Object.defineProperty(KHR_lights.prototype, "_lights", {
                     get: function () {
                         var extensions = this._loader._gltf.extensions;
-                        if (!extensions || !extensions[this._name]) {
-                            throw new Error("#/extensions: " + this._name + " not found");
+                        if (!extensions || !extensions[this.name]) {
+                            throw new Error("#/extensions: " + this.name + " not found");
                         }
-                        var extension = extensions[this._name];
+                        var extension = extensions[this.name];
                         return extension.lights;
                     },
                     enumerable: true,
                     configurable: true
                 });
-                return KHRLights;
+                return KHR_lights;
             }(GLTF2.GLTFLoaderExtension));
-            Extensions.KHRLights = KHRLights;
-            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHRLights(loader); });
+            Extensions.KHR_lights = KHR_lights;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHR_lights(loader); });
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));

ファイルの差分が大きいため隠しています
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 158 - 122
dist/preview release/loaders/babylonjs.loaders.js

@@ -1072,6 +1072,11 @@ var BABYLON;
             * Raised when the loader is disposed.
             */
             this.onDisposeObservable = new BABYLON.Observable();
+            /**
+             * Raised after a loader extension is created.
+             * Set additional options for a loader extension in this event.
+             */
+            this.onExtensionLoadedObservable = new BABYLON.Observable();
             // #endregion
             this._loader = null;
             this.name = "gltf";
@@ -1140,22 +1145,22 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
-            /**
-             * The loader state or null if not active.
-             */
-            get: function () {
-                return this._loader ? this._loader.state : null;
+        Object.defineProperty(GLTFFileLoader.prototype, "onExtensionLoaded", {
+            set: function (callback) {
+                if (this._onExtensionLoadedObserver) {
+                    this.onExtensionLoadedObservable.remove(this._onExtensionLoadedObserver);
+                }
+                this._onExtensionLoadedObserver = this.onExtensionLoadedObservable.add(callback);
             },
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(GLTFFileLoader.prototype, "loaderExtensions", {
+        Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
-             * The loader extensions or null if not active.
+             * The loader state or null if not active.
              */
             get: function () {
-                return this._loader ? this._loader.extensions : null;
+                return this._loader ? this._loader.state : null;
             },
             enumerable: true,
             configurable: true
@@ -1168,11 +1173,9 @@ var BABYLON;
                 this._loader.dispose();
                 this._loader = null;
             }
-            this.onParsedObservable.clear();
             this.onMeshLoadedObservable.clear();
             this.onTextureLoadedObservable.clear();
             this.onMaterialLoadedObservable.clear();
-            this.onCompleteObservable.clear();
             this.onDisposeObservable.notifyObservers(this);
             this.onDisposeObservable.clear();
         };
@@ -1225,6 +1228,7 @@ var BABYLON;
                 };
             }
             this.onParsedObservable.notifyObservers(parsedData);
+            this.onParsedObservable.clear();
             return parsedData;
         };
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
@@ -1261,7 +1265,14 @@ var BABYLON;
             loader.onMeshLoadedObservable.add(function (mesh) { return _this.onMeshLoadedObservable.notifyObservers(mesh); });
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
             loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
-            loader.onCompleteObservable.add(function () { return _this.onCompleteObservable.notifyObservers(_this); });
+            loader.onExtensionLoadedObservable.add(function (extension) { return _this.onExtensionLoadedObservable.notifyObservers(extension); });
+            loader.onCompleteObservable.add(function () {
+                _this.onMeshLoadedObservable.clear();
+                _this.onTextureLoadedObservable.clear();
+                _this.onMaterialLoadedObservable.clear();
+                _this.onCompleteObservable.notifyObservers(_this);
+                _this.onCompleteObservable.clear();
+            });
             return loader;
         };
         GLTFFileLoader._parseBinary = function (data) {
@@ -2813,8 +2824,8 @@ var BABYLON;
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
+                this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;
-                this.extensions = null;
             }
             GLTFLoader.RegisterExtension = function (extension) {
                 if (GLTFLoader.Extensions[extension.name]) {
@@ -3632,12 +3643,8 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
-                for (var _i = 0, _a = GLTFLoader._Names; _i < _a.length; _i++) {
-                    var name_1 = _a[_i];
-                    var extension = GLTFLoader._Factories[name_1](this);
-                    this._extensions[name_1] = extension;
-                }
             }
             GLTFLoader._Register = function (name, factory) {
                 if (GLTFLoader._Factories[name]) {
@@ -3655,21 +3662,14 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(GLTFLoader.prototype, "extensions", {
-                get: function () {
-                    return this.extensions;
-                },
-                enumerable: true,
-                configurable: true
-            });
             GLTFLoader.prototype.dispose = function () {
                 if (this._disposed) {
                     return;
                 }
                 this._disposed = true;
-                this._abortRequests();
-                this._releaseResources();
                 this.onDisposeObservable.notifyObservers(this);
+                this.onDisposeObservable.clear();
+                this._clear();
             };
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
                 var _this = this;
@@ -3709,6 +3709,7 @@ var BABYLON;
             GLTFLoader.prototype._loadAsync = function (nodes, scene, data, rootUrl, onProgress) {
                 var _this = this;
                 return Promise.resolve().then(function () {
+                    _this._loadExtensions();
                     _this._babylonScene = scene;
                     _this._rootUrl = rootUrl;
                     _this._progressCallback = onProgress;
@@ -3732,22 +3733,34 @@ var BABYLON;
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
                         BABYLON.Tools.SetImmediate(function () {
-                            Promise.all(_this._completePromises).then(function () {
-                                _this._releaseResources();
-                                _this._state = BABYLON.GLTFLoaderState.Complete;
-                                _this.onCompleteObservable.notifyObservers(_this);
-                            }).catch(function (error) {
-                                BABYLON.Tools.Error("glTF Loader: " + error.message);
-                                _this.dispose();
-                            });
+                            if (!_this._disposed) {
+                                Promise.all(_this._completePromises).then(function () {
+                                    _this._state = BABYLON.GLTFLoaderState.Complete;
+                                    _this.onCompleteObservable.notifyObservers(_this);
+                                    _this.onCompleteObservable.clear();
+                                    _this._clear();
+                                }).catch(function (error) {
+                                    BABYLON.Tools.Error("glTF Loader: " + error.message);
+                                    _this._clear();
+                                });
+                            }
                         });
-                    }).catch(function (error) {
-                        BABYLON.Tools.Error("glTF Loader: " + error.message);
-                        _this.dispose();
-                        throw error;
                     });
+                }).catch(function (error) {
+                    BABYLON.Tools.Error("glTF Loader: " + error.message);
+                    _this._clear();
+                    throw error;
                 });
             };
+            GLTFLoader.prototype._loadExtensions = function () {
+                for (var _i = 0, _a = GLTFLoader._Names; _i < _a.length; _i++) {
+                    var name_1 = _a[_i];
+                    var extension = GLTFLoader._Factories[name_1](this);
+                    this._extensions[name_1] = extension;
+                    this.onExtensionLoadedObservable.notifyObservers(extension);
+                }
+                this.onExtensionLoadedObservable.clear();
+            };
             GLTFLoader.prototype._loadData = function (data) {
                 this._gltf = data.json;
                 this._setupData();
@@ -3855,6 +3868,12 @@ var BABYLON;
                         if (node._babylonMesh) {
                             meshes.push(node._babylonMesh);
                         }
+                        if (node._primitiveBabylonMeshes) {
+                            for (var _a = 0, _b = node._primitiveBabylonMeshes; _a < _b.length; _a++) {
+                                var babylonMesh = _b[_a];
+                                meshes.push(babylonMesh);
+                            }
+                        }
                     }
                 }
                 return meshes;
@@ -3972,6 +3991,7 @@ var BABYLON;
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
                     promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
                 }
+                this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 return Promise.all(promises).then(function () {
                     babylonMesh.setEnabled(true);
                 });
@@ -4686,6 +4706,7 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._loadTextureAsync = function (context, textureInfo, assign) {
+                var _this = this;
                 var texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
                 context = "#/textures/" + textureInfo.index;
                 var promises = new Array();
@@ -4693,9 +4714,13 @@ var BABYLON;
                 var samplerData = this._loadSampler("#/samplers/" + sampler._index, sampler);
                 var deferred = new BABYLON.Deferred();
                 var babylonTexture = new BABYLON.Texture(null, this._babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, function () {
-                    deferred.resolve();
+                    if (!_this._disposed) {
+                        deferred.resolve();
+                    }
                 }, function (message, exception) {
-                    deferred.reject(new Error(context + ": " + (exception && exception.message) ? exception.message : message || "Failed to load texture"));
+                    if (!_this._disposed) {
+                        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;
@@ -4753,21 +4778,27 @@ var BABYLON;
                 }
                 return new Promise(function (resolve, reject) {
                     var request = BABYLON.Tools.LoadFile(_this._rootUrl + uri, function (data) {
-                        resolve(new Uint8Array(data));
+                        if (!_this._disposed) {
+                            resolve(new Uint8Array(data));
+                        }
                     }, function (event) {
-                        try {
-                            if (request && _this._state === BABYLON.GLTFLoaderState.Loading) {
-                                request._lengthComputable = event.lengthComputable;
-                                request._loaded = event.loaded;
-                                request._total = event.total;
-                                _this._onProgress();
+                        if (!_this._disposed) {
+                            try {
+                                if (request && _this._state === BABYLON.GLTFLoaderState.Loading) {
+                                    request._lengthComputable = event.lengthComputable;
+                                    request._loaded = event.loaded;
+                                    request._total = event.total;
+                                    _this._onProgress();
+                                }
+                            }
+                            catch (e) {
+                                reject(e);
                             }
-                        }
-                        catch (e) {
-                            reject(e);
                         }
                     }, _this._babylonScene.database, true, function (request, exception) {
-                        reject(new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request));
+                        if (!_this._disposed) {
+                            reject(new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request));
+                        }
                     });
                     _this._requests.push(request);
                 });
@@ -4889,17 +4920,15 @@ var BABYLON;
                 }
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._abortRequests = function () {
+            GLTFLoader.prototype._clear = function () {
                 for (var _i = 0, _a = this._requests; _i < _a.length; _i++) {
                     var request = _a[_i];
                     request.abort();
                 }
                 this._requests.length = 0;
-            };
-            GLTFLoader.prototype._releaseResources = function () {
-                if (this._gltf.images) {
-                    for (var _i = 0, _a = this._gltf.images; _i < _a.length; _i++) {
-                        var image = _a[_i];
+                if (this._gltf && this._gltf.images) {
+                    for (var _b = 0, _c = this._gltf.images; _b < _c.length; _b++) {
+                        var image = _c[_b];
                         if (image._objectURL) {
                             image._objectURL.then(function (value) {
                                 URL.revokeObjectURL(value);
@@ -4908,6 +4937,15 @@ var BABYLON;
                         }
                     }
                 }
+                delete this._gltf;
+                delete this._babylonScene;
+                this._completePromises.length = 0;
+                this._extensions = {};
+                delete this._rootBabylonMesh;
+                delete this._progressCallback;
+                this.onMeshLoadedObservable.clear();
+                this.onTextureLoadedObservable.clear();
+                this.onMaterialLoadedObservable.clear();
             };
             GLTFLoader.prototype._applyExtensions = function (actionAsync) {
                 for (var _i = 0, _a = GLTFLoader._Names; _i < _a.length; _i++) {
@@ -4960,15 +4998,15 @@ var BABYLON;
                     return null;
                 }
                 var extensions = property.extensions;
-                var extension = extensions[this._name];
+                var extension = extensions[this.name];
                 if (!extension) {
                     return null;
                 }
                 // Clear out the extension before executing the action to avoid recursing into the same property.
-                delete extensions[this._name];
-                return actionAsync(context + "extensions/" + this._name, extension).then(function () {
+                delete extensions[this.name];
+                return actionAsync(context + "/extensions/" + this.name, extension).then(function () {
                     // Restore the extension after completing the action.
-                    extensions[_this._name] = extension;
+                    extensions[_this.name] = extension;
                 });
             };
             /** Helper method called by the loader to allow extensions to override loading scenes. */
@@ -5005,28 +5043,26 @@ var BABYLON;
         (function (Extensions) {
             // https://github.com/sbtron/glTF/tree/MSFT_lod/extensions/Vendor/MSFT_lod
             var NAME = "MSFT_lod";
-            var MSFTLOD = /** @class */ (function (_super) {
-                __extends(MSFTLOD, _super);
-                function MSFTLOD() {
+            var MSFT_lod = /** @class */ (function (_super) {
+                __extends(MSFT_lod, _super);
+                function MSFT_lod() {
                     var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    /**
+                     * Maximum number of LODs to load, starting from the lowest LOD.
+                     */
+                    _this.maxLODsToLoad = Number.MAX_VALUE;
                     _this._loadingNodeLOD = null;
                     _this._loadNodeSignals = {};
                     _this._loadingMaterialLOD = null;
                     _this._loadMaterialSignals = {};
                     return _this;
                 }
-                Object.defineProperty(MSFTLOD.prototype, "_name", {
-                    get: function () {
-                        return NAME;
-                    },
-                    enumerable: true,
-                    configurable: true
-                });
-                MSFTLOD.prototype._loadNodeAsync = function (context, node) {
+                MSFT_lod.prototype._loadNodeAsync = function (context, node) {
                     var _this = this;
                     return this._loadExtensionAsync(context, node, function (context, extension) {
                         var firstPromise;
-                        var nodeLODs = MSFTLOD._GetLODs(context, node, _this._loader._gltf.nodes, extension.ids);
+                        var nodeLODs = _this._getLODs(context, node, _this._loader._gltf.nodes, extension.ids);
                         var _loop_1 = function (indexLOD) {
                             var nodeLOD = nodeLODs[indexLOD];
                             if (indexLOD !== 0) {
@@ -5058,11 +5094,15 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFTLOD.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
                     var _this = this;
+                    // Don't load material LODs if already loading a node LOD.
+                    if (this._loadingNodeLOD) {
+                        return null;
+                    }
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         var firstPromise;
-                        var materialLODs = MSFTLOD._GetLODs(context, material, _this._loader._gltf.materials, extension.ids);
+                        var materialLODs = _this._getLODs(context, material, _this._loader._gltf.materials, extension.ids);
                         var _loop_2 = function (indexLOD) {
                             var materialLOD = materialLODs[indexLOD];
                             if (indexLOD !== 0) {
@@ -5090,7 +5130,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFTLOD.prototype._loadUriAsync = function (context, uri) {
+                MSFT_lod.prototype._loadUriAsync = function (context, uri) {
                     var _this = this;
                     // Defer the loading of uris if loading a material or node LOD.
                     if (this._loadingMaterialLOD) {
@@ -5110,18 +5150,24 @@ var BABYLON;
                 /**
                  * Gets an array of LOD properties from lowest to highest.
                  */
-                MSFTLOD._GetLODs = function (context, property, array, ids) {
-                    var properties = [property];
-                    for (var _i = 0, ids_1 = ids; _i < ids_1.length; _i++) {
-                        var id = ids_1[_i];
-                        properties.push(GLTF2.GLTFLoader._GetProperty(context + "/ids/" + id, array, id));
+                MSFT_lod.prototype._getLODs = function (context, property, array, ids) {
+                    if (this.maxLODsToLoad <= 0) {
+                        throw new Error("maxLODsToLoad must be greater than zero");
+                    }
+                    var properties = new Array();
+                    for (var i = ids.length - 1; i >= 0; i--) {
+                        properties.push(GLTF2.GLTFLoader._GetProperty(context + "/ids/" + ids[i], array, ids[i]));
+                        if (properties.length === this.maxLODsToLoad) {
+                            return properties;
+                        }
                     }
-                    return properties.reverse();
+                    properties.push(property);
+                    return properties;
                 };
-                return MSFTLOD;
+                return MSFT_lod;
             }(GLTF2.GLTFLoaderExtension));
-            Extensions.MSFTLOD = MSFTLOD;
-            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new MSFTLOD(loader); });
+            Extensions.MSFT_lod = MSFT_lod;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new MSFT_lod(loader); });
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
@@ -5138,19 +5184,14 @@ var BABYLON;
         (function (Extensions) {
             // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
             var NAME = "KHR_materials_pbrSpecularGlossiness";
-            var KHRMaterialsPbrSpecularGlossiness = /** @class */ (function (_super) {
-                __extends(KHRMaterialsPbrSpecularGlossiness, _super);
-                function KHRMaterialsPbrSpecularGlossiness() {
-                    return _super !== null && _super.apply(this, arguments) || this;
+            var KHR_materials_pbrSpecularGlossiness = /** @class */ (function (_super) {
+                __extends(KHR_materials_pbrSpecularGlossiness, _super);
+                function KHR_materials_pbrSpecularGlossiness() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
                 }
-                Object.defineProperty(KHRMaterialsPbrSpecularGlossiness.prototype, "_name", {
-                    get: function () {
-                        return NAME;
-                    },
-                    enumerable: true,
-                    configurable: true
-                });
-                KHRMaterialsPbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
@@ -5169,7 +5210,7 @@ var BABYLON;
                         return (material._loaded = Promise.all(promises).then(function () { }));
                     });
                 };
-                KHRMaterialsPbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -5196,10 +5237,10 @@ var BABYLON;
                     loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
-                return KHRMaterialsPbrSpecularGlossiness;
+                return KHR_materials_pbrSpecularGlossiness;
             }(GLTF2.GLTFLoaderExtension));
-            Extensions.KHRMaterialsPbrSpecularGlossiness = KHRMaterialsPbrSpecularGlossiness;
-            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHRMaterialsPbrSpecularGlossiness(loader); });
+            Extensions.KHR_materials_pbrSpecularGlossiness = KHR_materials_pbrSpecularGlossiness;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHR_materials_pbrSpecularGlossiness(loader); });
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
@@ -5223,19 +5264,14 @@ var BABYLON;
                 LightType["POINT"] = "point";
                 LightType["SPOT"] = "spot";
             })(LightType || (LightType = {}));
-            var KHRLights = /** @class */ (function (_super) {
-                __extends(KHRLights, _super);
-                function KHRLights() {
-                    return _super !== null && _super.apply(this, arguments) || this;
+            var KHR_lights = /** @class */ (function (_super) {
+                __extends(KHR_lights, _super);
+                function KHR_lights() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
                 }
-                Object.defineProperty(KHRLights.prototype, "_name", {
-                    get: function () {
-                        return NAME;
-                    },
-                    enumerable: true,
-                    configurable: true
-                });
-                KHRLights.prototype._loadSceneAsync = function (context, scene) {
+                KHR_lights.prototype._loadSceneAsync = function (context, scene) {
                     var _this = this;
                     return this._loadExtensionAsync(context, scene, function (context, extension) {
                         var promise = _this._loader._loadSceneAsync(context, scene);
@@ -5247,7 +5283,7 @@ var BABYLON;
                         return promise;
                     });
                 };
-                KHRLights.prototype._loadNodeAsync = function (context, node) {
+                KHR_lights.prototype._loadNodeAsync = function (context, node) {
                     var _this = this;
                     return this._loadExtensionAsync(context, node, function (context, extension) {
                         var promise = _this._loader._loadNodeAsync(context, node);
@@ -5284,22 +5320,22 @@ var BABYLON;
                         return promise;
                     });
                 };
-                Object.defineProperty(KHRLights.prototype, "_lights", {
+                Object.defineProperty(KHR_lights.prototype, "_lights", {
                     get: function () {
                         var extensions = this._loader._gltf.extensions;
-                        if (!extensions || !extensions[this._name]) {
-                            throw new Error("#/extensions: " + this._name + " not found");
+                        if (!extensions || !extensions[this.name]) {
+                            throw new Error("#/extensions: " + this.name + " not found");
                         }
-                        var extension = extensions[this._name];
+                        var extension = extensions[this.name];
                         return extension.lights;
                     },
                     enumerable: true,
                     configurable: true
                 });
-                return KHRLights;
+                return KHR_lights;
             }(GLTF2.GLTFLoaderExtension));
-            Extensions.KHRLights = KHRLights;
-            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHRLights(loader); });
+            Extensions.KHR_lights = KHR_lights;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHR_lights(loader); });
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));

ファイルの差分が大きいため隠しています
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


+ 36 - 26
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -129,30 +129,34 @@ declare module BABYLON {
         json: Object;
         bin: Nullable<ArrayBufferView>;
     }
+    interface IGLTFLoaderExtension {
+        /**
+         * The name of this extension.
+         */
+        readonly name: string;
+        /**
+         * Whether this extension is enabled.
+         */
+        enabled: boolean;
+    }
     enum GLTFLoaderState {
         Loading = 0,
         Ready = 1,
         Complete = 2,
     }
-    interface IGLTFLoaderExtension {
-        enabled: boolean;
-    }
-    interface IGLTFLoaderExtensions {
-        [name: string]: IGLTFLoaderExtension;
-    }
     interface IGLTFLoader extends IDisposable {
         coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
         animationStartMode: GLTFLoaderAnimationStartMode;
         compileMaterials: boolean;
         useClipPlane: boolean;
         compileShadowGenerators: boolean;
-        onDisposeObservable: Observable<IGLTFLoader>;
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
         onCompleteObservable: Observable<IGLTFLoader>;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
-        extensions: Nullable<IGLTFLoaderExtensions>;
         importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{
             meshes: AbstractMesh[];
             particleSystems: ParticleSystem[];
@@ -226,13 +230,16 @@ declare module BABYLON {
         private _onDisposeObserver;
         onDispose: () => void;
         /**
-         * The loader state or null if not active.
+         * Raised after a loader extension is created.
+         * Set additional options for a loader extension in this event.
          */
-        readonly loaderState: Nullable<GLTFLoaderState>;
+        readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
+        private _onExtensionLoadedObserver;
+        onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
-         * The loader extensions or null if not active.
+         * The loader state or null if not active.
          */
-        readonly loaderExtensions: Nullable<IGLTFLoaderExtensions>;
+        readonly loaderState: Nullable<GLTFLoaderState>;
         private _loader;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
@@ -664,8 +671,8 @@ declare module BABYLON.GLTF1 {
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
         onCompleteObservable: Observable<IGLTFLoader>;
+        onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
-        extensions: Nullable<IGLTFLoaderExtensions>;
         dispose(): void;
         private _importMeshAsync(meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError);
         importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress: (event: SceneLoaderProgressEvent) => void): Promise<{
@@ -939,10 +946,9 @@ declare module BABYLON.GLTF2 {
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         readonly onMaterialLoadedObservable: Observable<Material>;
+        readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         readonly onCompleteObservable: Observable<IGLTFLoader>;
         readonly state: Nullable<GLTFLoaderState>;
-        readonly extensions: IGLTFLoaderExtensions;
-        constructor();
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
             meshes: AbstractMesh[];
@@ -951,6 +957,7 @@ declare module BABYLON.GLTF2 {
         }>;
         loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
         private _loadAsync(nodes, scene, data, rootUrl, onProgress?);
+        private _loadExtensions();
         private _loadData(data);
         private _setupData();
         private _createRootNode();
@@ -1001,19 +1008,18 @@ declare module BABYLON.GLTF2 {
         private static _ValidateUri(uri);
         private _compileMaterialsAsync();
         private _compileShadowGeneratorsAsync();
-        private _abortRequests();
-        private _releaseResources();
+        private _clear();
         _applyExtensions<T>(actionAsync: (extension: GLTFLoaderExtension) => Nullable<Promise<T>>): Nullable<Promise<T>>;
     }
 }
 
 
 declare module BABYLON.GLTF2 {
-    abstract class GLTFLoaderExtension {
+    abstract class GLTFLoaderExtension implements IGLTFLoaderExtension {
         enabled: boolean;
+        readonly abstract name: string;
         protected _loader: GLTFLoader;
         constructor(loader: GLTFLoader);
-        protected readonly abstract _name: string;
         /** Override this method to modify the default behavior for loading scenes. */
         protected _loadSceneAsync(context: string, node: ILoaderScene): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading nodes. */
@@ -1037,26 +1043,30 @@ declare module BABYLON.GLTF2 {
 
 
 declare module BABYLON.GLTF2.Extensions {
-    class MSFTLOD extends GLTFLoaderExtension {
+    class MSFT_lod extends GLTFLoaderExtension {
+        readonly name: string;
+        /**
+         * Maximum number of LODs to load, starting from the lowest LOD.
+         */
+        maxLODsToLoad: number;
         private _loadingNodeLOD;
         private _loadNodeSignals;
         private _loadingMaterialLOD;
         private _loadMaterialSignals;
-        protected readonly _name: string;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
         protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /**
          * Gets an array of LOD properties from lowest to highest.
          */
-        private static _GetLODs<T>(context, property, array, ids);
+        private _getLODs<T>(context, property, array, ids);
     }
 }
 
 
 declare module BABYLON.GLTF2.Extensions {
-    class KHRMaterialsPbrSpecularGlossiness extends GLTFLoaderExtension {
-        protected readonly _name: string;
+    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);
     }
@@ -1064,8 +1074,8 @@ declare module BABYLON.GLTF2.Extensions {
 
 
 declare module BABYLON.GLTF2.Extensions {
-    class KHRLights extends GLTFLoaderExtension {
-        protected readonly _name: string;
+    class KHR_lights extends GLTFLoaderExtension {
+        readonly name: string;
         protected _loadSceneAsync(context: string, scene: ILoaderScene): Nullable<Promise<void>>;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
         private readonly _lights;

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

@@ -88,6 +88,7 @@ var BABYLON;
                     }
                 }
             }
+            defines.ALPHATEST = this._opacityTexture ? true : false;
             // Misc.
             if (defines._areMiscDirty) {
                 defines.POINTSIZE = (this.pointsCloud || scene.forcePointsCloud);

ファイルの差分が大きいため隠しています
+ 1 - 1
dist/preview release/materialsLibrary/babylon.fireMaterial.min.js


+ 1 - 0
dist/preview release/materialsLibrary/babylonjs.materials.js

@@ -2118,6 +2118,7 @@ var BABYLON;
                     }
                 }
             }
+            defines.ALPHATEST = this._opacityTexture ? true : false;
             // Misc.
             if (defines._areMiscDirty) {
                 defines.POINTSIZE = (this.pointsCloud || scene.forcePointsCloud);

ファイルの差分が大きいため隠しています
+ 1 - 1
dist/preview release/materialsLibrary/babylonjs.materials.min.js


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

@@ -189,13 +189,6 @@ declare module BABYLON.GLTF2 {
          */
         private setNodeTransformation(node, babylonMesh, useRightHandedSystem);
         /**
-         *
-         * @param babylonTexture - Babylon texture to extract.
-         * @param mimeType - Mime Type of the babylonTexture.
-         * @return - glTF texture, or null if the texture format is not supported.
-         */
-        private exportTexture(babylonTexture, mimeType?);
-        /**
          * Creates a bufferview based on the vertices type for the Babylon mesh
          * @param kind - Indicates the type of vertices data.
          * @param babylonMesh - The Babylon mesh to get the vertices data from.
@@ -261,9 +254,25 @@ declare module BABYLON.GLTF2 {
          */
         private static readonly dielectricSpecular;
         /**
-         * Epsilon value, used as a small tolerance value for a numeric value.
+         * Allows the maximum specular power to be defined for material calculations.
+         */
+        private static maxSpecularPower;
+        /**
+         * 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
          */
-        private static readonly epsilon;
+        static ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        }, hasTextureCoords: boolean): void;
         /**
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
          * @param babylonStandardMaterial
@@ -271,19 +280,6 @@ declare module BABYLON.GLTF2 {
          */
         static ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
         /**
-         * Converts Specular Glossiness to Metallic Roughness.  This is based on the algorithm used in the Babylon glTF 3ds Max Exporter.
-         * {@link https://github.com/BabylonJS/Exporters/blob/master/3ds%20Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.Material.cs}
-         * @param  babylonSpecularGlossiness - Babylon specular glossiness parameters
-         * @returns - Babylon metallic roughness values
-         */
-        private static _ConvertToMetallicRoughness(babylonSpecularGlossiness);
-        /**
-         * Returns the perceived brightness value based on the provided color
-         * @param color - color used in calculating the perceived brightness
-         * @returns - perceived brightness value
-         */
-        private static PerceivedBrightness(color);
-        /**
          * Computes the metallic factor
          * @param diffuse - diffused value
          * @param specular - specular value
@@ -297,5 +293,52 @@ declare module BABYLON.GLTF2 {
          * @returns - The Babylon alpha mode value
          */
         static GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
+        /**
+         * Converts a Babylon Standard Material to a glTF Material.
+         * @param babylonStandardMaterial - BJS Standard 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 ConvertStandardMaterial(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        }, hasTextureCoords: boolean): void;
+        /**
+         * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
+         * @param babylonPBRMetalRoughMaterial - 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 ConvertPBRMetallicRoughnessMaterial(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, 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.
+         * @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.
+         */
+        static ExportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        }): Nullable<ITextureInfo>;
     }
 }

+ 297 - 248
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -502,84 +502,6 @@ var BABYLON;
                 }
             };
             /**
-             *
-             * @param babylonTexture - Babylon texture to extract.
-             * @param mimeType - Mime Type of the babylonTexture.
-             * @return - glTF texture, or null if the texture format is not supported.
-             */
-            _Exporter.prototype.exportTexture = function (babylonTexture, mimeType) {
-                if (mimeType === void 0) { mimeType = "image/jpeg" /* JPEG */; }
-                var textureInfo = null;
-                var glTFTexture;
-                glTFTexture = {
-                    source: this.images.length
-                };
-                var textureName = babylonTexture.getInternalTexture().url;
-                if (textureName.search('/') !== -1) {
-                    var splitFilename = textureName.split('/');
-                    textureName = splitFilename[splitFilename.length - 1];
-                    var basefile = textureName.split('.')[0];
-                    var extension = textureName.split('.')[1];
-                    if (mimeType === "image/jpeg" /* JPEG */) {
-                        extension = ".jpg";
-                    }
-                    else if (mimeType === "image/png" /* PNG */) {
-                        extension = ".png";
-                    }
-                    else {
-                        throw new Error("Unsupported mime type " + mimeType);
-                    }
-                    textureName = basefile + extension;
-                }
-                var pixels = babylonTexture.readPixels();
-                var imageCanvas = document.createElement('canvas');
-                imageCanvas.id = "ImageCanvas";
-                var ctx = imageCanvas.getContext('2d');
-                var size = babylonTexture.getSize();
-                imageCanvas.width = size.width;
-                imageCanvas.height = size.height;
-                var imgData = ctx.createImageData(size.width, size.height);
-                imgData.data.set(pixels);
-                ctx.putImageData(imgData, 0, 0);
-                var base64Data = imageCanvas.toDataURL(mimeType);
-                var binStr = atob(base64Data.split(',')[1]);
-                var arr = new Uint8Array(binStr.length);
-                for (var i = 0; i < binStr.length; ++i) {
-                    arr[i] = binStr.charCodeAt(i);
-                }
-                var imageValues = { data: arr, mimeType: mimeType };
-                this.imageData[textureName] = imageValues;
-                if (mimeType === "image/jpeg" /* JPEG */) {
-                    var glTFImage = {
-                        uri: textureName
-                    };
-                    var foundIndex = -1;
-                    for (var i = 0; i < this.images.length; ++i) {
-                        if (this.images[i].uri === textureName) {
-                            foundIndex = i;
-                            break;
-                        }
-                    }
-                    if (foundIndex === -1) {
-                        this.images.push(glTFImage);
-                        glTFTexture.source = this.images.length - 1;
-                        this.textures.push({
-                            source: this.images.length - 1
-                        });
-                        textureInfo = {
-                            index: this.images.length - 1
-                        };
-                    }
-                    else {
-                        glTFTexture.source = foundIndex;
-                        textureInfo = {
-                            index: foundIndex
-                        };
-                    }
-                }
-                return textureInfo;
-            };
-            /**
              * Creates a bufferview based on the vertices type for the Babylon mesh
              * @param kind - Indicates the type of vertices data.
              * @param babylonMesh - The Babylon mesh to get the vertices data from.
@@ -645,7 +567,7 @@ var BABYLON;
                                     break;
                                 }
                                 default: {
-                                    console.warn("Unsupported VertexBuffer kind: " + kind);
+                                    BABYLON.Tools.Warn("Unsupported VertexBuffer kind: " + kind);
                                 }
                             }
                             if (bufferViewName !== null) {
@@ -799,120 +721,20 @@ var BABYLON;
                             }
                         }
                         if (bufferMesh.material) {
-                            if (bufferMesh.material instanceof BABYLON.StandardMaterial) {
-                                console.warn("Standard Material is currently not fully supported/implemented in glTF serializer");
-                                var babylonStandardMaterial = bufferMesh.material;
-                                var glTFPbrMetallicRoughness = GLTF2._GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
-                                var glTFMaterial = { name: babylonStandardMaterial.name };
-                                if (!babylonStandardMaterial.backFaceCulling) {
-                                    glTFMaterial.doubleSided = true;
-                                }
-                                if (babylonStandardMaterial.diffuseTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                                    var glTFTexture = this.exportTexture(babylonStandardMaterial.diffuseTexture);
-                                    if (glTFTexture !== null) {
-                                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                                    }
-                                }
-                                if (babylonStandardMaterial.bumpTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                                    var glTFTexture = this.exportTexture(babylonStandardMaterial.bumpTexture);
-                                    if (glTFTexture) {
-                                        glTFMaterial.normalTexture = glTFTexture;
-                                    }
-                                }
-                                if (babylonStandardMaterial.emissiveTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                                    var glTFEmissiveTexture = this.exportTexture(babylonStandardMaterial.emissiveTexture);
-                                    if (glTFEmissiveTexture) {
-                                        glTFMaterial.emissiveTexture = glTFEmissiveTexture;
-                                    }
-                                    glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
-                                }
-                                if (babylonStandardMaterial.ambientTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                                    var glTFOcclusionTexture = this.exportTexture(babylonStandardMaterial.ambientTexture);
-                                    if (glTFOcclusionTexture) {
-                                        glTFMaterial.occlusionTexture = glTFOcclusionTexture;
-                                    }
-                                }
-                                if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
-                                    if (babylonStandardMaterial.alphaMode === BABYLON.Engine.ALPHA_COMBINE) {
-                                        glTFMaterial.alphaMode = "BLEND" /* BLEND */;
-                                    }
-                                    else {
-                                        console.warn("glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
-                                    }
-                                }
-                                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
-                                this.materials.push(glTFMaterial);
-                                meshPrimitive.material = this.materials.length - 1;
+                            if (bufferMesh.material instanceof BABYLON.StandardMaterial || bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial) {
+                                var materialIndex = babylonMesh.getScene().materials.indexOf(bufferMesh.material);
+                                meshPrimitive.material = materialIndex;
                             }
-                            else if (bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial) {
-                                var babylonPBRMaterial = bufferMesh.material;
-                                var glTFPbrMetallicRoughness = {};
-                                if (babylonPBRMaterial.baseColor) {
-                                    glTFPbrMetallicRoughness.baseColorFactor = [
-                                        babylonPBRMaterial.baseColor.r,
-                                        babylonPBRMaterial.baseColor.g,
-                                        babylonPBRMaterial.baseColor.b,
-                                        babylonPBRMaterial.alpha
-                                    ];
-                                }
-                                if (babylonPBRMaterial.baseTexture !== undefined) {
-                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.baseTexture);
-                                    if (glTFTexture !== null) {
-                                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                                    }
-                                    glTFPbrMetallicRoughness.baseColorTexture;
-                                }
-                                if (babylonPBRMaterial.metallic !== undefined) {
-                                    glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
-                                }
-                                if (babylonPBRMaterial.roughness !== undefined) {
-                                    glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
-                                }
-                                var glTFMaterial = {
-                                    name: babylonPBRMaterial.name
-                                };
-                                if (babylonPBRMaterial.doubleSided) {
-                                    glTFMaterial.doubleSided = babylonPBRMaterial.doubleSided;
-                                }
-                                if (babylonPBRMaterial.normalTexture) {
-                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.normalTexture);
-                                    if (glTFTexture) {
-                                        glTFMaterial.normalTexture = glTFTexture;
-                                    }
-                                }
-                                if (babylonPBRMaterial.occlusionTexture) {
-                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.occlusionTexture);
-                                    if (glTFTexture) {
-                                        glTFMaterial.occlusionTexture = glTFTexture;
-                                        if (babylonPBRMaterial.occlusionStrength !== undefined) {
-                                            glTFMaterial.occlusionTexture.strength = babylonPBRMaterial.occlusionStrength;
-                                        }
-                                    }
-                                }
-                                if (babylonPBRMaterial.emissiveTexture) {
-                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.emissiveTexture);
-                                    if (glTFTexture !== null) {
-                                        glTFMaterial.emissiveTexture = glTFTexture;
-                                    }
-                                }
-                                if (!babylonPBRMaterial.emissiveColor.equals(new BABYLON.Color3(0.0, 0.0, 0.0))) {
-                                    glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                            else if (bufferMesh.material instanceof BABYLON.MultiMaterial) {
+                                var babylonMultiMaterial = bufferMesh.material;
+                                var material = babylonMultiMaterial.subMaterials[submesh.materialIndex];
+                                if (material !== null) {
+                                    var materialIndex = babylonMesh.getScene().materials.indexOf(material);
+                                    meshPrimitive.material = materialIndex;
                                 }
-                                if (babylonPBRMaterial.transparencyMode) {
-                                    var alphaMode = GLTF2._GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
-                                    if (alphaMode !== "OPAQUE" /* OPAQUE */) {
-                                        glTFMaterial.alphaMode = alphaMode;
-                                        if (alphaMode === "BLEND" /* BLEND */) {
-                                            glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
-                                        }
-                                    }
-                                }
-                                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
-                                this.materials.push(glTFMaterial);
-                                meshPrimitive.material = this.materials.length - 1;
                             }
                             else {
-                                console.warn("Material type is not yet implemented in glTF serializer: " + bufferMesh.material.name);
+                                BABYLON.Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
                             }
                         }
                         mesh.primitives.push(meshPrimitive);
@@ -932,6 +754,9 @@ var BABYLON;
                 if (babylonScene.meshes.length > 0) {
                     var babylonMeshes = babylonScene.meshes;
                     var scene = { nodes: new Array() };
+                    if (dataBuffer == null) {
+                        GLTF2._GLTFMaterial.ConvertMaterialsToGLTF(babylonScene.materials, "image/jpeg" /* JPEG */, this.images, this.textures, this.materials, this.imageData, true);
+                    }
                     for (var i = 0; i < babylonMeshes.length; ++i) {
                         if (this.options &&
                             this.options.shouldExportMesh !== undefined &&
@@ -1042,70 +867,79 @@ var BABYLON;
             function _GLTFMaterial() {
             }
             /**
+             * 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
+             */
+            _GLTFMaterial.ConvertMaterialsToGLTF = function (babylonMaterials, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+                for (var i = 0; i < babylonMaterials.length; ++i) {
+                    var babylonMaterial = babylonMaterials[i];
+                    if (babylonMaterial instanceof BABYLON.StandardMaterial) {
+                        _GLTFMaterial.ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                    }
+                    else if (babylonMaterial instanceof BABYLON.PBRMetallicRoughnessMaterial) {
+                        _GLTFMaterial.ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                    }
+                }
+            };
+            /**
              * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
              * @param babylonStandardMaterial
              * @returns - glTF Metallic Roughness Material representation
              */
             _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness = function (babylonStandardMaterial) {
-                var babylonSpecularGlossiness = {
-                    diffuse: babylonStandardMaterial.diffuseColor,
-                    opacity: babylonStandardMaterial.alpha,
-                    specular: babylonStandardMaterial.specularColor || BABYLON.Color3.Black(),
-                    glossiness: babylonStandardMaterial.specularPower / 256
-                };
-                if (babylonStandardMaterial.specularTexture) {
+                var P0 = new BABYLON.Vector2(0, 1);
+                var P1 = new BABYLON.Vector2(0, 0.1);
+                var P2 = new BABYLON.Vector2(0, 0.1);
+                var P3 = new BABYLON.Vector2(1300, 0.1);
+                /**
+                 * Given the control points, solve for x based on a given t for a cubic bezier curve.
+                 * @param t - a value between 0 and 1.
+                 * @param p0 - first control point.
+                 * @param p1 - second control point.
+                 * @param p2 - third control point.
+                 * @param p3 - fourth control point.
+                 * @returns - number result of cubic bezier curve at the specified t.
+                 */
+                function cubicBezierCurve(t, p0, p1, p2, p3) {
+                    return ((1 - t) * (1 - t) * (1 - t) * p0 +
+                        3 * (1 - t) * (1 - t) * t * p1 +
+                        3 * (1 - t) * t * t * p2 +
+                        t * t * t * p3);
                 }
-                var babylonMetallicRoughness = _GLTFMaterial._ConvertToMetallicRoughness(babylonSpecularGlossiness);
+                /**
+                 * Evaluates a specified specular power value to determine the appropriate roughness value,
+                 * based on a pre-defined cubic bezier curve with specular on the abscissa axis (x-axis)
+                 * and roughness on the ordinant axis (y-axis).
+                 * @param specularPower - specular power of standard material.
+                 * @returns - Number representing the roughness value.
+                 */
+                function solveForRoughness(specularPower) {
+                    var t = Math.pow(specularPower / P3.x, 0.333333);
+                    return cubicBezierCurve(t, P0.y, P1.y, P2.y, P3.y);
+                }
+                var diffuse = babylonStandardMaterial.diffuseColor.toLinearSpace().scale(0.5);
+                var opacity = babylonStandardMaterial.alpha;
+                var specularPower = BABYLON.Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this.maxSpecularPower);
+                var roughness = solveForRoughness(specularPower);
                 var glTFPbrMetallicRoughness = {
                     baseColorFactor: [
-                        babylonMetallicRoughness.baseColor.r,
-                        babylonMetallicRoughness.baseColor.g,
-                        babylonMetallicRoughness.baseColor.b,
-                        babylonMetallicRoughness.opacity
+                        diffuse.r,
+                        diffuse.g,
+                        diffuse.b,
+                        opacity
                     ],
-                    metallicFactor: babylonMetallicRoughness.metallic,
-                    roughnessFactor: babylonMetallicRoughness.roughness
+                    metallicFactor: 0,
+                    roughnessFactor: roughness,
                 };
                 return glTFPbrMetallicRoughness;
             };
             /**
-             * Converts Specular Glossiness to Metallic Roughness.  This is based on the algorithm used in the Babylon glTF 3ds Max Exporter.
-             * {@link https://github.com/BabylonJS/Exporters/blob/master/3ds%20Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.Material.cs}
-             * @param  babylonSpecularGlossiness - Babylon specular glossiness parameters
-             * @returns - Babylon metallic roughness values
-             */
-            _GLTFMaterial._ConvertToMetallicRoughness = function (babylonSpecularGlossiness) {
-                var diffuse = babylonSpecularGlossiness.diffuse;
-                var opacity = babylonSpecularGlossiness.opacity;
-                var specular = babylonSpecularGlossiness.specular;
-                var glossiness = BABYLON.Scalar.Clamp(babylonSpecularGlossiness.glossiness);
-                var oneMinusSpecularStrength = 1 - Math.max(specular.r, Math.max(specular.g, specular.b));
-                var diffusePerceivedBrightness = _GLTFMaterial.PerceivedBrightness(diffuse);
-                var specularPerceivedBrightness = _GLTFMaterial.PerceivedBrightness(specular);
-                var metallic = _GLTFMaterial.SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
-                var diffuseScaleFactor = oneMinusSpecularStrength / (1 - this.dielectricSpecular.r) / Math.max(1 - metallic, this.epsilon);
-                var baseColorFromDiffuse = diffuse.scale(diffuseScaleFactor);
-                var baseColorFromSpecular = specular.subtract(this.dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this.epsilon));
-                var lerpColor = BABYLON.Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
-                var baseColor = new BABYLON.Color3();
-                lerpColor.clampToRef(0, 1, baseColor);
-                var babylonMetallicRoughness = {
-                    baseColor: baseColor,
-                    opacity: opacity,
-                    metallic: metallic,
-                    roughness: 1.0 - glossiness
-                };
-                return babylonMetallicRoughness;
-            };
-            /**
-             * Returns the perceived brightness value based on the provided color
-             * @param color - color used in calculating the perceived brightness
-             * @returns - perceived brightness value
-             */
-            _GLTFMaterial.PerceivedBrightness = function (color) {
-                return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
-            };
-            /**
              * Computes the metallic factor
              * @param diffuse - diffused value
              * @param specular - specular value
@@ -1113,14 +947,15 @@ var BABYLON;
              * @returns - metallic value
              */
             _GLTFMaterial.SolveMetallic = function (diffuse, specular, oneMinusSpecularStrength) {
-                if (specular < this.dielectricSpecular.r) {
+                if (specular < _GLTFMaterial.dielectricSpecular.r) {
+                    _GLTFMaterial.dielectricSpecular;
                     return 0;
                 }
-                var a = this.dielectricSpecular.r;
-                var b = diffuse * oneMinusSpecularStrength / (1.0 - this.dielectricSpecular.r) + specular - 2.0 * this.dielectricSpecular.r;
-                var c = this.dielectricSpecular.r - specular;
+                var a = _GLTFMaterial.dielectricSpecular.r;
+                var b = diffuse * oneMinusSpecularStrength / (1.0 - _GLTFMaterial.dielectricSpecular.r) + specular - 2.0 * _GLTFMaterial.dielectricSpecular.r;
+                var c = _GLTFMaterial.dielectricSpecular.r - specular;
                 var D = b * b - 4.0 * a * c;
-                return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a));
+                return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a), 0, 1);
             };
             /**
              * Gets the glTF alpha mode from the Babylon Material
@@ -1152,7 +987,7 @@ var BABYLON;
                             return "MASK" /* MASK */;
                         }
                         case BABYLON.PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND: {
-                            console.warn("GLTF Exporter | Alpha test and blend mode not supported in glTF.  Alpha blend used instead.");
+                            BABYLON.Tools.Warn(babylonMaterial.name + ": GLTF Exporter | Alpha test and blend mode not supported in glTF.  Alpha blend used instead.");
                             return "BLEND" /* BLEND */;
                         }
                         default: {
@@ -1165,13 +1000,227 @@ var BABYLON;
                 }
             };
             /**
+             * Converts a Babylon Standard Material to a glTF Material.
+             * @param babylonStandardMaterial - BJS Standard 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.ConvertStandardMaterial = function (babylonStandardMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+                BABYLON.Tools.Warn(babylonStandardMaterial.name + ": Standard Material is currently not fully supported/implemented in glTF serializer");
+                var glTFPbrMetallicRoughness = _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
+                var glTFMaterial = { name: babylonStandardMaterial.name };
+                if (babylonStandardMaterial.backFaceCulling) {
+                    if (!babylonStandardMaterial.twoSidedLighting) {
+                        BABYLON.Tools.Warn(babylonStandardMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
+                    }
+                    glTFMaterial.doubleSided = true;
+                }
+                if (hasTextureCoords) {
+                    if (babylonStandardMaterial.diffuseTexture) {
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture != null) {
+                            glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
+                        }
+                    }
+                    if (babylonStandardMaterial.bumpTexture) {
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            glTFMaterial.normalTexture = glTFTexture;
+                        }
+                    }
+                    if (babylonStandardMaterial.emissiveTexture) {
+                        var 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) {
+                        var glTFOcclusionTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
+                        if (glTFOcclusionTexture) {
+                            glTFMaterial.occlusionTexture = glTFOcclusionTexture;
+                        }
+                    }
+                }
+                if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
+                    if (babylonStandardMaterial.alphaMode === BABYLON.Engine.ALPHA_COMBINE) {
+                        glTFMaterial.alphaMode = "BLEND" /* BLEND */;
+                    }
+                    else {
+                        BABYLON.Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
+                    }
+                }
+                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
+                materials.push(glTFMaterial);
+            };
+            /**
+             * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
+             * @param babylonPBRMetalRoughMaterial - 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.ConvertPBRMetallicRoughnessMaterial = function (babylonPBRMetalRoughMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+                var glTFPbrMetallicRoughness = {};
+                if (babylonPBRMetalRoughMaterial.baseColor) {
+                    glTFPbrMetallicRoughness.baseColorFactor = [
+                        babylonPBRMetalRoughMaterial.baseColor.r,
+                        babylonPBRMetalRoughMaterial.baseColor.g,
+                        babylonPBRMetalRoughMaterial.baseColor.b,
+                        babylonPBRMetalRoughMaterial.alpha
+                    ];
+                }
+                if (babylonPBRMetalRoughMaterial.metallic != null) {
+                    glTFPbrMetallicRoughness.metallicFactor = babylonPBRMetalRoughMaterial.metallic;
+                }
+                if (babylonPBRMetalRoughMaterial.roughness != null) {
+                    glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMetalRoughMaterial.roughness;
+                }
+                var glTFMaterial = {
+                    name: babylonPBRMetalRoughMaterial.name
+                };
+                if (babylonPBRMetalRoughMaterial.doubleSided) {
+                    glTFMaterial.doubleSided = babylonPBRMetalRoughMaterial.doubleSided;
+                }
+                if (hasTextureCoords) {
+                    if (babylonPBRMetalRoughMaterial.baseTexture != null) {
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture != null) {
+                            glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
+                        }
+                    }
+                    if (babylonPBRMetalRoughMaterial.normalTexture) {
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            glTFMaterial.normalTexture = glTFTexture;
+                        }
+                    }
+                    if (babylonPBRMetalRoughMaterial.occlusionTexture) {
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            glTFMaterial.occlusionTexture = glTFTexture;
+                            if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
+                                glTFMaterial.occlusionTexture.strength = babylonPBRMetalRoughMaterial.occlusionStrength;
+                            }
+                        }
+                    }
+                    if (babylonPBRMetalRoughMaterial.emissiveTexture) {
+                        var 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)) {
+                    glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
+                }
+                if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
+                    var alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMetalRoughMaterial);
+                    if (alphaMode !== "OPAQUE" /* OPAQUE */) {
+                        glTFMaterial.alphaMode = alphaMode;
+                        if (alphaMode === "BLEND" /* BLEND */) {
+                            glTFMaterial.alphaCutoff = babylonPBRMetalRoughMaterial.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.
+             * @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.
+             */
+            _GLTFMaterial.ExportTexture = function (babylonTexture, mimeType, images, textures, imageData) {
+                var textureInfo = null;
+                var glTFTexture = {
+                    source: images.length
+                };
+                var textureName = "texture_" + (textures.length - 1).toString();
+                var textureData = babylonTexture.getInternalTexture();
+                if (textureData != null) {
+                    textureName = textureData.url;
+                }
+                textureName = BABYLON.Tools.GetFilename(textureName);
+                var baseFile = textureName.split('.')[0];
+                var extension = "";
+                if (mimeType === "image/jpeg" /* JPEG */) {
+                    extension = ".jpg";
+                }
+                else if (mimeType === "image/png" /* PNG */) {
+                    extension = ".png";
+                }
+                else {
+                    BABYLON.Tools.Error("Unsupported mime type " + mimeType);
+                }
+                textureName = baseFile + extension;
+                var pixels = babylonTexture.readPixels();
+                var imageCanvas = document.createElement('canvas');
+                imageCanvas.id = "ImageCanvas";
+                var ctx = imageCanvas.getContext('2d');
+                var size = babylonTexture.getSize();
+                imageCanvas.width = size.width;
+                imageCanvas.height = size.height;
+                var imgData = ctx.createImageData(size.width, size.height);
+                imgData.data.set(pixels);
+                ctx.putImageData(imgData, 0, 0);
+                var base64Data = imageCanvas.toDataURL(mimeType);
+                var binStr = atob(base64Data.split(',')[1]);
+                var arr = new Uint8Array(binStr.length);
+                for (var i = 0; i < binStr.length; ++i) {
+                    arr[i] = binStr.charCodeAt(i);
+                }
+                var imageValues = { data: arr, mimeType: mimeType };
+                imageData[textureName] = imageValues;
+                if (mimeType === "image/jpeg" /* JPEG */) {
+                    var glTFImage = {
+                        uri: textureName
+                    };
+                    var foundIndex = -1;
+                    for (var i = 0; i < images.length; ++i) {
+                        if (images[i].uri === textureName) {
+                            foundIndex = i;
+                            break;
+                        }
+                    }
+                    if (foundIndex === -1) {
+                        images.push(glTFImage);
+                        glTFTexture.source = images.length - 1;
+                        textures.push({
+                            source: images.length - 1
+                        });
+                        textureInfo = {
+                            index: images.length - 1
+                        };
+                    }
+                    else {
+                        glTFTexture.source = foundIndex;
+                        textureInfo = {
+                            index: foundIndex
+                        };
+                    }
+                }
+                return textureInfo;
+            };
+            /**
              * Represents the dielectric specular values for R, G and B.
              */
             _GLTFMaterial.dielectricSpecular = new BABYLON.Color3(0.04, 0.04, 0.04);
             /**
-             * Epsilon value, used as a small tolerance value for a numeric value.
+             * Allows the maximum specular power to be defined for material calculations.
              */
-            _GLTFMaterial.epsilon = 1e-6;
+            _GLTFMaterial.maxSpecularPower = 1024;
             return _GLTFMaterial;
         }());
         GLTF2._GLTFMaterial = _GLTFMaterial;

ファイルの差分が大きいため隠しています
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 297 - 248
dist/preview release/serializers/babylonjs.serializers.js

@@ -646,84 +646,6 @@ var BABYLON;
                 }
             };
             /**
-             *
-             * @param babylonTexture - Babylon texture to extract.
-             * @param mimeType - Mime Type of the babylonTexture.
-             * @return - glTF texture, or null if the texture format is not supported.
-             */
-            _Exporter.prototype.exportTexture = function (babylonTexture, mimeType) {
-                if (mimeType === void 0) { mimeType = "image/jpeg" /* JPEG */; }
-                var textureInfo = null;
-                var glTFTexture;
-                glTFTexture = {
-                    source: this.images.length
-                };
-                var textureName = babylonTexture.getInternalTexture().url;
-                if (textureName.search('/') !== -1) {
-                    var splitFilename = textureName.split('/');
-                    textureName = splitFilename[splitFilename.length - 1];
-                    var basefile = textureName.split('.')[0];
-                    var extension = textureName.split('.')[1];
-                    if (mimeType === "image/jpeg" /* JPEG */) {
-                        extension = ".jpg";
-                    }
-                    else if (mimeType === "image/png" /* PNG */) {
-                        extension = ".png";
-                    }
-                    else {
-                        throw new Error("Unsupported mime type " + mimeType);
-                    }
-                    textureName = basefile + extension;
-                }
-                var pixels = babylonTexture.readPixels();
-                var imageCanvas = document.createElement('canvas');
-                imageCanvas.id = "ImageCanvas";
-                var ctx = imageCanvas.getContext('2d');
-                var size = babylonTexture.getSize();
-                imageCanvas.width = size.width;
-                imageCanvas.height = size.height;
-                var imgData = ctx.createImageData(size.width, size.height);
-                imgData.data.set(pixels);
-                ctx.putImageData(imgData, 0, 0);
-                var base64Data = imageCanvas.toDataURL(mimeType);
-                var binStr = atob(base64Data.split(',')[1]);
-                var arr = new Uint8Array(binStr.length);
-                for (var i = 0; i < binStr.length; ++i) {
-                    arr[i] = binStr.charCodeAt(i);
-                }
-                var imageValues = { data: arr, mimeType: mimeType };
-                this.imageData[textureName] = imageValues;
-                if (mimeType === "image/jpeg" /* JPEG */) {
-                    var glTFImage = {
-                        uri: textureName
-                    };
-                    var foundIndex = -1;
-                    for (var i = 0; i < this.images.length; ++i) {
-                        if (this.images[i].uri === textureName) {
-                            foundIndex = i;
-                            break;
-                        }
-                    }
-                    if (foundIndex === -1) {
-                        this.images.push(glTFImage);
-                        glTFTexture.source = this.images.length - 1;
-                        this.textures.push({
-                            source: this.images.length - 1
-                        });
-                        textureInfo = {
-                            index: this.images.length - 1
-                        };
-                    }
-                    else {
-                        glTFTexture.source = foundIndex;
-                        textureInfo = {
-                            index: foundIndex
-                        };
-                    }
-                }
-                return textureInfo;
-            };
-            /**
              * Creates a bufferview based on the vertices type for the Babylon mesh
              * @param kind - Indicates the type of vertices data.
              * @param babylonMesh - The Babylon mesh to get the vertices data from.
@@ -789,7 +711,7 @@ var BABYLON;
                                     break;
                                 }
                                 default: {
-                                    console.warn("Unsupported VertexBuffer kind: " + kind);
+                                    BABYLON.Tools.Warn("Unsupported VertexBuffer kind: " + kind);
                                 }
                             }
                             if (bufferViewName !== null) {
@@ -943,120 +865,20 @@ var BABYLON;
                             }
                         }
                         if (bufferMesh.material) {
-                            if (bufferMesh.material instanceof BABYLON.StandardMaterial) {
-                                console.warn("Standard Material is currently not fully supported/implemented in glTF serializer");
-                                var babylonStandardMaterial = bufferMesh.material;
-                                var glTFPbrMetallicRoughness = GLTF2._GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
-                                var glTFMaterial = { name: babylonStandardMaterial.name };
-                                if (!babylonStandardMaterial.backFaceCulling) {
-                                    glTFMaterial.doubleSided = true;
-                                }
-                                if (babylonStandardMaterial.diffuseTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                                    var glTFTexture = this.exportTexture(babylonStandardMaterial.diffuseTexture);
-                                    if (glTFTexture !== null) {
-                                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                                    }
-                                }
-                                if (babylonStandardMaterial.bumpTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                                    var glTFTexture = this.exportTexture(babylonStandardMaterial.bumpTexture);
-                                    if (glTFTexture) {
-                                        glTFMaterial.normalTexture = glTFTexture;
-                                    }
-                                }
-                                if (babylonStandardMaterial.emissiveTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                                    var glTFEmissiveTexture = this.exportTexture(babylonStandardMaterial.emissiveTexture);
-                                    if (glTFEmissiveTexture) {
-                                        glTFMaterial.emissiveTexture = glTFEmissiveTexture;
-                                    }
-                                    glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
-                                }
-                                if (babylonStandardMaterial.ambientTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                                    var glTFOcclusionTexture = this.exportTexture(babylonStandardMaterial.ambientTexture);
-                                    if (glTFOcclusionTexture) {
-                                        glTFMaterial.occlusionTexture = glTFOcclusionTexture;
-                                    }
-                                }
-                                if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
-                                    if (babylonStandardMaterial.alphaMode === BABYLON.Engine.ALPHA_COMBINE) {
-                                        glTFMaterial.alphaMode = "BLEND" /* BLEND */;
-                                    }
-                                    else {
-                                        console.warn("glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
-                                    }
-                                }
-                                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
-                                this.materials.push(glTFMaterial);
-                                meshPrimitive.material = this.materials.length - 1;
+                            if (bufferMesh.material instanceof BABYLON.StandardMaterial || bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial) {
+                                var materialIndex = babylonMesh.getScene().materials.indexOf(bufferMesh.material);
+                                meshPrimitive.material = materialIndex;
                             }
-                            else if (bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial) {
-                                var babylonPBRMaterial = bufferMesh.material;
-                                var glTFPbrMetallicRoughness = {};
-                                if (babylonPBRMaterial.baseColor) {
-                                    glTFPbrMetallicRoughness.baseColorFactor = [
-                                        babylonPBRMaterial.baseColor.r,
-                                        babylonPBRMaterial.baseColor.g,
-                                        babylonPBRMaterial.baseColor.b,
-                                        babylonPBRMaterial.alpha
-                                    ];
-                                }
-                                if (babylonPBRMaterial.baseTexture !== undefined) {
-                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.baseTexture);
-                                    if (glTFTexture !== null) {
-                                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                                    }
-                                    glTFPbrMetallicRoughness.baseColorTexture;
-                                }
-                                if (babylonPBRMaterial.metallic !== undefined) {
-                                    glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
-                                }
-                                if (babylonPBRMaterial.roughness !== undefined) {
-                                    glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
-                                }
-                                var glTFMaterial = {
-                                    name: babylonPBRMaterial.name
-                                };
-                                if (babylonPBRMaterial.doubleSided) {
-                                    glTFMaterial.doubleSided = babylonPBRMaterial.doubleSided;
-                                }
-                                if (babylonPBRMaterial.normalTexture) {
-                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.normalTexture);
-                                    if (glTFTexture) {
-                                        glTFMaterial.normalTexture = glTFTexture;
-                                    }
-                                }
-                                if (babylonPBRMaterial.occlusionTexture) {
-                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.occlusionTexture);
-                                    if (glTFTexture) {
-                                        glTFMaterial.occlusionTexture = glTFTexture;
-                                        if (babylonPBRMaterial.occlusionStrength !== undefined) {
-                                            glTFMaterial.occlusionTexture.strength = babylonPBRMaterial.occlusionStrength;
-                                        }
-                                    }
-                                }
-                                if (babylonPBRMaterial.emissiveTexture) {
-                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.emissiveTexture);
-                                    if (glTFTexture !== null) {
-                                        glTFMaterial.emissiveTexture = glTFTexture;
-                                    }
-                                }
-                                if (!babylonPBRMaterial.emissiveColor.equals(new BABYLON.Color3(0.0, 0.0, 0.0))) {
-                                    glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                            else if (bufferMesh.material instanceof BABYLON.MultiMaterial) {
+                                var babylonMultiMaterial = bufferMesh.material;
+                                var material = babylonMultiMaterial.subMaterials[submesh.materialIndex];
+                                if (material !== null) {
+                                    var materialIndex = babylonMesh.getScene().materials.indexOf(material);
+                                    meshPrimitive.material = materialIndex;
                                 }
-                                if (babylonPBRMaterial.transparencyMode) {
-                                    var alphaMode = GLTF2._GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
-                                    if (alphaMode !== "OPAQUE" /* OPAQUE */) {
-                                        glTFMaterial.alphaMode = alphaMode;
-                                        if (alphaMode === "BLEND" /* BLEND */) {
-                                            glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
-                                        }
-                                    }
-                                }
-                                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
-                                this.materials.push(glTFMaterial);
-                                meshPrimitive.material = this.materials.length - 1;
                             }
                             else {
-                                console.warn("Material type is not yet implemented in glTF serializer: " + bufferMesh.material.name);
+                                BABYLON.Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
                             }
                         }
                         mesh.primitives.push(meshPrimitive);
@@ -1076,6 +898,9 @@ var BABYLON;
                 if (babylonScene.meshes.length > 0) {
                     var babylonMeshes = babylonScene.meshes;
                     var scene = { nodes: new Array() };
+                    if (dataBuffer == null) {
+                        GLTF2._GLTFMaterial.ConvertMaterialsToGLTF(babylonScene.materials, "image/jpeg" /* JPEG */, this.images, this.textures, this.materials, this.imageData, true);
+                    }
                     for (var i = 0; i < babylonMeshes.length; ++i) {
                         if (this.options &&
                             this.options.shouldExportMesh !== undefined &&
@@ -1186,70 +1011,79 @@ var BABYLON;
             function _GLTFMaterial() {
             }
             /**
+             * 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
+             */
+            _GLTFMaterial.ConvertMaterialsToGLTF = function (babylonMaterials, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+                for (var i = 0; i < babylonMaterials.length; ++i) {
+                    var babylonMaterial = babylonMaterials[i];
+                    if (babylonMaterial instanceof BABYLON.StandardMaterial) {
+                        _GLTFMaterial.ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                    }
+                    else if (babylonMaterial instanceof BABYLON.PBRMetallicRoughnessMaterial) {
+                        _GLTFMaterial.ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                    }
+                }
+            };
+            /**
              * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
              * @param babylonStandardMaterial
              * @returns - glTF Metallic Roughness Material representation
              */
             _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness = function (babylonStandardMaterial) {
-                var babylonSpecularGlossiness = {
-                    diffuse: babylonStandardMaterial.diffuseColor,
-                    opacity: babylonStandardMaterial.alpha,
-                    specular: babylonStandardMaterial.specularColor || BABYLON.Color3.Black(),
-                    glossiness: babylonStandardMaterial.specularPower / 256
-                };
-                if (babylonStandardMaterial.specularTexture) {
+                var P0 = new BABYLON.Vector2(0, 1);
+                var P1 = new BABYLON.Vector2(0, 0.1);
+                var P2 = new BABYLON.Vector2(0, 0.1);
+                var P3 = new BABYLON.Vector2(1300, 0.1);
+                /**
+                 * Given the control points, solve for x based on a given t for a cubic bezier curve.
+                 * @param t - a value between 0 and 1.
+                 * @param p0 - first control point.
+                 * @param p1 - second control point.
+                 * @param p2 - third control point.
+                 * @param p3 - fourth control point.
+                 * @returns - number result of cubic bezier curve at the specified t.
+                 */
+                function cubicBezierCurve(t, p0, p1, p2, p3) {
+                    return ((1 - t) * (1 - t) * (1 - t) * p0 +
+                        3 * (1 - t) * (1 - t) * t * p1 +
+                        3 * (1 - t) * t * t * p2 +
+                        t * t * t * p3);
                 }
-                var babylonMetallicRoughness = _GLTFMaterial._ConvertToMetallicRoughness(babylonSpecularGlossiness);
+                /**
+                 * Evaluates a specified specular power value to determine the appropriate roughness value,
+                 * based on a pre-defined cubic bezier curve with specular on the abscissa axis (x-axis)
+                 * and roughness on the ordinant axis (y-axis).
+                 * @param specularPower - specular power of standard material.
+                 * @returns - Number representing the roughness value.
+                 */
+                function solveForRoughness(specularPower) {
+                    var t = Math.pow(specularPower / P3.x, 0.333333);
+                    return cubicBezierCurve(t, P0.y, P1.y, P2.y, P3.y);
+                }
+                var diffuse = babylonStandardMaterial.diffuseColor.toLinearSpace().scale(0.5);
+                var opacity = babylonStandardMaterial.alpha;
+                var specularPower = BABYLON.Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this.maxSpecularPower);
+                var roughness = solveForRoughness(specularPower);
                 var glTFPbrMetallicRoughness = {
                     baseColorFactor: [
-                        babylonMetallicRoughness.baseColor.r,
-                        babylonMetallicRoughness.baseColor.g,
-                        babylonMetallicRoughness.baseColor.b,
-                        babylonMetallicRoughness.opacity
+                        diffuse.r,
+                        diffuse.g,
+                        diffuse.b,
+                        opacity
                     ],
-                    metallicFactor: babylonMetallicRoughness.metallic,
-                    roughnessFactor: babylonMetallicRoughness.roughness
+                    metallicFactor: 0,
+                    roughnessFactor: roughness,
                 };
                 return glTFPbrMetallicRoughness;
             };
             /**
-             * Converts Specular Glossiness to Metallic Roughness.  This is based on the algorithm used in the Babylon glTF 3ds Max Exporter.
-             * {@link https://github.com/BabylonJS/Exporters/blob/master/3ds%20Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.Material.cs}
-             * @param  babylonSpecularGlossiness - Babylon specular glossiness parameters
-             * @returns - Babylon metallic roughness values
-             */
-            _GLTFMaterial._ConvertToMetallicRoughness = function (babylonSpecularGlossiness) {
-                var diffuse = babylonSpecularGlossiness.diffuse;
-                var opacity = babylonSpecularGlossiness.opacity;
-                var specular = babylonSpecularGlossiness.specular;
-                var glossiness = BABYLON.Scalar.Clamp(babylonSpecularGlossiness.glossiness);
-                var oneMinusSpecularStrength = 1 - Math.max(specular.r, Math.max(specular.g, specular.b));
-                var diffusePerceivedBrightness = _GLTFMaterial.PerceivedBrightness(diffuse);
-                var specularPerceivedBrightness = _GLTFMaterial.PerceivedBrightness(specular);
-                var metallic = _GLTFMaterial.SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
-                var diffuseScaleFactor = oneMinusSpecularStrength / (1 - this.dielectricSpecular.r) / Math.max(1 - metallic, this.epsilon);
-                var baseColorFromDiffuse = diffuse.scale(diffuseScaleFactor);
-                var baseColorFromSpecular = specular.subtract(this.dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this.epsilon));
-                var lerpColor = BABYLON.Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
-                var baseColor = new BABYLON.Color3();
-                lerpColor.clampToRef(0, 1, baseColor);
-                var babylonMetallicRoughness = {
-                    baseColor: baseColor,
-                    opacity: opacity,
-                    metallic: metallic,
-                    roughness: 1.0 - glossiness
-                };
-                return babylonMetallicRoughness;
-            };
-            /**
-             * Returns the perceived brightness value based on the provided color
-             * @param color - color used in calculating the perceived brightness
-             * @returns - perceived brightness value
-             */
-            _GLTFMaterial.PerceivedBrightness = function (color) {
-                return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
-            };
-            /**
              * Computes the metallic factor
              * @param diffuse - diffused value
              * @param specular - specular value
@@ -1257,14 +1091,15 @@ var BABYLON;
              * @returns - metallic value
              */
             _GLTFMaterial.SolveMetallic = function (diffuse, specular, oneMinusSpecularStrength) {
-                if (specular < this.dielectricSpecular.r) {
+                if (specular < _GLTFMaterial.dielectricSpecular.r) {
+                    _GLTFMaterial.dielectricSpecular;
                     return 0;
                 }
-                var a = this.dielectricSpecular.r;
-                var b = diffuse * oneMinusSpecularStrength / (1.0 - this.dielectricSpecular.r) + specular - 2.0 * this.dielectricSpecular.r;
-                var c = this.dielectricSpecular.r - specular;
+                var a = _GLTFMaterial.dielectricSpecular.r;
+                var b = diffuse * oneMinusSpecularStrength / (1.0 - _GLTFMaterial.dielectricSpecular.r) + specular - 2.0 * _GLTFMaterial.dielectricSpecular.r;
+                var c = _GLTFMaterial.dielectricSpecular.r - specular;
                 var D = b * b - 4.0 * a * c;
-                return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a));
+                return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a), 0, 1);
             };
             /**
              * Gets the glTF alpha mode from the Babylon Material
@@ -1296,7 +1131,7 @@ var BABYLON;
                             return "MASK" /* MASK */;
                         }
                         case BABYLON.PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND: {
-                            console.warn("GLTF Exporter | Alpha test and blend mode not supported in glTF.  Alpha blend used instead.");
+                            BABYLON.Tools.Warn(babylonMaterial.name + ": GLTF Exporter | Alpha test and blend mode not supported in glTF.  Alpha blend used instead.");
                             return "BLEND" /* BLEND */;
                         }
                         default: {
@@ -1309,13 +1144,227 @@ var BABYLON;
                 }
             };
             /**
+             * Converts a Babylon Standard Material to a glTF Material.
+             * @param babylonStandardMaterial - BJS Standard 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.ConvertStandardMaterial = function (babylonStandardMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+                BABYLON.Tools.Warn(babylonStandardMaterial.name + ": Standard Material is currently not fully supported/implemented in glTF serializer");
+                var glTFPbrMetallicRoughness = _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
+                var glTFMaterial = { name: babylonStandardMaterial.name };
+                if (babylonStandardMaterial.backFaceCulling) {
+                    if (!babylonStandardMaterial.twoSidedLighting) {
+                        BABYLON.Tools.Warn(babylonStandardMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
+                    }
+                    glTFMaterial.doubleSided = true;
+                }
+                if (hasTextureCoords) {
+                    if (babylonStandardMaterial.diffuseTexture) {
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture != null) {
+                            glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
+                        }
+                    }
+                    if (babylonStandardMaterial.bumpTexture) {
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            glTFMaterial.normalTexture = glTFTexture;
+                        }
+                    }
+                    if (babylonStandardMaterial.emissiveTexture) {
+                        var 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) {
+                        var glTFOcclusionTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
+                        if (glTFOcclusionTexture) {
+                            glTFMaterial.occlusionTexture = glTFOcclusionTexture;
+                        }
+                    }
+                }
+                if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
+                    if (babylonStandardMaterial.alphaMode === BABYLON.Engine.ALPHA_COMBINE) {
+                        glTFMaterial.alphaMode = "BLEND" /* BLEND */;
+                    }
+                    else {
+                        BABYLON.Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
+                    }
+                }
+                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
+                materials.push(glTFMaterial);
+            };
+            /**
+             * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
+             * @param babylonPBRMetalRoughMaterial - 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.ConvertPBRMetallicRoughnessMaterial = function (babylonPBRMetalRoughMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+                var glTFPbrMetallicRoughness = {};
+                if (babylonPBRMetalRoughMaterial.baseColor) {
+                    glTFPbrMetallicRoughness.baseColorFactor = [
+                        babylonPBRMetalRoughMaterial.baseColor.r,
+                        babylonPBRMetalRoughMaterial.baseColor.g,
+                        babylonPBRMetalRoughMaterial.baseColor.b,
+                        babylonPBRMetalRoughMaterial.alpha
+                    ];
+                }
+                if (babylonPBRMetalRoughMaterial.metallic != null) {
+                    glTFPbrMetallicRoughness.metallicFactor = babylonPBRMetalRoughMaterial.metallic;
+                }
+                if (babylonPBRMetalRoughMaterial.roughness != null) {
+                    glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMetalRoughMaterial.roughness;
+                }
+                var glTFMaterial = {
+                    name: babylonPBRMetalRoughMaterial.name
+                };
+                if (babylonPBRMetalRoughMaterial.doubleSided) {
+                    glTFMaterial.doubleSided = babylonPBRMetalRoughMaterial.doubleSided;
+                }
+                if (hasTextureCoords) {
+                    if (babylonPBRMetalRoughMaterial.baseTexture != null) {
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture != null) {
+                            glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
+                        }
+                    }
+                    if (babylonPBRMetalRoughMaterial.normalTexture) {
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            glTFMaterial.normalTexture = glTFTexture;
+                        }
+                    }
+                    if (babylonPBRMetalRoughMaterial.occlusionTexture) {
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            glTFMaterial.occlusionTexture = glTFTexture;
+                            if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
+                                glTFMaterial.occlusionTexture.strength = babylonPBRMetalRoughMaterial.occlusionStrength;
+                            }
+                        }
+                    }
+                    if (babylonPBRMetalRoughMaterial.emissiveTexture) {
+                        var 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)) {
+                    glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
+                }
+                if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
+                    var alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMetalRoughMaterial);
+                    if (alphaMode !== "OPAQUE" /* OPAQUE */) {
+                        glTFMaterial.alphaMode = alphaMode;
+                        if (alphaMode === "BLEND" /* BLEND */) {
+                            glTFMaterial.alphaCutoff = babylonPBRMetalRoughMaterial.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.
+             * @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.
+             */
+            _GLTFMaterial.ExportTexture = function (babylonTexture, mimeType, images, textures, imageData) {
+                var textureInfo = null;
+                var glTFTexture = {
+                    source: images.length
+                };
+                var textureName = "texture_" + (textures.length - 1).toString();
+                var textureData = babylonTexture.getInternalTexture();
+                if (textureData != null) {
+                    textureName = textureData.url;
+                }
+                textureName = BABYLON.Tools.GetFilename(textureName);
+                var baseFile = textureName.split('.')[0];
+                var extension = "";
+                if (mimeType === "image/jpeg" /* JPEG */) {
+                    extension = ".jpg";
+                }
+                else if (mimeType === "image/png" /* PNG */) {
+                    extension = ".png";
+                }
+                else {
+                    BABYLON.Tools.Error("Unsupported mime type " + mimeType);
+                }
+                textureName = baseFile + extension;
+                var pixels = babylonTexture.readPixels();
+                var imageCanvas = document.createElement('canvas');
+                imageCanvas.id = "ImageCanvas";
+                var ctx = imageCanvas.getContext('2d');
+                var size = babylonTexture.getSize();
+                imageCanvas.width = size.width;
+                imageCanvas.height = size.height;
+                var imgData = ctx.createImageData(size.width, size.height);
+                imgData.data.set(pixels);
+                ctx.putImageData(imgData, 0, 0);
+                var base64Data = imageCanvas.toDataURL(mimeType);
+                var binStr = atob(base64Data.split(',')[1]);
+                var arr = new Uint8Array(binStr.length);
+                for (var i = 0; i < binStr.length; ++i) {
+                    arr[i] = binStr.charCodeAt(i);
+                }
+                var imageValues = { data: arr, mimeType: mimeType };
+                imageData[textureName] = imageValues;
+                if (mimeType === "image/jpeg" /* JPEG */) {
+                    var glTFImage = {
+                        uri: textureName
+                    };
+                    var foundIndex = -1;
+                    for (var i = 0; i < images.length; ++i) {
+                        if (images[i].uri === textureName) {
+                            foundIndex = i;
+                            break;
+                        }
+                    }
+                    if (foundIndex === -1) {
+                        images.push(glTFImage);
+                        glTFTexture.source = images.length - 1;
+                        textures.push({
+                            source: images.length - 1
+                        });
+                        textureInfo = {
+                            index: images.length - 1
+                        };
+                    }
+                    else {
+                        glTFTexture.source = foundIndex;
+                        textureInfo = {
+                            index: foundIndex
+                        };
+                    }
+                }
+                return textureInfo;
+            };
+            /**
              * Represents the dielectric specular values for R, G and B.
              */
             _GLTFMaterial.dielectricSpecular = new BABYLON.Color3(0.04, 0.04, 0.04);
             /**
-             * Epsilon value, used as a small tolerance value for a numeric value.
+             * Allows the maximum specular power to be defined for material calculations.
              */
-            _GLTFMaterial.epsilon = 1e-6;
+            _GLTFMaterial.maxSpecularPower = 1024;
             return _GLTFMaterial;
         }());
         GLTF2._GLTFMaterial = _GLTFMaterial;

ファイルの差分が大きいため隠しています
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


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

@@ -452,13 +452,6 @@ declare module BABYLON.GLTF2 {
          */
         private setNodeTransformation(node, babylonMesh, useRightHandedSystem);
         /**
-         *
-         * @param babylonTexture - Babylon texture to extract.
-         * @param mimeType - Mime Type of the babylonTexture.
-         * @return - glTF texture, or null if the texture format is not supported.
-         */
-        private exportTexture(babylonTexture, mimeType?);
-        /**
          * Creates a bufferview based on the vertices type for the Babylon mesh
          * @param kind - Indicates the type of vertices data.
          * @param babylonMesh - The Babylon mesh to get the vertices data from.
@@ -524,9 +517,25 @@ declare module BABYLON.GLTF2 {
          */
         private static readonly dielectricSpecular;
         /**
-         * Epsilon value, used as a small tolerance value for a numeric value.
+         * Allows the maximum specular power to be defined for material calculations.
+         */
+        private static maxSpecularPower;
+        /**
+         * 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
          */
-        private static readonly epsilon;
+        static ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        }, hasTextureCoords: boolean): void;
         /**
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
          * @param babylonStandardMaterial
@@ -534,19 +543,6 @@ declare module BABYLON.GLTF2 {
          */
         static ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
         /**
-         * Converts Specular Glossiness to Metallic Roughness.  This is based on the algorithm used in the Babylon glTF 3ds Max Exporter.
-         * {@link https://github.com/BabylonJS/Exporters/blob/master/3ds%20Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.Material.cs}
-         * @param  babylonSpecularGlossiness - Babylon specular glossiness parameters
-         * @returns - Babylon metallic roughness values
-         */
-        private static _ConvertToMetallicRoughness(babylonSpecularGlossiness);
-        /**
-         * Returns the perceived brightness value based on the provided color
-         * @param color - color used in calculating the perceived brightness
-         * @returns - perceived brightness value
-         */
-        private static PerceivedBrightness(color);
-        /**
          * Computes the metallic factor
          * @param diffuse - diffused value
          * @param specular - specular value
@@ -560,5 +556,52 @@ declare module BABYLON.GLTF2 {
          * @returns - The Babylon alpha mode value
          */
         static GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
+        /**
+         * Converts a Babylon Standard Material to a glTF Material.
+         * @param babylonStandardMaterial - BJS Standard 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 ConvertStandardMaterial(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        }, hasTextureCoords: boolean): void;
+        /**
+         * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
+         * @param babylonPBRMetalRoughMaterial - 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 ConvertPBRMetallicRoughnessMaterial(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, 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.
+         * @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.
+         */
+        static ExportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        }): Nullable<ITextureInfo>;
     }
 }

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

@@ -1,7 +1,7 @@
 {
-  "errors": 7933,
+  "errors": 7253,
   "babylon.typedoc.json": {
-    "errors": 7933,
+    "errors": 7253,
     "AnimationKeyInterpolation": {
       "Enumeration": {
         "Comments": {
@@ -334,11 +334,6 @@
             "MissingText": true
           }
         },
-        "_isDisposed": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
         "_lightSources": {
           "Comments": {
             "MissingText": true
@@ -1195,11 +1190,6 @@
             }
           }
         },
-        "isDisposed": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
         "isInFrustum": {
           "Comments": {
             "MissingReturn": true
@@ -4391,11 +4381,6 @@
             "MissingText": true
           }
         },
-        "coordinatesMode": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
         "delayLoadState": {
           "Comments": {
             "MissingText": true
@@ -4491,21 +4476,6 @@
             "MissingText": true
           }
         },
-        "wrapR": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "wrapU": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "wrapV": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
         "DEFAULT_ANISOTROPIC_FILTERING_LEVEL": {
           "Comments": {
             "MissingText": true
@@ -5763,13 +5733,6 @@
           }
         }
       },
-      "Property": {
-        "instanceDivisor": {
-          "Comments": {
-            "MissingText": true
-          }
-        }
-      },
       "Method": {
         "_rebuild": {
           "Comments": {
@@ -5788,33 +5751,6 @@
             }
           }
         },
-        "createVertexBuffer": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "kind": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "offset": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "size": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "stride": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
         "dispose": {
           "Comments": {
             "MissingText": true
@@ -5830,11 +5766,6 @@
             "MissingText": true
           }
         },
-        "getIsInstanced": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
         "getStrideSize": {
           "Comments": {
             "MissingText": true
@@ -9763,18 +9694,6 @@
             }
           }
         },
-        "_getRGBABufferInternalSizedFormat": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "type": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
         "_getRGBAMultiSampleBufferFormat": {
           "Comments": {
             "MissingText": true
@@ -11469,11 +11388,6 @@
             "MissingText": true
           }
         },
-        "initWebVR": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
         "isDeterministicLockStep": {
           "Comments": {
             "MissingText": true
@@ -14674,42 +14588,6 @@
         }
       }
     },
-    "HighlightLayer": {
-      "Property": {
-        "name": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "glowingMeshStencilReference": {
-          "Naming": {
-            "NotPascalCase": true
-          }
-        },
-        "neutralColor": {
-          "Naming": {
-            "NotPascalCase": true
-          }
-        },
-        "normalMeshStencilReference": {
-          "Naming": {
-            "NotPascalCase": true
-          }
-        }
-      },
-      "Method": {
-        "_rebuild": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "shouldRender": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        }
-      }
-    },
     "HighlightsPostProcess": {
       "Class": {
         "Comments": {
@@ -20177,418 +20055,6 @@
         }
       }
     },
-    "Node": {
-      "Constructor": {
-        "new Node": {
-          "Comments": {
-            "MissingText": true,
-            "MissingReturn": true
-          },
-          "Parameter": {
-            "scene": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      },
-      "Property": {
-        "_cache": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "_currentRenderId": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "_waitingParentId": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "animations": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "behaviors": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "doNotSerialize": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "id": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "metadata": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "name": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "onDispose": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "onReady": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "parent": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "state": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "uniqueId": {
-          "Comments": {
-            "MissingText": true
-          }
-        }
-      },
-      "Method": {
-        "_getDescendants": {
-          "Parameter": {
-            "predicate": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "_initCache": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "_isSynchronized": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "_markSyncedWithParent": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "_setReady": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "state": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "_updateCache": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "ignoreParentClass": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "addBehavior": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "behavior": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "computeWorldMatrix": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "force": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "createAnimationRange": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "name": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "from": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "to": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "deleteAnimationRange": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "name": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "deleteFrames": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "dispose": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "getAnimationByName": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "name": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getAnimationRange": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "name": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getBehaviorByName": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "name": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getChildMeshes": {
-          "Comments": {
-            "MissingReturn": true
-          },
-          "Parameter": {
-            "directDescendantsOnly": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "predicate": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getChildTransformNodes": {
-          "Comments": {
-            "MissingReturn": true
-          },
-          "Parameter": {
-            "directDescendantsOnly": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "predicate": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getChildren": {
-          "Comments": {
-            "MissingReturn": true
-          },
-          "Parameter": {
-            "predicate": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getClassName": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "getDescendants": {
-          "Parameter": {
-            "predicate": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getEngine": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "getScene": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "getWorldMatrix": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "hasNewParent": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "update": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "isDescendantOf": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "isEnabled": {
-          "Parameter": {
-            "checkAncestors": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "isSynchronized": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "updateCache": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "isSynchronizedWithParent": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "removeBehavior": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "behavior": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "serializeAnimationRanges": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "updateCache": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "force": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "ParseAnimationRanges": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "node": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "parsedNode": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "scene": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      }
-    },
     "NullEngineOptions": {
       "Class": {
         "Comments": {
@@ -27406,11 +26872,6 @@
             "MissingText": true
           }
         },
-        "highlightLayers": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
         "hoverCursor": {
           "Comments": {
             "MissingText": true
@@ -27441,11 +26902,6 @@
             "MissingText": true
           }
         },
-        "layers": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
         "lensFlareSystems": {
           "Comments": {
             "MissingText": true

ファイルの差分が大きいため隠しています
+ 67 - 66
dist/preview release/viewer/babylon.viewer.js


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

@@ -2,6 +2,7 @@
 
 ## Major updates
 
+-Support for [GPU particles](DOC HERE) ([deltakosh](https://github.com/deltakosh))
 - Improved building process: We now run a full visual validation test for each pull request. Furthermore, code comments and what's new updates are now mandatory ([sebavan](https://github.com/sebavan))
 - Introduced texture binding atlas. This optimization allows the engine to reuse texture bindings instead of rebinding textures when they are not on constant sampler indexes ([deltakosh](https://github.com/deltakosh))
 - New [AnimationGroup class](http://doc.babylonjs.com/how_to/group) to control simultaneously multiple animations with different targets ([deltakosh](https://github.com/deltakosh))
@@ -11,10 +12,12 @@
 - Introduced [Projection Texture on SpotLight](http://doc.babylonjs.com/babylon101/lights#projection-texture) ([lostink](https://github.com/lostink))
 - Introduced support for [local cubemaps](http://doc.babylonjs.com/how_to/reflect#using-local-cubemap-mode) ([deltakosh](https://github.com/deltakosh))
 - 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))
 
 ## Updates
 
 - Tons of functions and classes received the code comments they deserved (All the community)
+- Added `particleSystem.reset()` to clear a particle system ([deltakosh](https://github.com/deltakosh))
 - Added support for all RGBA orders (BGR, RGB, etc..) for the DDS loader ([deltakosh](https://github.com/deltakosh))
 - Improved [SceneOptimizer](http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer) to provide better adaptability ([deltakosh](https://github.com/deltakosh))
 - Improved `scene.isReady()` function which now takes in account shadows and LOD ([deltakosh](https://github.com/deltakosh))
@@ -25,7 +28,7 @@
   ([carloslanderas](https://github.com/carloslanderas))
 - VRHelper now has onBeforeCameraTeleport and onAfterCameraTeleport observables that will be notified before and after camera teleportation is triggered.
   ([carloslanderas](https://github.com/carloslanderas))
-- VRHelper now has the public property teleportationEnabled to enable / disable camera teleportation. 
+- VRHelper now has the public property teleportationEnabled to enable / disable camera teleportation.
    ([carloslanderas](https://github.com/carloslanderas))
 - VRHelper now exposes onNewMeshPicked observable that will notify a PickingInfo object after meshSelectionPredicate evaluation
    ([carloslanderas](https://github.com/carloslanderas))
@@ -47,12 +50,13 @@
 - (Viewer) The viewer supports custom shaders in the configuration. ([RaananW](https://github.com/RaananW))
 - Documented PostProcessRenderEffect, DefaultRenderingPipeline, BlurPostProcess, DepthOfFieldEffect, PostProcess, PostProcessManager, Effect classes ([trevordev](https://github.com/trevordev))
 - SPS internal storage of each solid particle rotation matrix ([jbousquie](https://github.com/jbousquie)) 
+- SPS particle parenting feature ([jbousquie](https://github.com/jbousquie))
 - (Viewer) Introducing the viewer labs - testing new features. ([RaananW](https://github.com/RaananW))
-- AssetContainer Class and loading methods. ([trevordev](https://github.com/trevordev))
+- AssetContainer Class and loading methods ([trevordev](https://github.com/trevordev))
 - KeepAssets class and AssetContainer.moveAllFromScene ([HoloLite](http://www.html5gamedevs.com/profile/28694-hololite/) [trevordev](https://github.com/trevordev))
-- (Viewer) It is now possible to update parts of the configuration without rcreating the objects. ([RaananW](https://github.com/RaananW))
+- (Viewer) It is now possible to update parts of the configuration without rcreating the objects. Extra configuration can be loaded sync (if provided) ([RaananW](https://github.com/RaananW))
 - (Gulp) extra/external declarations can be prepended to final declarations during build. ([RaananW](https://github.com/RaananW))
-- (Viewer) Model can be normalized using configuration. ([RaananW](https://github.com/RaananW))
+- (Viewer) Model can be normalized using configuration, camera is dynamically configured. ([RaananW](https://github.com/RaananW))
 - (Gulp) extra/external declarations can be prepended to final NPM declarations during build. ([RaananW](https://github.com/RaananW))
 - GUI.Line can have its world position set from one end or the other ([SvenFrankson](https://github.com/SvenFrankson))
 - Added FOV system to background material for zoom effects in skyboxes without adjusting camera FOV ([DavidHGillen](https://github.com/DavidHGillen))
@@ -63,6 +67,10 @@
 - Added checks to VertexData.merge to ensure data is valid before merging. ([bghgary](https://github.com/bghgary)]
 - Ability to set a mesh to customize the webVR gaze tracker ([trevordev](https://github.com/trevordev))
 - Added promise-based async functions for initWebVRAsync and useStandingMatrixAsync ([trevordev](https://github.com/trevordev))
+- Add stroke (outline) options on GUI text control ([SvenFrankson](https://github.com/SvenFrankson))
+- Add floating point texture support for RenderTargetCubeTexture ([PeapBoy](https://github.com/NicolasBuecher))
+- Support for mutli-touch when interacting with multiple gui elements simultaneously ([trevordev](https://github.com/trevordev))
+- Added Draco mesh compression support to glTF 2.0 loader. ([bghgary](https://github.com/bghgary)) 
 
 ## Bug fixes
 
@@ -70,9 +78,11 @@
 - 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))
+- Enabling teleportation in the vr helper class caused a redundant post process to be added ([trevordev](https://github.com/trevordev))
 
 ## Breaking changes
 
 - Removed the unused PostProcessRenderPass class and extended postProcessingRenderingEffect to support multiple PostProcesses ([trevordev](https://github.com/trevordev))
 - VertexData.merge no longer supports merging of data that do not have the same set of attributes. ([bghgary](https://github.com/bghgary)]
-- glTF 2.0 loader will now create a mesh for each primitive instead of merging the primitives together into one mesh. ([bghgary](https://github.com/bghgary)]
+- glTF 2.0 loader will now create a mesh for each primitive instead of merging the primitives together into one mesh. ([bghgary](https://github.com/bghgary)]

+ 16 - 9
gui/src/advancedDynamicTexture.ts

@@ -20,7 +20,7 @@ module BABYLON.GUI {
         public _lastPickedControl: Control;
         public _lastControlOver: Nullable<Control>;
         public _lastControlDown: Nullable<Control>;
-        public _capturingControl: Nullable<Control>;
+        public _capturingControl: {[pointerId:number]:Control} = {};
         public _shouldBlockPointer: boolean;
         public _layerToDispose: Nullable<Layer>;
         public _linkedControls = new Array<Control>();
@@ -196,6 +196,12 @@ module BABYLON.GUI {
 
         public markAsDirty() {
             this._isDirty = true;
+
+            this.executeOnAllControls((control) => {
+                if (control._isFontSizeInPercentage) {
+                    control._resetFontCache();
+                }
+            });
         }
 
         public addControl(control: Control): AdvancedDynamicTexture {
@@ -364,7 +370,7 @@ module BABYLON.GUI {
             this._rootContainer._draw(measure, context);
         }
 
-        private _doPicking(x: number, y: number, type: number, buttonIndex: number): void {
+        private _doPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): void {
             var scene = this.getScene();
 
             if (!scene) {
@@ -379,12 +385,12 @@ module BABYLON.GUI {
                 y = y * ((textureSize.height / this._renderScale) / engine.getRenderHeight());
             }
 
-            if (this._capturingControl) {
-                this._capturingControl._processObservables(type, x, y, buttonIndex);
+            if (this._capturingControl[pointerId]) {
+                this._capturingControl[pointerId]._processObservables(type, x, y, pointerId, buttonIndex);
                 return;
             }
 
-            if (!this._rootContainer._processPicking(x, y, type, buttonIndex)) {
+            if (!this._rootContainer._processPicking(x, y, type, pointerId, buttonIndex)) {
 
                 if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
                     if (this._lastControlOver) {
@@ -426,7 +432,7 @@ module BABYLON.GUI {
                 let y = (scene.pointerY / engine.getHardwareScalingLevel() - viewport.y * engine.getRenderHeight()) / viewport.height;
 
                 this._shouldBlockPointer = false;
-                this._doPicking(x, y, pi.type, pi.event.button);
+                this._doPicking(x, y, pi.type, (pi.event as PointerEvent).pointerId || 0, pi.event.button);
 
                 pi.skipOnPointerObservable = this._shouldBlockPointer;
             });
@@ -445,17 +451,18 @@ module BABYLON.GUI {
                     && pi.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
                     return;
                 }
-
+                var pointerId = (pi.event as PointerEvent).pointerId || 0;
                 if (pi.pickInfo && pi.pickInfo.hit && pi.pickInfo.pickedMesh === mesh) {
                     var uv = pi.pickInfo.getTextureCoordinates();
 
                     if (uv) {
                         let size = this.getSize();
-                        this._doPicking(uv.x * size.width, (1.0 - uv.y) * size.height, pi.type, pi.event.button);
+                        
+                        this._doPicking(uv.x * size.width, (1.0 - uv.y) * size.height, pi.type, pointerId, pi.event.button);
                     }
                 } else if (pi.type === BABYLON.PointerEventTypes.POINTERUP) {
                     if (this._lastControlDown) {
-                        this._lastControlDown.forcePointerUp();
+                        this._lastControlDown.forcePointerUp(pointerId);
                     }
                     this._lastControlDown = null;
 

+ 6 - 6
gui/src/controls/button.ts

@@ -37,7 +37,7 @@ module BABYLON.GUI {
         }
 
         // While being a container, the button behaves like a control.
-        public _processPicking(x: number, y: number, type: number, buttonIndex: number): boolean {
+        public _processPicking(x: number, y: number, type: number, pointerId:number, buttonIndex: number): boolean {
             if (!this.isHitTestVisible || !this.isVisible || this.notRenderable) {
                 return false;
             }
@@ -46,7 +46,7 @@ module BABYLON.GUI {
                 return false;
             }
 
-            this._processObservables(type, x, y, buttonIndex);
+            this._processObservables(type, x, y, pointerId, buttonIndex);
 
             return true;
         }
@@ -71,8 +71,8 @@ module BABYLON.GUI {
             super._onPointerOut(target);
         }
 
-        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, pointerId, buttonIndex)) {
                 return false;
             }
 
@@ -84,12 +84,12 @@ module BABYLON.GUI {
             return true;
         }
 
-        public _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void {
+        public _onPointerUp(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): void {
             if (this.pointerUpAnimation) {
                 this.pointerUpAnimation();
             }
 
-            super._onPointerUp(target, coordinates, buttonIndex);
+            super._onPointerUp(target, coordinates, pointerId, buttonIndex);
         }        
 
         // Statics

+ 2 - 2
gui/src/controls/checkbox.ts

@@ -115,8 +115,8 @@ module BABYLON.GUI {
         }
 
         // Events
-        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, pointerId, buttonIndex)) {
                 return false;
             }
 

+ 6 - 6
gui/src/controls/colorpicker.ts

@@ -377,8 +377,8 @@ module BABYLON.GUI {
             return false;
         }
 
-        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, pointerId, buttonIndex)) {
                 return false;
             }            
 
@@ -394,7 +394,7 @@ module BABYLON.GUI {
             }
 
             this._updateValueFromPointer(coordinates.x, coordinates.y);
-            this._host._capturingControl = this;
+            this._host._capturingControl[pointerId] = this;
 
             return true;
         }
@@ -407,11 +407,11 @@ module BABYLON.GUI {
             super._onPointerMove(target, coordinates);
         }
 
-        public _onPointerUp (target: Control, coordinates: Vector2, buttonIndex: number): void {
+        public _onPointerUp (target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): void {
             this._pointerIsDown = false;
             
-            this._host._capturingControl = null;
-            super._onPointerUp(target, coordinates, buttonIndex);
+            delete this._host._capturingControl[pointerId];
+            super._onPointerUp(target, coordinates, pointerId, buttonIndex);
         }     
     }    
 }

+ 4 - 3
gui/src/controls/container.ts

@@ -159,6 +159,7 @@ module BABYLON.GUI {
                 this._clipForChildren(context);
                 for (var child of this._children) {
                     if (child.isVisible && !child.notRenderable) {
+                        child._tempParentMeasure.copyFrom(this._measureForChildren);
                         child._draw(this._measureForChildren, context);
 
                         if (child.onAfterDrawObservable.hasObservers()) {
@@ -174,7 +175,7 @@ module BABYLON.GUI {
             }
         }
 
-        public _processPicking(x: number, y: number, type: number, buttonIndex: number): boolean {
+        public _processPicking(x: number, y: number, type: number, pointerId:number, buttonIndex: number): boolean {
             if (!this.isVisible || this.notRenderable) {
                 return false;
             }
@@ -186,7 +187,7 @@ module BABYLON.GUI {
             // Checking backwards to pick closest first
             for (var index = this._children.length - 1; index >= 0; index--) {
                 var child = this._children[index];
-                if (child._processPicking(x, y, type, buttonIndex)) {
+                if (child._processPicking(x, y, type, pointerId, buttonIndex)) {
                     return true;
                 }
             }
@@ -195,7 +196,7 @@ module BABYLON.GUI {
                 return false;
             }
 
-            return this._processObservables(type, x, y, buttonIndex);
+            return this._processObservables(type, x, y, pointerId, buttonIndex);
         }
 
         protected _clipForChildren(context: CanvasRenderingContext2D): void {

+ 40 - 13
gui/src/controls/control.ts

@@ -20,6 +20,7 @@ module BABYLON.GUI {
         protected _horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         protected _verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
         private _isDirty = true;
+        public _tempParentMeasure = Measure.Empty();
         protected _cachedParentMeasure = Measure.Empty();
         private _paddingLeft = new ValueAndUnit(0);
         private _paddingRight = new ValueAndUnit(0);
@@ -45,6 +46,7 @@ module BABYLON.GUI {
         private _downCount = 0;
         private _enterCount = 0;
         private _doNotRender = false;
+        private _downPointerIds:{[id:number] : boolean} = {};
 
         public isHitTestVisible = true;
         public isPointerBlocker = false;
@@ -277,8 +279,17 @@ module BABYLON.GUI {
             this._fontSet = true;
         }
 
+        /** @ignore */
+        public get _isFontSizeInPercentage(): boolean {
+            return this._fontSize.isPercentage;
+        }
+
         public get fontSizeInPixels(): number {
-            return this._fontSize.getValueInPixel(this._host, 100);
+            if (this._fontSize.isPixel) {
+                return this._fontSize.getValue(this._host);
+            }
+
+            return this._fontSize.getValueInPixel(this._host, this._tempParentMeasure.height || this._cachedParentMeasure.height);
         }
 
         public get fontSize(): string | number {
@@ -483,6 +494,11 @@ module BABYLON.GUI {
             return "Control";
         }
 
+        /** @ignore */
+        public _resetFontCache(): void {
+            this._fontSet = true;
+        }
+
         public getLocalCoordinates(globalCoordinates: Vector2): Vector2 {
             var result = Vector2.Zero();
 
@@ -844,7 +860,7 @@ module BABYLON.GUI {
             return true;
         }
 
-        public _processPicking(x: number, y: number, type: number, buttonIndex: number): boolean {
+        public _processPicking(x: number, y: number, type: number, pointerId:number, buttonIndex: number): boolean {
             if (!this.isHitTestVisible || !this.isVisible || this._doNotRender) {
                 return false;
             }
@@ -853,7 +869,7 @@ module BABYLON.GUI {
                 return false;
             }
 
-            this._processObservables(type, x, y, buttonIndex);
+            this._processObservables(type, x, y, pointerId, buttonIndex);
 
             return true;
         }
@@ -886,33 +902,43 @@ module BABYLON.GUI {
             if (canNotify && this.parent != null) this.parent._onPointerOut(target);
         }
 
-        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
+        public _onPointerDown(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): boolean {
             if (this._downCount !== 0) {
                 return false;
             }
 
             this._downCount++;
 
+            this._downPointerIds[pointerId] = true;
+
             var canNotify: boolean = this.onPointerDownObservable.notifyObservers(new Vector2WithInfo(coordinates, buttonIndex), -1, target, this);
 
-            if (canNotify && this.parent != null) this.parent._onPointerDown(target, coordinates, buttonIndex);
+            if (canNotify && this.parent != null) this.parent._onPointerDown(target, coordinates, pointerId, buttonIndex);
 
             return true;
         }
 
-        public _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void {
+        public _onPointerUp(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): void {
             this._downCount = 0;
 
+            delete this._downPointerIds[pointerId];
+
             var canNotify: boolean = this.onPointerUpObservable.notifyObservers(new Vector2WithInfo(coordinates, buttonIndex), -1, target, this);
 
-            if (canNotify && this.parent != null) this.parent._onPointerUp(target, coordinates, buttonIndex);
+            if (canNotify && this.parent != null) this.parent._onPointerUp(target, coordinates, pointerId, buttonIndex);
         }
 
-        public forcePointerUp() {
-            this._onPointerUp(this, Vector2.Zero(), 0);
+        public forcePointerUp(pointerId:Nullable<number> = null) {
+            if(pointerId !== null){
+                this._onPointerUp(this, Vector2.Zero(), pointerId, 0);
+            }else{
+                for(var key in this._downPointerIds){
+                    this._onPointerUp(this, Vector2.Zero(), +key as number, 0);
+                }
+            }
         }
 
-        public _processObservables(type: number, x: number, y: number, buttonIndex: number): boolean {
+        public _processObservables(type: number, x: number, y: number, pointerId:number, buttonIndex: number): boolean {
             this._dummyVector2.copyFromFloats(x, y);
             if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
                 this._onPointerMove(this, this._dummyVector2);
@@ -931,7 +957,7 @@ module BABYLON.GUI {
             }
 
             if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                this._onPointerDown(this, this._dummyVector2, buttonIndex);
+                this._onPointerDown(this, this._dummyVector2, pointerId, buttonIndex);
                 this._host._lastControlDown = this;
                 this._host._lastPickedControl = this;
                 return true;
@@ -939,7 +965,7 @@ module BABYLON.GUI {
 
             if (type === BABYLON.PointerEventTypes.POINTERUP) {
                 if (this._host._lastControlDown) {
-                    this._host._lastControlDown._onPointerUp(this, this._dummyVector2, buttonIndex);
+                    this._host._lastControlDown._onPointerUp(this, this._dummyVector2, pointerId, buttonIndex);
                 }
                 this._host._lastControlDown = null;
                 return true;
@@ -948,12 +974,13 @@ module BABYLON.GUI {
             return false;
         }
 
+
         private _prepareFont() {
             if (!this._font && !this._fontSet) {
                 return;
             }
 
-            this._font = this._fontStyle + " " + this._fontSize.getValue(this._host) + "px " + this._fontFamily;
+            this._font = this._fontStyle + " " + this.fontSizeInPixels + "px " + this._fontFamily;
 
             this._fontOffset = Control._GetFontOffset(this._font);
         }

+ 4 - 4
gui/src/controls/inputText.ts

@@ -432,8 +432,8 @@ module BABYLON.GUI {
             context.restore();
         }
 
-        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, pointerId, buttonIndex)) {
                 return false;
             }
 
@@ -449,8 +449,8 @@ module BABYLON.GUI {
             return true;
         }
 
-        public _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void {
-            super._onPointerUp(target, coordinates, buttonIndex);
+        public _onPointerUp(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): void {
+            super._onPointerUp(target, coordinates, pointerId, buttonIndex);
         }
 
         public dispose() {

+ 2 - 2
gui/src/controls/radioButton.ts

@@ -144,8 +144,8 @@ module BABYLON.GUI {
         }
 
         // Events
-        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, pointerId, buttonIndex)) {
                 return false;
             }
             this.isChecked = !this.isChecked;

+ 6 - 6
gui/src/controls/slider.ts

@@ -266,15 +266,15 @@ module BABYLON.GUI {
             this.value = this._minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this._maximum - this._minimum);
         }
 
-        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, pointerId, buttonIndex)) {
                 return false;
             }
 
             this._pointerIsDown = true;
 
             this._updateValueFromPointer(coordinates.x, coordinates.y);
-            this._host._capturingControl = this;
+            this._host._capturingControl[pointerId] = this;
 
             return true;
         }
@@ -287,11 +287,11 @@ module BABYLON.GUI {
             super._onPointerMove(target, coordinates);
         }
 
-        public _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void {
+        public _onPointerUp(target: Control, coordinates: Vector2, pointerId:number, buttonIndex: number): void {
             this._pointerIsDown = false;
 
-            this._host._capturingControl = null;
-            super._onPointerUp(target, coordinates, buttonIndex);
+            delete this._host._capturingControl[pointerId];
+            super._onPointerUp(target, coordinates, pointerId, buttonIndex);
         }
     }
 }

+ 49 - 0
gui/src/controls/textBlock.ts

@@ -10,6 +10,8 @@ module BABYLON.GUI {
         private _lines: any[];
         private _resizeToFit: boolean = false;
         private _lineSpacing: ValueAndUnit = new ValueAndUnit(0);
+        private _outlineWidth: number = 0;
+        private _outlineColor: string = "white";
         /**
         * An event triggered after the text is changed
         * @type {BABYLON.Observable}
@@ -141,6 +143,42 @@ module BABYLON.GUI {
         }
 
         /**
+         * Gets or sets outlineWidth of the text to display
+         */
+        public get outlineWidth(): number {
+            return this._outlineWidth;
+        }
+
+        /**
+         * Gets or sets outlineWidth of the text to display
+         */
+        public set outlineWidth(value: number) {
+            if (this._outlineWidth === value) {
+                return;
+            }
+            this._outlineWidth = value;
+            this._markAsDirty();
+        }
+
+        /**
+         * Gets or sets outlineColor of the text to display
+         */
+        public get outlineColor(): string {
+            return this._outlineColor;
+        }
+
+        /**
+         * Gets or sets outlineColor of the text to display
+         */
+        public set outlineColor(value: string) {
+            if (this._outlineColor === value) {
+                return;
+            }
+            this._outlineColor = value;
+            this._markAsDirty();
+        }
+
+        /**
          * Creates a new TextBlock object
          * @param name defines the name of the control
          * @param text defines the text to display (emptry string by default)
@@ -182,6 +220,9 @@ module BABYLON.GUI {
                 context.shadowOffsetY = this.shadowOffsetY;
             }
 
+            if (this.outlineWidth) {
+                context.strokeText(text, this._currentMeasure.left + x, y);
+            }
             context.fillText(text, this._currentMeasure.left + x, y);
         }
 
@@ -198,6 +239,14 @@ module BABYLON.GUI {
             context.restore();
         }
 
+        protected _applyStates(context: CanvasRenderingContext2D): void {
+            super._applyStates(context);
+            if (this.outlineWidth) {
+                context.lineWidth = this.outlineWidth;
+                context.strokeStyle = this.outlineColor;
+            }
+        }
+
         protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             this._lines = [];
             var _lines = this.text.split("\n");

+ 3 - 2
inspector/sass/_detailPanel.scss

@@ -31,7 +31,7 @@
     .prop-value {
         @extend .base-property;
         width:59%;
-        padding-left:10px;
+        padding-left:5px;
         &.clickable {
             cursor:pointer;
             &:hover {
@@ -113,7 +113,8 @@
   // The div displaying a color
   .color-element {
       @extend .element-viewer;
-      top: 2px;
+      width: 20px;
+      height: 15px;
   }
 
   // The div displaying a texture element

+ 3 - 0
inspector/sass/_tree.scss

@@ -65,4 +65,7 @@
             
         }
     }
+    .line_invisible {
+        display: none;
+    }
 }

+ 1 - 1
inspector/src/properties.ts

@@ -28,7 +28,7 @@ module INSPECTOR {
         },
         'Color3': {
             type: BABYLON.Color3,
-            format: (color: BABYLON.Color3) => { return `R:${color.r}, G:${color.g}, B:${color.b}` },
+            format: (color: BABYLON.Color3) => { return `R:${color.r.toPrecision(2)}, G:${color.g.toPrecision(2)}, B:${color.b.toPrecision(2)}` },
             slider: {
                 r: { min: 0, max: 1, step: 0.01 },
                 g: { min: 0, max: 1, step: 0.01 },

+ 12 - 1
inspector/src/tree/TreeItem.ts

@@ -80,7 +80,18 @@ module INSPECTOR {
 
         /** Build the HTML of this item */
         protected _build() {
-            this._div.className = 'line';
+
+            /**
+             *  Hide the debug objects :
+             * - Axis : xline, yline, zline
+             * */
+            let adapterId = this._adapter.id();
+            if (adapterId == "xline"
+                || adapterId == "yline"
+                || adapterId == "zline") {
+                this._div.className = "line_invisible";
+            }
+            else this._div.className = 'line';
 
             // special class for transform node ONLY
             if (this.adapter instanceof MeshAdapter) {

+ 70 - 0
loaders/src/glTF/2.0/Extensions/KHR_draco_mesh_compression.ts

@@ -0,0 +1,70 @@
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GLTF2.Extensions {
+    // https://github.com/KhronosGroup/glTF/pull/874
+
+    const NAME = "KHR_draco_mesh_compression";
+
+    interface IKHRDracoMeshCompression {
+        bufferView: number;
+        attributes: { [name: string]: number };
+    }
+
+    export class KHR_draco_mesh_compression extends GLTFLoaderExtension {
+        public readonly name = NAME;
+
+        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> {
+            return this._loadExtensionAsync<IKHRDracoMeshCompression, VertexData>(context, primitive, (extensionContext, extension) => {
+                if (primitive.mode != undefined) {
+                    if (primitive.mode !== MeshPrimitiveMode.TRIANGLE_STRIP &&
+                        primitive.mode !== MeshPrimitiveMode.TRIANGLES) {
+                        throw new Error(`${context}: Unsupported mode ${primitive.mode}`);
+                    }
+
+                    // TODO: handle triangle strips
+                    if (primitive.mode === MeshPrimitiveMode.TRIANGLE_STRIP) {
+                        throw new Error(`${context}: Mode ${primitive.mode} is not currently supported`);
+                    }
+                }
+
+                const attributes: { [kind: string]: number } = {};
+                const loadAttribute = (name: string, kind: string) => {
+                    const uniqueId = extension.attributes[name];
+                    if (uniqueId == undefined) {
+                        return;
+                    }
+
+                    babylonMesh._delayInfo = babylonMesh._delayInfo || [];
+                    if (babylonMesh._delayInfo.indexOf(kind) === -1) {
+                        babylonMesh._delayInfo.push(kind);
+                    }
+
+                    attributes[kind] = uniqueId;
+                };
+
+                loadAttribute("POSITION", VertexBuffer.PositionKind);
+                loadAttribute("NORMAL", VertexBuffer.NormalKind);
+                loadAttribute("TANGENT", VertexBuffer.TangentKind);
+                loadAttribute("TEXCOORD_0", VertexBuffer.UVKind);
+                loadAttribute("TEXCOORD_1", VertexBuffer.UV2Kind);
+                loadAttribute("JOINTS_0", VertexBuffer.MatricesIndicesKind);
+                loadAttribute("WEIGHTS_0", VertexBuffer.MatricesWeightsKind);
+                loadAttribute("COLOR_0", VertexBuffer.ColorKind);
+
+                var bufferView = GLTFLoader._GetProperty(extensionContext, this._loader._gltf.bufferViews, extension.bufferView);
+                return this._loader._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView).then(data => {
+                    try {
+                        return DracoCompression.Decode(data, attributes);
+                    }
+                    catch (e) {
+                        throw new Error(`${context}: ${e.message}`);
+                    }
+                });
+            });
+        }
+    }
+
+    if (DracoCompression.IsSupported) {
+        GLTFLoader._Register(NAME, loader => new KHR_draco_mesh_compression(loader));
+    }
+}

+ 47 - 17
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -68,10 +68,10 @@ module BABYLON.GLTF2 {
 
             this._disposed = true;
 
-            this._abortRequests();
-            this._releaseResources();
-
             this.onDisposeObservable.notifyObservers(this);
+            this.onDisposeObservable.clear();
+
+            this._clear();
         }
 
         public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[] }> {
@@ -115,11 +115,7 @@ module BABYLON.GLTF2 {
 
         private _loadAsync(nodes: Nullable<Array<ILoaderNode>>, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
             return Promise.resolve().then(() => {
-                for (const name of GLTFLoader._Names) {
-                    const extension = GLTFLoader._Factories[name](this);
-                    this._extensions[name] = extension;
-                    this.onExtensionLoadedObservable.notifyObservers(extension);
-                }
+                this._loadExtensions();
 
                 this._babylonScene = scene;
                 this._rootUrl = rootUrl;
@@ -153,23 +149,35 @@ module BABYLON.GLTF2 {
                     Tools.SetImmediate(() => {
                         if (!this._disposed) {
                             Promise.all(this._completePromises).then(() => {
-                                this._releaseResources();
                                 this._state = GLTFLoaderState.Complete;
                                 this.onCompleteObservable.notifyObservers(this);
+                                this.onCompleteObservable.clear();
+                                this._clear();
                             }).catch(error => {
                                 Tools.Error("glTF Loader: " + error.message);
-                                this.dispose();
+                                this._clear();
                             });
                         }
                     });
                 });
             }).catch(error => {
                 Tools.Error("glTF Loader: " + error.message);
-                this.dispose();
+                this._clear();
                 throw error;
             });
         }
 
+        private _loadExtensions(): void {
+            for (const name of GLTFLoader._Names) {
+                const extension = GLTFLoader._Factories[name](this);
+                this._extensions[name] = extension;
+
+                this.onExtensionLoadedObservable.notifyObservers(extension);
+            }
+
+            this.onExtensionLoadedObservable.clear();
+        }
+
         private _loadData(data: IGLTFLoaderData): void {
             this._gltf = data.json as ILoaderGLTF;
             this._setupData();
@@ -290,6 +298,12 @@ module BABYLON.GLTF2 {
                     if (node._babylonMesh) {
                         meshes.push(node._babylonMesh);
                     }
+
+                    if (node._primitiveBabylonMeshes) {
+                        for (const babylonMesh of node._primitiveBabylonMeshes) {
+                            meshes.push(babylonMesh);
+                        }
+                    }
                 }
             }
 
@@ -430,18 +444,25 @@ module BABYLON.GLTF2 {
                 promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
             }
 
+            this.onMeshLoadedObservable.notifyObservers(babylonMesh);
+
             return Promise.all(promises).then(() => {
                 babylonMesh.setEnabled(true);
             });
         }
 
         private _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Promise<VertexData> {
+            const promise = GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+            if (promise) {
+                return promise;
+            }
+
             const attributes = primitive.attributes;
             if (!attributes) {
                 throw new Error(context + ": Attributes are missing");
             }
 
-            if (primitive.mode && primitive.mode !== MeshPrimitiveMode.TRIANGLES) {
+            if (primitive.mode != undefined && primitive.mode !== MeshPrimitiveMode.TRIANGLES) {
                 // TODO: handle other primitive modes
                 throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
             }
@@ -988,7 +1009,7 @@ module BABYLON.GLTF2 {
             return buffer._data;
         }
 
-        private _loadBufferViewAsync(context: string, bufferView: ILoaderBufferView): Promise<ArrayBufferView> {
+        public _loadBufferViewAsync(context: string, bufferView: ILoaderBufferView): Promise<ArrayBufferView> {
             if (bufferView._data) {
                 return bufferView._data;
             }
@@ -1513,16 +1534,14 @@ module BABYLON.GLTF2 {
             return Promise.all(promises).then(() => {});
         }
 
-        private _abortRequests(): void {
+        private _clear(): void {
             for (const request of this._requests) {
                 request.abort();
             }
 
             this._requests.length = 0;
-        }
 
-        private _releaseResources(): void {
-            if (this._gltf.images) {
+            if (this._gltf && this._gltf.images) {
                 for (const image of this._gltf.images) {
                     if (image._objectURL) {
                         image._objectURL.then(value => {
@@ -1533,6 +1552,17 @@ module BABYLON.GLTF2 {
                     }
                 }
             }
+
+            delete this._gltf;
+            delete this._babylonScene;
+            this._completePromises.length = 0;
+            this._extensions = {};
+            delete this._rootBabylonMesh;
+            delete this._progressCallback;
+
+            this.onMeshLoadedObservable.clear();
+            this.onTextureLoadedObservable.clear();
+            this.onMaterialLoadedObservable.clear();
         }
 
         public _applyExtensions<T>(actionAsync: (extension: GLTFLoaderExtension) => Nullable<Promise<T>>) {

+ 17 - 7
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -3,6 +3,7 @@
 module BABYLON.GLTF2 {
     export abstract class GLTFLoaderExtension implements IGLTFLoaderExtension {
         public enabled = true;
+        public abstract readonly name: string;
 
         protected _loader: GLTFLoader;
 
@@ -10,8 +11,6 @@ module BABYLON.GLTF2 {
             this._loader = loader;
         }
 
-        public abstract readonly name: string;
-
         // #region Overridable Methods
 
         /** Override this method to modify the default behavior for loading scenes. */
@@ -20,6 +19,9 @@ module BABYLON.GLTF2 {
         /** Override this method to modify the default behavior for loading nodes. */
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>> { return null; }
 
+        /** 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>> { 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; }
 
@@ -29,14 +31,14 @@ module BABYLON.GLTF2 {
         // #endregion
 
         /** Helper method called by a loader extension to load an glTF extension. */
-        protected _loadExtensionAsync<T>(context: string, property: IProperty, actionAsync: (context: string, extension: T) => Promise<void>): Nullable<Promise<void>> {
+        protected _loadExtensionAsync<TProperty, TResult = void>(context: string, property: IProperty, actionAsync: (context: string, extension: TProperty) => Promise<TResult>): Nullable<Promise<TResult>> {
             if (!property.extensions) {
                 return null;
             }
 
             const extensions = property.extensions;
 
-            const extension = extensions[this.name] as T;
+            const extension = extensions[this.name] as TProperty;
             if (!extension) {
                 return null;
             }
@@ -44,10 +46,13 @@ module BABYLON.GLTF2 {
             // Clear out the extension before executing the action to avoid recursing into the same property.
             delete extensions[this.name];
 
-            return actionAsync(context + "/extensions/" + this.name, extension).then(() => {
-                // Restore the extension after completing the action.
+            try {
+                return actionAsync(context + "/extensions/" + this.name, extension);
+            }
+            finally {
+                // Restore the extension after executing the action.
                 extensions[this.name] = extension;
-            });
+            }
         }
 
         /** Helper method called by the loader to allow extensions to override loading scenes. */
@@ -60,6 +65,11 @@ module BABYLON.GLTF2 {
             return loader._applyExtensions(extension => extension._loadNodeAsync(context, node));
         }
 
+        /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
+        public static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> {
+            return loader._applyExtensions(extension => extension._loadVertexDataAsync(context, primitive, babylonMesh));
+        }
+
         /** 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));

+ 12 - 3
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -239,11 +239,9 @@ module BABYLON {
                 this._loader = null;
             }
 
-            this.onParsedObservable.clear();
             this.onMeshLoadedObservable.clear();
             this.onTextureLoadedObservable.clear();
             this.onMaterialLoadedObservable.clear();
-            this.onCompleteObservable.clear();
 
             this.onDisposeObservable.notifyObservers(this);
             this.onDisposeObservable.clear();
@@ -303,6 +301,8 @@ module BABYLON {
             }
 
             this.onParsedObservable.notifyObservers(parsedData);
+            this.onParsedObservable.clear();
+
             return parsedData;
         }
 
@@ -346,8 +346,17 @@ module BABYLON {
             loader.onMeshLoadedObservable.add(mesh => this.onMeshLoadedObservable.notifyObservers(mesh));
             loader.onTextureLoadedObservable.add(texture => this.onTextureLoadedObservable.notifyObservers(texture));
             loader.onMaterialLoadedObservable.add(material => this.onMaterialLoadedObservable.notifyObservers(material));
-            loader.onCompleteObservable.add(() => this.onCompleteObservable.notifyObservers(this));
             loader.onExtensionLoadedObservable.add(extension => this.onExtensionLoadedObservable.notifyObservers(extension));
+
+            loader.onCompleteObservable.add(() => {
+                this.onMeshLoadedObservable.clear();
+                this.onTextureLoadedObservable.clear();
+                this.onMaterialLoadedObservable.clear();
+
+                this.onCompleteObservable.notifyObservers(this);
+                this.onCompleteObservable.clear();
+            });
+
             return loader;
         }
 

+ 1 - 0
localDev/index.html

@@ -6,6 +6,7 @@
 
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <script src="https://preview.babylonjs.com/cannon.js"></script>
+    <script src="https://preview.babylonjs.com/draco_decoder.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
     <script src="../Tools/DevLoader/BabylonLoader.js"></script>

+ 2 - 0
materialsLibrary/src/fire/babylon.fireMaterial.ts

@@ -100,6 +100,8 @@ module BABYLON {
                 }
             }
 
+            defines.ALPHATEST = this._opacityTexture ? true : false;
+
             // Misc.
             if (defines._areMiscDirty) {
                 defines.POINTSIZE = (this.pointsCloud || scene.forcePointsCloud);

+ 1 - 1
package.json

@@ -41,4 +41,4 @@
     },
     "readme": "Babylon.js is a 3D engine based on webgl and javascript",
     "readmeFilename": "README.md"
-}
+}

+ 17 - 19
readme.md

@@ -5,8 +5,6 @@ Getting started? Play directly with the Babylon.js API via our [playground](http
 
 [![Build Status](https://travis-ci.org/BabylonJS/Babylon.js.svg)](https://travis-ci.org/BabylonJS/Babylon.js) 
 
-[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=d0pZcGpNU0NPeTM5cEpEc2lTU2JJTWc2Mk9NSlAzTzdIaFBpTnF2TjBycz0tLStUWkVBejdVQmN4Y0dIUVVYeU0yc2c9PQ==--4e576f7b7c21279bc6d026b6f51796f58134856b)](https://www.browserstack.com/automate/public-build/d0pZcGpNU0NPeTM5cEpEc2lTU2JJTWc2Mk9NSlAzTzdIaFBpTnF2TjBycz0tLStUWkVBejdVQmN4Y0dIUVVYeU0yc2c9PQ==--4e576f7b7c21279bc6d026b6f51796f58134856b)
-
 **Any questions?** Here is our official [forum](http://www.html5gamedevs.com/forum/16-babylonjs/) on www.html5gamedevs.com.
 
 ## CDN
@@ -60,29 +58,29 @@ To add a module install the respected package. A list of extra packages and thei
 ## Usage
 See [Getting Started](http://doc.babylonjs.com/#getting-started)
 ```javascript
-// get the canvas DOM element
+// Get the canvas DOM element
 var canvas = document.getElementById('renderCanvas');
-// load the 3D engine
-var engine = new BABYLON.Engine(canvas, true);
-// createScene function that creates and return the scene
+// Load the 3D engine
+var engine = new BABYLON.Engine(canvas, true, {preserveDrawingBuffer: true, stencil: true});
+// CreateScene function that creates and return the scene
 var createScene = function(){
-    // create a basic BJS Scene object
+    // Create a basic BJS Scene object
     var scene = new BABYLON.Scene(engine);
-    // create a FreeCamera, and set its position to (x:0, y:5, z:-10)
-    var camera = new BABYLON.FreeCamera('camera1', new BABYLON.Vector3(0, 5,-10), scene);
-    // target the camera to scene origin
+    // Create a FreeCamera, and set its position to {x: 0, y: 5, z: -10}
+    var camera = new BABYLON.FreeCamera('camera1', new BABYLON.Vector3(0, 5, -10), scene);
+    // Target the camera to scene origin
     camera.setTarget(BABYLON.Vector3.Zero());
-    // attach the camera to the canvas
+    // Attach the camera to the canvas
     camera.attachControl(canvas, false);
-    // create a basic light, aiming 0,1,0 - meaning, to the sky
-    var light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0,1,0), scene);
-    // create a built-in "sphere" shape; its constructor takes 6 params: name, segment, diameter, scene, updatable, sideOrientation
-    var sphere = BABYLON.Mesh.CreateSphere('sphere1', 16, 2, scene);
-    // move the sphere upward 1/2 of its height
+    // Create a basic light, aiming 0, 1, 0 - meaning, to the sky
+    var light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0, 1, 0), scene);
+    // Create a built-in "sphere" shape; its constructor takes 6 params: name, segment, diameter, scene, updatable, sideOrientation
+    var sphere = BABYLON.Mesh.CreateSphere('sphere1', 16, 2, scene, false, BABYLON.Mesh.FRONTSIDE);
+    // Move the sphere upward 1/2 of its height
     sphere.position.y = 1;
-    // create a built-in "ground" shape; its constructor takes the same 6 params : name, width, height, subdivision, scene, updatable
-    var ground = BABYLON.Mesh.CreateGround('ground1', 6, 6, 2, scene);
-    // return the created scene
+    // Create a built-in "ground" shape; its constructor takes 6 params : name, width, height, subdivision, scene, updatable
+    var ground = BABYLON.Mesh.CreateGround('ground1', 6, 6, 2, scene, false);
+    // Return the created scene
     return scene;
 }
 // call the createScene function

+ 5 - 4
sandbox/index-local.html

@@ -3,9 +3,10 @@
 <head>
     <title>BabylonJS - Sandbox</title>
     <link href="index.css" rel="stylesheet" />
-    <script src="https://babylonjs.azurewebsites.net/cannon.js"></script>
-    <script src="https://babylonjs.azurewebsites.net/Oimo.js"></script>
-	<script src="../Tools/DevLoader/BabylonLoader.js"></script>
+    <script src="../dist/preview%20release/cannon.js"></script>
+    <script src="../dist/preview%20release/draco_decoder.js"></script>
+    <script src="../dist/preview%20release/Oimo.js"></script>
+    <script src="../Tools/DevLoader/BabylonLoader.js"></script>
 </head>
 <body>
     <canvas id="renderCanvas"></canvas>
@@ -52,6 +53,6 @@
     <script>
         BABYLONDEVTOOLS.Loader.require('index.js')
             .load();
-    </script>	
+    </script>
 </body>
 </html>

+ 1 - 1
sandbox/index.html

@@ -27,6 +27,7 @@
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
 
     <script src="https://preview.babylonjs.com/cannon.js"></script>
+    <script src="https://preview.babylonjs.com/draco_decoder.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/babylon.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
@@ -34,7 +35,6 @@
     <script src="https://preview.babylonjs.com/loaders/babylon.glTFFileLoader.js"></script>
     <script src="https://preview.babylonjs.com/loaders/babylon.objFileLoader.js"></script>
     <script src="https://preview.babylonjs.com/loaders/babylon.stlFileLoader.js"></script>
-
 </head>
 
 <body>

+ 0 - 28
sandbox/index.js

@@ -20,7 +20,6 @@ if (BABYLON.Engine.isSupported()) {
     var currentSkybox;
     var enableDebugLayer = false;
     var currentPluginName;
-    var toExecuteAfterSceneCreation;
 
     canvas.addEventListener("contextmenu", function (evt) {
         evt.preventDefault();
@@ -129,10 +128,6 @@ if (BABYLON.Engine.isSupported()) {
             }
         }
 
-        if (toExecuteAfterSceneCreation) {
-            toExecuteAfterSceneCreation();
-        }
-
     };
 
     var sceneError = function (sceneFile, babylonScene, message) {
@@ -152,29 +147,6 @@ if (BABYLON.Engine.isSupported()) {
     };
 
     filesInput = new BABYLON.FilesInput(engine, null, sceneLoaded, null, null, null, function () { BABYLON.Tools.ClearLogCache() }, null, sceneError);
-    filesInput.onProcessFileCallback = (function (file, name, extension) {
-        if (extension === "dds") {
-            BABYLON.FilesInput.FilesToLoad[name] = file;
-            var loadTexture = () => {
-                if (currentPluginName === "gltf") { // currentPluginName is updated only once scene is loaded
-                    var newHdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("file:" + file.correctName, currentScene);
-                    if (currentSkybox) {
-                        currentSkybox.dispose();
-                    }
-                    currentSkybox = currentScene.createDefaultSkybox(newHdrTexture, true, (currentScene.activeCamera.maxZ - currentScene.activeCamera.minZ) / 2, 0.3);
-                }
-            }
-            if (currentScene) {
-                loadTexture();
-            }
-            else {
-                // Postpone texture loading until scene is loaded
-                toExecuteAfterSceneCreation = loadTexture;
-            }
-            return false;
-        }
-        return true;
-    }).bind(this);
     filesInput.monitorElementForDragNDrop(canvas);
 
     window.addEventListener("keydown", function (evt) {

+ 22 - 218
serializers/src/glTF/2.0/babylon.glTFExporter.ts

@@ -556,97 +556,6 @@ module BABYLON.GLTF2 {
         }
 
         /**
-         * 
-         * @param babylonTexture - Babylon texture to extract.
-         * @param mimeType - Mime Type of the babylonTexture.
-         * @return - glTF texture, or null if the texture format is not supported.
-         */
-        private exportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType = ImageMimeType.JPEG): Nullable<ITextureInfo> {
-            let textureInfo: Nullable<ITextureInfo> = null;
-
-            let glTFTexture: Nullable<ITexture>;
-
-            glTFTexture = {
-                source: this.images.length
-            };
-
-            let textureName = babylonTexture.getInternalTexture()!.url;
-            if (textureName.search('/') !== -1) {
-                const splitFilename = textureName.split('/');
-                textureName = splitFilename[splitFilename.length - 1];
-                const basefile = textureName.split('.')[0];
-                let extension = textureName.split('.')[1];
-                if (mimeType === ImageMimeType.JPEG) {
-                    extension = ".jpg";
-                }
-                else if (mimeType === ImageMimeType.PNG) {
-                    extension = ".png";
-                }
-                else {
-                    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);
-
-
-            imgData.data.set(pixels!);
-            ctx.putImageData(imgData, 0, 0);
-            const base64Data = imageCanvas.toDataURL(mimeType);
-            const binStr = atob(base64Data.split(',')[1]);
-            const arr = new Uint8Array(binStr.length);
-            for (let i = 0; i < binStr.length; ++i) {
-                arr[i] = binStr.charCodeAt(i);
-            }
-            const imageValues = { data: arr, mimeType: mimeType };
-
-            this.imageData[textureName] = imageValues;
-            if (mimeType === ImageMimeType.JPEG) {
-                const glTFImage: IImage = {
-                    uri: textureName
-                }
-                let foundIndex = -1;
-                for (let i = 0; i < this.images.length; ++i) {
-                    if (this.images[i].uri === textureName) {
-                        foundIndex = i;
-                        break;
-                    }
-                }
-                if (foundIndex === -1) {
-                    this.images.push(glTFImage);
-                    glTFTexture.source = this.images.length - 1;
-                    this.textures.push({
-                        source: this.images.length - 1
-                    });
-
-                    textureInfo = {
-                        index: this.images.length - 1
-                    }
-                }
-                else {
-                    glTFTexture.source = foundIndex;
-
-                    textureInfo = {
-                        index: foundIndex
-                    }
-                }
-            }
-
-            return textureInfo;
-        }
-
-        /**
          * Creates a bufferview based on the vertices type for the Babylon mesh
          * @param kind - Indicates the type of vertices data.
          * @param babylonMesh - The Babylon mesh to get the vertices data from.
@@ -721,7 +630,7 @@ module BABYLON.GLTF2 {
                                 break;
                             }
                             default: {
-                                console.warn("Unsupported VertexBuffer kind: " + kind);
+                                Tools.Warn("Unsupported VertexBuffer kind: " + kind);
                             }
                         }
 
@@ -817,7 +726,6 @@ module BABYLON.GLTF2 {
                 const submesh = babylonMesh.subMeshes[j];
                 const meshPrimitive: IMeshPrimitive = { attributes: {} };
 
-
                 if (bufferMesh !== null) {
                     // Create a bufferview storing all the positions
                     if (!dataBuffer) {
@@ -829,8 +737,8 @@ module BABYLON.GLTF2 {
                             const positionStrideSize = positionVertexBuffer!.getStrideSize();
 
                             // Create accessor
-                            const result = this.calculateMinMax(positions!, 0, positions!.length/positionStrideSize, positionStrideSize!, useRightHandedSystem);
-                            const accessor = this.createAccessor(positionBufferViewIndex, "Position", AccessorType.VEC3, AccessorComponentType.FLOAT, positions!.length/positionStrideSize, 0, result.min, result.max);
+                            const result = this.calculateMinMax(positions!, 0, positions!.length / positionStrideSize, positionStrideSize!, useRightHandedSystem);
+                            const accessor = this.createAccessor(positionBufferViewIndex, "Position", AccessorType.VEC3, AccessorComponentType.FLOAT, positions!.length / positionStrideSize, 0, result.min, result.max);
                             this.accessors.push(accessor);
 
                             meshPrimitive.attributes.POSITION = this.accessors.length - 1;
@@ -842,7 +750,7 @@ module BABYLON.GLTF2 {
                             const normalStrideSize = normalVertexBuffer!.getStrideSize();
 
                             // Create accessor
-                            const accessor = this.createAccessor(normalBufferViewIndex, "Normal", AccessorType.VEC3, AccessorComponentType.FLOAT, normals!.length/normalStrideSize);
+                            const accessor = this.createAccessor(normalBufferViewIndex, "Normal", AccessorType.VEC3, AccessorComponentType.FLOAT, normals!.length / normalStrideSize);
                             this.accessors.push(accessor);
 
                             meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
@@ -854,7 +762,7 @@ module BABYLON.GLTF2 {
                             const tangentStrideSize = tangentVertexBuffer!.getStrideSize();
 
                             // Create accessor
-                            const accessor = this.createAccessor(tangentBufferViewIndex, "Tangent", AccessorType.VEC4, AccessorComponentType.FLOAT, tangents!.length/tangentStrideSize);
+                            const accessor = this.createAccessor(tangentBufferViewIndex, "Tangent", AccessorType.VEC4, AccessorComponentType.FLOAT, tangents!.length / tangentStrideSize);
                             this.accessors.push(accessor);
 
                             meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
@@ -866,7 +774,7 @@ module BABYLON.GLTF2 {
                             const colorStrideSize = colorVertexBuffer!.getStrideSize();
 
                             // Create accessor
-                            const accessor = this.createAccessor(colorBufferViewIndex, "Color", AccessorType.VEC4, AccessorComponentType.FLOAT, colors!.length/colorStrideSize);
+                            const accessor = this.createAccessor(colorBufferViewIndex, "Color", AccessorType.VEC4, AccessorComponentType.FLOAT, colors!.length / colorStrideSize);
                             this.accessors.push(accessor);
 
                             meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
@@ -877,7 +785,7 @@ module BABYLON.GLTF2 {
                             const texCoord0VertexBuffer = bufferMesh.getVertexBuffer(VertexBuffer.UVKind);
                             const texCoord0s = texCoord0VertexBuffer!.getData();
                             const texCoord0StrideSize = texCoord0VertexBuffer!.getStrideSize();
-                            const accessor = this.createAccessor(texCoord0BufferViewIndex, "Texture Coords 0", AccessorType.VEC2, AccessorComponentType.FLOAT, texCoord0s!.length/texCoord0StrideSize);
+                            const accessor = this.createAccessor(texCoord0BufferViewIndex, "Texture Coords 0", AccessorType.VEC2, AccessorComponentType.FLOAT, texCoord0s!.length / texCoord0StrideSize);
                             this.accessors.push(accessor);
 
                             meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
@@ -888,7 +796,7 @@ module BABYLON.GLTF2 {
                             const texCoord1VertexBuffer = bufferMesh.getVertexBuffer(VertexBuffer.UV2Kind);
                             const texCoord1s = texCoord1VertexBuffer!.getData();
                             const texCoord1StrideSize = texCoord1VertexBuffer!.getStrideSize();
-                            const accessor = this.createAccessor(texCoord1BufferViewIndex, "Texture Coords 1", AccessorType.VEC2, AccessorComponentType.FLOAT, texCoord1s!.length/texCoord1StrideSize);
+                            const accessor = this.createAccessor(texCoord1BufferViewIndex, "Texture Coords 1", AccessorType.VEC2, AccessorComponentType.FLOAT, texCoord1s!.length / texCoord1StrideSize);
                             this.accessors.push(accessor);
 
                             meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
@@ -904,129 +812,22 @@ module BABYLON.GLTF2 {
                         }
                     }
                     if (bufferMesh.material) {
-                        if (bufferMesh.material instanceof StandardMaterial) {
-                            console.warn("Standard Material is currently not fully supported/implemented in glTF serializer");
-                            const babylonStandardMaterial = bufferMesh.material as StandardMaterial;
-                            const glTFPbrMetallicRoughness = _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
-
-                            const glTFMaterial: IMaterial = { name: babylonStandardMaterial.name };
-                            if (!babylonStandardMaterial.backFaceCulling) {
-                                glTFMaterial.doubleSided = true;
-                            }
-                            if (babylonStandardMaterial.diffuseTexture && bufferMesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
-                                const glTFTexture = this.exportTexture(babylonStandardMaterial.diffuseTexture);
-                                if (glTFTexture !== null) {
-                                    glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                                }
-                            }
-                            if (babylonStandardMaterial.bumpTexture && bufferMesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
-                                const glTFTexture = this.exportTexture(babylonStandardMaterial.bumpTexture);
-                                if (glTFTexture) {
-                                    glTFMaterial.normalTexture = glTFTexture;
-                                }
-                            }
-                            if (babylonStandardMaterial.emissiveTexture && bufferMesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
-                                const glTFEmissiveTexture = this.exportTexture(babylonStandardMaterial.emissiveTexture);
-                                if (glTFEmissiveTexture) {
-                                    glTFMaterial.emissiveTexture = glTFEmissiveTexture;
-                                }
-                                glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
-                            }
-                            if (babylonStandardMaterial.ambientTexture && bufferMesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
-                                const glTFOcclusionTexture = this.exportTexture(babylonStandardMaterial.ambientTexture);
-                                if (glTFOcclusionTexture) {
-                                    glTFMaterial.occlusionTexture = glTFOcclusionTexture;
-                                }
-                            }
-                            if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
-
-                                if (babylonStandardMaterial.alphaMode === Engine.ALPHA_COMBINE) {
-                                    glTFMaterial.alphaMode = GLTF2.MaterialAlphaMode.BLEND;
-                                }
-                                else {
-                                    console.warn("glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
-                                }
-                            }
-
-                            glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
-
-                            this.materials.push(glTFMaterial);
-                            meshPrimitive.material = this.materials.length - 1;
+                        if (bufferMesh.material instanceof StandardMaterial || bufferMesh.material instanceof PBRMetallicRoughnessMaterial) {
+                            const materialIndex = babylonMesh.getScene().materials.indexOf(bufferMesh.material);
+                            meshPrimitive.material = materialIndex;
                         }
-                        else if (bufferMesh.material instanceof PBRMetallicRoughnessMaterial) {
-                            const babylonPBRMaterial = bufferMesh.material as PBRMetallicRoughnessMaterial;
-                            const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {};
-
-                            if (babylonPBRMaterial.baseColor) {
-                                glTFPbrMetallicRoughness.baseColorFactor = [
-                                    babylonPBRMaterial.baseColor.r,
-                                    babylonPBRMaterial.baseColor.g,
-                                    babylonPBRMaterial.baseColor.b,
-                                    babylonPBRMaterial.alpha
-                                ];
-                            }
-                            if (babylonPBRMaterial.baseTexture !== undefined) {
-                                const glTFTexture = this.exportTexture(babylonPBRMaterial.baseTexture);
-                                if (glTFTexture !== null) {
-                                    glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                                }
-                                glTFPbrMetallicRoughness.baseColorTexture
-                            }
-                            if (babylonPBRMaterial.metallic !== undefined) {
-                                glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
-                            }
-                            if (babylonPBRMaterial.roughness !== undefined) {
-                                glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
-                            }
+                        else if (bufferMesh.material instanceof MultiMaterial) {
+                            const babylonMultiMaterial = bufferMesh.material as MultiMaterial;
 
-                            const glTFMaterial: IMaterial = {
-                                name: babylonPBRMaterial.name
-                            };
-                            if (babylonPBRMaterial.doubleSided) {
-                                glTFMaterial.doubleSided = babylonPBRMaterial.doubleSided;
-                            }
-                            if (babylonPBRMaterial.normalTexture) {
-                                const glTFTexture = this.exportTexture(babylonPBRMaterial.normalTexture);
-                                if (glTFTexture) {
-                                    glTFMaterial.normalTexture = glTFTexture;
-                                }
-                            }
-                            if (babylonPBRMaterial.occlusionTexture) {
-                                const glTFTexture = this.exportTexture(babylonPBRMaterial.occlusionTexture);
-                                if (glTFTexture) {
-                                    glTFMaterial.occlusionTexture = glTFTexture;
-                                    if (babylonPBRMaterial.occlusionStrength !== undefined) {
-                                        glTFMaterial.occlusionTexture.strength = babylonPBRMaterial.occlusionStrength;
-                                    }
-                                }
-                            }
-                            if (babylonPBRMaterial.emissiveTexture) {
-                                const glTFTexture = this.exportTexture(babylonPBRMaterial.emissiveTexture);
-                                if (glTFTexture !== null) {
-                                    glTFMaterial.emissiveTexture = glTFTexture;
-                                }
-                            }
-                            if (!babylonPBRMaterial.emissiveColor.equals(new Color3(0.0, 0.0, 0.0))) {
-                                glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
-                            }
-                            if (babylonPBRMaterial.transparencyMode) {
-                                const alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
-
-                                if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
-                                    glTFMaterial.alphaMode = alphaMode;
-                                    if (alphaMode === MaterialAlphaMode.BLEND) {
-                                        glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
-                                    }
-                                }
-                            }
+                            const material = babylonMultiMaterial.subMaterials[submesh.materialIndex];
 
-                            glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
-
-                            this.materials.push(glTFMaterial);
-                            meshPrimitive.material = this.materials.length - 1;
+                            if (material !== null) {
+                                const materialIndex = babylonMesh.getScene().materials.indexOf(material);
+                                meshPrimitive.material = materialIndex;
+                            }
                         }
                         else {
-                            console.warn("Material type is not yet implemented in glTF serializer: " + bufferMesh.material.name);
+                            Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
                         }
                     }
                     mesh.primitives.push(meshPrimitive);
@@ -1047,6 +848,9 @@ module BABYLON.GLTF2 {
             if (babylonScene.meshes.length > 0) {
                 const babylonMeshes = babylonScene.meshes;
                 const scene = { nodes: new Array<number>() };
+                if (dataBuffer == null) {
+                    _GLTFMaterial.ConvertMaterialsToGLTF(babylonScene.materials, ImageMimeType.JPEG, this.images, this.textures, this.materials, this.imageData, true);
+                }
 
                 for (let i = 0; i < babylonMeshes.length; ++i) {
                     if (this.options &&

+ 328 - 113
serializers/src/glTF/2.0/babylon.glTFMaterial.ts

@@ -2,62 +2,40 @@
 
 module BABYLON.GLTF2 {
     /**
-     * Represents the components used for representing a physically-based specular glossiness material
+     * Utility methods for working with glTF material conversion properties.  This class should only be used internally.
      */
-    interface IBabylonPbrSpecularGlossiness {
-        /**
-         * The diffuse color of the model, whose color values should be 
-         * normalized from 0 to 1.  
-         */
-        diffuse: Color3;
-        /**
-         * Represents the transparency of the material, from a range of 0 to 1.
-         */
-        opacity: number;
-        /**
-         * Represents how specular the material is, from a range of 0 to 1.
-         */
-        specular: Color3;
+    export class _GLTFMaterial {
         /**
-         * Represents how glossy the material is, from a range of 0 to 1.
+         * Represents the dielectric specular values for R, G and B.
          */
-        glossiness: number;
-    }
+        private static readonly dielectricSpecular: Color3 = new Color3(0.04, 0.04, 0.04);
 
-    /**
-     * Represents the components used for representing a physically-based metallic roughness material.
-     */
-    interface _IBabylonPbrMetallicRoughness {
-        /**
-         * The albedo color of the material, whose color components should be normalized from 0 to 1.
-         */
-        baseColor: Color3;
-        /**
-         * Represents the transparency of the material, from a range of 0 (transparent) to 1 (opaque).
-         */
-        opacity: number;
-        /**
-         * Represents the "metalness" of a material, from a range of 0 (dielectric) to 1 (metal).
-         */
-        metallic: number;
         /**
-         * Represents the "roughness" of a material, from a range of 0 (completely smooth) to 1 (completely rough).
+         * Allows the maximum specular power to be defined for material calculations.
          */
-        roughness: number;
-    }
+        private static maxSpecularPower = 1024;
 
-    /**
-     * Utility methods for working with glTF material conversion properties.  This class should only be used internally.
-     */
-    export class _GLTFMaterial {
         /**
-         * Represents the dielectric specular values for R, G and B.
-         */
-        private static readonly dielectricSpecular = new Color3(0.04, 0.04, 0.04);
-        /**
-         * Epsilon value, used as a small tolerance value for a numeric value.
+         * 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
          */
-        private static readonly epsilon = 1e-6;
+        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);
+                }
+                else if (babylonMaterial instanceof PBRMetallicRoughnessMaterial) {
+                    _GLTFMaterial.ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                }
+            }
+        }
 
         /**
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
@@ -65,72 +43,61 @@ module BABYLON.GLTF2 {
          * @returns - glTF Metallic Roughness Material representation
          */
         public static ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness {
-            const babylonSpecularGlossiness: IBabylonPbrSpecularGlossiness = {
-                diffuse: babylonStandardMaterial.diffuseColor,
-                opacity: babylonStandardMaterial.alpha,
-                specular: babylonStandardMaterial.specularColor || Color3.Black(),
-                glossiness: babylonStandardMaterial.specularPower / 256
-            };
-            if (babylonStandardMaterial.specularTexture) {
+            const P0 = new BABYLON.Vector2(0, 1);
+            const P1 = new BABYLON.Vector2(0, 0.1);
+            const P2 = new BABYLON.Vector2(0, 0.1);
+            const P3 = new BABYLON.Vector2(1300, 0.1);
 
+            /**
+             * Given the control points, solve for x based on a given t for a cubic bezier curve.
+             * @param t - a value between 0 and 1.
+             * @param p0 - first control point.
+             * @param p1 - second control point.
+             * @param p2 - third control point.
+             * @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 {
+                return (
+                    (1 - t) * (1 - t) * (1 - t) * p0 +
+                    3 * (1 - t) * (1 - t) * t * p1 +
+                    3 * (1 - t) * t * t * p2 +
+                    t * t * t * p3
+                );
             }
-            const babylonMetallicRoughness = _GLTFMaterial._ConvertToMetallicRoughness(babylonSpecularGlossiness);
+
+            /**
+             * Evaluates a specified specular power value to determine the appropriate roughness value, 
+             * based on a pre-defined cubic bezier curve with specular on the abscissa axis (x-axis) 
+             * and roughness on the ordinant axis (y-axis).
+             * @param specularPower - specular power of standard material.
+             * @returns - Number representing the roughness value.
+             */
+            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);
+            }
+
+            let diffuse = babylonStandardMaterial.diffuseColor.toLinearSpace().scale(0.5);
+            let opacity = babylonStandardMaterial.alpha;
+            let specularPower = Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this.maxSpecularPower);
+
+            const roughness = solveForRoughness(specularPower);
 
             const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {
                 baseColorFactor: [
-                    babylonMetallicRoughness.baseColor.r,
-                    babylonMetallicRoughness.baseColor.g,
-                    babylonMetallicRoughness.baseColor.b,
-                    babylonMetallicRoughness.opacity
+                    diffuse.r,
+                    diffuse.g,
+                    diffuse.b,
+                    opacity
                 ],
-                metallicFactor: babylonMetallicRoughness.metallic,
-                roughnessFactor: babylonMetallicRoughness.roughness
+                metallicFactor: 0,
+                roughnessFactor: roughness,
             };
 
             return glTFPbrMetallicRoughness;
         }
 
-        /**
-         * Converts Specular Glossiness to Metallic Roughness.  This is based on the algorithm used in the Babylon glTF 3ds Max Exporter.
-         * {@link https://github.com/BabylonJS/Exporters/blob/master/3ds%20Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.Material.cs}
-         * @param  babylonSpecularGlossiness - Babylon specular glossiness parameters
-         * @returns - Babylon metallic roughness values
-         */
-        private static _ConvertToMetallicRoughness(babylonSpecularGlossiness: IBabylonPbrSpecularGlossiness): _IBabylonPbrMetallicRoughness {
-            const diffuse = babylonSpecularGlossiness.diffuse;
-            const opacity = babylonSpecularGlossiness.opacity;
-            const specular = babylonSpecularGlossiness.specular;
-            const glossiness = BABYLON.Scalar.Clamp(babylonSpecularGlossiness.glossiness);
-            
-            const oneMinusSpecularStrength = 1 - Math.max(specular.r, Math.max(specular.g, specular.b));
-            const diffusePerceivedBrightness = _GLTFMaterial.PerceivedBrightness(diffuse);
-            const specularPerceivedBrightness = _GLTFMaterial.PerceivedBrightness(specular);
-            const metallic = _GLTFMaterial.SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
-            const diffuseScaleFactor = oneMinusSpecularStrength/(1 - this.dielectricSpecular.r) / Math.max(1 - metallic, this.epsilon);
-            const baseColorFromDiffuse = diffuse.scale(diffuseScaleFactor);
-            const baseColorFromSpecular = specular.subtract(this.dielectricSpecular.scale(1 - metallic)).scale(1/ Math.max(metallic, this.epsilon));
-            const lerpColor = Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
-            let baseColor = new Color3();
-            lerpColor.clampToRef(0, 1, baseColor);
-
-            const babylonMetallicRoughness: _IBabylonPbrMetallicRoughness = {
-                baseColor: baseColor,
-                opacity: opacity,
-                metallic: metallic,
-                roughness: 1.0 - glossiness
-            };
-
-            return babylonMetallicRoughness;
-        }
-
-        /**
-         * Returns the perceived brightness value based on the provided color
-         * @param color - color used in calculating the perceived brightness
-         * @returns - perceived brightness value
-         */
-        private static PerceivedBrightness(color: Color3): number {
-            return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
-        }
 
         /**
          * Computes the metallic factor
@@ -140,17 +107,18 @@ module BABYLON.GLTF2 {
          * @returns - metallic value
          */
         public static SolveMetallic(diffuse: number, specular: number, oneMinusSpecularStrength: number): number {
-            if (specular < this.dielectricSpecular.r) {
+            if (specular < _GLTFMaterial.dielectricSpecular.r) {
+                _GLTFMaterial.dielectricSpecular
                 return 0;
             }
 
-            const a = this.dielectricSpecular.r;
-            const b = diffuse * oneMinusSpecularStrength /(1.0 - this.dielectricSpecular.r) + specular - 2.0 * this.dielectricSpecular.r;
-            const c = this.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));
+            return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a), 0, 1);
         }
-        
+
         /**
          * Gets the glTF alpha mode from the Babylon Material
          * @param babylonMaterial - Babylon Material
@@ -159,10 +127,10 @@ module BABYLON.GLTF2 {
         public static GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode {
             if (babylonMaterial instanceof StandardMaterial) {
                 const babylonStandardMaterial = babylonMaterial as StandardMaterial;
-                if ((babylonStandardMaterial.alpha != 1.0) || 
+                if ((babylonStandardMaterial.alpha != 1.0) ||
                     (babylonStandardMaterial.diffuseTexture != null && babylonStandardMaterial.diffuseTexture.hasAlpha) ||
                     (babylonStandardMaterial.opacityTexture != null)) {
-                    return  MaterialAlphaMode.BLEND;
+                    return MaterialAlphaMode.BLEND;
                 }
                 else {
                     return MaterialAlphaMode.OPAQUE;
@@ -171,7 +139,7 @@ module BABYLON.GLTF2 {
             else if (babylonMaterial instanceof PBRMetallicRoughnessMaterial) {
                 const babylonPBRMetallicRoughness = babylonMaterial as PBRMetallicRoughnessMaterial;
 
-                switch(babylonPBRMetallicRoughness.transparencyMode) {
+                switch (babylonPBRMetallicRoughness.transparencyMode) {
                     case PBRMaterial.PBRMATERIAL_OPAQUE: {
                         return MaterialAlphaMode.OPAQUE;
                     }
@@ -182,7 +150,7 @@ module BABYLON.GLTF2 {
                         return MaterialAlphaMode.MASK;
                     }
                     case PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND: {
-                        console.warn("GLTF Exporter | Alpha test and blend mode not supported in glTF.  Alpha blend used instead.");
+                        Tools.Warn(babylonMaterial.name + ": GLTF Exporter | Alpha test and blend mode not supported in glTF.  Alpha blend used instead.");
                         return MaterialAlphaMode.BLEND;
                     }
                     default: {
@@ -192,7 +160,254 @@ module BABYLON.GLTF2 {
             }
             else {
                 throw new Error("Unsupported Babylon material type");
-            }   
+            }
+        }
+
+        /**
+         * Converts a Babylon Standard Material to a glTF Material.
+         * @param babylonStandardMaterial - BJS Standard 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.
+         */
+        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 glTFMaterial: IMaterial = { name: babylonStandardMaterial.name };
+            if (babylonStandardMaterial.backFaceCulling) {
+                if (!babylonStandardMaterial.twoSidedLighting) {
+                    Tools.Warn(babylonStandardMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
+                }
+                glTFMaterial.doubleSided = true;
+            }
+            if (hasTextureCoords) {
+                if (babylonStandardMaterial.diffuseTexture) {
+                    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)
+                    if (glTFTexture) {
+                        glTFMaterial.normalTexture = glTFTexture;
+                    }
+                }
+                if (babylonStandardMaterial.emissiveTexture) {
+                    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 glTFOcclusionTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData)
+                    if (glTFOcclusionTexture) {
+                        glTFMaterial.occlusionTexture = glTFOcclusionTexture;
+                    }
+                }
+            }
+
+            if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
+
+                if (babylonStandardMaterial.alphaMode === Engine.ALPHA_COMBINE) {
+                    glTFMaterial.alphaMode = GLTF2.MaterialAlphaMode.BLEND;
+                }
+                else {
+                    Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
+                }
+            }
+
+            glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
+
+            materials.push(glTFMaterial);
+        }
+
+        /**
+         * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
+         * @param babylonPBRMetalRoughMaterial - 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.
+         */
+        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) {
+                glTFPbrMetallicRoughness.baseColorFactor = [
+                    babylonPBRMetalRoughMaterial.baseColor.r,
+                    babylonPBRMetalRoughMaterial.baseColor.g,
+                    babylonPBRMetalRoughMaterial.baseColor.b,
+                    babylonPBRMetalRoughMaterial.alpha
+                ];
+            }
+
+            if (babylonPBRMetalRoughMaterial.metallic != null) {
+                glTFPbrMetallicRoughness.metallicFactor = babylonPBRMetalRoughMaterial.metallic;
+            }
+            if (babylonPBRMetalRoughMaterial.roughness != null) {
+                glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMetalRoughMaterial.roughness;
+            }
+
+            const glTFMaterial: IMaterial = {
+                name: babylonPBRMetalRoughMaterial.name
+            };
+            if (babylonPBRMetalRoughMaterial.doubleSided) {
+                glTFMaterial.doubleSided = babylonPBRMetalRoughMaterial.doubleSided;
+            }
+
+            if (hasTextureCoords) {
+                if (babylonPBRMetalRoughMaterial.baseTexture != null) {
+                    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);
+                    if (glTFTexture) {
+                        glTFMaterial.normalTexture = glTFTexture;
+                    }
+                }
+                if (babylonPBRMetalRoughMaterial.occlusionTexture) {
+                    const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
+                    if (glTFTexture) {
+                        glTFMaterial.occlusionTexture = glTFTexture;
+                        if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
+                            glTFMaterial.occlusionTexture.strength = babylonPBRMetalRoughMaterial.occlusionStrength;
+                        }
+                    }
+                }
+                if (babylonPBRMetalRoughMaterial.emissiveTexture) {
+                    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)) {
+                glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
+            }
+            if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
+                const alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMetalRoughMaterial);
+
+                if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
+                    glTFMaterial.alphaMode = alphaMode;
+                    if (alphaMode === MaterialAlphaMode.BLEND) {
+                        glTFMaterial.alphaCutoff = babylonPBRMetalRoughMaterial.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.
+         * @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.
+         */
+        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
+            };
+
+            let textureName = "texture_" + (textures.length - 1).toString();
+            let textureData = babylonTexture.getInternalTexture();
+
+            if (textureData != null) {
+                textureName = textureData.url;
+            }
+
+            textureName = Tools.GetFilename(textureName);
+            const baseFile = textureName.split('.')[0];
+            let extension = "";
+
+
+            if (mimeType === ImageMimeType.JPEG) {
+                extension = ".jpg";
+            }
+            else if (mimeType === ImageMimeType.PNG) {
+                extension = ".png";
+            }
+            else {
+                Tools.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);
+
+
+            imgData.data.set(pixels);
+            ctx.putImageData(imgData, 0, 0);
+            const base64Data = imageCanvas.toDataURL(mimeType);
+            const binStr = atob(base64Data.split(',')[1]);
+            const arr = new Uint8Array(binStr.length);
+            for (let i = 0; i < binStr.length; ++i) {
+                arr[i] = binStr.charCodeAt(i);
+            }
+            const imageValues = { data: arr, mimeType: mimeType };
+
+            imageData[textureName] = imageValues;
+            if (mimeType === ImageMimeType.JPEG) {
+                const glTFImage: IImage = {
+                    uri: textureName
+                }
+                let foundIndex = -1;
+                for (let i = 0; i < images.length; ++i) {
+                    if (images[i].uri === textureName) {
+                        foundIndex = i;
+                        break;
+                    }
+                }
+                if (foundIndex === -1) {
+                    images.push(glTFImage);
+                    glTFTexture.source = images.length - 1;
+                    textures.push({
+                        source: images.length - 1
+                    });
+
+                    textureInfo = {
+                        index: images.length - 1
+                    }
+                }
+                else {
+                    glTFTexture.source = foundIndex;
+
+                    textureInfo = {
+                        index: foundIndex
+                    }
+                }
+            }
+
+            return textureInfo;
         }
     }
 }

+ 1 - 1
src/Animations/babylon.animatable.ts

@@ -110,7 +110,7 @@
                 var fps = runtimeAnimations[0].animation.framePerSecond;
                 var currentFrame = runtimeAnimations[0].currentFrame;
                 var adjustTime = frame - currentFrame;
-                var delay = adjustTime * 1000 / fps;
+                var delay = adjustTime * 1000 / (fps * this.speedRatio);
                 if (this._localDelayOffset === null) {
                     this._localDelayOffset = 0;
                 }

+ 1 - 1
src/Audio/babylon.soundtrack.ts

@@ -35,7 +35,7 @@
         }
 
         public dispose() {
-            if (Engine.audioEngine.canUseWebAudio) {
+            if (Engine.audioEngine && Engine.audioEngine.canUseWebAudio) {
                 if (this._connectedAnalyser) {
                     this._connectedAnalyser.stopDebugCanvas();
                 }

+ 5 - 0
src/Bones/babylon.skeleton.ts

@@ -18,6 +18,11 @@
 
         private _lastAbsoluteTransformsUpdateId = -1;
 
+        /**
+         * Specifies if the skeleton should be serialized.
+         */
+        public doNotSerialize = false;        
+
         // Events
         /**
          * An event triggered before computing the skeleton's matrices

+ 1 - 7
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -188,7 +188,6 @@ module BABYLON {
         private _teleportationTarget: Mesh;
         private _isDefaultTeleportationTarget = true;
         private _postProcessMove: ImageProcessingPostProcess;
-        private _passProcessMove: PassPostProcess;
         private _teleportationFillColor: string = "#444444";
         private _teleportationBorderColor: string = "#FFFFFF";
         private _rotationAngle: number = 0;
@@ -897,7 +896,6 @@ module BABYLON {
                     imageProcessingConfiguration);
 
                 this._webVRCamera.detachPostProcess(this._postProcessMove)
-                this._passProcessMove = new PassPostProcess("pass", 1.0, this._webVRCamera);
                 this._teleportationInitialized = true;
                 if (this._isDefaultTeleportationTarget) {
                     this._createTeleportationCircles();
@@ -1320,7 +1318,7 @@ module BABYLON {
 
             this._postProcessMove.imageProcessingConfiguration.vignetteWeight = 0;
             this._postProcessMove.imageProcessingConfiguration.vignetteStretch = 0;
-
+            this._postProcessMove.samples = 4;
             this._webVRCamera.attachPostProcess(this._postProcessMove)
             this._scene.beginAnimation(this._postProcessMove, 0, 6, false, 1, () => {
                 this._webVRCamera.detachPostProcess(this._postProcessMove)
@@ -1611,10 +1609,6 @@ module BABYLON {
                 this.exitVR();
             }
 
-            if (this._passProcessMove) {
-                this._passProcessMove.dispose();
-            }
-
             if (this._postProcessMove) {
                 this._postProcessMove.dispose();
             }

+ 106 - 43
src/Engine/babylon.engine.ts

@@ -270,7 +270,7 @@
     }
 
     export interface IDisplayChangedEventArgs {
-        vrDisplay: any;
+        vrDisplay: Nullable<any>;
         vrSupported: boolean;
     }
 
@@ -282,6 +282,7 @@
         public static ExceptionList = [
             { key: "Chrome/63.0", capture: "63\\.0\\.3239\\.(\\d+)", captureConstraint: 108, targets: ["uniformBuffer"] },
             { key: "Firefox/58", capture: null, captureConstraint: null, targets: ["uniformBuffer"] },
+            { key: "Firefox/59", capture: null, captureConstraint: null, targets: ["uniformBuffer"] },
             { key: "Macintosh", capture: null, captureConstraint: null, targets: ["textureBindingOptimization"] },
             { key: "iPhone", capture: null, captureConstraint: null, targets: ["textureBindingOptimization"] },
         ];
@@ -345,6 +346,9 @@
         private static _TEXTUREFORMAT_LUMINANCE_ALPHA = 2;
         private static _TEXTUREFORMAT_RGB = 4;
         private static _TEXTUREFORMAT_RGBA = 5;
+        private static _TEXTUREFORMAT_R32F = 6;
+        private static _TEXTUREFORMAT_RG32F = 7;
+        private static _TEXTUREFORMAT_RGB32F = 8;
 
         private static _TEXTURETYPE_UNSIGNED_INT = 0;
         private static _TEXTURETYPE_FLOAT = 1;
@@ -497,6 +501,27 @@
             return Engine._TEXTUREFORMAT_LUMINANCE;
         }
 
+        /**
+         * R32F
+         */
+        public static get TEXTUREFORMAT_R32F(): number {
+            return Engine._TEXTUREFORMAT_R32F;
+        }       
+        
+        /**
+         * RG32F
+         */
+        public static get TEXTUREFORMAT_RG32F(): number {
+            return Engine._TEXTUREFORMAT_RG32F;
+        }       
+        
+        /**
+         * RGB32F
+         */
+        public static get TEXTUREFORMAT_RGB32F(): number {
+            return Engine._TEXTUREFORMAT_RGB32F;
+        }               
+
         public static get TEXTUREFORMAT_LUMINANCE_ALPHA(): number {
             return Engine._TEXTUREFORMAT_LUMINANCE_ALPHA;
         }
@@ -594,6 +619,7 @@
         private _oldSize: Size;
         private _oldHardwareScaleFactor: number;
         private _vrExclusivePointerMode = false;
+        private _webVRInitPromise: Promise<IDisplayChangedEventArgs>;
 
         public get isInVRExclusivePointerMode(): boolean {
             return this._vrExclusivePointerMode;
@@ -1789,7 +1815,7 @@
          * The onVRDisplayChangedObservable will be notified upon these changes.
          * @returns The onVRDisplayChangedObservable.
          */
-        public initWebVR(): Observable<{ vrDisplay: any, vrSupported: any }> {
+        public initWebVR(): Observable<IDisplayChangedEventArgs> {
             this.initWebVRAsync();
             return this.onVRDisplayChangedObservable;
         }
@@ -1799,13 +1825,14 @@
          * The onVRDisplayChangedObservable will be notified upon these changes.
          * @returns A promise containing a VRDisplay and if vr is supported.
          */
-        public initWebVRAsync(): Promise<{ vrDisplay: Nullable<any>, vrSupported: boolean }> {
+        public initWebVRAsync(): Promise<IDisplayChangedEventArgs> {
             var notifyObservers = () => {
                 var eventArgs = {
                     vrDisplay: this._vrDisplay,
                     vrSupported: this._vrSupported
                 };
                 this.onVRDisplayChangedObservable.notifyObservers(eventArgs);
+                this._webVRInitPromise = new Promise((res)=>{res(eventArgs)});
             }
 
             if (!this._onVrDisplayConnect) {
@@ -1826,10 +1853,9 @@
                 window.addEventListener('vrdisplaydisconnect', this._onVrDisplayDisconnect);
                 window.addEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
             }
-
-            var returnPromise = this._getVRDisplaysAsync();
-            returnPromise.then(notifyObservers);
-            return returnPromise;
+            this._webVRInitPromise = this._webVRInitPromise || this._getVRDisplaysAsync();
+            this._webVRInitPromise.then(notifyObservers);
+            return this._webVRInitPromise;
         }
 
         public enableVR() {
@@ -1869,7 +1895,7 @@
             }
         }
 
-        private _getVRDisplaysAsync():Promise<{vrDisplay: any, vrSupported: boolean}> {
+        private _getVRDisplaysAsync():Promise<IDisplayChangedEventArgs> {
             return new Promise((res, rej)=>{    
                 if (navigator.getVRDisplays) {
                     navigator.getVRDisplays().then((devices: Array<any>)=>{
@@ -2941,6 +2967,18 @@
             this._gl.uniform4f(uniform, color3.r, color3.g, color3.b, alpha);
         }
 
+        /**
+         * Sets a Color4 on a uniform variable
+         * @param uniform defines the uniform location
+         * @param color4 defines the value to be set
+         */
+        public setDirectColor4(uniform: Nullable<WebGLUniformLocation>, color4: Color4): void {
+            if (!uniform)
+                return;
+
+            this._gl.uniform4f(uniform, color4.r, color4.g, color4.b, color4.a);
+        }        
+
         // States
         public setState(culling: boolean, zOffset: number = 0, force?: boolean, reverseSide = false): void {
             // Culling
@@ -3369,11 +3407,18 @@
                     internalFormat = this._gl.LUMINANCE_ALPHA;
                     break;
                 case Engine.TEXTUREFORMAT_RGB:
+                case Engine.TEXTUREFORMAT_RGB32F:
                     internalFormat = this._gl.RGB;
                     break;
                 case Engine.TEXTUREFORMAT_RGBA:
                     internalFormat = this._gl.RGBA;
                     break;
+                case Engine.TEXTUREFORMAT_R32F:
+                    internalFormat = this._gl.RED;
+                    break;       
+                case Engine.TEXTUREFORMAT_RG32F:
+                    internalFormat = this._gl.RG;
+                    break;                                    
             }
 
             return internalFormat;
@@ -3384,8 +3429,8 @@
                 return;
             }
 
+            var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type, format);
             var internalFormat = this._getInternalFormat(format);
-            var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
             var textureType = this._getWebGLTextureType(type);
             this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
@@ -4004,37 +4049,35 @@
             this._gl.compressedTexImage2D(target, lod, internalFormat, width, height, 0, data);
         }
 
-        public createRenderTargetCubeTexture(size: number, options?: RenderTargetCreationOptions): InternalTexture {
-            var gl = this._gl;
-
-            var texture = new InternalTexture(this, InternalTexture.DATASOURCE_RENDERTARGET);
-
-            var generateMipMaps = true;
-            var generateDepthBuffer = true;
-            var generateStencilBuffer = false;
-
-            var samplingMode = Texture.TRILINEAR_SAMPLINGMODE;
-            if (options !== undefined) {
-                generateMipMaps = options.generateMipMaps === undefined ? true : options.generateMipMaps;
-                generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
-                generateStencilBuffer = (generateDepthBuffer && options.generateStencilBuffer) ? true : false;
+        public createRenderTargetCubeTexture(size: number, options?: Partial<RenderTargetCreationOptions>): InternalTexture {
+            let fullOptions = {
+              generateMipMaps: true,
+              generateDepthBuffer: true,
+              generateStencilBuffer: false,
+              type: Engine.TEXTURETYPE_UNSIGNED_INT,
+              samplingMode: Texture.TRILINEAR_SAMPLINGMODE,
+              ...options
+            };
+            fullOptions.generateStencilBuffer = fullOptions.generateDepthBuffer && fullOptions.generateStencilBuffer;
 
-                if (options.samplingMode !== undefined) {
-                    samplingMode = options.samplingMode;
-                }
+            if (fullOptions.type === Engine.TEXTURETYPE_FLOAT && !this._caps.textureFloatLinearFiltering) {
+              // if floating point linear (gl.FLOAT) then force to NEAREST_SAMPLINGMODE
+              fullOptions.samplingMode = Texture.NEAREST_SAMPLINGMODE;
             }
+            else if (fullOptions.type === Engine.TEXTURETYPE_HALF_FLOAT && !this._caps.textureHalfFloatLinearFiltering) {
+              // if floating point linear (HALF_FLOAT) then force to NEAREST_SAMPLINGMODE
+              fullOptions.samplingMode = Texture.NEAREST_SAMPLINGMODE;
+            }
+            var gl = this._gl
 
-            texture.isCube = true;
-            texture.generateMipMaps = generateMipMaps;
-            texture.samples = 1;
-            texture.samplingMode = samplingMode;
-
-            var filters = getSamplingParameters(samplingMode, generateMipMaps, gl);
-
+            var texture = new InternalTexture(this, InternalTexture.DATASOURCE_RENDERTARGET);
             this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
 
-            for (var face = 0; face < 6; face++) {
-                gl.texImage2D((gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+            var filters = getSamplingParameters(fullOptions.samplingMode, fullOptions.generateMipMaps, gl);
+
+            if (fullOptions.type === Engine.TEXTURETYPE_FLOAT && !this._caps.textureFloat) {
+              fullOptions.type = Engine.TEXTURETYPE_UNSIGNED_INT;
+              Tools.Warn("Float textures are not supported. Cube render target forced to TEXTURETYPE_UNESIGNED_BYTE type");
             }
 
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, filters.mag);
@@ -4042,15 +4085,19 @@
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
+            for (var face = 0; face < 6; face++) {
+                gl.texImage2D((gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, this._getRGBABufferInternalSizedFormat(fullOptions.type), size, size, 0, gl.RGBA, this._getWebGLTextureType(fullOptions.type), null);
+            }
+
             // Create the framebuffer
             var framebuffer = gl.createFramebuffer();
             this.bindUnboundFramebuffer(framebuffer);
 
-            texture._depthStencilBuffer = this._setupFramebufferDepthAttachments(generateStencilBuffer, generateDepthBuffer, size, size);
+            texture._depthStencilBuffer = this._setupFramebufferDepthAttachments(fullOptions.generateStencilBuffer, fullOptions.generateDepthBuffer, size, size);
 
-            // Mipmaps
-            if (texture.generateMipMaps) {
-                gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
+            // MipMaps
+            if (fullOptions.generateMipMaps) {
+              gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
             }
 
             // Unbind
@@ -4062,8 +4109,13 @@
             texture.width = size;
             texture.height = size;
             texture.isReady = true;
-
-            //this.resetTextureCache();
+            texture.isCube = true;
+            texture.samples = 1;
+            texture.generateMipMaps = fullOptions.generateMipMaps;
+            texture.samplingMode = fullOptions.samplingMode;
+            texture.type = fullOptions.type;
+            texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
+            texture._generateStencilBuffer = fullOptions.generateStencilBuffer;
 
             this._internalTexturesCache.push(texture);
 
@@ -4831,7 +4883,7 @@
             if (this.disableTextureBindingOptimization) {
                 return -1;
             }
-            
+
             // Remove from bound list
             this._linkTrackers(internalTexture.previous, internalTexture.next);
 
@@ -5545,12 +5597,23 @@
             return this._gl.UNSIGNED_BYTE;
         };
 
-        public _getRGBABufferInternalSizedFormat(type: number): number {
+        /** @ignore */
+        public _getRGBABufferInternalSizedFormat(type: number, format?: number): number {
             if (this._webGLVersion === 1) {
                 return this._gl.RGBA;
             }
 
             if (type === Engine.TEXTURETYPE_FLOAT) {
+                if (format) {
+                    switch(format) {
+                        case Engine.TEXTUREFORMAT_R32F:
+                            return this._gl.R32F;
+                        case Engine.TEXTUREFORMAT_RG32F:
+                            return this._gl.RG32F;
+                            case Engine.TEXTUREFORMAT_RGB32F:
+                            return this._gl.RGB32F;                            
+                    }                    
+                }
                 return this._gl.RGBA32F;
             }
             else if (type === Engine.TEXTURETYPE_HALF_FLOAT) {

+ 0 - 1
src/Engine/babylon.nullEngine.ts

@@ -265,7 +265,6 @@
 
                 this._stencilState.reset();
                 this._depthCullingState.reset();
-                this.setDepthFunctionToLessOrEqual();
                 this._alphaState.reset();
             }
 

+ 639 - 0
src/Layer/babylon.effectLayer.ts

@@ -0,0 +1,639 @@
+module BABYLON {
+    /**
+     * Effect layer options. This helps customizing the behaviour
+     * of the effect layer.
+     */
+    export interface IEffectLayerOptions {
+        /**
+         * Multiplication factor apply to the canvas size to compute the render target size
+         * used to generated the objects (the smaller the faster).
+         */
+        mainTextureRatio: number;
+
+        /**
+         * Enforces a fixed size texture to ensure effect stability across devices.
+         */
+        mainTextureFixedSize?: number;
+
+        /**
+         * Alpha blending mode used to apply the blur. Default depends of the implementation.
+         */
+        alphaBlendingMode: number;
+
+        /**
+         * The camera attached to the layer.
+         */
+        camera: Nullable<Camera>;
+    }
+
+    /**
+     * The effect layer Helps adding post process effect blended with the main pass.
+     * 
+     * This can be for instance use to generate glow or higlight effects on the scene.
+     * 
+     * The effect layer class can not be used directly and is intented to inherited from to be 
+     * customized per effects.
+     */
+    export abstract class EffectLayer {
+
+        private _vertexBuffers: { [key: string]: Nullable<VertexBuffer> } = {};
+        private _indexBuffer: Nullable<WebGLBuffer>;
+        private _cachedDefines: string;
+        private _effectLayerMapGenerationEffect: Effect;
+        private _effectLayerOptions: IEffectLayerOptions;
+        private _mergeEffect: Effect;
+
+        protected _scene: Scene;
+        protected _engine: Engine;
+        protected _maxSize: number = 0;
+        protected _mainTextureDesiredSize: ISize = { width: 0, height: 0 };
+        protected _mainTexture: RenderTargetTexture;
+        protected _shouldRender = true;
+        protected _postProcesses: PostProcess[] = [];
+        protected _textures: BaseTexture[] = [];
+        protected _emissiveTextureAndColor: { texture: Nullable<BaseTexture>, color: Color4 } = { texture: null, color: new Color4() };
+
+        /**
+         * The clear color of the texture used to generate the glow map.
+         */
+        public neutralColor: Color4 = new Color4();
+
+        /**
+         * Specifies wether the highlight layer is enabled or not.
+         */
+        public isEnabled: boolean = true;
+
+        /**
+         * Gets the camera attached to the layer.
+         */
+        public get camera(): Nullable<Camera> {
+            return this._effectLayerOptions.camera;
+        }
+
+        /**
+         * An event triggered when the effect layer has been disposed.
+         */
+        public onDisposeObservable = new Observable<EffectLayer>();
+
+        /**
+         * An event triggered when the effect layer is about rendering the main texture with the glowy parts.
+         */
+        public onBeforeRenderMainTextureObservable = new Observable<EffectLayer>();
+
+        /**
+         * An event triggered when the generated texture is being merged in the scene.
+         */
+        public onBeforeComposeObservable = new Observable<EffectLayer>();
+
+        /**
+         * An event triggered when the generated texture has been merged in the scene.
+         */
+        public onAfterComposeObservable = new Observable<EffectLayer>();
+
+        /**
+         * An event triggered when the efffect layer changes its size.
+         */
+        public onSizeChangedObservable = new Observable<EffectLayer>();
+
+        /**
+         * Instantiates a new effect Layer and references it in the scene.
+         * @param name The name of the layer
+         * @param scene The scene to use the layer in
+         */
+        constructor(
+            /** The Friendly of the effect in the scene */
+            public name: string, 
+            scene: Scene) {
+            this._scene = scene || Engine.LastCreatedScene;
+            this._engine = scene.getEngine();
+            this._maxSize = this._engine.getCaps().maxTextureSize;
+            this._scene.effectLayers.push(this);
+
+            // Generate Buffers
+            this._generateIndexBuffer();
+            this._genrateVertexBuffer();
+        }
+
+        /**
+         * Get the effect name of the layer.
+         * @return The effect name
+         */ 
+        public abstract getEffectName(): string;
+
+        /**
+         * Checks for the readiness of the element composing the layer.
+         * @param subMesh the mesh to check for
+         * @param useInstances specify wether or not to use instances to render the mesh
+         * @return true if ready otherwise, false
+         */ 
+        public abstract isReady(subMesh: SubMesh, useInstances: boolean): boolean;
+
+        /**
+         * Returns wether or nood the layer needs stencil enabled during the mesh rendering.
+         * @returns true if the effect requires stencil during the main canvas render pass.
+         */
+        public abstract needStencil(): boolean;
+
+        /**
+         * Create the merge effect. This is the shader use to blit the information back
+         * to the main canvas at the end of the scene rendering.
+         * @returns The effect containing the shader used to merge the effect on the  main canvas
+         */
+        protected abstract _createMergeEffect(): Effect;
+        
+        /**
+         * Creates the render target textures and post processes used in the effect layer.
+         */
+        protected abstract _createTextureAndPostProcesses(): void;
+        
+        /**
+         * Implementation specific of rendering the generating effect on the main canvas.
+         * @param effect The effect used to render through
+         */
+        protected abstract _internalRender(effect: Effect): void;
+
+        /**
+         * Sets the required values for both the emissive texture and and the main color.
+         */
+        protected abstract _setEmissiveTextureAndColor(mesh: Mesh, subMesh: SubMesh, material: Material): void;
+
+        /**
+         * Free any resources and references associated to a mesh.
+         * Internal use
+         * @param mesh The mesh to free.
+         */
+        public abstract _disposeMesh(mesh: Mesh): void;
+
+        /**
+         * Initializes the effect layer with the required options.
+         * @param options Sets of none mandatory options to use with the layer (see IEffectLayerOptions for more information)
+         */
+        protected _init(options: Partial<IEffectLayerOptions>): void {
+            // Adapt options
+            this._effectLayerOptions = {
+                mainTextureRatio: 0.5,
+                alphaBlendingMode: Engine.ALPHA_COMBINE,
+                camera: null,
+                ...options,
+            };
+
+            this._setMainTextureSize();
+            this._createMainTexture();
+            this._createTextureAndPostProcesses();
+            this._mergeEffect = this._createMergeEffect();
+        }
+
+        /**
+         * Generates the index buffer of the full screen quad blending to the main canvas.
+         */
+        private _generateIndexBuffer(): void {
+            // Indices
+            var indices = [];
+            indices.push(0);
+            indices.push(1);
+            indices.push(2);
+
+            indices.push(0);
+            indices.push(2);
+            indices.push(3);
+
+            this._indexBuffer = this._engine.createIndexBuffer(indices);
+        }
+
+        /**
+         * Generates the vertex buffer of the full screen quad blending to the main canvas.
+         */
+        private _genrateVertexBuffer(): void {
+            // VBO
+            var vertices = [];
+            vertices.push(1, 1);
+            vertices.push(-1, 1);
+            vertices.push(-1, -1);
+            vertices.push(1, -1);
+
+            var vertexBuffer = new VertexBuffer(this._engine, vertices, VertexBuffer.PositionKind, false, false, 2);
+            this._vertexBuffers[VertexBuffer.PositionKind] = vertexBuffer;
+        }
+
+        /**
+         * Sets the main texture desired size which is the closest power of two
+         * of the engine canvas size.
+         */
+        private _setMainTextureSize(): void {
+            if (this._effectLayerOptions.mainTextureFixedSize) {
+                this._mainTextureDesiredSize.width = this._effectLayerOptions.mainTextureFixedSize;
+                this._mainTextureDesiredSize.height = this._effectLayerOptions.mainTextureFixedSize;
+            }
+            else {
+                this._mainTextureDesiredSize.width = this._engine.getRenderWidth() * this._effectLayerOptions.mainTextureRatio;
+                this._mainTextureDesiredSize.height = this._engine.getRenderHeight() * this._effectLayerOptions.mainTextureRatio;
+
+                this._mainTextureDesiredSize.width = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize) : this._mainTextureDesiredSize.width;
+                this._mainTextureDesiredSize.height = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize) : this._mainTextureDesiredSize.height;
+            }
+
+            this._mainTextureDesiredSize.width = Math.floor(this._mainTextureDesiredSize.width);
+            this._mainTextureDesiredSize.height = Math.floor(this._mainTextureDesiredSize.height);
+        }
+
+        /**
+         * Creates the main texture for the effect layer.
+         */
+        protected _createMainTexture(): void {
+            this._mainTexture = new RenderTargetTexture("HighlightLayerMainRTT",
+                {
+                    width: this._mainTextureDesiredSize.width,
+                    height: this._mainTextureDesiredSize.height
+                },
+                this._scene,
+                false,
+                true,
+                Engine.TEXTURETYPE_UNSIGNED_INT);
+            this._mainTexture.activeCamera = this._effectLayerOptions.camera;
+            this._mainTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._mainTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._mainTexture.anisotropicFilteringLevel = 1;
+            this._mainTexture.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
+            this._mainTexture.renderParticles = false;
+            this._mainTexture.renderList = null;
+            this._mainTexture.ignoreCameraViewport = true;
+
+            // Custom render function
+            this._mainTexture.customRenderFunction = (opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void => {
+                this.onBeforeRenderMainTextureObservable.notifyObservers(this);
+
+                var index: number;
+
+                let engine = this._scene.getEngine();
+
+                if (depthOnlySubMeshes.length) {
+                    engine.setColorWrite(false);
+                    for (index = 0; index < depthOnlySubMeshes.length; index++) {
+                        this._renderSubMesh(depthOnlySubMeshes.data[index]);
+                    }
+                    engine.setColorWrite(true);
+                }
+
+                for (index = 0; index < opaqueSubMeshes.length; index++) {
+                    this._renderSubMesh(opaqueSubMeshes.data[index]);
+                }
+
+                for (index = 0; index < alphaTestSubMeshes.length; index++) {
+                    this._renderSubMesh(alphaTestSubMeshes.data[index]);
+                }
+
+                for (index = 0; index < transparentSubMeshes.length; index++) {
+                    this._renderSubMesh(transparentSubMeshes.data[index]);
+                }
+            };
+
+            this._mainTexture.onClearObservable.add((engine: Engine) => {
+                engine.clear(this.neutralColor, true, true, true);
+            });
+        }
+
+        /**
+         * Checks for the readiness of the element composing the layer.
+         * @param subMesh the mesh to check for
+         * @param useInstances specify wether or not to use instances to render the mesh
+         * @param emissiveTexture the associated emissive texture used to generate the glow
+         * @return true if ready otherwise, false
+         */
+        protected _isReady(subMesh: SubMesh, useInstances: boolean, emissiveTexture: Nullable<BaseTexture>): boolean {
+            let material = subMesh.getMaterial();
+
+            if (!material) {
+                return false;
+            }
+
+            if (!material.isReady(subMesh.getMesh(), useInstances)) {
+                return false;
+            }
+
+            var defines = [];
+
+            var attribs = [VertexBuffer.PositionKind];
+
+            var mesh = subMesh.getMesh();
+            var uv1 = false;
+            var uv2 = false;
+
+            // Alpha test
+            if (material && material.needAlphaTesting()) {
+                var alphaTexture = material.getAlphaTestTexture();
+                if (alphaTexture) {
+                    defines.push("#define ALPHATEST");
+                    if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
+                        alphaTexture.coordinatesIndex === 1) {
+                        defines.push("#define DIFFUSEUV2");
+                        uv2 = true;
+                    }
+                    else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
+                        defines.push("#define DIFFUSEUV1");
+                        uv1 = true;
+                    }
+                }
+            }
+
+            // Emissive
+            if (emissiveTexture) {
+                defines.push("#define EMISSIVE");
+                if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
+                    emissiveTexture.coordinatesIndex === 1) {
+                    defines.push("#define EMISSIVEUV2");
+                    uv2 = true;
+                }
+                else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
+                    defines.push("#define EMISSIVEUV1");
+                    uv1 = true;
+                }
+            }
+
+            if (uv1) {
+                attribs.push(VertexBuffer.UVKind);
+                defines.push("#define UV1");
+            }
+            if (uv2) {
+                attribs.push(VertexBuffer.UV2Kind);
+                defines.push("#define UV2");
+            }
+
+            // Bones
+            if (mesh.useBones && mesh.computeBonesUsingShaders) {
+                attribs.push(VertexBuffer.MatricesIndicesKind);
+                attribs.push(VertexBuffer.MatricesWeightsKind);
+                if (mesh.numBoneInfluencers > 4) {
+                    attribs.push(VertexBuffer.MatricesIndicesExtraKind);
+                    attribs.push(VertexBuffer.MatricesWeightsExtraKind);
+                }
+                defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
+                defines.push("#define BonesPerMesh " + (mesh.skeleton ? (mesh.skeleton.bones.length + 1) : 0));
+            } else {
+                defines.push("#define NUM_BONE_INFLUENCERS 0");
+            }
+
+            // Instances
+            if (useInstances) {
+                defines.push("#define INSTANCES");
+                attribs.push("world0");
+                attribs.push("world1");
+                attribs.push("world2");
+                attribs.push("world3");
+            }
+
+            // Get correct effect
+            var join = defines.join("\n");
+            if (this._cachedDefines !== join) {
+                this._cachedDefines = join;
+                this._effectLayerMapGenerationEffect = this._scene.getEngine().createEffect("glowMapGeneration",
+                    attribs,
+                    ["world", "mBones", "viewProjection", "diffuseMatrix", "color", "emissiveMatrix"],
+                    ["diffuseSampler", "emissiveSampler"], join);
+            }
+
+            return this._effectLayerMapGenerationEffect.isReady();
+        }
+
+        /**
+         * Renders the glowing part of the scene by blending the blurred glowing meshes on top of the rendered scene.
+         */
+        public render(): void {
+            var currentEffect = this._mergeEffect;
+
+            // Check
+            if (!currentEffect.isReady())
+                return;
+
+            for (var i = 0; i < this._postProcesses.length; i++) {
+                if (!this._postProcesses[i].isReady()) {
+                    return;
+                }
+            }
+
+            var engine = this._scene.getEngine();
+
+            this.onBeforeComposeObservable.notifyObservers(this);
+
+            // Render
+            engine.enableEffect(currentEffect);
+            engine.setState(false);
+            
+            // VBOs
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, currentEffect);
+
+            // Cache
+            var previousAlphaMode = engine.getAlphaMode();
+
+            // Go Blend.
+            engine.setAlphaMode(this._effectLayerOptions.alphaBlendingMode);
+
+            // Blends the map on the main canvas.
+            this._internalRender(currentEffect);
+
+            // Restore Alpha
+            engine.setAlphaMode(previousAlphaMode);
+
+            this.onAfterComposeObservable.notifyObservers(this);
+
+            // Handle size changes.
+            var size = this._mainTexture.getSize();
+            this._setMainTextureSize();
+            if (size.width !== this._mainTextureDesiredSize.width || size.height !== this._mainTextureDesiredSize.height) {
+                // Recreate RTT and post processes on size change.
+                this.onSizeChangedObservable.notifyObservers(this);
+                this._disposeTextureAndPostProcesses();
+                this._createMainTexture();
+                this._createTextureAndPostProcesses();
+            }
+        }
+
+        /**
+         * Determine if a given mesh will be used in the current effect.
+         * @param mesh mesh to test
+         * @returns true if the mesh will be used
+         */
+        public hasMesh(mesh: AbstractMesh): boolean {
+            return true;
+        }
+
+        /**
+         * Returns true if the layer contains information to display, otherwise false.
+         * @returns true if the glow layer should be rendered
+         */
+        public shouldRender(): boolean {
+            return this.isEnabled && this._shouldRender;
+        }
+
+        /**
+         * Returns true if the mesh should render, otherwise false.
+         * @param mesh The mesh to render
+         * @returns true if it should render otherwise false
+         */
+        protected _shouldRenderMesh(mesh: Mesh): boolean {
+            return true;
+        }
+
+        /**
+         * Returns true if the mesh should render, otherwise false.
+         * @param mesh The mesh to render
+         * @returns true if it should render otherwise false
+         */
+        protected _shouldRenderEmissiveTextureForMesh(mesh: Mesh): boolean {
+            return true;
+        }
+
+        /**
+         * Renders the submesh passed in parameter to the generation map.
+         */
+        protected _renderSubMesh(subMesh: SubMesh): void {
+            if (!this.shouldRender()) {
+                return;
+            }
+
+            var material = subMesh.getMaterial();
+            var mesh = subMesh.getRenderingMesh();
+            var scene = this._scene;
+            var engine = scene.getEngine();
+
+            if (!material) {
+                return;
+            }
+
+            // Do not block in blend mode.
+            if (material.needAlphaBlendingForMesh(mesh)) {
+                return;
+            }
+
+            // Culling
+            engine.setState(material.backFaceCulling);
+
+            // Managing instances
+            var batch = mesh._getInstancesRenderList(subMesh._id);
+            if (batch.mustReturn) {
+                return;
+            }
+
+            // Early Exit per mesh
+            if (!this._shouldRenderMesh(mesh)) {
+                return;
+            }
+
+            var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
+
+            this._setEmissiveTextureAndColor(mesh, subMesh, material);
+
+            if (this._isReady(subMesh, hardwareInstancedRendering, this._emissiveTextureAndColor.texture)) {
+                engine.enableEffect(this._effectLayerMapGenerationEffect);
+                mesh._bind(subMesh, this._effectLayerMapGenerationEffect, Material.TriangleFillMode);
+
+                this._effectLayerMapGenerationEffect.setMatrix("viewProjection", scene.getTransformMatrix());
+
+                this._effectLayerMapGenerationEffect.setFloat4("color",
+                    this._emissiveTextureAndColor.color.r,
+                    this._emissiveTextureAndColor.color.g,
+                    this._emissiveTextureAndColor.color.b,
+                    this._emissiveTextureAndColor.color.a);
+
+                // Alpha test
+                if (material && material.needAlphaTesting()) {
+                    var alphaTexture = material.getAlphaTestTexture();
+                    if (alphaTexture) {
+                        this._effectLayerMapGenerationEffect.setTexture("diffuseSampler", alphaTexture);
+                        let textureMatrix = alphaTexture.getTextureMatrix();
+
+                        if (textureMatrix) {
+                            this._effectLayerMapGenerationEffect.setMatrix("diffuseMatrix", textureMatrix);
+                        }
+                    }
+                }
+
+                // Glow emissive only
+                if (this._emissiveTextureAndColor.texture) {
+                    this._effectLayerMapGenerationEffect.setTexture("emissiveSampler", this._emissiveTextureAndColor.texture);
+                    this._effectLayerMapGenerationEffect.setMatrix("emissiveMatrix", this._emissiveTextureAndColor.texture.getTextureMatrix());
+                }
+
+                // Bones
+                if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
+                    this._effectLayerMapGenerationEffect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
+                }
+
+                // Draw
+                mesh._processRendering(subMesh, this._effectLayerMapGenerationEffect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
+                    (isInstance, world) => this._effectLayerMapGenerationEffect.setMatrix("world", world));
+            } else {
+                // Need to reset refresh rate of the shadowMap
+                this._mainTexture.resetRefreshCounter();
+            }
+        }
+
+        /**
+         * Rebuild the required buffers.
+         * @ignore Internal use only.
+         */
+        public _rebuild(): void {
+            let vb = this._vertexBuffers[VertexBuffer.PositionKind];
+
+            if (vb) {
+                vb._rebuild();
+            }
+
+            this._generateIndexBuffer();
+        }
+
+        /**
+         * Dispose only the render target textures and post process.
+         */
+        private _disposeTextureAndPostProcesses(): void {
+            this._mainTexture.dispose();
+
+            for (var i = 0; i < this._postProcesses.length; i++) {
+                if (this._postProcesses[i]) {
+                    this._postProcesses[i].dispose();
+                }
+            }
+            this._postProcesses = [];
+
+            for (var i = 0; i < this._textures.length; i++) {
+                if (this._textures[i]) {
+                    this._textures[i].dispose();
+                }
+            }
+            this._textures = [];
+        }
+
+        /**
+         * Dispose the highlight layer and free resources.
+         */
+        public dispose(): void {
+            var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
+            if (vertexBuffer) {
+                vertexBuffer.dispose();
+                this._vertexBuffers[VertexBuffer.PositionKind] = null;
+            }
+
+            if (this._indexBuffer) {
+                this._scene.getEngine()._releaseBuffer(this._indexBuffer);
+                this._indexBuffer = null;
+            }
+
+            // Clean textures and post processes
+            this._disposeTextureAndPostProcesses();
+
+            // Remove from scene
+            var index = this._scene.effectLayers.indexOf(this, 0);
+            if (index > -1) {
+                this._scene.effectLayers.splice(index, 1);
+            }
+
+            // Callback
+            this.onDisposeObservable.notifyObservers(this);
+
+            this.onDisposeObservable.clear();
+            this.onBeforeRenderMainTextureObservable.clear();
+            this.onBeforeComposeObservable.clear();
+            this.onAfterComposeObservable.clear();
+            this.onSizeChangedObservable.clear();
+        }
+    }
+} 

+ 437 - 0
src/Layer/babylon.glowLayer.ts

@@ -0,0 +1,437 @@
+module BABYLON {
+    /**
+     * Glow layer options. This helps customizing the behaviour
+     * of the glow layer.
+     */
+    export interface IGlowLayerOptions {
+        /**
+         * Multiplication factor apply to the canvas size to compute the render target size
+         * used to generated the glowing objects (the smaller the faster).
+         */
+        mainTextureRatio: number;
+
+        /**
+         * Enforces a fixed size texture to ensure resize independant blur.
+         */
+        mainTextureFixedSize?: number;
+
+        /**
+         * How big is the kernel of the blur texture.
+         */
+        blurKernelSize: number;
+
+        /**
+         * The camera attached to the layer.
+         */
+        camera: Nullable<Camera>;
+
+        /**
+         * Enable MSAA by chosing the number of samples.
+         */
+        mainTextureSamples?: number;
+    }
+
+    /**
+     * The glow layer Helps adding a glow effect around the emissive parts of a mesh.
+     * 
+     * Once instantiated in a scene, simply use the pushMesh or removeMesh method to add or remove
+     * glowy meshes to your scene.
+     * 
+     * Documentation: https://doc.babylonjs.com/how_to/glow_layer
+     */
+    export class GlowLayer extends EffectLayer {
+        /**
+         * Effect Name of the layer.
+         */
+        public static readonly EffectName = "GlowLayer";
+
+        /**
+         * The default blur kernel size used for the glow.
+         */
+        public static DefaultBlurKernelSize = 32;
+
+        /**
+         * The default texture size ratio used for the glow.
+         */
+        public static DefaultTextureRatio = 0.5;
+
+        /**
+         * Sets the kernel size of the blur.
+         */
+        public set blurKernelSize(value: number) {
+            this._horizontalBlurPostprocess1.kernel = value;
+            this._verticalBlurPostprocess1.kernel = value;
+            this._horizontalBlurPostprocess2.kernel = value;
+            this._verticalBlurPostprocess2.kernel = value;
+        }
+
+        /**
+         * Gets the kernel size of the blur.
+         */
+        public get blurKernelSize(): number {
+            return this._horizontalBlurPostprocess1.kernel;
+        }
+
+        /**
+         * Sets the glow intensity.
+         */
+        public set intensity(value: number) {
+            this._intensity = value;
+        }
+
+        /**
+         * Gets the glow intensity.
+         */
+        public get intensity(): number {
+            return this._intensity;
+        }
+
+        private _options: IGlowLayerOptions;
+        private _intensity: number = 1.0;
+        private _horizontalBlurPostprocess1: BlurPostProcess;
+        private _verticalBlurPostprocess1: BlurPostProcess;
+        private _horizontalBlurPostprocess2: BlurPostProcess;
+        private _verticalBlurPostprocess2: BlurPostProcess;
+        private _blurTexture1: RenderTargetTexture;
+        private _blurTexture2: RenderTargetTexture;
+        private _postProcesses1: PostProcess[];
+        private _postProcesses2: PostProcess[];
+
+        private _includedOnlyMeshes: number[] = [];
+        private _excludedMeshes: number[] = [];
+
+        /**
+         * Callback used to let the user override the color selection on a per mesh basis
+         */
+        public customEmissiveColorSelector: (mesh: Mesh, subMesh: SubMesh, material: Material, result: Color4) => void;
+        /**
+         * Callback used to let the user override the texture selection on a per mesh basis
+         */
+        public customEmissiveTextureSelector: (mesh: Mesh, subMesh: SubMesh, material: Material) => Texture;
+
+        /**
+         * Instantiates a new glow Layer and references it to the scene.
+         * @param name The name of the layer
+         * @param scene The scene to use the layer in
+         * @param options Sets of none mandatory options to use with the layer (see IGlowLayerOptions for more information)
+         */
+        constructor(public name: string, scene: Scene, options?: Partial<IGlowLayerOptions>) {
+            super(name, scene);
+            this.neutralColor = new Color4(0, 0, 0, 1);
+
+            // Adapt options
+            this._options = {
+                mainTextureRatio: GlowLayer.DefaultTextureRatio,
+                blurKernelSize: 32,
+                mainTextureFixedSize: undefined,
+                camera: null,
+                mainTextureSamples: 1,
+                ...options,
+            };
+
+            // Initialize the layer
+            this._init({
+                alphaBlendingMode: Engine.ALPHA_ADD,
+                camera: this._options.camera,
+                mainTextureFixedSize: this._options.mainTextureFixedSize,
+                mainTextureRatio: this._options.mainTextureRatio
+            });
+        }
+
+        /**
+         * Get the effect name of the layer.
+         * @return The effect name
+         */ 
+        public getEffectName(): string {
+            return GlowLayer.EffectName;
+        }
+
+        /**
+         * Create the merge effect. This is the shader use to blit the information back
+         * to the main canvas at the end of the scene rendering.
+         */
+        protected _createMergeEffect(): Effect {
+             // Effect
+             return this._engine.createEffect("glowMapMerge",
+                [VertexBuffer.PositionKind],
+                ["offset"],
+                ["textureSampler", "textureSampler2"],
+                "#define EMISSIVE \n");
+
+        }
+
+        /**
+         * Creates the render target textures and post processes used in the glow layer.
+         */
+        protected _createTextureAndPostProcesses(): void {
+            var blurTextureWidth = this._mainTextureDesiredSize.width;
+            var blurTextureHeight = this._mainTextureDesiredSize.height;
+            blurTextureWidth = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureWidth, this._maxSize) : blurTextureWidth;
+            blurTextureHeight = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureHeight, this._maxSize) : blurTextureHeight;
+
+            this._blurTexture1 = new RenderTargetTexture("GlowLayerBlurRTT",
+                {
+                    width: blurTextureWidth,
+                    height: blurTextureHeight
+                },
+                this._scene,
+                false,
+                true,
+                Engine.TEXTURETYPE_HALF_FLOAT);
+            this._blurTexture1.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture1.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture1.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
+            this._blurTexture1.renderParticles = false;
+            this._blurTexture1.ignoreCameraViewport = true;
+
+            var blurTextureWidth2 = Math.floor(blurTextureWidth / 2);
+            var blurTextureHeight2 = Math.floor(blurTextureHeight / 2);
+
+            this._blurTexture2 = new RenderTargetTexture("GlowLayerBlurRTT2",
+                {
+                    width: blurTextureWidth2,
+                    height: blurTextureHeight2
+                },
+                this._scene,
+                false,
+                true,
+                Engine.TEXTURETYPE_HALF_FLOAT);
+            this._blurTexture2.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture2.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
+            this._blurTexture2.renderParticles = false;
+            this._blurTexture2.ignoreCameraViewport = true;
+
+            this._textures = [ this._blurTexture1, this._blurTexture2 ];
+
+            this._horizontalBlurPostprocess1 = new BlurPostProcess("GlowLayerHBP1", new Vector2(1.0, 0), this._options.blurKernelSize / 2, {
+                    width:  blurTextureWidth,
+                    height: blurTextureHeight
+                },
+                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+            this._horizontalBlurPostprocess1.width = blurTextureWidth;
+            this._horizontalBlurPostprocess1.height = blurTextureHeight;
+            this._horizontalBlurPostprocess1.onApplyObservable.add(effect => {
+                effect.setTexture("textureSampler", this._mainTexture);
+            });
+
+            this._verticalBlurPostprocess1 = new BlurPostProcess("GlowLayerVBP1", new Vector2(0, 1.0), this._options.blurKernelSize / 2, {
+                    width:  blurTextureWidth,
+                    height: blurTextureHeight
+                },
+                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+
+            this._horizontalBlurPostprocess2 = new BlurPostProcess("GlowLayerHBP2", new Vector2(1.0, 0), this._options.blurKernelSize / 2, {
+                    width:  blurTextureWidth2,
+                    height: blurTextureHeight2
+                },
+                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+            this._horizontalBlurPostprocess2.width = blurTextureWidth2;
+            this._horizontalBlurPostprocess2.height = blurTextureHeight2;
+            this._horizontalBlurPostprocess2.onApplyObservable.add(effect => {
+                effect.setTexture("textureSampler", this._blurTexture1);
+            });
+
+            this._verticalBlurPostprocess2 = new BlurPostProcess("GlowLayerVBP2", new Vector2(0, 1.0), this._options.blurKernelSize / 2, {
+                    width:  blurTextureWidth2,
+                    height: blurTextureHeight2
+                },
+                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+
+            this._postProcesses = [ this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1, this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2 ];
+            this._postProcesses1 = [ this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1 ];
+            this._postProcesses2 = [ this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2 ];
+
+            this._mainTexture.samples = this._options.mainTextureSamples!;
+            this._mainTexture.onAfterUnbindObservable.add(() => {
+                let internalTexture = this._blurTexture1.getInternalTexture();
+                if (internalTexture) {
+                    this._scene.postProcessManager.directRender(
+                        this._postProcesses1,
+                        internalTexture, 
+                        true);
+
+                        internalTexture = this._blurTexture2.getInternalTexture();
+                        if (internalTexture) {
+                            this._scene.postProcessManager.directRender(
+                                this._postProcesses2,
+                                internalTexture, 
+                                true);
+                        }
+                }
+            });
+
+            // Prevent autoClear.
+            this._postProcesses.map(pp => { pp.autoClear = false; });
+        }
+
+        /**
+         * Checks for the readiness of the element composing the layer.
+         * @param subMesh the mesh to check for
+         * @param useInstances specify wether or not to use instances to render the mesh
+         * @param emissiveTexture the associated emissive texture used to generate the glow
+         * @return true if ready otherwise, false
+         */
+        public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
+            let material = subMesh.getMaterial();
+            let mesh = subMesh.getRenderingMesh();
+
+            if (!material || !mesh) {
+                return false;
+            }
+
+            let emissiveTexture = (<any>material).emissiveTexture;
+            return super._isReady(subMesh, useInstances, emissiveTexture);
+        }
+
+        /**
+         * Returns wether or nood the layer needs stencil enabled during the mesh rendering.
+         */
+        public needStencil(): boolean {
+            return false;
+        }
+
+        /**
+         * Implementation specific of rendering the generating effect on the main canvas.
+         * @param effect The effect used to render through
+         */
+        protected _internalRender(effect: Effect): void {
+            // Texture
+            effect.setTexture("textureSampler", this._blurTexture1);
+            effect.setTexture("textureSampler2", this._blurTexture2);
+            effect.setFloat("offset", this._intensity);
+
+            // Cache
+            var engine = this._engine;
+            var previousStencilBuffer = engine.getStencilBuffer();
+                
+            // Draw order
+            engine.setStencilBuffer(false);
+
+            engine.drawElementsType(Material.TriangleFillMode, 0, 6);
+
+            // Draw order
+            engine.setStencilBuffer(previousStencilBuffer);
+        }
+
+        /**
+         * Sets the required values for both the emissive texture and and the main color.
+         */
+        protected _setEmissiveTextureAndColor(mesh: Mesh, subMesh: SubMesh, material: Material): void {
+            var textureLevel = 1.0;
+
+            if (this.customEmissiveTextureSelector) {
+                this._emissiveTextureAndColor.texture = this.customEmissiveTextureSelector(mesh, subMesh, material);
+            } else {
+                if (material) {
+                    this._emissiveTextureAndColor.texture = (<any>material).emissiveTexture;
+                    if (this._emissiveTextureAndColor.texture) {
+                        textureLevel = this._emissiveTextureAndColor.texture.level;
+                    }
+                }
+                else {
+                    this._emissiveTextureAndColor.texture = null;
+                }
+            }
+
+            if (this.customEmissiveColorSelector) {
+                this.customEmissiveColorSelector(mesh, subMesh, material, this._emissiveTextureAndColor.color);
+            } else {
+                if ((<any>material).emissiveColor) {
+                    this._emissiveTextureAndColor.color.set(
+                        (<any>material).emissiveColor.r * textureLevel,
+                        (<any>material).emissiveColor.g * textureLevel,
+                        (<any>material).emissiveColor .b * textureLevel,
+                        1.0);
+                }
+                else {
+                    this._emissiveTextureAndColor.color.set(
+                        this.neutralColor.r,
+                        this.neutralColor.g,
+                        this.neutralColor.b,
+                        this.neutralColor.a);
+                }
+            }
+        }
+
+        /**
+         * Returns true if the mesh should render, otherwise false.
+         * @param mesh The mesh to render
+         * @returns true if it should render otherwise false
+         */
+        protected _shouldRenderMesh(mesh: Mesh): boolean {
+            return this.hasMesh(mesh);
+        }
+
+        /**
+         * Add a mesh in the exclusion list to prevent it to impact or being impacted by the glow layer.
+         * @param mesh The mesh to exclude from the glow layer
+         */
+        public addExcludedMesh(mesh: Mesh): void {
+            if (this._excludedMeshes.indexOf(mesh.uniqueId) === -1) {
+                this._excludedMeshes.push(mesh.uniqueId);
+            }
+        }
+
+        /**
+          * Remove a mesh from the exclusion list to let it impact or being impacted by the glow layer.
+          * @param mesh The mesh to remove
+          */
+        public removeExcludedMesh(mesh: Mesh): void {
+            var index = this._excludedMeshes.indexOf(mesh.uniqueId);
+            if (index !== -1) {
+                this._excludedMeshes.splice(index, 1);
+            } 
+        }
+
+        /**
+         * Add a mesh in the inclusion list to impact or being impacted by the glow layer.
+         * @param mesh The mesh to include in the glow layer
+         */
+        public addIncludedOnlyMesh(mesh: Mesh): void {
+            if (this._includedOnlyMeshes.indexOf(mesh.uniqueId) === -1) {
+                this._includedOnlyMeshes.push(mesh.uniqueId);
+            }
+        }
+
+        /**
+          * Remove a mesh from the Inclusion list to prevent it to impact or being impacted by the glow layer.
+          * @param mesh The mesh to remove
+          */
+        public removeIncludedOnlyMesh(mesh: Mesh): void {
+            var index = this._includedOnlyMeshes.indexOf(mesh.uniqueId);
+            if (index !== -1) {
+                this._includedOnlyMeshes.splice(index, 1);
+            } 
+        }
+
+        /**
+         * Determine if a given mesh will be used in the glow layer
+         * @param mesh The mesh to test
+         * @returns true if the mesh will be highlighted by the current glow layer
+         */
+        public hasMesh(mesh: AbstractMesh): boolean {
+            // Included Mesh
+            if (this._includedOnlyMeshes.length) {
+                return this._includedOnlyMeshes.indexOf(mesh.uniqueId) !== -1;
+            };
+
+            // Excluded Mesh
+            if (this._excludedMeshes.length) {
+                return this._excludedMeshes.indexOf(mesh.uniqueId) === -1;
+            };
+
+            return true;
+        }
+
+        /**
+         * Free any resources and references associated to a mesh.
+         * Internal use
+         * @param mesh The mesh to free.
+         */
+        public _disposeMesh(mesh: Mesh): void {
+            this.removeIncludedOnlyMesh(mesh);
+            this.removeExcludedMesh(mesh);
+        }
+    }
+} 

+ 672 - 0
src/Layer/babylon.highlightLayer.ts

@@ -0,0 +1,672 @@
+module BABYLON {
+    /**
+     * Special Glow Blur post process only blurring the alpha channel
+     * It enforces keeping the most luminous color in the color channel.
+     */
+    class GlowBlurPostProcess extends PostProcess {
+        constructor(name: string, public direction: Vector2, public kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean) {
+            super(name, "glowBlurPostProcess", ["screenSize", "direction", "blurWidth"], null, options, camera, samplingMode, engine, reusable);
+            
+            this.onApplyObservable.add((effect: Effect) => {
+                effect.setFloat2("screenSize", this.width, this.height);
+                effect.setVector2("direction", this.direction);
+                effect.setFloat("blurWidth", this.kernel);
+            });
+        }
+    }
+
+    /**
+     * Highlight layer options. This helps customizing the behaviour
+     * of the highlight layer.
+     */
+    export interface IHighlightLayerOptions {
+        /**
+         * Multiplication factor apply to the canvas size to compute the render target size
+         * used to generated the glowing objects (the smaller the faster).
+         */
+        mainTextureRatio: number;
+
+        /**
+         * Enforces a fixed size texture to ensure resize independant blur.
+         */
+        mainTextureFixedSize?: number;
+
+        /**
+         * Multiplication factor apply to the main texture size in the first step of the blur to reduce the size 
+         * of the picture to blur (the smaller the faster).
+         */
+        blurTextureSizeRatio: number;
+
+        /**
+         * How big in texel of the blur texture is the vertical blur.
+         */
+        blurVerticalSize: number;
+
+        /**
+         * How big in texel of the blur texture is the horizontal blur.
+         */
+        blurHorizontalSize: number;
+
+        /**
+         * Alpha blending mode used to apply the blur. Default is combine.
+         */
+        alphaBlendingMode: number
+
+        /**
+         * The camera attached to the layer.
+         */
+        camera: Nullable<Camera>;
+
+        /**
+         * Should we display highlight as a solid stroke?
+         */
+        isStroke?: boolean;
+    }
+
+    /**
+     * Storage interface grouping all the information required for glowing a mesh.
+     */
+    interface IHighlightLayerMesh {
+        /** 
+         * The glowy mesh
+         */
+        mesh: Mesh;
+        /**
+         * The color of the glow
+         */
+        color: Color3;
+        /**
+         * The mesh render callback use to insert stencil information
+         */
+        observerHighlight: Nullable<Observer<Mesh>>;
+        /**
+         * The mesh render callback use to come to the default behavior
+         */
+        observerDefault: Nullable<Observer<Mesh>>;
+        /**
+         * If it exists, the emissive color of the material will be used to generate the glow.
+         * Else it falls back to the current color.
+         */
+        glowEmissiveOnly: boolean;
+    }
+
+    /**
+     * Storage interface grouping all the information required for an excluded mesh.
+     */
+    interface IHighlightLayerExcludedMesh {
+        /** 
+         * The glowy mesh
+         */
+        mesh: Mesh;
+        /**
+         * The mesh render callback use to prevent stencil use
+         */
+        beforeRender: Nullable<Observer<Mesh>>;
+        /**
+         * The mesh render callback use to restore previous stencil use
+         */
+        afterRender: Nullable<Observer<Mesh>>;
+    }
+
+    /**
+     * The highlight layer Helps adding a glow effect around a mesh.
+     * 
+     * Once instantiated in a scene, simply use the pushMesh or removeMesh method to add or remove
+     * glowy meshes to your scene.
+     * 
+     * !!! THIS REQUIRES AN ACTIVE STENCIL BUFFER ON THE CANVAS !!!
+     */
+    export class HighlightLayer extends EffectLayer {
+        /**
+         * Effect Name of the highlight layer.
+         */
+        public static readonly EffectName = "HighlightLayer";
+
+        /**
+         * The neutral color used during the preparation of the glow effect.
+         * This is black by default as the blend operation is a blend operation. 
+         */
+        public static NeutralColor: Color4 = new Color4(0, 0, 0, 0);
+
+        /**
+         * Stencil value used for glowing meshes.
+         */
+        public static GlowingMeshStencilReference = 0x02;
+
+        /**
+         * Stencil value used for the other meshes in the scene.
+         */
+        public static NormalMeshStencilReference = 0x01;
+
+        /**
+         * Specifies whether or not the inner glow is ACTIVE in the layer.
+         */
+        public innerGlow: boolean = true;
+
+        /**
+         * Specifies whether or not the outer glow is ACTIVE in the layer.
+         */
+        public outerGlow: boolean = true;
+
+        /**
+         * Specifies the horizontal size of the blur.
+         */
+        public set blurHorizontalSize(value: number) {
+            this._horizontalBlurPostprocess.kernel = value;
+        }
+
+        /**
+         * Specifies the vertical size of the blur.
+         */
+        public set blurVerticalSize(value: number) {
+            this._verticalBlurPostprocess.kernel = value;
+        }
+
+        /**
+         * Gets the horizontal size of the blur.
+         */
+        public get blurHorizontalSize(): number {
+            return this._horizontalBlurPostprocess.kernel
+        }
+
+        /**
+         * Gets the vertical size of the blur.
+         */
+        public get blurVerticalSize(): number {
+            return this._verticalBlurPostprocess.kernel;
+        }
+
+        /**
+         * An event triggered when the highlight layer is being blurred.
+         */
+        public onBeforeBlurObservable = new Observable<HighlightLayer>();
+
+        /**
+         * An event triggered when the highlight layer has been blurred.
+         */
+        public onAfterBlurObservable = new Observable<HighlightLayer>();
+
+        private _instanceGlowingMeshStencilReference = HighlightLayer.GlowingMeshStencilReference++;
+
+        private _options: IHighlightLayerOptions;
+        private _downSamplePostprocess: PassPostProcess;
+        private _horizontalBlurPostprocess: GlowBlurPostProcess;
+        private _verticalBlurPostprocess: GlowBlurPostProcess;
+        private _blurTexture: RenderTargetTexture;
+
+        private _meshes: Nullable<{ [id: string]: Nullable<IHighlightLayerMesh> }> = {};
+        private _excludedMeshes: Nullable<{ [id: string]: Nullable<IHighlightLayerExcludedMesh> }> = {};
+
+        /**
+         * Instantiates a new highlight Layer and references it to the scene..
+         * @param name The name of the layer
+         * @param scene The scene to use the layer in
+         * @param options Sets of none mandatory options to use with the layer (see IHighlightLayerOptions for more information)
+         */
+        constructor(public name: string, scene: Scene, options?: Partial<IHighlightLayerOptions>) {
+            super(name, scene);
+            this.neutralColor = HighlightLayer.NeutralColor;
+
+            // Warn on stencil
+            if (!this._engine.isStencilEnable) {
+                Tools.Warn("Rendering the Highlight Layer requires the stencil to be active on the canvas. var engine = new BABYLON.Engine(canvas, antialias, { stencil: true }");
+            }
+
+            // Adapt options
+            this._options = {
+                mainTextureRatio: 0.5,
+                blurTextureSizeRatio: 0.5,
+                blurHorizontalSize: 1.0,
+                blurVerticalSize: 1.0,
+                alphaBlendingMode: Engine.ALPHA_COMBINE,
+                camera: null,
+                ...options,
+            };
+
+            // Initialize the layer
+            this._init({
+                alphaBlendingMode: this._options.alphaBlendingMode,
+                camera: this._options.camera,
+                mainTextureFixedSize: this._options.mainTextureFixedSize,
+                mainTextureRatio: this._options.mainTextureRatio
+            });
+
+            // Do not render as long as no meshes have been added
+            this._shouldRender = false;
+        }
+
+        /**
+         * Get the effect name of the layer.
+         * @return The effect name
+         */ 
+        public getEffectName(): string {
+            return HighlightLayer.EffectName;
+        }
+
+        /**
+         * Create the merge effect. This is the shader use to blit the information back
+         * to the main canvas at the end of the scene rendering.
+         */
+        protected _createMergeEffect(): Effect {
+             // Effect
+             return this._engine.createEffect("glowMapMerge",
+                [VertexBuffer.PositionKind],
+                ["offset"],
+                ["textureSampler"],
+                this._options.isStroke ? "#define STROKE \n" : undefined);
+
+        }
+
+        /**
+         * Creates the render target textures and post processes used in the highlight layer.
+         */
+        protected _createTextureAndPostProcesses(): void {
+            var blurTextureWidth = this._mainTextureDesiredSize.width * this._options.blurTextureSizeRatio;
+            var blurTextureHeight = this._mainTextureDesiredSize.height * this._options.blurTextureSizeRatio;
+            blurTextureWidth = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureWidth, this._maxSize) : blurTextureWidth;
+            blurTextureHeight = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureHeight, this._maxSize) : blurTextureHeight;
+
+            this._blurTexture = new RenderTargetTexture("HighlightLayerBlurRTT",
+                {
+                    width: blurTextureWidth,
+                    height: blurTextureHeight
+                },
+                this._scene,
+                false,
+                true,
+                Engine.TEXTURETYPE_HALF_FLOAT);
+            this._blurTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._blurTexture.anisotropicFilteringLevel = 16;
+            this._blurTexture.updateSamplingMode(Texture.TRILINEAR_SAMPLINGMODE);
+            this._blurTexture.renderParticles = false;
+            this._blurTexture.ignoreCameraViewport = true;
+
+            this._textures = [ this._blurTexture ];
+
+            if (this._options.alphaBlendingMode === Engine.ALPHA_COMBINE) {
+                this._downSamplePostprocess = new PassPostProcess("HighlightLayerPPP", this._options.blurTextureSizeRatio,
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
+                this._downSamplePostprocess.onApplyObservable.add(effect => {
+                    effect.setTexture("textureSampler", this._mainTexture);
+                });
+
+                this._horizontalBlurPostprocess = new GlowBlurPostProcess("HighlightLayerHBP", new Vector2(1.0, 0), this._options.blurHorizontalSize, 1,
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
+                this._horizontalBlurPostprocess.onApplyObservable.add(effect => {
+                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
+                });
+
+                this._verticalBlurPostprocess = new GlowBlurPostProcess("HighlightLayerVBP", new Vector2(0, 1.0), this._options.blurVerticalSize, 1,
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
+                this._verticalBlurPostprocess.onApplyObservable.add(effect => {
+                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
+                });
+
+                this._postProcesses = [this._downSamplePostprocess, this._horizontalBlurPostprocess, this._verticalBlurPostprocess];
+            }
+            else {
+                this._horizontalBlurPostprocess = new BlurPostProcess("HighlightLayerHBP", new Vector2(1.0, 0), this._options.blurHorizontalSize / 2, {
+                        width:  blurTextureWidth,
+                        height: blurTextureHeight
+                    },
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+                this._horizontalBlurPostprocess.width = blurTextureWidth;
+                this._horizontalBlurPostprocess.height = blurTextureHeight;
+                this._horizontalBlurPostprocess.onApplyObservable.add(effect => {
+                    effect.setTexture("textureSampler", this._mainTexture);
+                });
+
+                this._verticalBlurPostprocess = new BlurPostProcess("HighlightLayerVBP", new Vector2(0, 1.0), this._options.blurVerticalSize / 2, {
+                        width:  blurTextureWidth,
+                        height: blurTextureHeight
+                    },
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
+
+                this._postProcesses = [this._horizontalBlurPostprocess, this._verticalBlurPostprocess];
+            }
+
+            this._mainTexture.onAfterUnbindObservable.add(() => {
+                this.onBeforeBlurObservable.notifyObservers(this);
+
+                let internalTexture = this._blurTexture.getInternalTexture();
+                if (internalTexture) {
+                    this._scene.postProcessManager.directRender(
+                        this._postProcesses,
+                        internalTexture, 
+                        true);
+                }
+
+                this.onAfterBlurObservable.notifyObservers(this);
+            });
+
+            // Prevent autoClear.
+            this._postProcesses.map(pp => { pp.autoClear = false; });
+        }
+
+        /**
+         * Returns wether or nood the layer needs stencil enabled during the mesh rendering.
+         */
+        public needStencil(): boolean {
+            return true;
+        }
+
+        /**
+         * Checks for the readiness of the element composing the layer.
+         * @param subMesh the mesh to check for
+         * @param useInstances specify wether or not to use instances to render the mesh
+         * @param emissiveTexture the associated emissive texture used to generate the glow
+         * @return true if ready otherwise, false
+         */
+        public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
+            let material = subMesh.getMaterial();
+            let mesh = subMesh.getRenderingMesh();
+
+            if (!material || !mesh || !this._meshes) {
+                return false;
+            }
+
+            let emissiveTexture: Nullable<Texture> = null;
+            let highlightLayerMesh = this._meshes[mesh.uniqueId];
+
+            if (highlightLayerMesh && highlightLayerMesh.glowEmissiveOnly && material) {
+                emissiveTexture = (<any>material).emissiveTexture;
+            }
+            return super._isReady(subMesh, useInstances, emissiveTexture);
+        }
+
+        /**
+         * Implementation specific of rendering the generating effect on the main canvas.
+         * @param effect The effect used to render through
+         */
+        protected _internalRender(effect: Effect): void {
+            // Texture
+            effect.setTexture("textureSampler", this._blurTexture);
+
+            // Cache
+            var engine = this._engine;
+            var previousStencilBuffer = engine.getStencilBuffer();
+            var previousStencilFunction = engine.getStencilFunction();
+            var previousStencilMask = engine.getStencilMask();
+            var previousStencilOperationPass = engine.getStencilOperationPass();
+            var previousStencilOperationFail = engine.getStencilOperationFail();
+            var previousStencilOperationDepthFail = engine.getStencilOperationDepthFail();
+            var previousStencilReference = engine.getStencilFunctionReference();
+
+            // Stencil operations
+            engine.setStencilOperationPass(Engine.REPLACE);
+            engine.setStencilOperationFail(Engine.KEEP);
+            engine.setStencilOperationDepthFail(Engine.KEEP);
+
+            // Draw order
+            engine.setStencilMask(0x00);
+            engine.setStencilBuffer(true);
+            engine.setStencilFunctionReference(this._instanceGlowingMeshStencilReference);
+
+            // 2 passes inner outer
+            if (this.outerGlow) {
+                effect.setFloat("offset", 0);
+                engine.setStencilFunction(Engine.NOTEQUAL);
+                engine.drawElementsType(Material.TriangleFillMode, 0, 6);
+            }
+            if (this.innerGlow) {
+                effect.setFloat("offset", 1);
+                engine.setStencilFunction(Engine.EQUAL);
+                engine.drawElementsType(Material.TriangleFillMode, 0, 6);
+            }
+
+            // Restore Cache
+            engine.setStencilFunction(previousStencilFunction);
+            engine.setStencilMask(previousStencilMask);
+            engine.setStencilBuffer(previousStencilBuffer);
+            engine.setStencilOperationPass(previousStencilOperationPass);
+            engine.setStencilOperationFail(previousStencilOperationFail);
+            engine.setStencilOperationDepthFail(previousStencilOperationDepthFail);
+            engine.setStencilFunctionReference(previousStencilReference);
+        }
+
+        /**
+         * Returns true if the layer contains information to display, otherwise false.
+         */
+        public shouldRender(): boolean {
+            if (super.shouldRender()) {
+                return this._meshes ? true : false;
+            }
+
+            return false;
+        }
+
+        /**
+         * Returns true if the mesh should render, otherwise false.
+         * @param mesh The mesh to render
+         * @returns true if it should render otherwise false
+         */
+        protected _shouldRenderMesh(mesh: Mesh): boolean {
+            // Excluded Mesh
+            if (this._excludedMeshes && this._excludedMeshes[mesh.uniqueId]) {
+                return false;
+            };
+
+            return true;
+        }
+
+        /**
+         * Sets the required values for both the emissive texture and and the main color.
+         */
+        protected _setEmissiveTextureAndColor(mesh: Mesh, subMesh: SubMesh, material: Material): void {
+            var highlightLayerMesh = this._meshes![mesh.uniqueId];
+            if (highlightLayerMesh) {
+                this._emissiveTextureAndColor.color.set(
+                    highlightLayerMesh.color.r,
+                    highlightLayerMesh.color.g,
+                    highlightLayerMesh.color.b,
+                    1.0);
+            }
+            else {
+                this._emissiveTextureAndColor.color.set(
+                    this.neutralColor.r,
+                    this.neutralColor.g,
+                    this.neutralColor.b,
+                    this.neutralColor.a);
+            }
+
+            if (highlightLayerMesh && highlightLayerMesh.glowEmissiveOnly && material) {
+                this._emissiveTextureAndColor.texture = (<any>material).emissiveTexture;
+                this._emissiveTextureAndColor.color.set(
+                    1.0,
+                    1.0,
+                    1.0,
+                    1.0);
+            }
+            else {
+                this._emissiveTextureAndColor.texture = null;
+            }
+
+        }
+
+        /**
+         * Add a mesh in the exclusion list to prevent it to impact or being impacted by the highlight layer.
+         * @param mesh The mesh to exclude from the highlight layer
+         */
+        public addExcludedMesh(mesh: Mesh) {
+            if (!this._excludedMeshes) {
+                return;
+            }
+
+            var meshExcluded = this._excludedMeshes[mesh.uniqueId];
+            if (!meshExcluded) {
+                this._excludedMeshes[mesh.uniqueId] = {
+                    mesh: mesh,
+                    beforeRender: mesh.onBeforeRenderObservable.add((mesh: Mesh) => {
+                        mesh.getEngine().setStencilBuffer(false);
+                    }),
+                    afterRender: mesh.onAfterRenderObservable.add((mesh: Mesh) => {
+                        mesh.getEngine().setStencilBuffer(true);
+                    }),
+                }
+            }
+        }
+
+        /**
+          * Remove a mesh from the exclusion list to let it impact or being impacted by the highlight layer.
+          * @param mesh The mesh to highlight
+          */
+        public removeExcludedMesh(mesh: Mesh) {
+            if (!this._excludedMeshes) {
+                return;
+            }
+
+            var meshExcluded = this._excludedMeshes[mesh.uniqueId];
+            if (meshExcluded) {
+                if (meshExcluded.beforeRender) {
+                    mesh.onBeforeRenderObservable.remove(meshExcluded.beforeRender);
+                }
+
+                if (meshExcluded.afterRender) {
+                    mesh.onAfterRenderObservable.remove(meshExcluded.afterRender);
+                }
+            }
+
+            this._excludedMeshes[mesh.uniqueId] = null;
+        }
+
+        /**
+         * Determine if a given mesh will be highlighted by the current HighlightLayer
+         * @param mesh mesh to test
+         * @returns true if the mesh will be highlighted by the current HighlightLayer
+         */
+        public hasMesh(mesh: AbstractMesh): boolean {
+            if (!this._meshes) {
+                return false;
+            }
+
+            return this._meshes[mesh.uniqueId] !== undefined && this._meshes[mesh.uniqueId] !== null;
+        }
+
+        /**
+         * Add a mesh in the highlight layer in order to make it glow with the chosen color.
+         * @param mesh The mesh to highlight
+         * @param color The color of the highlight
+         * @param glowEmissiveOnly Extract the glow from the emissive texture
+         */
+        public addMesh(mesh: Mesh, color: Color3, glowEmissiveOnly = false) {
+            if (!this._meshes) {
+                return;
+            }
+
+            var meshHighlight = this._meshes[mesh.uniqueId];
+            if (meshHighlight) {
+                meshHighlight.color = color;
+            }
+            else {
+                this._meshes[mesh.uniqueId] = {
+                    mesh: mesh,
+                    color: color,
+                    // Lambda required for capture due to Observable this context
+                    observerHighlight: mesh.onBeforeRenderObservable.add((mesh: Mesh) => {
+                        if (this._excludedMeshes && this._excludedMeshes[mesh.uniqueId]) {
+                            this._defaultStencilReference(mesh);
+                        }
+                        else {
+                            mesh.getScene().getEngine().setStencilFunctionReference(this._instanceGlowingMeshStencilReference);
+                        }
+                    }),
+                    observerDefault: mesh.onAfterRenderObservable.add(this._defaultStencilReference),
+                    glowEmissiveOnly: glowEmissiveOnly
+                };
+            }
+
+            this._shouldRender = true;
+        }
+
+        /**
+         * Remove a mesh from the highlight layer in order to make it stop glowing.
+         * @param mesh The mesh to highlight
+         */
+        public removeMesh(mesh: Mesh) {
+            if (!this._meshes) {
+                return;
+            }
+
+            var meshHighlight = this._meshes[mesh.uniqueId];
+            if (meshHighlight) {
+
+                if (meshHighlight.observerHighlight) {
+                    mesh.onBeforeRenderObservable.remove(meshHighlight.observerHighlight);
+                }
+
+                if (meshHighlight.observerDefault) {
+                    mesh.onAfterRenderObservable.remove(meshHighlight.observerDefault);
+                }
+                delete this._meshes[mesh.uniqueId];
+            }
+
+            this._shouldRender = false;
+            for (var meshHighlightToCheck in this._meshes) {
+                if (this._meshes[meshHighlightToCheck]) {
+                    this._shouldRender = true;
+                    break;
+                }
+            }
+        }
+
+        /**
+         * Force the stencil to the normal expected value for none glowing parts
+         */
+        private _defaultStencilReference(mesh: Mesh) {
+            mesh.getScene().getEngine().setStencilFunctionReference(HighlightLayer.NormalMeshStencilReference);
+        }
+
+        /**
+         * Free any resources and references associated to a mesh.
+         * Internal use
+         * @param mesh The mesh to free.
+         */
+        public _disposeMesh(mesh: Mesh): void {
+            this.removeMesh(mesh);
+            this.removeExcludedMesh(mesh);
+        }
+
+        /**
+         * Dispose the highlight layer and free resources.
+         */
+        public dispose(): void {
+            if (this._meshes) {
+                // Clean mesh references 
+                for (let id in this._meshes) {
+                    let meshHighlight = this._meshes[id];
+                    if (meshHighlight && meshHighlight.mesh) {
+
+                        if (meshHighlight.observerHighlight) {
+                            meshHighlight.mesh.onBeforeRenderObservable.remove(meshHighlight.observerHighlight);
+                        }
+
+                        if (meshHighlight.observerDefault) {
+                            meshHighlight.mesh.onAfterRenderObservable.remove(meshHighlight.observerDefault);
+                        }
+                    }
+                }
+                this._meshes = null;
+            }
+
+            if (this._excludedMeshes) {
+                for (let id in this._excludedMeshes) {
+                    let meshHighlight = this._excludedMeshes[id];
+                    if (meshHighlight) {
+
+                        if (meshHighlight.beforeRender) {
+                            meshHighlight.mesh.onBeforeRenderObservable.remove(meshHighlight.beforeRender);
+                        }
+
+                        if (meshHighlight.afterRender) {
+                            meshHighlight.mesh.onAfterRenderObservable.remove(meshHighlight.afterRender);
+                        }
+                    }
+                }
+                this._excludedMeshes = null;
+            }
+
+            super.dispose();
+        }
+    }
+} 

+ 0 - 996
src/Layer/babylon.highlightlayer.ts

@@ -1,996 +0,0 @@
-module BABYLON {
-    /**
-     * Special Glow Blur post process only blurring the alpha channel
-     * It enforces keeping the most luminous color in the color channel.
-     */
-    class GlowBlurPostProcess extends PostProcess {
-        constructor(name: string, public direction: Vector2, public kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean) {
-            super(name, "glowBlurPostProcess", ["screenSize", "direction", "blurWidth"], null, options, camera, samplingMode, engine, reusable);
-            
-            this.onApplyObservable.add((effect: Effect) => {
-                effect.setFloat2("screenSize", this.width, this.height);
-                effect.setVector2("direction", this.direction);
-                effect.setFloat("blurWidth", this.kernel);
-            });
-        }
-    }
-
-    /**
-     * Highlight layer options. This helps customizing the behaviour
-     * of the highlight layer.
-     */
-    export interface IHighlightLayerOptions {
-        /**
-         * Multiplication factor apply to the canvas size to compute the render target size
-         * used to generated the glowing objects (the smaller the faster).
-         */
-        mainTextureRatio: number;
-
-        /**
-         * Enforces a fixed size texture to ensure resize independant blur.
-         */
-        mainTextureFixedSize?: number;
-
-        /**
-         * Multiplication factor apply to the main texture size in the first step of the blur to reduce the size 
-         * of the picture to blur (the smaller the faster).
-         */
-        blurTextureSizeRatio: number;
-
-        /**
-         * How big in texel of the blur texture is the vertical blur.
-         */
-        blurVerticalSize: number;
-
-        /**
-         * How big in texel of the blur texture is the horizontal blur.
-         */
-        blurHorizontalSize: number;
-
-        /**
-         * Alpha blending mode used to apply the blur. Default is combine.
-         */
-        alphaBlendingMode: number
-
-        /**
-         * The camera attached to the layer.
-         */
-        camera: Nullable<Camera>;
-
-        /**
-         * Should we display highlight as a solid stroke?
-         */
-        isStroke?: boolean;
-    }
-
-    /**
-     * Storage interface grouping all the information required for glowing a mesh.
-     */
-    interface IHighlightLayerMesh {
-        /** 
-         * The glowy mesh
-         */
-        mesh: Mesh;
-        /**
-         * The color of the glow
-         */
-        color: Color3;
-        /**
-         * The mesh render callback use to insert stencil information
-         */
-        observerHighlight: Nullable<Observer<Mesh>>;
-        /**
-         * The mesh render callback use to come to the default behavior
-         */
-        observerDefault: Nullable<Observer<Mesh>>;
-        /**
-         * If it exists, the emissive color of the material will be used to generate the glow.
-         * Else it falls back to the current color.
-         */
-        glowEmissiveOnly: boolean;
-    }
-
-    /**
-     * Storage interface grouping all the information required for an excluded mesh.
-     */
-    interface IHighlightLayerExcludedMesh {
-        /** 
-         * The glowy mesh
-         */
-        mesh: Mesh;
-        /**
-         * The mesh render callback use to prevent stencil use
-         */
-        beforeRender: Nullable<Observer<Mesh>>;
-        /**
-         * The mesh render callback use to restore previous stencil use
-         */
-        afterRender: Nullable<Observer<Mesh>>;
-    }
-
-    /**
-     * The highlight layer Helps adding a glow effect around a mesh.
-     * 
-     * Once instantiated in a scene, simply use the pushMesh or removeMesh method to add or remove
-     * glowy meshes to your scene.
-     * 
-     * !!! THIS REQUIRES AN ACTIVE STENCIL BUFFER ON THE CANVAS !!!
-     */
-    export class HighlightLayer {
-        /**
-         * The neutral color used during the preparation of the glow effect.
-         * This is black by default as the blend operation is a blend operation. 
-         */
-        public static neutralColor: Color4 = new Color4(0, 0, 0, 0);
-
-        /**
-         * Stencil value used for glowing meshes.
-         */
-        public static glowingMeshStencilReference = 0x02;
-
-        /**
-         * Stencil value used for the other meshes in the scene.
-         */
-        public static normalMeshStencilReference = 0x01;
-
-        private _scene: Scene;
-        private _engine: Engine;
-        private _options: IHighlightLayerOptions;
-        private _vertexBuffers: { [key: string]: Nullable<VertexBuffer> } = {};
-        private _indexBuffer: Nullable<WebGLBuffer>;
-        private _downSamplePostprocess: PassPostProcess;
-        private _horizontalBlurPostprocess: GlowBlurPostProcess;
-        private _verticalBlurPostprocess: GlowBlurPostProcess;
-        private _cachedDefines: string;
-        private _glowMapGenerationEffect: Effect;
-        private _glowMapMergeEffect: Effect;
-        private _blurTexture: RenderTargetTexture;
-        private _mainTexture: RenderTargetTexture;
-        private _mainTextureDesiredSize: ISize = { width: 0, height: 0 };
-        private _meshes: Nullable<{ [id: string]: Nullable<IHighlightLayerMesh> }> = {};
-        private _maxSize: number = 0;
-        private _shouldRender = false;
-        private _instanceGlowingMeshStencilReference = HighlightLayer.glowingMeshStencilReference++;
-        private _excludedMeshes: Nullable<{ [id: string]: Nullable<IHighlightLayerExcludedMesh> }> = {};
-
-        /**
-         * Specifies whether or not the inner glow is ACTIVE in the layer.
-         */
-        public innerGlow: boolean = true;
-
-        /**
-         * Specifies whether or not the outer glow is ACTIVE in the layer.
-         */
-        public outerGlow: boolean = true;
-
-        /**
-         * Specifies wether the highlight layer is enabled or not.
-         */
-        public isEnabled: boolean = true;
-
-        /**
-         * Specifies the horizontal size of the blur.
-         */
-        public set blurHorizontalSize(value: number) {
-            this._horizontalBlurPostprocess.kernel = value;
-        }
-
-        /**
-         * Specifies the vertical size of the blur.
-         */
-        public set blurVerticalSize(value: number) {
-            this._verticalBlurPostprocess.kernel = value;
-        }
-
-        /**
-         * Gets the horizontal size of the blur.
-         */
-        public get blurHorizontalSize(): number {
-            return this._horizontalBlurPostprocess.kernel
-        }
-
-        /**
-         * Gets the vertical size of the blur.
-         */
-        public get blurVerticalSize(): number {
-            return this._verticalBlurPostprocess.kernel;
-        }
-
-        /**
-         * Gets the camera attached to the layer.
-         */
-        public get camera(): Nullable<Camera> {
-            return this._options.camera;
-        }
-
-        /**
-         * An event triggered when the highlight layer has been disposed.
-         * @type {BABYLON.Observable}
-         */
-        public onDisposeObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the highlight layer is about rendering the main texture with the glowy parts.
-         * @type {BABYLON.Observable}
-         */
-        public onBeforeRenderMainTextureObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the highlight layer is being blurred.
-         * @type {BABYLON.Observable}
-         */
-        public onBeforeBlurObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the highlight layer has been blurred.
-         * @type {BABYLON.Observable}
-         */
-        public onAfterBlurObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the glowing blurred texture is being merged in the scene.
-         * @type {BABYLON.Observable}
-         */
-        public onBeforeComposeObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the glowing blurred texture has been merged in the scene.
-         * @type {BABYLON.Observable}
-         */
-        public onAfterComposeObservable = new Observable<HighlightLayer>();
-
-        /**
-         * An event triggered when the highlight layer changes its size.
-         * @type {BABYLON.Observable}
-         */
-        public onSizeChangedObservable = new Observable<HighlightLayer>();
-
-        /**
-         * Instantiates a new highlight Layer and references it to the scene..
-         * @param name The name of the layer
-         * @param scene The scene to use the layer in
-         * @param options Sets of none mandatory options to use with the layer (see IHighlightLayerOptions for more information)
-         */
-        constructor(public name: string, scene: Scene, options?: IHighlightLayerOptions) {
-            this._scene = scene || Engine.LastCreatedScene;
-            var engine = scene.getEngine();
-            this._engine = engine;
-            this._maxSize = this._engine.getCaps().maxTextureSize;
-            this._scene.highlightLayers.push(this);
-
-            // Warn on stencil.
-            if (!this._engine.isStencilEnable) {
-                Tools.Warn("Rendering the Highlight Layer requires the stencil to be active on the canvas. var engine = new BABYLON.Engine(canvas, antialias, { stencil: true }");
-            }
-
-            // Adapt options
-            this._options = options || {
-                mainTextureRatio: 0.5,
-                blurTextureSizeRatio: 0.5,
-                blurHorizontalSize: 1.0,
-                blurVerticalSize: 1.0,
-                alphaBlendingMode: Engine.ALPHA_COMBINE,
-                camera: null
-            };
-            this._options.mainTextureRatio = this._options.mainTextureRatio || 0.5;
-            this._options.blurTextureSizeRatio = this._options.blurTextureSizeRatio || 1.0;
-            this._options.blurHorizontalSize = this._options.blurHorizontalSize || 1;
-            this._options.blurVerticalSize = this._options.blurVerticalSize || 1;
-            this._options.alphaBlendingMode = this._options.alphaBlendingMode || Engine.ALPHA_COMBINE;
-
-            // VBO
-            var vertices = [];
-            vertices.push(1, 1);
-            vertices.push(-1, 1);
-            vertices.push(-1, -1);
-            vertices.push(1, -1);
-
-            var vertexBuffer = new VertexBuffer(engine, vertices, VertexBuffer.PositionKind, false, false, 2);
-            this._vertexBuffers[VertexBuffer.PositionKind] = vertexBuffer;
-
-            this._createIndexBuffer();
-
-            // Effect
-            this._glowMapMergeEffect = engine.createEffect("glowMapMerge",
-                [VertexBuffer.PositionKind],
-                ["offset"],
-                ["textureSampler"],
-                this._options.isStroke ? "#define STROKE \n" : undefined);
-
-            // Render target
-            this.setMainTextureSize();
-
-            // Create Textures and post processes
-            this.createTextureAndPostProcesses();
-        }
-
-        private _createIndexBuffer(): void {
-            var engine = this._scene.getEngine();
-
-            // Indices
-            var indices = [];
-            indices.push(0);
-            indices.push(1);
-            indices.push(2);
-
-            indices.push(0);
-            indices.push(2);
-            indices.push(3);
-
-            this._indexBuffer = engine.createIndexBuffer(indices);
-        }
-
-        public _rebuild(): void {
-            let vb = this._vertexBuffers[VertexBuffer.PositionKind];
-
-            if (vb) {
-                vb._rebuild();
-            }
-
-            this._createIndexBuffer();
-        }
-
-        /**
-         * Creates the render target textures and post processes used in the highlight layer.
-         */
-        private createTextureAndPostProcesses(): void {
-            var blurTextureWidth = this._mainTextureDesiredSize.width * this._options.blurTextureSizeRatio;
-            var blurTextureHeight = this._mainTextureDesiredSize.height * this._options.blurTextureSizeRatio;
-            blurTextureWidth = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureWidth, this._maxSize) : blurTextureWidth;
-            blurTextureHeight = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureHeight, this._maxSize) : blurTextureHeight;
-
-            this._mainTexture = new RenderTargetTexture("HighlightLayerMainRTT",
-                {
-                    width: this._mainTextureDesiredSize.width,
-                    height: this._mainTextureDesiredSize.height
-                },
-                this._scene,
-                false,
-                true,
-                Engine.TEXTURETYPE_UNSIGNED_INT);
-            this._mainTexture.activeCamera = this._options.camera;
-            this._mainTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
-            this._mainTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            this._mainTexture.anisotropicFilteringLevel = 1;
-            this._mainTexture.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
-            this._mainTexture.renderParticles = false;
-            this._mainTexture.renderList = null;
-            this._mainTexture.ignoreCameraViewport = true;
-
-            this._blurTexture = new RenderTargetTexture("HighlightLayerBlurRTT",
-                {
-                    width: blurTextureWidth,
-                    height: blurTextureHeight
-                },
-                this._scene,
-                false,
-                true,
-                Engine.TEXTURETYPE_UNSIGNED_INT);
-            this._blurTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
-            this._blurTexture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            this._blurTexture.anisotropicFilteringLevel = 16;
-            this._blurTexture.updateSamplingMode(Texture.TRILINEAR_SAMPLINGMODE);
-            this._blurTexture.renderParticles = false;
-            this._blurTexture.ignoreCameraViewport = true;
-
-            this._downSamplePostprocess = new PassPostProcess("HighlightLayerPPP", this._options.blurTextureSizeRatio,
-                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
-            this._downSamplePostprocess.onApplyObservable.add(effect => {
-                effect.setTexture("textureSampler", this._mainTexture);
-            });
-
-            if (this._options.alphaBlendingMode === Engine.ALPHA_COMBINE) {
-                this._horizontalBlurPostprocess = new GlowBlurPostProcess("HighlightLayerHBP", new Vector2(1.0, 0), this._options.blurHorizontalSize, 1,
-                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
-                this._horizontalBlurPostprocess.onApplyObservable.add(effect => {
-                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
-                });
-
-                this._verticalBlurPostprocess = new GlowBlurPostProcess("HighlightLayerVBP", new Vector2(0, 1.0), this._options.blurVerticalSize, 1,
-                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
-                this._verticalBlurPostprocess.onApplyObservable.add(effect => {
-                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
-                });
-            }
-            else {
-                this._horizontalBlurPostprocess = new BlurPostProcess("HighlightLayerHBP", new Vector2(1.0, 0), this._options.blurHorizontalSize, 1,
-                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
-                this._horizontalBlurPostprocess.onApplyObservable.add(effect => {
-                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
-                });
-
-                this._verticalBlurPostprocess = new BlurPostProcess("HighlightLayerVBP", new Vector2(0, 1.0), this._options.blurVerticalSize, 1,
-                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
-                this._verticalBlurPostprocess.onApplyObservable.add(effect => {
-                    effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
-                });
-            }
-
-            this._mainTexture.onAfterUnbindObservable.add(() => {
-                this.onBeforeBlurObservable.notifyObservers(this);
-
-                let internalTexture = this._blurTexture.getInternalTexture();
-
-                if (internalTexture) {
-                    this._scene.postProcessManager.directRender(
-                        [this._downSamplePostprocess, this._horizontalBlurPostprocess, this._verticalBlurPostprocess],
-                        internalTexture, true);
-                }
-
-                this.onAfterBlurObservable.notifyObservers(this);
-            });
-
-            // Custom render function
-            var renderSubMesh = (subMesh: SubMesh): void => {
-                if (!this._meshes) {
-                    return;
-                }
-
-                var material = subMesh.getMaterial();
-                var mesh = subMesh.getRenderingMesh();
-                var scene = this._scene;
-                var engine = scene.getEngine();
-
-                if (!material) {
-                    return;
-                }
-
-                // Do not block in blend mode.
-                if (material.needAlphaBlendingForMesh(mesh)) {
-                    return;
-                }
-
-                // Culling
-                engine.setState(material.backFaceCulling);
-
-                // Managing instances
-                var batch = mesh._getInstancesRenderList(subMesh._id);
-                if (batch.mustReturn) {
-                    return;
-                }
-
-                // Excluded Mesh
-                if (this._excludedMeshes && this._excludedMeshes[mesh.uniqueId]) {
-                    return;
-                };
-
-                var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
-
-                var highlightLayerMesh = this._meshes[mesh.uniqueId];
-                var emissiveTexture: Nullable<Texture> = null;
-                if (highlightLayerMesh && highlightLayerMesh.glowEmissiveOnly && material) {
-                    emissiveTexture = (<any>material).emissiveTexture;
-                }
-
-                if (this._isReady(subMesh, hardwareInstancedRendering, emissiveTexture)) {
-                    engine.enableEffect(this._glowMapGenerationEffect);
-                    mesh._bind(subMesh, this._glowMapGenerationEffect, Material.TriangleFillMode);
-
-                    this._glowMapGenerationEffect.setMatrix("viewProjection", scene.getTransformMatrix());
-                    if (highlightLayerMesh) {
-                        this._glowMapGenerationEffect.setFloat4("color",
-                            highlightLayerMesh.color.r,
-                            highlightLayerMesh.color.g,
-                            highlightLayerMesh.color.b,
-                            1.0);
-                    }
-                    else {
-                        this._glowMapGenerationEffect.setFloat4("color",
-                            HighlightLayer.neutralColor.r,
-                            HighlightLayer.neutralColor.g,
-                            HighlightLayer.neutralColor.b,
-                            HighlightLayer.neutralColor.a);
-                    }
-
-                    // Alpha test
-                    if (material && material.needAlphaTesting()) {
-                        var alphaTexture = material.getAlphaTestTexture();
-                        if (alphaTexture) {
-                            this._glowMapGenerationEffect.setTexture("diffuseSampler", alphaTexture);
-                            let textureMatrix = alphaTexture.getTextureMatrix();
-
-                            if (textureMatrix) {
-                                this._glowMapGenerationEffect.setMatrix("diffuseMatrix", textureMatrix);
-                            }
-                        }
-                    }
-
-                    // Glow emissive only
-                    if (emissiveTexture) {
-                        this._glowMapGenerationEffect.setTexture("emissiveSampler", emissiveTexture);
-                        this._glowMapGenerationEffect.setMatrix("emissiveMatrix", emissiveTexture.getTextureMatrix());
-                    }
-
-                    // Bones
-                    if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
-                        this._glowMapGenerationEffect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
-                    }
-
-                    // Draw
-                    mesh._processRendering(subMesh, this._glowMapGenerationEffect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
-                        (isInstance, world) => this._glowMapGenerationEffect.setMatrix("world", world));
-                } else {
-                    // Need to reset refresh rate of the shadowMap
-                    this._mainTexture.resetRefreshCounter();
-                }
-            };
-
-            this._mainTexture.customRenderFunction = (opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void => {
-                this.onBeforeRenderMainTextureObservable.notifyObservers(this);
-
-                var index: number;
-
-                let engine = this._scene.getEngine();
-
-                if (depthOnlySubMeshes.length) {
-                    engine.setColorWrite(false);
-                    for (index = 0; index < depthOnlySubMeshes.length; index++) {
-                        renderSubMesh(depthOnlySubMeshes.data[index]);
-                    }
-                    engine.setColorWrite(true);
-                }
-
-                for (index = 0; index < opaqueSubMeshes.length; index++) {
-                    renderSubMesh(opaqueSubMeshes.data[index]);
-                }
-
-                for (index = 0; index < alphaTestSubMeshes.length; index++) {
-                    renderSubMesh(alphaTestSubMeshes.data[index]);
-                }
-
-                for (index = 0; index < transparentSubMeshes.length; index++) {
-                    renderSubMesh(transparentSubMeshes.data[index]);
-                }
-            };
-
-            this._mainTexture.onClearObservable.add((engine: Engine) => {
-                engine.clear(HighlightLayer.neutralColor, true, true, true);
-            });
-        }
-
-        /**
-         * Checks for the readiness of the element composing the layer.
-         * @param subMesh the mesh to check for
-         * @param useInstances specify wether or not to use instances to render the mesh
-         * @param emissiveTexture the associated emissive texture used to generate the glow
-         * @return true if ready otherwise, false
-         */
-        public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
-            let material = subMesh.getMaterial();
-            let mesh = subMesh.getRenderingMesh();
-
-            if (!material || !mesh || !this._meshes) {
-                return false;
-            }
-
-            let emissiveTexture: Nullable<Texture> = null;
-            let highlightLayerMesh = this._meshes[mesh.uniqueId];
-
-            if (highlightLayerMesh && highlightLayerMesh.glowEmissiveOnly && material) {
-                emissiveTexture = (<any>material).emissiveTexture;
-            }
-            return this._isReady(subMesh, useInstances, emissiveTexture);
-        }
-
-        /**
-         * Checks for the readiness of the element composing the layer.
-         * @param subMesh the mesh to check for
-         * @param useInstances specify wether or not to use instances to render the mesh
-         * @param emissiveTexture the associated emissive texture used to generate the glow
-         * @return true if ready otherwise, false
-         */
-        private _isReady(subMesh: SubMesh, useInstances: boolean, emissiveTexture: Nullable<Texture>): boolean {
-            let material = subMesh.getMaterial();
-
-            if (!material) {
-                return false;
-            }
-
-            if (!material.isReady(subMesh.getMesh(), useInstances)) {
-                return false;
-            }
-
-            var defines = [];
-
-            var attribs = [VertexBuffer.PositionKind];
-
-            var mesh = subMesh.getMesh();
-            var uv1 = false;
-            var uv2 = false;
-
-            // Alpha test
-            if (material && material.needAlphaTesting()) {
-                var alphaTexture = material.getAlphaTestTexture();
-                if (alphaTexture) {
-                    defines.push("#define ALPHATEST");
-                    if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
-                        alphaTexture.coordinatesIndex === 1) {
-                        defines.push("#define DIFFUSEUV2");
-                        uv2 = true;
-                    }
-                    else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
-                        defines.push("#define DIFFUSEUV1");
-                        uv1 = true;
-                    }
-                }
-            }
-
-            // Emissive
-            if (emissiveTexture) {
-                defines.push("#define EMISSIVE");
-                if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind) &&
-                    emissiveTexture.coordinatesIndex === 1) {
-                    defines.push("#define EMISSIVEUV2");
-                    uv2 = true;
-                }
-                else if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
-                    defines.push("#define EMISSIVEUV1");
-                    uv1 = true;
-                }
-            }
-
-            if (uv1) {
-                attribs.push(VertexBuffer.UVKind);
-                defines.push("#define UV1");
-            }
-            if (uv2) {
-                attribs.push(VertexBuffer.UV2Kind);
-                defines.push("#define UV2");
-            }
-
-            // Bones
-            if (mesh.useBones && mesh.computeBonesUsingShaders) {
-                attribs.push(VertexBuffer.MatricesIndicesKind);
-                attribs.push(VertexBuffer.MatricesWeightsKind);
-                if (mesh.numBoneInfluencers > 4) {
-                    attribs.push(VertexBuffer.MatricesIndicesExtraKind);
-                    attribs.push(VertexBuffer.MatricesWeightsExtraKind);
-                }
-                defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
-                defines.push("#define BonesPerMesh " + (mesh.skeleton ? (mesh.skeleton.bones.length + 1) : 0));
-            } else {
-                defines.push("#define NUM_BONE_INFLUENCERS 0");
-            }
-
-            // Instances
-            if (useInstances) {
-                defines.push("#define INSTANCES");
-                attribs.push("world0");
-                attribs.push("world1");
-                attribs.push("world2");
-                attribs.push("world3");
-            }
-
-            // Get correct effect      
-            var join = defines.join("\n");
-            if (this._cachedDefines !== join) {
-                this._cachedDefines = join;
-                this._glowMapGenerationEffect = this._scene.getEngine().createEffect("glowMapGeneration",
-                    attribs,
-                    ["world", "mBones", "viewProjection", "diffuseMatrix", "color", "emissiveMatrix"],
-                    ["diffuseSampler", "emissiveSampler"], join);
-            }
-
-            return this._glowMapGenerationEffect.isReady();
-        }
-
-        /**
-         * Renders the glowing part of the scene by blending the blurred glowing meshes on top of the rendered scene.
-         */
-        public render(): void {
-            var currentEffect = this._glowMapMergeEffect;
-
-            // Check
-            if (!currentEffect.isReady() || !this._blurTexture.isReady())
-                return;
-
-            var engine = this._scene.getEngine();
-
-            this.onBeforeComposeObservable.notifyObservers(this);
-
-            // Render
-            engine.enableEffect(currentEffect);
-            engine.setState(false);
-
-            // Cache
-            var previousStencilBuffer = engine.getStencilBuffer();
-            var previousStencilFunction = engine.getStencilFunction();
-            var previousStencilMask = engine.getStencilMask();
-            var previousStencilOperationPass = engine.getStencilOperationPass();
-            var previousStencilOperationFail = engine.getStencilOperationFail();
-            var previousStencilOperationDepthFail = engine.getStencilOperationDepthFail();
-            var previousAlphaMode = engine.getAlphaMode();
-
-            // Texture
-            currentEffect.setTexture("textureSampler", this._blurTexture);
-
-            // VBOs
-            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, currentEffect);
-
-            // Stencil operations
-            engine.setStencilOperationPass(Engine.REPLACE);
-            engine.setStencilOperationFail(Engine.KEEP);
-            engine.setStencilOperationDepthFail(Engine.KEEP);
-
-            // Draw order
-            engine.setAlphaMode(this._options.alphaBlendingMode);
-            engine.setStencilMask(0x00);
-            engine.setStencilBuffer(true);
-            engine.setStencilFunctionReference(this._instanceGlowingMeshStencilReference);
-
-            if (this.outerGlow) {
-                currentEffect.setFloat("offset", 0);
-                engine.setStencilFunction(Engine.NOTEQUAL);
-                engine.drawElementsType(Material.TriangleFillMode, 0, 6);
-            }
-            if (this.innerGlow) {
-                currentEffect.setFloat("offset", 1);
-                engine.setStencilFunction(Engine.EQUAL);
-                engine.drawElementsType(Material.TriangleFillMode, 0, 6);
-            }
-
-            // Restore Cache
-            engine.setStencilFunction(previousStencilFunction);
-            engine.setStencilMask(previousStencilMask);
-            engine.setAlphaMode(previousAlphaMode);
-            engine.setStencilBuffer(previousStencilBuffer);
-            engine.setStencilOperationPass(previousStencilOperationPass);
-            engine.setStencilOperationFail(previousStencilOperationFail);
-            engine.setStencilOperationDepthFail(previousStencilOperationDepthFail);
-
-            (<any>engine)._stencilState.reset();
-
-            this.onAfterComposeObservable.notifyObservers(this);
-
-            // Handle size changes.
-            var size = this._mainTexture.getSize();
-            this.setMainTextureSize();
-            if (size.width !== this._mainTextureDesiredSize.width || size.height !== this._mainTextureDesiredSize.height) {
-                // Recreate RTT and post processes on size change.
-                this.onSizeChangedObservable.notifyObservers(this);
-                this.disposeTextureAndPostProcesses();
-                this.createTextureAndPostProcesses();
-            }
-        }
-
-        /**
-         * Add a mesh in the exclusion list to prevent it to impact or being impacted by the highlight layer.
-         * @param mesh The mesh to exclude from the highlight layer
-         */
-        public addExcludedMesh(mesh: Mesh) {
-            if (!this._excludedMeshes) {
-                return;
-            }
-
-            var meshExcluded = this._excludedMeshes[mesh.uniqueId];
-            if (!meshExcluded) {
-                this._excludedMeshes[mesh.uniqueId] = {
-                    mesh: mesh,
-                    beforeRender: mesh.onBeforeRenderObservable.add((mesh: Mesh) => {
-                        mesh.getEngine().setStencilBuffer(false);
-                    }),
-                    afterRender: mesh.onAfterRenderObservable.add((mesh: Mesh) => {
-                        mesh.getEngine().setStencilBuffer(true);
-                    }),
-                }
-            }
-        }
-
-        /**
-          * Remove a mesh from the exclusion list to let it impact or being impacted by the highlight layer.
-          * @param mesh The mesh to highlight
-          */
-        public removeExcludedMesh(mesh: Mesh) {
-            if (!this._excludedMeshes) {
-                return;
-            }
-
-            var meshExcluded = this._excludedMeshes[mesh.uniqueId];
-            if (meshExcluded) {
-                if (meshExcluded.beforeRender) {
-                    mesh.onBeforeRenderObservable.remove(meshExcluded.beforeRender);
-                }
-
-                if (meshExcluded.afterRender) {
-                    mesh.onAfterRenderObservable.remove(meshExcluded.afterRender);
-                }
-            }
-
-            this._excludedMeshes[mesh.uniqueId] = null;
-        }
-
-        /**
-         * Determine if a given mesh will be highlighted by the current HighlightLayer
-         * @param mesh mesh to test
-         * @returns true if the mesh will be highlighted by the current HighlightLayer
-         */
-        public hasMesh(mesh: AbstractMesh): boolean {
-            if (!this._meshes) {
-                return false;
-            }
-
-            return this._meshes[mesh.uniqueId] !== undefined && this._meshes[mesh.uniqueId] !== null;
-        }
-
-        /**
-         * Add a mesh in the highlight layer in order to make it glow with the chosen color.
-         * @param mesh The mesh to highlight
-         * @param color The color of the highlight
-         * @param glowEmissiveOnly Extract the glow from the emissive texture
-         */
-        public addMesh(mesh: Mesh, color: Color3, glowEmissiveOnly = false) {
-            if (!this._meshes) {
-                return;
-            }
-
-            var meshHighlight = this._meshes[mesh.uniqueId];
-            if (meshHighlight) {
-                meshHighlight.color = color;
-            }
-            else {
-                this._meshes[mesh.uniqueId] = {
-                    mesh: mesh,
-                    color: color,
-                    // Lambda required for capture due to Observable this context
-                    observerHighlight: mesh.onBeforeRenderObservable.add((mesh: Mesh) => {
-                        if (this._excludedMeshes && this._excludedMeshes[mesh.uniqueId]) {
-                            this.defaultStencilReference(mesh);
-                        }
-                        else {
-                            mesh.getScene().getEngine().setStencilFunctionReference(this._instanceGlowingMeshStencilReference);
-                        }
-                    }),
-                    observerDefault: mesh.onAfterRenderObservable.add(this.defaultStencilReference),
-                    glowEmissiveOnly: glowEmissiveOnly
-                };
-            }
-
-            this._shouldRender = true;
-        }
-
-        /**
-         * Remove a mesh from the highlight layer in order to make it stop glowing.
-         * @param mesh The mesh to highlight
-         */
-        public removeMesh(mesh: Mesh) {
-            if (!this._meshes) {
-                return;
-            }
-
-            var meshHighlight = this._meshes[mesh.uniqueId];
-            if (meshHighlight) {
-
-                if (meshHighlight.observerHighlight) {
-                    mesh.onBeforeRenderObservable.remove(meshHighlight.observerHighlight);
-                }
-
-                if (meshHighlight.observerDefault) {
-                    mesh.onAfterRenderObservable.remove(meshHighlight.observerDefault);
-                }
-                delete this._meshes[mesh.uniqueId];
-            }
-
-            this._shouldRender = false;
-            for (var meshHighlightToCheck in this._meshes) {
-                if (this._meshes[meshHighlightToCheck]) {
-                    this._shouldRender = true;
-                    break;
-                }
-            }
-        }
-
-        /**
-         * Returns true if the layer contains information to display, otherwise false.
-         */
-        public shouldRender(): boolean {
-            return this.isEnabled && this._shouldRender;
-        }
-
-        /**
-         * Sets the main texture desired size which is the closest power of two
-         * of the engine canvas size.
-         */
-        private setMainTextureSize(): void {
-            if (this._options.mainTextureFixedSize) {
-                this._mainTextureDesiredSize.width = this._options.mainTextureFixedSize;
-                this._mainTextureDesiredSize.height = this._options.mainTextureFixedSize;
-            }
-            else {
-                this._mainTextureDesiredSize.width = this._engine.getRenderWidth() * this._options.mainTextureRatio;
-                this._mainTextureDesiredSize.height = this._engine.getRenderHeight() * this._options.mainTextureRatio;
-
-                this._mainTextureDesiredSize.width = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.width, this._maxSize) : this._mainTextureDesiredSize.width;
-                this._mainTextureDesiredSize.height = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(this._mainTextureDesiredSize.height, this._maxSize) : this._mainTextureDesiredSize.height;
-            }
-        }
-
-        /**
-         * Force the stencil to the normal expected value for none glowing parts
-         */
-        private defaultStencilReference(mesh: Mesh) {
-            mesh.getScene().getEngine().setStencilFunctionReference(HighlightLayer.normalMeshStencilReference);
-        }
-
-        /**
-         * Dispose only the render target textures and post process.
-         */
-        private disposeTextureAndPostProcesses(): void {
-            this._blurTexture.dispose();
-            this._mainTexture.dispose();
-
-            this._downSamplePostprocess.dispose();
-            this._horizontalBlurPostprocess.dispose();
-            this._verticalBlurPostprocess.dispose();
-        }
-
-        /**
-         * Dispose the highlight layer and free resources.
-         */
-        public dispose(): void {
-            var vertexBuffer = this._vertexBuffers[VertexBuffer.PositionKind];
-            if (vertexBuffer) {
-                vertexBuffer.dispose();
-                this._vertexBuffers[VertexBuffer.PositionKind] = null;
-            }
-
-            if (this._indexBuffer) {
-                this._scene.getEngine()._releaseBuffer(this._indexBuffer);
-                this._indexBuffer = null;
-            }
-
-            // Clean textures and post processes
-            this.disposeTextureAndPostProcesses();
-
-            if (this._meshes) {
-                // Clean mesh references 
-                for (let id in this._meshes) {
-                    let meshHighlight = this._meshes[id];
-                    if (meshHighlight && meshHighlight.mesh) {
-
-                        if (meshHighlight.observerHighlight) {
-                            meshHighlight.mesh.onBeforeRenderObservable.remove(meshHighlight.observerHighlight);
-                        }
-
-                        if (meshHighlight.observerDefault) {
-                            meshHighlight.mesh.onAfterRenderObservable.remove(meshHighlight.observerDefault);
-                        }
-                    }
-                }
-                this._meshes = null;
-            }
-
-            if (this._excludedMeshes) {
-                for (let id in this._excludedMeshes) {
-                    let meshHighlight = this._excludedMeshes[id];
-                    if (meshHighlight) {
-
-                        if (meshHighlight.beforeRender) {
-                            meshHighlight.mesh.onBeforeRenderObservable.remove(meshHighlight.beforeRender);
-                        }
-
-                        if (meshHighlight.afterRender) {
-                            meshHighlight.mesh.onAfterRenderObservable.remove(meshHighlight.afterRender);
-                        }
-                    }
-                }
-                this._excludedMeshes = null;
-            }
-
-            // Remove from scene
-            var index = this._scene.highlightLayers.indexOf(this, 0);
-            if (index > -1) {
-                this._scene.highlightLayers.splice(index, 1);
-            }
-
-            // Callback
-            this.onDisposeObservable.notifyObservers(this);
-
-            this.onDisposeObservable.clear();
-            this.onBeforeRenderMainTextureObservable.clear();
-            this.onBeforeBlurObservable.clear();
-            this.onBeforeComposeObservable.clear();
-            this.onAfterComposeObservable.clear();
-            this.onSizeChangedObservable.clear();
-        }
-    }
-} 

+ 0 - 0
src/Loading/Plugins/babylon.babylonFileLoader.ts


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません