Sebastien Vandenberghe 7 lat temu
rodzic
commit
17bb06b594
100 zmienionych plików z 40089 dodań i 41407 usunięć
  1. 7528 7366
      Playground/babylon.d.txt
  2. 0 14
      Playground/package-lock.json
  3. BIN
      Playground/scenes/CompileMaterialsTest/BaseColor.png
  4. BIN
      Playground/scenes/CompileMaterialsTest/Test.bin
  5. 127 0
      Playground/scenes/CompileMaterialsTest/Test.gltf
  6. 31 3
      Tools/Gulp/config.json
  7. 8 7
      Viewer/package.json
  8. 5 0
      Viewer/src/configuration/configuration.ts
  9. 6 3
      Viewer/src/configuration/mappers.ts
  10. 2 3
      Viewer/src/index.ts
  11. 167 0
      Viewer/src/model/modelAnimation.ts
  12. 142 0
      Viewer/src/model/viewerModel.ts
  13. 9 112
      Viewer/src/viewer/defaultViewer.ts
  14. 50 63
      Viewer/src/viewer/viewer.ts
  15. 11744 11582
      dist/preview release/babylon.d.ts
  16. 55 55
      dist/preview release/babylon.js
  17. 1003 565
      dist/preview release/babylon.max.js
  18. 56 56
      dist/preview release/babylon.worker.js
  19. 11661 11499
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  20. 51 51
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  21. 1062 568
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  22. 1064 570
      dist/preview release/customConfigurations/minimalGLTFViewer/es6.js
  23. 1005 567
      dist/preview release/es6.js
  24. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  25. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  26. 5 4
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  27. 78 60
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  28. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  29. 5 4
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  30. 78 60
      dist/preview release/loaders/babylon.glTFFileLoader.js
  31. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  32. 78 60
      dist/preview release/loaders/babylonjs.loaders.js
  33. 2 2
      dist/preview release/loaders/babylonjs.loaders.min.js
  34. 5 4
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  35. 4 282
      dist/preview release/materialsLibrary/babylon.customMaterial.d.ts
  36. 19 1908
      dist/preview release/materialsLibrary/babylon.customMaterial.js
  37. 1 2
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  38. 22 1906
      dist/preview release/materialsLibrary/babylonjs.materials.js
  39. 3 5
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  40. 4 282
      dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts
  41. 86 29
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  42. 385 158
      dist/preview release/serializers/babylon.glTF2Serializer.js
  43. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  44. 385 158
      dist/preview release/serializers/babylonjs.serializers.js
  45. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  46. 86 29
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  47. 2 24
      dist/preview release/typedocValidationBaseline.json
  48. 80 24
      dist/preview release/viewer/babylon.viewer.d.ts
  49. 68 68
      dist/preview release/viewer/babylon.viewer.js
  50. 1493 736
      dist/preview release/viewer/babylon.viewer.max.js
  51. 7 2
      dist/preview release/what's new.md
  52. 4 4
      loaders/src/glTF/2.0/Extensions/KHR_lights.ts
  53. 2 2
      loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts
  54. 13 4
      loaders/src/glTF/2.0/Extensions/MSFT_lod.ts
  55. 138 127
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  56. 1 1
      loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts
  57. 35 1973
      materialsLibrary/src/custom/babylon.customMaterial.ts
  58. 4 4
      serializers/src/glTF/2.0/babylon.glTFExporter.ts
  59. 475 158
      serializers/src/glTF/2.0/babylon.glTFMaterial.ts
  60. 1 0
      src/Behaviors/Cameras/babylon.framingBehavior.ts
  61. 1 0
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  62. 11 7
      src/Cameras/babylon.arcRotateCamera.ts
  63. 32 8
      src/Cameras/babylon.camera.ts
  64. 2 1
      src/Engine/babylon.engine.ts
  65. 5 4
      src/Helpers/babylon.videoDome.ts
  66. 5 6
      src/Lights/Shadows/babylon.shadowGenerator.ts
  67. 5 3
      src/Lights/babylon.light.ts
  68. 9 0
      src/Materials/Textures/babylon.renderTargetTexture.ts
  69. 53 0
      src/Materials/babylon.effect.ts
  70. 1 0
      src/Materials/babylon.material.ts
  71. 19 3
      src/Math/babylon.math.ts
  72. 4 0
      src/Mesh/Compression/babylon.dracoCompression.ts
  73. 9 5
      src/Mesh/babylon.abstractMesh.ts
  74. 2 2
      src/Mesh/babylon.instancedMesh.ts
  75. 4 4
      src/Mesh/babylon.mesh.ts
  76. 13 20
      src/Mesh/babylon.transformNode.ts
  77. 16 3
      src/Particles/babylon.gpuParticleSystem.ts
  78. 13 5
      src/Particles/babylon.particle.ts
  79. 99 22
      src/Particles/babylon.particleSystem.ts
  80. 109 40
      src/PostProcess/RenderPipeline/Pipelines/babylon.defaultRenderingPipeline.ts
  81. 1 1
      src/PostProcess/RenderPipeline/babylon.postProcessRenderEffect.ts
  82. 25 6
      src/PostProcess/babylon.blurPostProcess.ts
  83. 3 2
      src/PostProcess/babylon.chromaticAberrationPostProcess.ts
  84. 23 9
      src/PostProcess/babylon.circleOfConfusionPostProcess.ts
  85. 3 2
      src/PostProcess/babylon.depthOfFieldBlurPostProcess.ts
  86. 87 52
      src/PostProcess/babylon.depthOfFieldEffect.ts
  87. 21 2
      src/PostProcess/babylon.depthOfFieldMergePostProcess.ts
  88. 39 0
      src/PostProcess/babylon.imageProcessingPostProcess.ts
  89. 2 2
      src/PostProcess/babylon.passPostProcess.ts
  90. 17 2
      src/PostProcess/babylon.postProcess.ts
  91. 3 3
      src/PostProcess/babylon.postProcessManager.ts
  92. 3 2
      src/PostProcess/babylon.sharpenPostProcess.ts
  93. 1 1
      src/Rendering/babylon.depthRenderer.ts
  94. 8 2
      src/Rendering/babylon.renderingManager.ts
  95. 1 1
      src/Shaders/gpuRenderParticles.vertex.fx
  96. 13 2
      src/Shaders/gpuUpdateParticles.vertex.fx
  97. 7 3
      src/Tools/babylon.tga.ts
  98. 19 4
      src/babylon.node.ts
  99. 45 3
      src/babylon.scene.ts
  100. 0 0
      tests/unit/babylon/serializers/babylon.glTFSerializer.tests.ts

Plik diff jest za duży
+ 7528 - 7366
Playground/babylon.d.txt


+ 0 - 14
Playground/package-lock.json

@@ -1,14 +0,0 @@
-{
-  "name": "babylonjsplayground",
-  "version": "3.0.0",
-  "lockfileVersion": 1,
-  "requires": true,
-  "dependencies": {
-    "monaco-editor": {
-      "version": "0.10.1",
-      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.10.1.tgz",
-      "integrity": "sha1-jJbE8VtrUli/ksvek8rYp+MAfhQ=",
-      "dev": true
-    }
-  }
-}

BIN
Playground/scenes/CompileMaterialsTest/BaseColor.png


BIN
Playground/scenes/CompileMaterialsTest/Test.bin


+ 127 - 0
Playground/scenes/CompileMaterialsTest/Test.gltf

@@ -0,0 +1,127 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "componentType": 5126,
+      "count": 4,
+      "type": "VEC3",
+      "max": [
+        0.5,
+        0.5,
+        0.0
+      ],
+      "min": [
+        -0.5,
+        -0.5,
+        0.0
+      ]
+    },
+    {
+      "bufferView": 1,
+      "componentType": 5126,
+      "count": 4,
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 2,
+      "componentType": 5126,
+      "count": 4,
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 3,
+      "componentType": 5125,
+      "count": 6,
+      "type": "SCALAR"
+    }
+  ],
+  "asset": {
+    "version": "2.0"
+  },
+  "buffers": [
+    {
+      "uri": "Test.bin",
+      "byteLength": 168
+    }
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 48
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 48,
+      "byteLength": 64
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 112,
+      "byteLength": 32
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 144,
+      "byteLength": 24
+    }
+  ],
+  "images": [
+    {
+      "uri": "BaseColor.png"
+    }
+  ],
+  "materials": [
+    {
+      "pbrMetallicRoughness": {
+        "baseColorFactor": [
+          1.0,
+          1.0,
+          1.0,
+          0.7
+        ],
+        "baseColorTexture": {
+          "index": 0
+        }
+      },
+      "alphaMode": "BLEND"
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 0,
+            "COLOR_0": 1,
+            "TEXCOORD_0": 2
+          },
+          "indices": 3,
+          "material": 0
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0,
+      "scale": [
+        0.5,
+        1,
+        1
+      ]
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "nodes": [
+        0
+      ]
+    }
+  ],
+  "textures": [
+    {
+      "source": 0
+    }
+  ]
+}

+ 31 - 3
Tools/Gulp/config.json

@@ -744,7 +744,8 @@
                 "../../src/PostProcess/babylon.depthOfFieldBlurPostProcess.js"
                 "../../src/PostProcess/babylon.depthOfFieldBlurPostProcess.js"
             ],
             ],
             "dependUpon": [
             "dependUpon": [
-                "postProcesses"
+                "postProcesses",
+                "additionalPostProcess_blur"
             ],
             ],
             "shaders": [
             "shaders": [
                 "kernelBlur.vertex",
                 "kernelBlur.vertex",
@@ -768,6 +769,28 @@
                 "circleOfConfusion.fragment"
                 "circleOfConfusion.fragment"
             ]
             ]
         },
         },
+        "additionalPostProcess_sharpen": {
+            "files": [
+                "../../src/PostProcess/babylon.sharpenPostProcess.js"
+            ],
+            "dependUpon": [
+                "postProcesses"
+            ],
+            "shaders": [
+                "sharpen.fragment"
+            ]
+        },
+        "additionalPostProcess_chromaticAberration": {
+            "files": [
+                "../../src/PostProcess/babylon.chromaticAberrationPostProcess.js"
+            ],
+            "dependUpon": [
+                "postProcesses"
+            ],
+            "shaders": [
+                "chromaticAberration.fragment"
+            ]
+        },
         "additionalPostProcess_depthOfFieldMerge": {
         "additionalPostProcess_depthOfFieldMerge": {
             "files": [
             "files": [
                 "../../src/PostProcess/babylon.depthOfFieldMergePostProcess.js"
                 "../../src/PostProcess/babylon.depthOfFieldMergePostProcess.js"
@@ -782,6 +805,11 @@
         "additionalPostProcess_depthOfFieldEffect": {
         "additionalPostProcess_depthOfFieldEffect": {
             "files": [
             "files": [
                 "../../src/PostProcess/babylon.depthOfFieldEffect.js"
                 "../../src/PostProcess/babylon.depthOfFieldEffect.js"
+            ],
+            "dependUpon": [
+                "additionalPostProcess_depthOfFieldBlur",
+                "additionalPostProcess_depthOfFieldMerge",
+                "additionalPostProcess_circleOfConfusion"
             ]
             ]
         },
         },
         "additionalPostProcess_fxaa": {
         "additionalPostProcess_fxaa": {
@@ -892,8 +920,8 @@
             "dependUpon": [
             "dependUpon": [
                 "renderingPipeline",
                 "renderingPipeline",
                 "additionalPostProcess_fxaa",
                 "additionalPostProcess_fxaa",
-                "additionalPostProcess_circleOfConfusion",
-                "additionalPostProcess_depthOfFieldMerge",
+                "additionalPostProcess_chromaticAberration",
+                "additionalPostProcess_sharpen",
                 "additionalPostProcess_depthOfFieldEffect"
                 "additionalPostProcess_depthOfFieldEffect"
             ]
             ]
         },
         },

+ 8 - 7
Viewer/package.json

@@ -23,14 +23,15 @@
     },
     },
     "homepage": "https://github.com/BabylonJS/Babylon.js#readme",
     "homepage": "https://github.com/BabylonJS/Babylon.js#readme",
     "devDependencies": {
     "devDependencies": {
-        "@types/node": "^8.5.8",
+        "@types/node": "^8.9.4",
         "base64-image-loader": "^1.2.1",
         "base64-image-loader": "^1.2.1",
-        "html-loader": "^0.5.4",
+        "html-loader": "^0.5.5",
         "json-loader": "^0.5.7",
         "json-loader": "^0.5.7",
         "ts-loader": "^2.3.7",
         "ts-loader": "^2.3.7",
-        "typescript": "^2.6.2",
-        "uglifyjs-webpack-plugin": "^1.1.6",
-        "webpack": "^3.10.0",
-        "webpack-dev-server": "^2.11.0"
-    }
+        "typescript": "^2.7.2",
+        "uglifyjs-webpack-plugin": "^1.2.2",
+        "webpack": "^3.11.0",
+        "webpack-dev-server": "^2.11.2"
+    },
+    "dependencies": {}
 }
 }

+ 5 - 0
Viewer/src/configuration/configuration.ts

@@ -85,6 +85,11 @@ export interface IModelConfiguration {
     subtitle?: string;
     subtitle?: string;
     thumbnail?: string; // URL or data-url
     thumbnail?: string; // URL or data-url
 
 
+    animation?: {
+        autoStart?: boolean | string;
+        playOnce?: boolean;
+    }
+
     // [propName: string]: any; // further configuration, like title and creator
     // [propName: string]: any; // further configuration, like title and creator
 }
 }
 
 

+ 6 - 3
Viewer/src/configuration/mappers.ts

@@ -26,9 +26,12 @@ class HTMLMapper implements IMapper {
                     } else if (val === "false") {
                     } else if (val === "false") {
                         val = false;
                         val = false;
                     } else {
                     } else {
-                        let number = parseFloat(val);
-                        if (!isNaN(number)) {
-                            val = number;
+                        var isnum = /^\d+$/.test(val);
+                        if (isnum) {
+                            let number = parseFloat(val);
+                            if (!isNaN(number)) {
+                                val = number;
+                            }
                         }
                         }
                     }
                     }
                     currentConfig[camelKey] = val;
                     currentConfig[camelKey] = val;

+ 2 - 3
Viewer/src/index.ts

@@ -9,13 +9,12 @@ import { AbstractViewer } from './viewer/viewer';
  * An HTML-Based viewer for 3D models, based on BabylonJS and its extensions.
  * An HTML-Based viewer for 3D models, based on BabylonJS and its extensions.
  */
  */
 
 
+import { PromisePolyfill } from 'babylonjs';
 
 
-// load babylon and needed modules.
-import 'babylonjs';
+// load needed modules.
 import 'babylonjs-loaders';
 import 'babylonjs-loaders';
 import '../assets/pep.min';
 import '../assets/pep.min';
 
 
-import { PromisePolyfill } from 'babylonjs';
 
 
 import { InitTags } from './initializer';
 import { InitTags } from './initializer';
 
 

+ 167 - 0
Viewer/src/model/modelAnimation.ts

@@ -0,0 +1,167 @@
+import { AnimationGroup, Animatable, Skeleton, IDisposable } from "babylonjs";
+
+export enum AnimationPlayMode {
+    ONCE,
+    LOOP
+}
+
+export enum AnimationState {
+    INIT,
+    PLAYING,
+    PAUSED,
+    STOPPED,
+    ENDED
+}
+
+export interface IModelAnimation extends IDisposable {
+    readonly state: AnimationState;
+    readonly name: string;
+    readonly frames: number;
+    readonly currentFrame: number;
+    readonly fps: number;
+    speedRatio: number;
+    playMode: AnimationPlayMode;
+    start();
+    stop();
+    pause();
+    reset();
+    restart();
+    goToFrame(frameNumber: number);
+}
+
+export class GroupModelAnimation implements IModelAnimation {
+
+    private _playMode: AnimationPlayMode;
+    private _state: AnimationState;
+
+    constructor(private _animationGroup: AnimationGroup) {
+        this._state = AnimationState.INIT;
+        this._playMode = AnimationPlayMode.LOOP;
+
+        this._animationGroup.onAnimationEndObservable.add(() => {
+            this.stop();
+            this._state = AnimationState.ENDED;
+        })
+    }
+
+    public get name() {
+        return this._animationGroup.name;
+    }
+
+    public get state() {
+        return this._state;
+    }
+
+    /**
+     * Gets or sets the speed ratio to use for all animations
+     */
+    public get speedRatio(): number {
+        return this._animationGroup.speedRatio;
+    }
+
+    /**
+     * Gets or sets the speed ratio to use for all animations
+     */
+    public set speedRatio(value: number) {
+        this._animationGroup.speedRatio = value;
+    }
+
+    public get frames(): number {
+        let animationFrames = this._animationGroup.targetedAnimations.map(ta => {
+            let keys = ta.animation.getKeys();
+            return keys[keys.length - 1].frame;
+        });
+        return Math.max.apply(null, animationFrames);
+    }
+
+    public get currentFrame(): number {
+        // get the first currentFrame found
+        for (let i = 0; i < this._animationGroup.animatables.length; ++i) {
+            let animatable: Animatable = this._animationGroup.animatables[i];
+            let animations = animatable.getAnimations();
+            if (!animations || !animations.length) {
+                continue;
+            }
+            for (let idx = 0; idx < animations.length; ++idx) {
+                if (animations[idx].currentFrame) {
+                    return animations[idx].currentFrame;
+                }
+            }
+        }
+        return 0;
+    }
+
+    public get fps(): number {
+        // get the first currentFrame found
+        for (let i = 0; i < this._animationGroup.animatables.length; ++i) {
+            let animatable: Animatable = this._animationGroup.animatables[i];
+            let animations = animatable.getAnimations();
+            if (!animations || !animations.length) {
+                continue;
+            }
+            for (let idx = 0; idx < animations.length; ++idx) {
+                if (animations[idx].animation && animations[idx].animation.framePerSecond) {
+                    return animations[idx].animation.framePerSecond;
+                }
+            }
+        }
+        return 0;
+    }
+
+    public get playMode(): AnimationPlayMode {
+        return this._playMode;
+    }
+
+    public set playMode(value: AnimationPlayMode) {
+        if (value === this._playMode) {
+            return;
+        }
+
+        this._playMode = value;
+
+        if (this.state === AnimationState.PLAYING) {
+            this._animationGroup.play(this._playMode === AnimationPlayMode.LOOP);
+        } else {
+            this._animationGroup.reset();
+            this._state = AnimationState.INIT;
+        }
+    }
+
+    reset() {
+        this._animationGroup.reset();
+    }
+
+    restart() {
+        this._animationGroup.restart();
+    }
+
+    goToFrame(frameNumber: number) {
+        // this._animationGroup.goToFrame(frameNumber);
+        this._animationGroup['_animatables'].forEach(a => {
+            a.goToFrame(frameNumber);
+        })
+    }
+
+    public start() {
+        this._animationGroup.start(this.playMode === AnimationPlayMode.LOOP, this.speedRatio);
+        if (this._animationGroup.isStarted) {
+            this._state = AnimationState.PLAYING;
+        }
+    }
+
+    pause() {
+        this._animationGroup.pause();
+        this._state = AnimationState.PAUSED;
+    }
+
+    public stop() {
+        this._animationGroup.stop();
+        if (!this._animationGroup.isStarted) {
+            this._state = AnimationState.STOPPED;
+        }
+    }
+
+    public dispose() {
+        this._animationGroup.dispose();
+    }
+}

+ 142 - 0
Viewer/src/model/viewerModel.ts

@@ -0,0 +1,142 @@
+import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, AnimationGroup, Animatable, AbstractMesh, Tools, Scene, SceneLoader, Observable, SceneLoaderProgressEvent, Tags, ParticleSystem, Skeleton, IDisposable, Nullable, Animation } from "babylonjs";
+import { IModelConfiguration } from "../configuration/configuration";
+import { IModelAnimation, GroupModelAnimation, AnimationPlayMode } from "./modelAnimation";
+
+export class ViewerModel implements IDisposable {
+
+    public loader: ISceneLoaderPlugin | ISceneLoaderPluginAsync;
+
+    private _animations: Array<IModelAnimation>;
+    public meshes: Array<AbstractMesh>;
+    public particleSystems: Array<ParticleSystem>;
+    public skeletons: Array<Skeleton>;
+    public currentAnimation: IModelAnimation;
+
+    public onLoadedObservable: Observable<ViewerModel>;
+    public onLoadProgressObservable: Observable<SceneLoaderProgressEvent>;
+    public onLoadErrorObservable: Observable<{ message: string; exception: any }>;
+
+    constructor(private _modelConfiguration: IModelConfiguration, private _scene: Scene, disableAutoLoad = false) {
+        this.onLoadedObservable = new Observable();
+        this.onLoadErrorObservable = new Observable();
+        this.onLoadProgressObservable = new Observable();
+
+        this._animations = [];
+
+        if (!disableAutoLoad) {
+            this._initLoad();
+        }
+    }
+
+    public load() {
+        if (this.loader) {
+            Tools.Error("Model was already loaded or in the process of loading.");
+        } else {
+            this._initLoad();
+        }
+    }
+
+    //public getAnimations() {
+    //    return this._animations;
+    //}
+
+    public getAnimationNames() {
+        return this._animations.map(a => a.name);
+    }
+
+    protected _getAnimationByName(name: string): Nullable<IModelAnimation> {
+        // can't use .find, noe available on IE
+        let filtered = this._animations.filter(a => a.name === name);
+        // what the next line means - if two animations have the same name, they will not be returned!
+        if (filtered.length === 1) {
+            return filtered[0];
+        } else {
+            return null;
+        }
+    }
+
+    public playAnimation(name: string): IModelAnimation {
+        let animation = this._getAnimationByName(name);
+        if (animation) {
+            if (this.currentAnimation) {
+                this.currentAnimation.stop();
+            }
+            this.currentAnimation = animation;
+            animation.start();
+            return animation;
+        } else {
+            throw new Error("animation not found - " + name);
+        }
+    }
+
+    private _initLoad() {
+        if (!this._modelConfiguration || !this._modelConfiguration.url) {
+            return Tools.Error("No model URL to load.");
+        }
+        let parts = this._modelConfiguration.url.split('/');
+        let filename = parts.pop() || this._modelConfiguration.url;
+        let base = parts.length ? parts.join('/') + '/' : './';
+
+        let plugin = this._modelConfiguration.loader;
+
+        //temp solution for animation group handling
+        let animationsArray = this._scene.animationGroups.slice();
+
+        this.loader = SceneLoader.ImportMesh(undefined, base, filename, this._scene, (meshes, particleSystems, skeletons) => {
+            meshes.forEach(mesh => {
+                Tags.AddTagsTo(mesh, "viewerMesh");
+            });
+            this.meshes = meshes;
+            this.particleSystems = particleSystems;
+            this.skeletons = skeletons;
+
+            // check if this is a gltf loader and load the animations
+            if (this.loader.name === 'gltf') {
+                this._scene.animationGroups.forEach(ag => {
+                    // add animations that didn't exist before
+                    if (animationsArray.indexOf(ag) === -1) {
+                        this._animations.push(new GroupModelAnimation(ag));
+                    }
+                })
+            } else {
+                skeletons.forEach((skeleton, idx) => {
+                    let ag = new BABYLON.AnimationGroup("animation-" + idx, this._scene);
+                    skeleton.getAnimatables().forEach(a => {
+                        if (a.animations[0]) {
+                            ag.addTargetedAnimation(a.animations[0], a);
+                        }
+                    });
+                    this._animations.push(new GroupModelAnimation(ag));
+                });
+            }
+
+            if (this._modelConfiguration.animation) {
+                if (this._modelConfiguration.animation.playOnce) {
+                    this._animations.forEach(a => {
+                        a.playMode = AnimationPlayMode.ONCE;
+                    });
+                }
+                if (this._modelConfiguration.animation.autoStart && this._animations.length) {
+                    let animationName = this._modelConfiguration.animation.autoStart === true ?
+                        this._animations[0].name : this._modelConfiguration.animation.autoStart;
+                    this.playAnimation(animationName);
+                }
+            }
+
+            this.onLoadedObservable.notifyObserversWithPromise(this);
+        }, (progressEvent) => {
+            this.onLoadProgressObservable.notifyObserversWithPromise(progressEvent);
+        }, (e, m, exception) => {
+            this.onLoadErrorObservable.notifyObserversWithPromise({ message: m, exception: exception });
+        }, plugin)!;
+
+        this.loader['animationStartMode'] = 0;
+    }
+
+    public dispose() {
+        this.particleSystems.forEach(ps => ps.dispose());
+        this.skeletons.forEach(s => s.dispose());
+        this._animations.forEach(ag => ag.dispose());
+        this.meshes.forEach(m => m.dispose());
+    }
+}

+ 9 - 112
Viewer/src/viewer/defaultViewer.ts

@@ -5,6 +5,7 @@ import { Template, EventCallback } from './../templateManager';
 import { AbstractViewer } from './viewer';
 import { AbstractViewer } from './viewer';
 import { SpotLight, MirrorTexture, Plane, ShadowGenerator, Texture, BackgroundMaterial, Observable, ShadowLight, CubeTexture, BouncingBehavior, FramingBehavior, Behavior, Light, Engine, Scene, AutoRotationBehavior, AbstractMesh, Quaternion, StandardMaterial, ArcRotateCamera, ImageProcessingConfiguration, Color3, Vector3, SceneLoader, Mesh, HemisphericLight } from 'babylonjs';
 import { SpotLight, MirrorTexture, Plane, ShadowGenerator, Texture, BackgroundMaterial, Observable, ShadowLight, CubeTexture, BouncingBehavior, FramingBehavior, Behavior, Light, Engine, Scene, AutoRotationBehavior, AbstractMesh, Quaternion, StandardMaterial, ArcRotateCamera, ImageProcessingConfiguration, Color3, Vector3, SceneLoader, Mesh, HemisphericLight } from 'babylonjs';
 import { CameraBehavior } from '../interfaces';
 import { CameraBehavior } from '../interfaces';
+import { ViewerModel } from '../model/viewerModel';
 
 
 export class DefaultViewer extends AbstractViewer {
 export class DefaultViewer extends AbstractViewer {
 
 
@@ -103,8 +104,8 @@ export class DefaultViewer extends AbstractViewer {
         this.containerElement.style.display = 'flex';
         this.containerElement.style.display = 'flex';
     }
     }
 
 
-    protected configureModel(modelConfiguration: Partial<IModelConfiguration>, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
-        super.configureModel(modelConfiguration, focusMeshes);
+    protected configureModel(modelConfiguration: Partial<IModelConfiguration>, model: ViewerModel) {
+        super.configureModel(modelConfiguration, model);
 
 
         let navbar = this.templateManager.getTemplate('navBar');
         let navbar = this.templateManager.getTemplate('navBar');
         if (!navbar) return;
         if (!navbar) return;
@@ -131,17 +132,17 @@ export class DefaultViewer extends AbstractViewer {
         }
         }
     }
     }
 
 
-    public loadModel(model: any = this.configuration.model): Promise<Scene> {
+    public loadModel(model: any = this.configuration.model): Promise<ViewerModel> {
         this.showLoadingScreen();
         this.showLoadingScreen();
         return super.loadModel(model, true).catch((error) => {
         return super.loadModel(model, true).catch((error) => {
             console.log(error);
             console.log(error);
             this.hideLoadingScreen();
             this.hideLoadingScreen();
             this.showOverlayScreen('error');
             this.showOverlayScreen('error');
-            return this.scene;
+            return Promise.reject(error);
         });
         });
     }
     }
 
 
-    private onModelLoaded = (meshes: Array<AbstractMesh>) => {
+    private onModelLoaded = (model: ViewerModel) => {
         // with a short timeout, making sure everything is there already.
         // with a short timeout, making sure everything is there already.
         let hideLoadingDelay = 500;
         let hideLoadingDelay = 500;
         if (this.configuration.lab && this.configuration.lab.hideLoadingDelay !== undefined) {
         if (this.configuration.lab && this.configuration.lab.hideLoadingDelay !== undefined) {
@@ -151,113 +152,9 @@ export class DefaultViewer extends AbstractViewer {
             this.hideLoadingScreen();
             this.hideLoadingScreen();
         }, hideLoadingDelay);
         }, hideLoadingDelay);
 
 
-        meshes[0].rotation.y += Math.PI;
-
-        return; //this.initEnvironment(meshes);
+        return;
     }
     }
 
 
-    /*protected initEnvironment(focusMeshes: Array<AbstractMesh> = []): Promise<Scene> {
-        if (this.configuration.skybox) {
-            // Define a general environment textue
-            let texture;
-            // this is obligatory, but still - making sure it is there.
-            if (this.configuration.skybox.cubeTexture) {
-                if (typeof this.configuration.skybox.cubeTexture.url === 'string') {
-                    texture = CubeTexture.CreateFromPrefilteredData(this.configuration.skybox.cubeTexture.url, this.scene);
-                } else {
-                    texture = CubeTexture.CreateFromImages(this.configuration.skybox.cubeTexture.url, this.scene, this.configuration.skybox.cubeTexture.noMipMap);
-                }
-            }
-            if (texture) {
-                this.extendClassWithConfig(texture, this.configuration.skybox.cubeTexture);
-
-                let scale = this.configuration.skybox.scale || this.scene.activeCamera && (this.scene.activeCamera.maxZ - this.scene.activeCamera.minZ) / 2 || 1;
-
-                let box = this.scene.createDefaultSkybox(texture, this.configuration.skybox.pbr, scale, this.configuration.skybox.blur);
-
-                // before extending, set the material's imageprocessing configuration object, if needed:
-                if (this.configuration.skybox.material && this.configuration.skybox.material.imageProcessingConfiguration && box) {
-                    (<StandardMaterial>box.material).imageProcessingConfiguration = new ImageProcessingConfiguration();
-                }
-
-                this.extendClassWithConfig(box, this.configuration.skybox);
-
-                box && focusMeshes.push(box);
-            }
-        }
-
-        if (this.configuration.ground) {
-            let groundConfig = (typeof this.configuration.ground === 'boolean') ? {} : this.configuration.ground;
-
-            let groundSize = groundConfig.size || (this.configuration.skybox && this.configuration.skybox.scale) || 3000;
-
-            let ground = Mesh.CreatePlane("BackgroundPlane", groundSize, this.scene);
-            let backgroundMaterial = new BackgroundMaterial('groundmat', this.scene);
-            ground.rotation.x = Math.PI / 2; // Face up by default.
-            ground.receiveShadows = groundConfig.receiveShadows || false;
-
-            // position the ground correctly
-            let groundPosition = focusMeshes[0].getHierarchyBoundingVectors().min.y;
-            ground.position.y = groundPosition;
-
-            // default values
-            backgroundMaterial.alpha = 0.9;
-            backgroundMaterial.alphaMode = Engine.ALPHA_PREMULTIPLIED_PORTERDUFF;
-            backgroundMaterial.shadowLevel = 0.5;
-            backgroundMaterial.primaryLevel = 1;
-            backgroundMaterial.primaryColor = new Color3(0.2, 0.2, 0.3).toLinearSpace().scale(3);
-            backgroundMaterial.secondaryLevel = 0;
-            backgroundMaterial.tertiaryLevel = 0;
-            backgroundMaterial.useRGBColor = false;
-            backgroundMaterial.enableNoise = true;
-
-            // if config provided, extend the default values
-            if (groundConfig.material) {
-                this.extendClassWithConfig(ground, ground.material);
-            }
-
-            ground.material = backgroundMaterial;
-            if (this.configuration.ground === true || groundConfig.shadowOnly) {
-                // shadow only:
-                ground.receiveShadows = true;
-                const diffuseTexture = new Texture("https://assets.babylonjs.com/environments/backgroundGround.png", this.scene);
-                diffuseTexture.gammaSpace = false;
-                diffuseTexture.hasAlpha = true;
-                backgroundMaterial.diffuseTexture = diffuseTexture;
-            } else if (groundConfig.mirror) {
-                var mirror = new MirrorTexture("mirror", 512, this.scene);
-                mirror.mirrorPlane = new Plane(0, -1, 0, 0);
-                mirror.renderList = mirror.renderList || [];
-                focusMeshes.length && focusMeshes.forEach(m => {
-                    m && mirror.renderList && mirror.renderList.push(m);
-                });
-
-                backgroundMaterial.reflectionTexture = mirror;
-            } else {
-                if (groundConfig.material) {
-                    if (groundConfig.material.diffuseTexture) {
-                        const diffuseTexture = new Texture(groundConfig.material.diffuseTexture, this.scene);
-                        backgroundMaterial.diffuseTexture = diffuseTexture;
-                    }
-                }
-                // ground.material = new StandardMaterial('groundmat', this.scene);
-            }
-            //default configuration
-            if (this.configuration.ground === true) {
-                ground.receiveShadows = true;
-                if (ground.material)
-                    ground.material.alpha = 0.4;
-            }
-
-
-
-
-            this.extendClassWithConfig(ground, groundConfig);
-        }
-
-        return Promise.resolve(this.scene);
-    }*/
-
     public showOverlayScreen(subScreen: string) {
     public showOverlayScreen(subScreen: string) {
         let template = this.templateManager.getTemplate('overlay');
         let template = this.templateManager.getTemplate('overlay');
         if (!template) return Promise.resolve('Overlay template not found');
         if (!template) return Promise.resolve('Overlay template not found');
@@ -345,8 +242,8 @@ export class DefaultViewer extends AbstractViewer {
         }));
         }));
     }
     }
 
 
-    protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean } = {}, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
-        super.configureLights(lightsConfiguration, focusMeshes);
+    protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean } = {}, model: ViewerModel) {
+        super.configureLights(lightsConfiguration, model);
         // labs feature - flashlight
         // labs feature - flashlight
         if (this.configuration.lab && this.configuration.lab.flashlight) {
         if (this.configuration.lab && this.configuration.lab.flashlight) {
             let pointerPosition = BABYLON.Vector3.Zero();
             let pointerPosition = BABYLON.Vector3.Zero();

+ 50 - 63
Viewer/src/viewer/viewer.ts

@@ -6,6 +6,7 @@ import { ViewerConfiguration, ISceneConfiguration, ISceneOptimizerConfiguration,
 
 
 import * as deepmerge from '../../assets/deepmerge.min.js';
 import * as deepmerge from '../../assets/deepmerge.min.js';
 import { CameraBehavior } from 'src/interfaces';
 import { CameraBehavior } from 'src/interfaces';
+import { ViewerModel } from '../model/viewerModel';
 
 
 export abstract class AbstractViewer {
 export abstract class AbstractViewer {
 
 
@@ -16,12 +17,10 @@ export abstract class AbstractViewer {
     public camera: ArcRotateCamera;
     public camera: ArcRotateCamera;
     public sceneOptimizer: SceneOptimizer;
     public sceneOptimizer: SceneOptimizer;
     public baseId: string;
     public baseId: string;
+    public models: Array<ViewerModel>;
 
 
     /**
     /**
      * The last loader used to load a model. 
      * The last loader used to load a model. 
-     * 
-     * @type {(ISceneLoaderPlugin | ISceneLoaderPluginAsync)}
-     * @memberof AbstractViewer
      */
      */
     public lastUsedLoader: ISceneLoaderPlugin | ISceneLoaderPluginAsync;
     public lastUsedLoader: ISceneLoaderPlugin | ISceneLoaderPluginAsync;
 
 
@@ -42,7 +41,7 @@ export abstract class AbstractViewer {
     // observables
     // observables
     public onSceneInitObservable: Observable<Scene>;
     public onSceneInitObservable: Observable<Scene>;
     public onEngineInitObservable: Observable<Engine>;
     public onEngineInitObservable: Observable<Engine>;
-    public onModelLoadedObservable: Observable<AbstractMesh[]>;
+    public onModelLoadedObservable: Observable<ViewerModel>;
     public onModelLoadProgressObservable: Observable<SceneLoaderProgressEvent>;
     public onModelLoadProgressObservable: Observable<SceneLoaderProgressEvent>;
     public onModelLoadErrorObservable: Observable<{ message: string; exception: any }>;
     public onModelLoadErrorObservable: Observable<{ message: string; exception: any }>;
     public onLoaderInitObservable: Observable<ISceneLoaderPlugin | ISceneLoaderPluginAsync>;
     public onLoaderInitObservable: Observable<ISceneLoaderPlugin | ISceneLoaderPluginAsync>;
@@ -69,6 +68,7 @@ export abstract class AbstractViewer {
         this.onLoaderInitObservable = new Observable();
         this.onLoaderInitObservable = new Observable();
 
 
         this.registeredOnBeforerenderFunctions = [];
         this.registeredOnBeforerenderFunctions = [];
+        this.models = [];
 
 
         // add this viewer to the viewer manager
         // add this viewer to the viewer manager
         viewerManager.addViewer(this);
         viewerManager.addViewer(this);
@@ -397,7 +397,9 @@ export abstract class AbstractViewer {
         }
         }
     }
     }
 
 
-    protected configureCamera(cameraConfig: ICameraConfiguration, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
+    protected configureCamera(cameraConfig: ICameraConfiguration, model?: ViewerModel) {
+        let focusMeshes = model ? model.meshes : this.scene.meshes;
+
         if (!this.scene.activeCamera) {
         if (!this.scene.activeCamera) {
             this.scene.createDefaultCamera(true, true, true);
             this.scene.createDefaultCamera(true, true, true);
             this.camera = <ArcRotateCamera>this.scene.activeCamera!;
             this.camera = <ArcRotateCamera>this.scene.activeCamera!;
@@ -430,7 +432,8 @@ export abstract class AbstractViewer {
             this.camera.upperRadiusLimit = sceneDiagonalLenght * 3;
             this.camera.upperRadiusLimit = sceneDiagonalLenght * 3;
     }
     }
 
 
-    protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean } = {}, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
+    protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean } = {}, model?: ViewerModel) {
+        let focusMeshes = model ? model.meshes : this.scene.meshes;
         // sanity check!
         // sanity check!
         if (!Object.keys(lightsConfiguration).length) return;
         if (!Object.keys(lightsConfiguration).length) return;
 
 
@@ -516,17 +519,10 @@ export abstract class AbstractViewer {
                 }
                 }
             }
             }
         });
         });
-
-        // remove the unneeded lights
-        /*lightsAvailable.forEach(name => {
-            let light = this.scene.getLightByName(name);
-            if (light && !Tags.MatchesQuery(light, "fixed")) {
-                light.dispose();
-            }
-        });*/
     }
     }
 
 
-    protected configureModel(modelConfiguration: Partial<IModelConfiguration>, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
+    protected configureModel(modelConfiguration: Partial<IModelConfiguration>, model?: ViewerModel) {
+        let focusMeshes = model ? model.meshes : this.scene.meshes;
         let meshesWithNoParent: Array<AbstractMesh> = focusMeshes.filter(m => !m.parent);
         let meshesWithNoParent: Array<AbstractMesh> = focusMeshes.filter(m => !m.parent);
         let updateMeshesWithNoParent = (variable: string, value: any, param?: string) => {
         let updateMeshesWithNoParent = (variable: string, value: any, param?: string) => {
             meshesWithNoParent.forEach(mesh => {
             meshesWithNoParent.forEach(mesh => {
@@ -665,7 +661,7 @@ export abstract class AbstractViewer {
                 return this.onEngineInitObservable.notifyObserversWithPromise(engine);
                 return this.onEngineInitObservable.notifyObserversWithPromise(engine);
             }).then(() => {
             }).then(() => {
                 if (autoLoadModel) {
                 if (autoLoadModel) {
-                    return this.loadModel();
+                    return this.loadModel().then(() => { return this.scene });
                 } else {
                 } else {
                     return this.scene || this.initScene();
                     return this.scene || this.initScene();
                 }
                 }
@@ -673,8 +669,6 @@ export abstract class AbstractViewer {
                 return this.onSceneInitObservable.notifyObserversWithPromise(scene);
                 return this.onSceneInitObservable.notifyObserversWithPromise(scene);
             }).then(() => {
             }).then(() => {
                 return this.onInitDoneObservable.notifyObserversWithPromise(this);
                 return this.onInitDoneObservable.notifyObserversWithPromise(this);
-            }).then(() => {
-                return this;
             });
             });
         })
         })
     }
     }
@@ -747,88 +741,82 @@ export abstract class AbstractViewer {
     private isLoading: boolean;
     private isLoading: boolean;
     private nextLoading: Function;
     private nextLoading: Function;
 
 
-    public loadModel(model: any = this.configuration.model, clearScene: boolean = true): Promise<Scene> {
+    public loadModel(modelConfig: any = this.configuration.model, clearScene: boolean = true): Promise<ViewerModel> {
         // no model was provided? Do nothing!
         // no model was provided? Do nothing!
-        let modelUrl = (typeof model === 'string') ? model : model.url;
+        let modelUrl = (typeof modelConfig === 'string') ? modelConfig : modelConfig.url;
         if (!modelUrl) {
         if (!modelUrl) {
-            return Promise.resolve(this.scene);
+            return Promise.reject("no model configuration found");
         }
         }
         if (this.isLoading) {
         if (this.isLoading) {
             //another model is being model. Wait for it to finish, trigger the load afterwards
             //another model is being model. Wait for it to finish, trigger the load afterwards
-            this.nextLoading = () => {
+            /*this.nextLoading = () => {
                 delete this.nextLoading;
                 delete this.nextLoading;
-                this.loadModel(model, clearScene);
-            }
-            return Promise.resolve(this.scene);
+                return this.loadModel(modelConfig, clearScene);
+            }*/
+            return Promise.reject("sanother model is curently being loaded.");
         }
         }
         this.isLoading = true;
         this.isLoading = true;
-        if ((typeof model === 'string')) {
+        if ((typeof modelConfig === 'string')) {
             if (this.configuration.model && typeof this.configuration.model === 'object') {
             if (this.configuration.model && typeof this.configuration.model === 'object') {
-                this.configuration.model.url = model;
+                this.configuration.model.url = modelConfig;
             }
             }
         } else {
         } else {
             if (this.configuration.model) {
             if (this.configuration.model) {
-                deepmerge(this.configuration.model, model)
+                deepmerge(this.configuration.model, modelConfig)
             } else {
             } else {
-                this.configuration.model = model;
+                this.configuration.model = modelConfig;
             }
             }
         }
         }
 
 
-        let parts = modelUrl.split('/');
-        let filename = parts.pop();
-        let base = parts.join('/') + '/';
-        let plugin = (typeof model === 'string') ? undefined : model.loader;
-
         return Promise.resolve(this.scene).then((scene) => {
         return Promise.resolve(this.scene).then((scene) => {
             if (!scene) return this.initScene();
             if (!scene) return this.initScene();
 
 
             if (clearScene) {
             if (clearScene) {
-                scene.meshes.forEach(mesh => {
-                    if (Tags.MatchesQuery(mesh, "viewerMesh")) {
-                        mesh.dispose();
-                    }
-                });
+                this.models.forEach(m => m.dispose());
+                this.models.length = 0;
             }
             }
-            return scene!;
+            return scene;
         }).then(() => {
         }).then(() => {
-            return new Promise<Array<AbstractMesh>>((resolve, reject) => {
-                this.lastUsedLoader = SceneLoader.ImportMesh(undefined, base, filename, this.scene, (meshes) => {
-                    meshes.forEach(mesh => {
-                        Tags.AddTagsTo(mesh, "viewerMesh");
-                    });
-                    resolve(meshes);
-                }, (progressEvent) => {
-                    this.onModelLoadProgressObservable.notifyObserversWithPromise(progressEvent);
-                }, (e, m, exception) => {
-                    // console.log(m, exception);
-                    this.onModelLoadErrorObservable.notifyObserversWithPromise({ message: m, exception: exception }).then(() => {
-                        reject(exception);
+            return new Promise<ViewerModel>((resolve, reject) => {
+                // at this point, configuration.model is an object, not a string
+                let model = new ViewerModel(<IModelConfiguration>this.configuration.model, this.scene);
+                this.models.push(model);
+                this.lastUsedLoader = model.loader;
+                model.onLoadedObservable.add((model) => {
+                    resolve(model);
+                });
+                model.onLoadErrorObservable.add((errorObject) => {
+                    this.onModelLoadErrorObservable.notifyObserversWithPromise(errorObject).then(() => {
+                        reject(errorObject.exception);
                     });
                     });
-                }, plugin)!;
+                });
+                model.onLoadProgressObservable.add((progressEvent) => {
+                    return this.onModelLoadProgressObservable.notifyObserversWithPromise(progressEvent);
+                });
                 this.onLoaderInitObservable.notifyObserversWithPromise(this.lastUsedLoader);
                 this.onLoaderInitObservable.notifyObserversWithPromise(this.lastUsedLoader);
             });
             });
-        }).then((meshes: Array<AbstractMesh>) => {
-            return this.onModelLoadedObservable.notifyObserversWithPromise(meshes)
+        }).then((model: ViewerModel) => {
+            return this.onModelLoadedObservable.notifyObserversWithPromise(model)
                 .then(() => {
                 .then(() => {
                     // update the models' configuration
                     // update the models' configuration
-                    this.configureModel(this.configuration.model || model, meshes);
+                    this.configureModel(this.configuration.model || modelConfig, model);
                     this.configureLights(this.configuration.lights);
                     this.configureLights(this.configuration.lights);
 
 
                     if (this.configuration.camera) {
                     if (this.configuration.camera) {
-                        this.configureCamera(this.configuration.camera, meshes);
+                        this.configureCamera(this.configuration.camera, model);
                     }
                     }
-                    return this.initEnvironment(meshes);
+                    return this.initEnvironment(model);
                 }).then(() => {
                 }).then(() => {
                     this.isLoading = false;
                     this.isLoading = false;
-                    if (this.nextLoading) {
+                    /*if (this.nextLoading) {
                         return this.nextLoading();
                         return this.nextLoading();
-                    }
-                    return this.scene;
+                    }*/
+                    return model;
                 });
                 });
         });
         });
     }
     }
 
 
-    protected initEnvironment(focusMeshes: Array<AbstractMesh> = this.scene.meshes): Promise<Scene> {
+    protected initEnvironment(model?: ViewerModel): Promise<Scene> {
         this.configureEnvironment(this.configuration.skybox, this.configuration.ground);
         this.configureEnvironment(this.configuration.skybox, this.configuration.ground);
 
 
         return Promise.resolve(this.scene);
         return Promise.resolve(this.scene);
@@ -944,7 +932,6 @@ export abstract class AbstractViewer {
             if (typeof behaviorConfig === "object") {
             if (typeof behaviorConfig === "object") {
                 this.extendClassWithConfig(behavior, behaviorConfig);
                 this.extendClassWithConfig(behavior, behaviorConfig);
             }
             }
-            //this.camera.addBehavior(behavior);
         }
         }
 
 
         // post attach configuration. Some functionalities require the attached camera.
         // post attach configuration. Some functionalities require the attached camera.

Plik diff jest za duży
+ 11744 - 11582
dist/preview release/babylon.d.ts


Plik diff jest za duży
+ 55 - 55
dist/preview release/babylon.js


Plik diff jest za duży
+ 1003 - 565
dist/preview release/babylon.max.js


Plik diff jest za duży
+ 56 - 56
dist/preview release/babylon.worker.js


Plik diff jest za duży
+ 11661 - 11499
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Plik diff jest za duży
+ 51 - 51
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


Plik diff jest za duży
+ 1062 - 568
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


Plik diff jest za duży
+ 1064 - 570
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js


Plik diff jest za duży
+ 1005 - 567
dist/preview release/es6.js


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

@@ -116,7 +116,7 @@ declare module BABYLON {
         private _onMaterialLoadedObserver;
         private _onMaterialLoadedObserver;
         onMaterialLoaded: (material: Material) => void;
         onMaterialLoaded: (material: Material) => void;
         /**
         /**
-         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
          */
          */
         readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         private _onAnimationGroupLoadedObserver;
         private _onAnimationGroupLoadedObserver;
@@ -143,7 +143,7 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
         /**
-         * Gets a promise that resolves when the asset to be completely loaded.
+         * Gets a promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          */
          */
         whenCompleteAsync(): Promise<void>;
         whenCompleteAsync(): Promise<void>;

+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -77,7 +77,7 @@ var BABYLON;
              */
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
             /**
-             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
              */
              */
             this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
             this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
             /**
             /**
@@ -184,7 +184,7 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         /**
         /**
-         * Gets a promise that resolves when the asset to be completely loaded.
+         * Gets a promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          */
          */
         GLTFFileLoader.prototype.whenCompleteAsync = function () {
         GLTFFileLoader.prototype.whenCompleteAsync = function () {

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

@@ -116,7 +116,7 @@ declare module BABYLON {
         private _onMaterialLoadedObserver;
         private _onMaterialLoadedObserver;
         onMaterialLoaded: (material: Material) => void;
         onMaterialLoaded: (material: Material) => void;
         /**
         /**
-         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
          */
          */
         readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         private _onAnimationGroupLoadedObserver;
         private _onAnimationGroupLoadedObserver;
@@ -143,7 +143,7 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
         /**
-         * Gets a promise that resolves when the asset to be completely loaded.
+         * Gets a promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          */
          */
         whenCompleteAsync(): Promise<void>;
         whenCompleteAsync(): Promise<void>;
@@ -321,12 +321,13 @@ declare module BABYLON.GLTF2 {
         private _createRootNode();
         private _createRootNode();
         private _loadNodesAsync(nodes);
         private _loadNodesAsync(nodes);
         _loadSceneAsync(context: string, scene: ILoaderScene): Promise<void>;
         _loadSceneAsync(context: string, scene: ILoaderScene): Promise<void>;
+        private _forEachNodeMesh(node, callback);
         private _getMeshes();
         private _getMeshes();
         private _getSkeletons();
         private _getSkeletons();
         private _startAnimations();
         private _startAnimations();
         _loadNodeAsync(context: string, node: ILoaderNode): Promise<void>;
         _loadNodeAsync(context: string, node: ILoaderNode): Promise<void>;
-        private _loadMeshAsync(context, node, mesh);
-        private _loadPrimitiveAsync(context, node, mesh, primitive);
+        private _loadMeshAsync(context, node, mesh, babylonMesh);
+        private _loadPrimitiveAsync(context, node, mesh, primitive, babylonMesh);
         private _loadVertexDataAsync(context, primitive, babylonMesh);
         private _loadVertexDataAsync(context, primitive, babylonMesh);
         private _createMorphTargets(context, node, mesh, primitive, babylonMesh);
         private _createMorphTargets(context, node, mesh, primitive, babylonMesh);
         private _loadMorphTargetsAsync(context, primitive, babylonMesh, babylonVertexData);
         private _loadMorphTargetsAsync(context, primitive, babylonMesh, babylonVertexData);

+ 78 - 60
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -77,7 +77,7 @@ var BABYLON;
              */
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
             /**
-             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
              */
              */
             this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
             this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
             /**
             /**
@@ -184,7 +184,7 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         /**
         /**
-         * Gets a promise that resolves when the asset to be completely loaded.
+         * Gets a promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          */
          */
         GLTFFileLoader.prototype.whenCompleteAsync = function () {
         GLTFFileLoader.prototype.whenCompleteAsync = function () {
@@ -572,7 +572,7 @@ var BABYLON;
                         nodes = names.map(function (name) {
                         nodes = names.map(function (name) {
                             var node = nodeMap_1[name];
                             var node = nodeMap_1[name];
                             if (!node) {
                             if (!node) {
-                                throw new Error("Failed to find node " + name);
+                                throw new Error("Failed to find node '" + name + "'");
                             }
                             }
                             return node;
                             return node;
                         });
                         });
@@ -728,7 +728,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error("Invalid coordinate system mode " + this.coordinateSystemMode);
+                        throw new Error("Invalid coordinate system mode (" + this.coordinateSystemMode + ")");
                     }
                     }
                 }
                 }
                 this.onMeshLoadedObservable.notifyObservers(this._rootBabylonMesh);
                 this.onMeshLoadedObservable.notifyObservers(this._rootBabylonMesh);
@@ -757,6 +757,17 @@ var BABYLON;
                 promises.push(this._loadAnimationsAsync());
                 promises.push(this._loadAnimationsAsync());
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
+            GLTFLoader.prototype._forEachNodeMesh = function (node, callback) {
+                if (node._babylonMesh) {
+                    callback(node._babylonMesh);
+                }
+                if (node._primitiveBabylonMeshes) {
+                    for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
+                        var babylonMesh = _a[_i];
+                        callback(babylonMesh);
+                    }
+                }
+            };
             GLTFLoader.prototype._getMeshes = function () {
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = new Array();
                 var meshes = new Array();
                 // Root mesh is always first.
                 // Root mesh is always first.
@@ -765,15 +776,9 @@ var BABYLON;
                 if (nodes) {
                 if (nodes) {
                     for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
                     for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
                         var node = nodes_2[_i];
                         var node = nodes_2[_i];
-                        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);
-                            }
-                        }
+                        this._forEachNodeMesh(node, function (mesh) {
+                            meshes.push(mesh);
+                        });
                     }
                     }
                 }
                 }
                 return meshes;
                 return meshes;
@@ -814,7 +819,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        BABYLON.Tools.Error("Invalid animation start mode " + this.animationStartMode);
+                        BABYLON.Tools.Error("Invalid animation start mode (" + this.animationStartMode + ")");
                         return;
                         return;
                     }
                     }
                 }
                 }
@@ -835,7 +840,7 @@ var BABYLON;
                 GLTFLoader._LoadTransform(node, babylonMesh);
                 GLTFLoader._LoadTransform(node, babylonMesh);
                 if (node.mesh != undefined) {
                 if (node.mesh != undefined) {
                     var mesh = GLTFLoader._GetProperty(context + "/mesh", this._gltf.meshes, node.mesh);
                     var mesh = GLTFLoader._GetProperty(context + "/mesh", this._gltf.meshes, node.mesh);
-                    promises.push(this._loadMeshAsync("#/meshes/" + mesh._index, node, mesh));
+                    promises.push(this._loadMeshAsync("#/meshes/" + mesh._index, node, mesh, babylonMesh));
                 }
                 }
                 if (node.children) {
                 if (node.children) {
                     for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
                     for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
@@ -847,37 +852,42 @@ var BABYLON;
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
-            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh) {
+            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh, babylonMesh) {
                 // TODO: instancing
                 // TODO: instancing
+                var _this = this;
                 var promises = new Array();
                 var promises = new Array();
                 var primitives = mesh.primitives;
                 var primitives = mesh.primitives;
                 if (!primitives || primitives.length === 0) {
                 if (!primitives || primitives.length === 0) {
                     throw new Error(context + ": Primitives are missing");
                     throw new Error(context + ": Primitives are missing");
                 }
                 }
                 GLTF2.ArrayItem.Assign(primitives);
                 GLTF2.ArrayItem.Assign(primitives);
-                for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
-                    var primitive = primitives_1[_i];
-                    promises.push(this._loadPrimitiveAsync(context + "/primitives/" + primitive._index, node, mesh, primitive));
+                if (primitives.length === 1) {
+                    var primitive = primitives[0];
+                    promises.push(this._loadPrimitiveAsync(context + "/primitives/" + primitive._index, node, mesh, primitive, babylonMesh));
+                }
+                else {
+                    node._primitiveBabylonMeshes = [];
+                    for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
+                        var primitive = primitives_1[_i];
+                        var primitiveBabylonMesh = new BABYLON.Mesh((mesh.name || babylonMesh.name) + "_" + primitive._index, this._babylonScene, babylonMesh);
+                        node._primitiveBabylonMeshes.push(babylonMesh);
+                        promises.push(this._loadPrimitiveAsync(context + "/primitives/" + primitive._index, node, mesh, primitive, primitiveBabylonMesh));
+                        this.onMeshLoadedObservable.notifyObservers(babylonMesh);
+                    }
                 }
                 }
                 if (node.skin != undefined) {
                 if (node.skin != undefined) {
                     var skin = GLTFLoader._GetProperty(context + "/skin", this._gltf.skins, node.skin);
                     var skin = GLTFLoader._GetProperty(context + "/skin", this._gltf.skins, node.skin);
                     promises.push(this._loadSkinAsync("#/skins/" + skin._index, node, mesh, skin));
                     promises.push(this._loadSkinAsync("#/skins/" + skin._index, node, mesh, skin));
                 }
                 }
                 return Promise.all(promises).then(function () {
                 return Promise.all(promises).then(function () {
-                    if (node._primitiveBabylonMeshes) {
-                        for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
-                            var primitiveBabylonMesh = _a[_i];
-                            primitiveBabylonMesh._refreshBoundingInfo(true);
-                        }
-                    }
+                    _this._forEachNodeMesh(node, function (babylonMesh) {
+                        babylonMesh._refreshBoundingInfo(true);
+                    });
                 });
                 });
             };
             };
-            GLTFLoader.prototype._loadPrimitiveAsync = function (context, node, mesh, primitive) {
+            GLTFLoader.prototype._loadPrimitiveAsync = function (context, node, mesh, primitive, babylonMesh) {
                 var _this = this;
                 var _this = this;
                 var promises = new Array();
                 var promises = new Array();
-                var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
-                node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
                 promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(function (babylonVertexData) {
                 promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(function (babylonVertexData) {
                     new BABYLON.Geometry(babylonMesh.name, _this._babylonScene, babylonVertexData, false, babylonMesh);
                     new BABYLON.Geometry(babylonMesh.name, _this._babylonScene, babylonVertexData, false, babylonMesh);
@@ -887,12 +897,11 @@ var BABYLON;
                     babylonMesh.material = this._getDefaultMaterial();
                     babylonMesh.material = this._getDefaultMaterial();
                 }
                 }
                 else {
                 else {
-                    var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
+                    var material = GLTFLoader._GetProperty(context + "/material}", this._gltf.materials, primitive.material);
                     promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
                     promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
                         babylonMesh.material = babylonMaterial;
                         babylonMesh.material = babylonMaterial;
                     }));
                     }));
                 }
                 }
-                this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
@@ -907,7 +916,7 @@ var BABYLON;
                 }
                 }
                 if (primitive.mode != undefined && primitive.mode !== 4 /* TRIANGLES */) {
                 if (primitive.mode != undefined && primitive.mode !== 4 /* TRIANGLES */) {
                     // TODO: handle other primitive modes
                     // TODO: handle other primitive modes
-                    throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
+                    throw new Error(context + ": Mode (" + primitive.mode + ") is not currently supported");
                 }
                 }
                 var promises = new Array();
                 var promises = new Array();
                 var babylonVertexData = new BABYLON.VertexData();
                 var babylonVertexData = new BABYLON.VertexData();
@@ -935,15 +944,15 @@ var BABYLON;
                     if (babylonMesh._delayInfo.indexOf(kind) === -1) {
                     if (babylonMesh._delayInfo.indexOf(kind) === -1) {
                         babylonMesh._delayInfo.push(kind);
                         babylonMesh._delayInfo.push(kind);
                     }
                     }
+                    if (attribute === "COLOR_0") {
+                        // Assume vertex color has alpha on the mesh. The alphaMode of the material controls whether the material should use alpha or not.
+                        babylonMesh.hasVertexAlpha = true;
+                    }
                     var accessor = GLTFLoader._GetProperty(context + "/attributes/" + attribute, _this._gltf.accessors, attributes[attribute]);
                     var accessor = GLTFLoader._GetProperty(context + "/attributes/" + attribute, _this._gltf.accessors, attributes[attribute]);
                     promises.push(_this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(function (data) {
                     promises.push(_this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(function (data) {
                         var attributeData = GLTFLoader._ConvertToFloat32Array(context, accessor, data);
                         var attributeData = GLTFLoader._ConvertToFloat32Array(context, accessor, data);
-                        if (attribute === "COLOR_0") {
-                            // Assume vertex color has alpha on the mesh. The alphaMode of the material controls whether the material should use alpha or not.
-                            babylonMesh.hasVertexAlpha = true;
-                            if (accessor.type === "VEC3") {
-                                attributeData = GLTFLoader._ConvertVec3ToVec4(context, attributeData);
-                            }
+                        if (attribute === "COLOR_0" && accessor.type === "VEC3") {
+                            attributeData = GLTFLoader._ConvertVec3ToVec4(context, attributeData);
                         }
                         }
                         babylonVertexData.set(attributeData, kind);
                         babylonVertexData.set(attributeData, kind);
                     }));
                     }));
@@ -1049,7 +1058,7 @@ var BABYLON;
                             break;
                             break;
                         }
                         }
                         default: {
                         default: {
-                            throw new Error(context + ": Invalid component type " + accessor.componentType);
+                            throw new Error(context + ": Invalid component type (" + accessor.componentType + ")");
                         }
                         }
                     }
                     }
                 }
                 }
@@ -1095,10 +1104,9 @@ var BABYLON;
             GLTFLoader.prototype._loadSkinAsync = function (context, node, mesh, skin) {
             GLTFLoader.prototype._loadSkinAsync = function (context, node, mesh, skin) {
                 var _this = this;
                 var _this = this;
                 var assignSkeleton = function () {
                 var assignSkeleton = function () {
-                    for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
-                        var babylonMesh = _a[_i];
+                    _this._forEachNodeMesh(node, function (babylonMesh) {
                         babylonMesh.skeleton = skin._babylonSkeleton;
                         babylonMesh.skeleton = skin._babylonSkeleton;
-                    }
+                    });
                     node._babylonMesh.parent = _this._rootBabylonMesh;
                     node._babylonMesh.parent = _this._rootBabylonMesh;
                     node._babylonMesh.position = BABYLON.Vector3.Zero();
                     node._babylonMesh.position = BABYLON.Vector3.Zero();
                     node._babylonMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     node._babylonMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
@@ -1194,6 +1202,7 @@ var BABYLON;
                 });
                 });
             };
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
+                var _this = this;
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
                 if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                 if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                     return Promise.resolve();
@@ -1224,7 +1233,7 @@ var BABYLON;
                             break;
                             break;
                         }
                         }
                         default: {
                         default: {
-                            throw new Error(context + ": Invalid target path " + channel.target.path);
+                            throw new Error(context + ": Invalid target path (" + channel.target.path + ")");
                         }
                         }
                     }
                     }
                     var outputBufferOffset = 0;
                     var outputBufferOffset = 0;
@@ -1316,11 +1325,10 @@ var BABYLON;
                                 value: key.value[targetIndex],
                                 value: key.value[targetIndex],
                                 outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                                 outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                             }); }));
                             }); }));
-                            for (var _i = 0, _a = targetNode._primitiveBabylonMeshes; _i < _a.length; _i++) {
-                                var babylonMesh = _a[_i];
+                            _this._forEachNodeMesh(targetNode, function (babylonMesh) {
                                 var morphTarget = babylonMesh.morphTargetManager.getTarget(targetIndex);
                                 var morphTarget = babylonMesh.morphTargetManager.getTarget(targetIndex);
                                 babylonAnimationGroup.addTargetedAnimation(babylonAnimation, morphTarget);
                                 babylonAnimationGroup.addTargetedAnimation(babylonAnimation, morphTarget);
-                            }
+                            });
                         };
                         };
                         for (var targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) {
                         for (var targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) {
                             _loop_1(targetIndex);
                             _loop_1(targetIndex);
@@ -1351,7 +1359,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error(context + ": Invalid interpolation " + sampler.interpolation);
+                        throw new Error(context + ": Invalid interpolation (" + sampler.interpolation + ")");
                     }
                     }
                 }
                 }
                 var inputData;
                 var inputData;
@@ -1436,12 +1444,12 @@ var BABYLON;
                                 return _this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
                                 return _this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
                             }
                             }
                             default: {
                             default: {
-                                throw new Error(context + ": Invalid component type " + accessor.componentType);
+                                throw new Error(context + ": Invalid component type (" + accessor.componentType + ")");
                             }
                             }
                         }
                         }
                     }
                     }
                     catch (e) {
                     catch (e) {
-                        throw new Error(context + ": " + e);
+                        throw new Error(context + ": " + e.messsage);
                     }
                     }
                 });
                 });
                 return accessor._data;
                 return accessor._data;
@@ -1605,7 +1613,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error(context + ": Invalid alpha mode " + material.alphaMode);
+                        throw new Error(context + ": Invalid alpha mode (" + material.alphaMode + ")");
                     }
                     }
                 }
                 }
             };
             };
@@ -1623,7 +1631,7 @@ var BABYLON;
                     }
                     }
                 }, function (message, exception) {
                 }, function (message, exception) {
                     if (!_this._disposed) {
                     if (!_this._disposed) {
-                        deferred.reject(new Error(context + ": " + (exception && exception.message) ? exception.message : message || "Failed to load texture"));
+                        deferred.reject(new Error(context + ": " + ((exception && exception.message) ? exception.message : message || "Failed to load texture")));
                     }
                     }
                 });
                 });
                 promises.push(deferred.promise);
                 promises.push(deferred.promise);
@@ -1727,7 +1735,7 @@ var BABYLON;
             };
             };
             GLTFLoader._GetProperty = function (context, array, index) {
             GLTFLoader._GetProperty = function (context, array, index) {
                 if (!array || index == undefined || !array[index]) {
                 if (!array || index == undefined || !array[index]) {
-                    throw new Error(context + ": Failed to find index " + index);
+                    throw new Error(context + ": Failed to find index (" + index + ")");
                 }
                 }
                 return array[index];
                 return array[index];
             };
             };
@@ -1739,7 +1747,7 @@ var BABYLON;
                     case 33648 /* MIRRORED_REPEAT */: return BABYLON.Texture.MIRROR_ADDRESSMODE;
                     case 33648 /* MIRRORED_REPEAT */: return BABYLON.Texture.MIRROR_ADDRESSMODE;
                     case 10497 /* REPEAT */: return BABYLON.Texture.WRAP_ADDRESSMODE;
                     case 10497 /* REPEAT */: return BABYLON.Texture.WRAP_ADDRESSMODE;
                     default:
                     default:
-                        BABYLON.Tools.Warn(context + ": Invalid texture wrap mode " + mode);
+                        BABYLON.Tools.Warn(context + ": Invalid texture wrap mode (" + mode + ")");
                         return BABYLON.Texture.WRAP_ADDRESSMODE;
                         return BABYLON.Texture.WRAP_ADDRESSMODE;
                 }
                 }
             };
             };
@@ -1756,13 +1764,13 @@ var BABYLON;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_NEAREST_MIPLINEAR;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_NEAREST_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                         default:
                         default:
-                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter " + minFilter);
+                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter (" + minFilter + ")");
                             return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                             return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                     }
                     }
                 }
                 }
                 else {
                 else {
                     if (magFilter !== 9728 /* NEAREST */) {
                     if (magFilter !== 9728 /* NEAREST */) {
-                        BABYLON.Tools.Warn(context + ": Invalid texture magnification filter " + magFilter);
+                        BABYLON.Tools.Warn(context + ": Invalid texture magnification filter (" + magFilter + ")");
                     }
                     }
                     switch (minFilter) {
                     switch (minFilter) {
                         case 9728 /* NEAREST */: return BABYLON.Texture.NEAREST_NEAREST;
                         case 9728 /* NEAREST */: return BABYLON.Texture.NEAREST_NEAREST;
@@ -1772,7 +1780,7 @@ var BABYLON;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_NEAREST_MIPLINEAR;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_NEAREST_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_LINEAR_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_LINEAR_MIPLINEAR;
                         default:
                         default:
-                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter " + minFilter);
+                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter (" + minFilter + ")");
                             return BABYLON.Texture.NEAREST_NEAREST_MIPNEAREST;
                             return BABYLON.Texture.NEAREST_NEAREST_MIPNEAREST;
                     }
                     }
                 }
                 }
@@ -1787,7 +1795,7 @@ var BABYLON;
                     case "MAT3": return 9;
                     case "MAT3": return 9;
                     case "MAT4": return 16;
                     case "MAT4": return 16;
                 }
                 }
-                throw new Error(context + ": Invalid type " + type);
+                throw new Error(context + ": Invalid type (" + type + ")");
             };
             };
             GLTFLoader._ValidateUri = function (uri) {
             GLTFLoader._ValidateUri = function (uri) {
                 return (BABYLON.Tools.IsBase64(uri) || uri.indexOf("..") === -1);
                 return (BABYLON.Tools.IsBase64(uri) || uri.indexOf("..") === -1);
@@ -1802,6 +1810,8 @@ var BABYLON;
                         if (babylonMaterial && babylonMeshes) {
                         if (babylonMaterial && babylonMeshes) {
                             for (var _b = 0, babylonMeshes_1 = babylonMeshes; _b < babylonMeshes_1.length; _b++) {
                             for (var _b = 0, babylonMeshes_1 = babylonMeshes; _b < babylonMeshes_1.length; _b++) {
                                 var babylonMesh = babylonMeshes_1[_b];
                                 var babylonMesh = babylonMeshes_1[_b];
+                                // Ensure nonUniformScaling is set if necessary.
+                                babylonMesh.computeWorldMatrix(true);
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                 if (this.useClipPlane) {
                                 if (this.useClipPlane) {
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
@@ -2001,7 +2011,10 @@ var BABYLON;
                             var promise = _this._loader._loadNodeAsync("#/nodes/" + nodeLOD._index, nodeLOD).then(function () {
                             var promise = _this._loader._loadNodeAsync("#/nodes/" + nodeLOD._index, nodeLOD).then(function () {
                                 if (indexLOD !== 0) {
                                 if (indexLOD !== 0) {
                                     var previousNodeLOD = nodeLODs[indexLOD - 1];
                                     var previousNodeLOD = nodeLODs[indexLOD - 1];
-                                    previousNodeLOD._babylonMesh.setEnabled(false);
+                                    if (previousNodeLOD._babylonMesh) {
+                                        previousNodeLOD._babylonMesh.dispose();
+                                        delete previousNodeLOD._babylonMesh;
+                                    }
                                 }
                                 }
                                 if (indexLOD !== nodeLODs.length - 1) {
                                 if (indexLOD !== nodeLODs.length - 1) {
                                     var nodeIndex = nodeLODs[indexLOD + 1]._index;
                                     var nodeIndex = nodeLODs[indexLOD + 1]._index;
@@ -2045,6 +2058,11 @@ var BABYLON;
                             var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
                             var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
                                 if (indexLOD !== 0) {
                                 if (indexLOD !== 0) {
                                     assign(materialLOD._babylonMaterial);
                                     assign(materialLOD._babylonMaterial);
+                                    var previousMaterialLOD = materialLODs[indexLOD - 1];
+                                    if (previousMaterialLOD._babylonMaterial) {
+                                        previousMaterialLOD._babylonMaterial.dispose();
+                                        delete previousMaterialLOD._babylonMaterial;
+                                    }
                                 }
                                 }
                                 if (indexLOD !== materialLODs.length - 1) {
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
@@ -2360,7 +2378,7 @@ var BABYLON;
                                 break;
                                 break;
                             }
                             }
                             default: {
                             default: {
-                                throw new Error(context + ": Invalid light type " + light.type);
+                                throw new Error(context + ": Invalid light type (" + light.type + ")");
                             }
                             }
                         }
                         }
                         babylonLight.diffuse = light.color ? BABYLON.Color3.FromArray(light.color) : BABYLON.Color3.White();
                         babylonLight.diffuse = light.color ? BABYLON.Color3.FromArray(light.color) : BABYLON.Color3.White();
@@ -2373,7 +2391,7 @@ var BABYLON;
                     get: function () {
                     get: function () {
                         var extensions = this._loader._gltf.extensions;
                         var extensions = this._loader._gltf.extensions;
                         if (!extensions || !extensions[this.name]) {
                         if (!extensions || !extensions[this.name]) {
-                            throw new Error("#/extensions: " + this.name + " not found");
+                            throw new Error("#/extensions: '" + this.name + "' not found");
                         }
                         }
                         var extension = extensions[this.name];
                         var extension = extensions[this.name];
                         return extension.lights;
                         return extension.lights;

Plik diff jest za duży
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


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

@@ -116,7 +116,7 @@ declare module BABYLON {
         private _onMaterialLoadedObserver;
         private _onMaterialLoadedObserver;
         onMaterialLoaded: (material: Material) => void;
         onMaterialLoaded: (material: Material) => void;
         /**
         /**
-         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
          */
          */
         readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         private _onAnimationGroupLoadedObserver;
         private _onAnimationGroupLoadedObserver;
@@ -143,7 +143,7 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
         /**
-         * Gets a promise that resolves when the asset to be completely loaded.
+         * Gets a promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          */
          */
         whenCompleteAsync(): Promise<void>;
         whenCompleteAsync(): Promise<void>;
@@ -877,12 +877,13 @@ declare module BABYLON.GLTF2 {
         private _createRootNode();
         private _createRootNode();
         private _loadNodesAsync(nodes);
         private _loadNodesAsync(nodes);
         _loadSceneAsync(context: string, scene: ILoaderScene): Promise<void>;
         _loadSceneAsync(context: string, scene: ILoaderScene): Promise<void>;
+        private _forEachNodeMesh(node, callback);
         private _getMeshes();
         private _getMeshes();
         private _getSkeletons();
         private _getSkeletons();
         private _startAnimations();
         private _startAnimations();
         _loadNodeAsync(context: string, node: ILoaderNode): Promise<void>;
         _loadNodeAsync(context: string, node: ILoaderNode): Promise<void>;
-        private _loadMeshAsync(context, node, mesh);
-        private _loadPrimitiveAsync(context, node, mesh, primitive);
+        private _loadMeshAsync(context, node, mesh, babylonMesh);
+        private _loadPrimitiveAsync(context, node, mesh, primitive, babylonMesh);
         private _loadVertexDataAsync(context, primitive, babylonMesh);
         private _loadVertexDataAsync(context, primitive, babylonMesh);
         private _createMorphTargets(context, node, mesh, primitive, babylonMesh);
         private _createMorphTargets(context, node, mesh, primitive, babylonMesh);
         private _loadMorphTargetsAsync(context, primitive, babylonMesh, babylonVertexData);
         private _loadMorphTargetsAsync(context, primitive, babylonMesh, babylonVertexData);

+ 78 - 60
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -77,7 +77,7 @@ var BABYLON;
              */
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
             /**
-             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
              */
              */
             this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
             this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
             /**
             /**
@@ -184,7 +184,7 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         /**
         /**
-         * Gets a promise that resolves when the asset to be completely loaded.
+         * Gets a promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          */
          */
         GLTFFileLoader.prototype.whenCompleteAsync = function () {
         GLTFFileLoader.prototype.whenCompleteAsync = function () {
@@ -2749,7 +2749,7 @@ var BABYLON;
                         nodes = names.map(function (name) {
                         nodes = names.map(function (name) {
                             var node = nodeMap_1[name];
                             var node = nodeMap_1[name];
                             if (!node) {
                             if (!node) {
-                                throw new Error("Failed to find node " + name);
+                                throw new Error("Failed to find node '" + name + "'");
                             }
                             }
                             return node;
                             return node;
                         });
                         });
@@ -2905,7 +2905,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error("Invalid coordinate system mode " + this.coordinateSystemMode);
+                        throw new Error("Invalid coordinate system mode (" + this.coordinateSystemMode + ")");
                     }
                     }
                 }
                 }
                 this.onMeshLoadedObservable.notifyObservers(this._rootBabylonMesh);
                 this.onMeshLoadedObservable.notifyObservers(this._rootBabylonMesh);
@@ -2934,6 +2934,17 @@ var BABYLON;
                 promises.push(this._loadAnimationsAsync());
                 promises.push(this._loadAnimationsAsync());
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
+            GLTFLoader.prototype._forEachNodeMesh = function (node, callback) {
+                if (node._babylonMesh) {
+                    callback(node._babylonMesh);
+                }
+                if (node._primitiveBabylonMeshes) {
+                    for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
+                        var babylonMesh = _a[_i];
+                        callback(babylonMesh);
+                    }
+                }
+            };
             GLTFLoader.prototype._getMeshes = function () {
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = new Array();
                 var meshes = new Array();
                 // Root mesh is always first.
                 // Root mesh is always first.
@@ -2942,15 +2953,9 @@ var BABYLON;
                 if (nodes) {
                 if (nodes) {
                     for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
                     for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
                         var node = nodes_2[_i];
                         var node = nodes_2[_i];
-                        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);
-                            }
-                        }
+                        this._forEachNodeMesh(node, function (mesh) {
+                            meshes.push(mesh);
+                        });
                     }
                     }
                 }
                 }
                 return meshes;
                 return meshes;
@@ -2991,7 +2996,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        BABYLON.Tools.Error("Invalid animation start mode " + this.animationStartMode);
+                        BABYLON.Tools.Error("Invalid animation start mode (" + this.animationStartMode + ")");
                         return;
                         return;
                     }
                     }
                 }
                 }
@@ -3012,7 +3017,7 @@ var BABYLON;
                 GLTFLoader._LoadTransform(node, babylonMesh);
                 GLTFLoader._LoadTransform(node, babylonMesh);
                 if (node.mesh != undefined) {
                 if (node.mesh != undefined) {
                     var mesh = GLTFLoader._GetProperty(context + "/mesh", this._gltf.meshes, node.mesh);
                     var mesh = GLTFLoader._GetProperty(context + "/mesh", this._gltf.meshes, node.mesh);
-                    promises.push(this._loadMeshAsync("#/meshes/" + mesh._index, node, mesh));
+                    promises.push(this._loadMeshAsync("#/meshes/" + mesh._index, node, mesh, babylonMesh));
                 }
                 }
                 if (node.children) {
                 if (node.children) {
                     for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
                     for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
@@ -3024,37 +3029,42 @@ var BABYLON;
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
-            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh) {
+            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh, babylonMesh) {
                 // TODO: instancing
                 // TODO: instancing
+                var _this = this;
                 var promises = new Array();
                 var promises = new Array();
                 var primitives = mesh.primitives;
                 var primitives = mesh.primitives;
                 if (!primitives || primitives.length === 0) {
                 if (!primitives || primitives.length === 0) {
                     throw new Error(context + ": Primitives are missing");
                     throw new Error(context + ": Primitives are missing");
                 }
                 }
                 GLTF2.ArrayItem.Assign(primitives);
                 GLTF2.ArrayItem.Assign(primitives);
-                for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
-                    var primitive = primitives_1[_i];
-                    promises.push(this._loadPrimitiveAsync(context + "/primitives/" + primitive._index, node, mesh, primitive));
+                if (primitives.length === 1) {
+                    var primitive = primitives[0];
+                    promises.push(this._loadPrimitiveAsync(context + "/primitives/" + primitive._index, node, mesh, primitive, babylonMesh));
+                }
+                else {
+                    node._primitiveBabylonMeshes = [];
+                    for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
+                        var primitive = primitives_1[_i];
+                        var primitiveBabylonMesh = new BABYLON.Mesh((mesh.name || babylonMesh.name) + "_" + primitive._index, this._babylonScene, babylonMesh);
+                        node._primitiveBabylonMeshes.push(babylonMesh);
+                        promises.push(this._loadPrimitiveAsync(context + "/primitives/" + primitive._index, node, mesh, primitive, primitiveBabylonMesh));
+                        this.onMeshLoadedObservable.notifyObservers(babylonMesh);
+                    }
                 }
                 }
                 if (node.skin != undefined) {
                 if (node.skin != undefined) {
                     var skin = GLTFLoader._GetProperty(context + "/skin", this._gltf.skins, node.skin);
                     var skin = GLTFLoader._GetProperty(context + "/skin", this._gltf.skins, node.skin);
                     promises.push(this._loadSkinAsync("#/skins/" + skin._index, node, mesh, skin));
                     promises.push(this._loadSkinAsync("#/skins/" + skin._index, node, mesh, skin));
                 }
                 }
                 return Promise.all(promises).then(function () {
                 return Promise.all(promises).then(function () {
-                    if (node._primitiveBabylonMeshes) {
-                        for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
-                            var primitiveBabylonMesh = _a[_i];
-                            primitiveBabylonMesh._refreshBoundingInfo(true);
-                        }
-                    }
+                    _this._forEachNodeMesh(node, function (babylonMesh) {
+                        babylonMesh._refreshBoundingInfo(true);
+                    });
                 });
                 });
             };
             };
-            GLTFLoader.prototype._loadPrimitiveAsync = function (context, node, mesh, primitive) {
+            GLTFLoader.prototype._loadPrimitiveAsync = function (context, node, mesh, primitive, babylonMesh) {
                 var _this = this;
                 var _this = this;
                 var promises = new Array();
                 var promises = new Array();
-                var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
-                node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
                 promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(function (babylonVertexData) {
                 promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(function (babylonVertexData) {
                     new BABYLON.Geometry(babylonMesh.name, _this._babylonScene, babylonVertexData, false, babylonMesh);
                     new BABYLON.Geometry(babylonMesh.name, _this._babylonScene, babylonVertexData, false, babylonMesh);
@@ -3064,12 +3074,11 @@ var BABYLON;
                     babylonMesh.material = this._getDefaultMaterial();
                     babylonMesh.material = this._getDefaultMaterial();
                 }
                 }
                 else {
                 else {
-                    var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
+                    var material = GLTFLoader._GetProperty(context + "/material}", this._gltf.materials, primitive.material);
                     promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
                     promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
                         babylonMesh.material = babylonMaterial;
                         babylonMesh.material = babylonMaterial;
                     }));
                     }));
                 }
                 }
-                this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
@@ -3084,7 +3093,7 @@ var BABYLON;
                 }
                 }
                 if (primitive.mode != undefined && primitive.mode !== 4 /* TRIANGLES */) {
                 if (primitive.mode != undefined && primitive.mode !== 4 /* TRIANGLES */) {
                     // TODO: handle other primitive modes
                     // TODO: handle other primitive modes
-                    throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
+                    throw new Error(context + ": Mode (" + primitive.mode + ") is not currently supported");
                 }
                 }
                 var promises = new Array();
                 var promises = new Array();
                 var babylonVertexData = new BABYLON.VertexData();
                 var babylonVertexData = new BABYLON.VertexData();
@@ -3112,15 +3121,15 @@ var BABYLON;
                     if (babylonMesh._delayInfo.indexOf(kind) === -1) {
                     if (babylonMesh._delayInfo.indexOf(kind) === -1) {
                         babylonMesh._delayInfo.push(kind);
                         babylonMesh._delayInfo.push(kind);
                     }
                     }
+                    if (attribute === "COLOR_0") {
+                        // Assume vertex color has alpha on the mesh. The alphaMode of the material controls whether the material should use alpha or not.
+                        babylonMesh.hasVertexAlpha = true;
+                    }
                     var accessor = GLTFLoader._GetProperty(context + "/attributes/" + attribute, _this._gltf.accessors, attributes[attribute]);
                     var accessor = GLTFLoader._GetProperty(context + "/attributes/" + attribute, _this._gltf.accessors, attributes[attribute]);
                     promises.push(_this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(function (data) {
                     promises.push(_this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(function (data) {
                         var attributeData = GLTFLoader._ConvertToFloat32Array(context, accessor, data);
                         var attributeData = GLTFLoader._ConvertToFloat32Array(context, accessor, data);
-                        if (attribute === "COLOR_0") {
-                            // Assume vertex color has alpha on the mesh. The alphaMode of the material controls whether the material should use alpha or not.
-                            babylonMesh.hasVertexAlpha = true;
-                            if (accessor.type === "VEC3") {
-                                attributeData = GLTFLoader._ConvertVec3ToVec4(context, attributeData);
-                            }
+                        if (attribute === "COLOR_0" && accessor.type === "VEC3") {
+                            attributeData = GLTFLoader._ConvertVec3ToVec4(context, attributeData);
                         }
                         }
                         babylonVertexData.set(attributeData, kind);
                         babylonVertexData.set(attributeData, kind);
                     }));
                     }));
@@ -3226,7 +3235,7 @@ var BABYLON;
                             break;
                             break;
                         }
                         }
                         default: {
                         default: {
-                            throw new Error(context + ": Invalid component type " + accessor.componentType);
+                            throw new Error(context + ": Invalid component type (" + accessor.componentType + ")");
                         }
                         }
                     }
                     }
                 }
                 }
@@ -3272,10 +3281,9 @@ var BABYLON;
             GLTFLoader.prototype._loadSkinAsync = function (context, node, mesh, skin) {
             GLTFLoader.prototype._loadSkinAsync = function (context, node, mesh, skin) {
                 var _this = this;
                 var _this = this;
                 var assignSkeleton = function () {
                 var assignSkeleton = function () {
-                    for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
-                        var babylonMesh = _a[_i];
+                    _this._forEachNodeMesh(node, function (babylonMesh) {
                         babylonMesh.skeleton = skin._babylonSkeleton;
                         babylonMesh.skeleton = skin._babylonSkeleton;
-                    }
+                    });
                     node._babylonMesh.parent = _this._rootBabylonMesh;
                     node._babylonMesh.parent = _this._rootBabylonMesh;
                     node._babylonMesh.position = BABYLON.Vector3.Zero();
                     node._babylonMesh.position = BABYLON.Vector3.Zero();
                     node._babylonMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     node._babylonMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
@@ -3371,6 +3379,7 @@ var BABYLON;
                 });
                 });
             };
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
+                var _this = this;
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
                 if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                 if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                     return Promise.resolve();
@@ -3401,7 +3410,7 @@ var BABYLON;
                             break;
                             break;
                         }
                         }
                         default: {
                         default: {
-                            throw new Error(context + ": Invalid target path " + channel.target.path);
+                            throw new Error(context + ": Invalid target path (" + channel.target.path + ")");
                         }
                         }
                     }
                     }
                     var outputBufferOffset = 0;
                     var outputBufferOffset = 0;
@@ -3493,11 +3502,10 @@ var BABYLON;
                                 value: key.value[targetIndex],
                                 value: key.value[targetIndex],
                                 outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                                 outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                             }); }));
                             }); }));
-                            for (var _i = 0, _a = targetNode._primitiveBabylonMeshes; _i < _a.length; _i++) {
-                                var babylonMesh = _a[_i];
+                            _this._forEachNodeMesh(targetNode, function (babylonMesh) {
                                 var morphTarget = babylonMesh.morphTargetManager.getTarget(targetIndex);
                                 var morphTarget = babylonMesh.morphTargetManager.getTarget(targetIndex);
                                 babylonAnimationGroup.addTargetedAnimation(babylonAnimation, morphTarget);
                                 babylonAnimationGroup.addTargetedAnimation(babylonAnimation, morphTarget);
-                            }
+                            });
                         };
                         };
                         for (var targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) {
                         for (var targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) {
                             _loop_1(targetIndex);
                             _loop_1(targetIndex);
@@ -3528,7 +3536,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error(context + ": Invalid interpolation " + sampler.interpolation);
+                        throw new Error(context + ": Invalid interpolation (" + sampler.interpolation + ")");
                     }
                     }
                 }
                 }
                 var inputData;
                 var inputData;
@@ -3613,12 +3621,12 @@ var BABYLON;
                                 return _this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
                                 return _this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
                             }
                             }
                             default: {
                             default: {
-                                throw new Error(context + ": Invalid component type " + accessor.componentType);
+                                throw new Error(context + ": Invalid component type (" + accessor.componentType + ")");
                             }
                             }
                         }
                         }
                     }
                     }
                     catch (e) {
                     catch (e) {
-                        throw new Error(context + ": " + e);
+                        throw new Error(context + ": " + e.messsage);
                     }
                     }
                 });
                 });
                 return accessor._data;
                 return accessor._data;
@@ -3782,7 +3790,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error(context + ": Invalid alpha mode " + material.alphaMode);
+                        throw new Error(context + ": Invalid alpha mode (" + material.alphaMode + ")");
                     }
                     }
                 }
                 }
             };
             };
@@ -3800,7 +3808,7 @@ var BABYLON;
                     }
                     }
                 }, function (message, exception) {
                 }, function (message, exception) {
                     if (!_this._disposed) {
                     if (!_this._disposed) {
-                        deferred.reject(new Error(context + ": " + (exception && exception.message) ? exception.message : message || "Failed to load texture"));
+                        deferred.reject(new Error(context + ": " + ((exception && exception.message) ? exception.message : message || "Failed to load texture")));
                     }
                     }
                 });
                 });
                 promises.push(deferred.promise);
                 promises.push(deferred.promise);
@@ -3904,7 +3912,7 @@ var BABYLON;
             };
             };
             GLTFLoader._GetProperty = function (context, array, index) {
             GLTFLoader._GetProperty = function (context, array, index) {
                 if (!array || index == undefined || !array[index]) {
                 if (!array || index == undefined || !array[index]) {
-                    throw new Error(context + ": Failed to find index " + index);
+                    throw new Error(context + ": Failed to find index (" + index + ")");
                 }
                 }
                 return array[index];
                 return array[index];
             };
             };
@@ -3916,7 +3924,7 @@ var BABYLON;
                     case 33648 /* MIRRORED_REPEAT */: return BABYLON.Texture.MIRROR_ADDRESSMODE;
                     case 33648 /* MIRRORED_REPEAT */: return BABYLON.Texture.MIRROR_ADDRESSMODE;
                     case 10497 /* REPEAT */: return BABYLON.Texture.WRAP_ADDRESSMODE;
                     case 10497 /* REPEAT */: return BABYLON.Texture.WRAP_ADDRESSMODE;
                     default:
                     default:
-                        BABYLON.Tools.Warn(context + ": Invalid texture wrap mode " + mode);
+                        BABYLON.Tools.Warn(context + ": Invalid texture wrap mode (" + mode + ")");
                         return BABYLON.Texture.WRAP_ADDRESSMODE;
                         return BABYLON.Texture.WRAP_ADDRESSMODE;
                 }
                 }
             };
             };
@@ -3933,13 +3941,13 @@ var BABYLON;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_NEAREST_MIPLINEAR;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_NEAREST_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                         default:
                         default:
-                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter " + minFilter);
+                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter (" + minFilter + ")");
                             return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                             return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                     }
                     }
                 }
                 }
                 else {
                 else {
                     if (magFilter !== 9728 /* NEAREST */) {
                     if (magFilter !== 9728 /* NEAREST */) {
-                        BABYLON.Tools.Warn(context + ": Invalid texture magnification filter " + magFilter);
+                        BABYLON.Tools.Warn(context + ": Invalid texture magnification filter (" + magFilter + ")");
                     }
                     }
                     switch (minFilter) {
                     switch (minFilter) {
                         case 9728 /* NEAREST */: return BABYLON.Texture.NEAREST_NEAREST;
                         case 9728 /* NEAREST */: return BABYLON.Texture.NEAREST_NEAREST;
@@ -3949,7 +3957,7 @@ var BABYLON;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_NEAREST_MIPLINEAR;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_NEAREST_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_LINEAR_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_LINEAR_MIPLINEAR;
                         default:
                         default:
-                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter " + minFilter);
+                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter (" + minFilter + ")");
                             return BABYLON.Texture.NEAREST_NEAREST_MIPNEAREST;
                             return BABYLON.Texture.NEAREST_NEAREST_MIPNEAREST;
                     }
                     }
                 }
                 }
@@ -3964,7 +3972,7 @@ var BABYLON;
                     case "MAT3": return 9;
                     case "MAT3": return 9;
                     case "MAT4": return 16;
                     case "MAT4": return 16;
                 }
                 }
-                throw new Error(context + ": Invalid type " + type);
+                throw new Error(context + ": Invalid type (" + type + ")");
             };
             };
             GLTFLoader._ValidateUri = function (uri) {
             GLTFLoader._ValidateUri = function (uri) {
                 return (BABYLON.Tools.IsBase64(uri) || uri.indexOf("..") === -1);
                 return (BABYLON.Tools.IsBase64(uri) || uri.indexOf("..") === -1);
@@ -3979,6 +3987,8 @@ var BABYLON;
                         if (babylonMaterial && babylonMeshes) {
                         if (babylonMaterial && babylonMeshes) {
                             for (var _b = 0, babylonMeshes_1 = babylonMeshes; _b < babylonMeshes_1.length; _b++) {
                             for (var _b = 0, babylonMeshes_1 = babylonMeshes; _b < babylonMeshes_1.length; _b++) {
                                 var babylonMesh = babylonMeshes_1[_b];
                                 var babylonMesh = babylonMeshes_1[_b];
+                                // Ensure nonUniformScaling is set if necessary.
+                                babylonMesh.computeWorldMatrix(true);
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                 if (this.useClipPlane) {
                                 if (this.useClipPlane) {
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
@@ -4178,7 +4188,10 @@ var BABYLON;
                             var promise = _this._loader._loadNodeAsync("#/nodes/" + nodeLOD._index, nodeLOD).then(function () {
                             var promise = _this._loader._loadNodeAsync("#/nodes/" + nodeLOD._index, nodeLOD).then(function () {
                                 if (indexLOD !== 0) {
                                 if (indexLOD !== 0) {
                                     var previousNodeLOD = nodeLODs[indexLOD - 1];
                                     var previousNodeLOD = nodeLODs[indexLOD - 1];
-                                    previousNodeLOD._babylonMesh.setEnabled(false);
+                                    if (previousNodeLOD._babylonMesh) {
+                                        previousNodeLOD._babylonMesh.dispose();
+                                        delete previousNodeLOD._babylonMesh;
+                                    }
                                 }
                                 }
                                 if (indexLOD !== nodeLODs.length - 1) {
                                 if (indexLOD !== nodeLODs.length - 1) {
                                     var nodeIndex = nodeLODs[indexLOD + 1]._index;
                                     var nodeIndex = nodeLODs[indexLOD + 1]._index;
@@ -4222,6 +4235,11 @@ var BABYLON;
                             var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
                             var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
                                 if (indexLOD !== 0) {
                                 if (indexLOD !== 0) {
                                     assign(materialLOD._babylonMaterial);
                                     assign(materialLOD._babylonMaterial);
+                                    var previousMaterialLOD = materialLODs[indexLOD - 1];
+                                    if (previousMaterialLOD._babylonMaterial) {
+                                        previousMaterialLOD._babylonMaterial.dispose();
+                                        delete previousMaterialLOD._babylonMaterial;
+                                    }
                                 }
                                 }
                                 if (indexLOD !== materialLODs.length - 1) {
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
@@ -4537,7 +4555,7 @@ var BABYLON;
                                 break;
                                 break;
                             }
                             }
                             default: {
                             default: {
-                                throw new Error(context + ": Invalid light type " + light.type);
+                                throw new Error(context + ": Invalid light type (" + light.type + ")");
                             }
                             }
                         }
                         }
                         babylonLight.diffuse = light.color ? BABYLON.Color3.FromArray(light.color) : BABYLON.Color3.White();
                         babylonLight.diffuse = light.color ? BABYLON.Color3.FromArray(light.color) : BABYLON.Color3.White();
@@ -4550,7 +4568,7 @@ var BABYLON;
                     get: function () {
                     get: function () {
                         var extensions = this._loader._gltf.extensions;
                         var extensions = this._loader._gltf.extensions;
                         if (!extensions || !extensions[this.name]) {
                         if (!extensions || !extensions[this.name]) {
-                            throw new Error("#/extensions: " + this.name + " not found");
+                            throw new Error("#/extensions: '" + this.name + "' not found");
                         }
                         }
                         var extension = extensions[this.name];
                         var extension = extensions[this.name];
                         return extension.lights;
                         return extension.lights;

Plik diff jest za duży
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 78 - 60
dist/preview release/loaders/babylonjs.loaders.js

@@ -1073,7 +1073,7 @@ var BABYLON;
              */
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
             /**
-             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
              */
              */
             this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
             this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
             /**
             /**
@@ -1180,7 +1180,7 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         /**
         /**
-         * Gets a promise that resolves when the asset to be completely loaded.
+         * Gets a promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          */
          */
         GLTFFileLoader.prototype.whenCompleteAsync = function () {
         GLTFFileLoader.prototype.whenCompleteAsync = function () {
@@ -3727,7 +3727,7 @@ var BABYLON;
                         nodes = names.map(function (name) {
                         nodes = names.map(function (name) {
                             var node = nodeMap_1[name];
                             var node = nodeMap_1[name];
                             if (!node) {
                             if (!node) {
-                                throw new Error("Failed to find node " + name);
+                                throw new Error("Failed to find node '" + name + "'");
                             }
                             }
                             return node;
                             return node;
                         });
                         });
@@ -3883,7 +3883,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error("Invalid coordinate system mode " + this.coordinateSystemMode);
+                        throw new Error("Invalid coordinate system mode (" + this.coordinateSystemMode + ")");
                     }
                     }
                 }
                 }
                 this.onMeshLoadedObservable.notifyObservers(this._rootBabylonMesh);
                 this.onMeshLoadedObservable.notifyObservers(this._rootBabylonMesh);
@@ -3912,6 +3912,17 @@ var BABYLON;
                 promises.push(this._loadAnimationsAsync());
                 promises.push(this._loadAnimationsAsync());
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
+            GLTFLoader.prototype._forEachNodeMesh = function (node, callback) {
+                if (node._babylonMesh) {
+                    callback(node._babylonMesh);
+                }
+                if (node._primitiveBabylonMeshes) {
+                    for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
+                        var babylonMesh = _a[_i];
+                        callback(babylonMesh);
+                    }
+                }
+            };
             GLTFLoader.prototype._getMeshes = function () {
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = new Array();
                 var meshes = new Array();
                 // Root mesh is always first.
                 // Root mesh is always first.
@@ -3920,15 +3931,9 @@ var BABYLON;
                 if (nodes) {
                 if (nodes) {
                     for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
                     for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
                         var node = nodes_2[_i];
                         var node = nodes_2[_i];
-                        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);
-                            }
-                        }
+                        this._forEachNodeMesh(node, function (mesh) {
+                            meshes.push(mesh);
+                        });
                     }
                     }
                 }
                 }
                 return meshes;
                 return meshes;
@@ -3969,7 +3974,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        BABYLON.Tools.Error("Invalid animation start mode " + this.animationStartMode);
+                        BABYLON.Tools.Error("Invalid animation start mode (" + this.animationStartMode + ")");
                         return;
                         return;
                     }
                     }
                 }
                 }
@@ -3990,7 +3995,7 @@ var BABYLON;
                 GLTFLoader._LoadTransform(node, babylonMesh);
                 GLTFLoader._LoadTransform(node, babylonMesh);
                 if (node.mesh != undefined) {
                 if (node.mesh != undefined) {
                     var mesh = GLTFLoader._GetProperty(context + "/mesh", this._gltf.meshes, node.mesh);
                     var mesh = GLTFLoader._GetProperty(context + "/mesh", this._gltf.meshes, node.mesh);
-                    promises.push(this._loadMeshAsync("#/meshes/" + mesh._index, node, mesh));
+                    promises.push(this._loadMeshAsync("#/meshes/" + mesh._index, node, mesh, babylonMesh));
                 }
                 }
                 if (node.children) {
                 if (node.children) {
                     for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
                     for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
@@ -4002,37 +4007,42 @@ var BABYLON;
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
-            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh) {
+            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh, babylonMesh) {
                 // TODO: instancing
                 // TODO: instancing
+                var _this = this;
                 var promises = new Array();
                 var promises = new Array();
                 var primitives = mesh.primitives;
                 var primitives = mesh.primitives;
                 if (!primitives || primitives.length === 0) {
                 if (!primitives || primitives.length === 0) {
                     throw new Error(context + ": Primitives are missing");
                     throw new Error(context + ": Primitives are missing");
                 }
                 }
                 GLTF2.ArrayItem.Assign(primitives);
                 GLTF2.ArrayItem.Assign(primitives);
-                for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
-                    var primitive = primitives_1[_i];
-                    promises.push(this._loadPrimitiveAsync(context + "/primitives/" + primitive._index, node, mesh, primitive));
+                if (primitives.length === 1) {
+                    var primitive = primitives[0];
+                    promises.push(this._loadPrimitiveAsync(context + "/primitives/" + primitive._index, node, mesh, primitive, babylonMesh));
+                }
+                else {
+                    node._primitiveBabylonMeshes = [];
+                    for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
+                        var primitive = primitives_1[_i];
+                        var primitiveBabylonMesh = new BABYLON.Mesh((mesh.name || babylonMesh.name) + "_" + primitive._index, this._babylonScene, babylonMesh);
+                        node._primitiveBabylonMeshes.push(babylonMesh);
+                        promises.push(this._loadPrimitiveAsync(context + "/primitives/" + primitive._index, node, mesh, primitive, primitiveBabylonMesh));
+                        this.onMeshLoadedObservable.notifyObservers(babylonMesh);
+                    }
                 }
                 }
                 if (node.skin != undefined) {
                 if (node.skin != undefined) {
                     var skin = GLTFLoader._GetProperty(context + "/skin", this._gltf.skins, node.skin);
                     var skin = GLTFLoader._GetProperty(context + "/skin", this._gltf.skins, node.skin);
                     promises.push(this._loadSkinAsync("#/skins/" + skin._index, node, mesh, skin));
                     promises.push(this._loadSkinAsync("#/skins/" + skin._index, node, mesh, skin));
                 }
                 }
                 return Promise.all(promises).then(function () {
                 return Promise.all(promises).then(function () {
-                    if (node._primitiveBabylonMeshes) {
-                        for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
-                            var primitiveBabylonMesh = _a[_i];
-                            primitiveBabylonMesh._refreshBoundingInfo(true);
-                        }
-                    }
+                    _this._forEachNodeMesh(node, function (babylonMesh) {
+                        babylonMesh._refreshBoundingInfo(true);
+                    });
                 });
                 });
             };
             };
-            GLTFLoader.prototype._loadPrimitiveAsync = function (context, node, mesh, primitive) {
+            GLTFLoader.prototype._loadPrimitiveAsync = function (context, node, mesh, primitive, babylonMesh) {
                 var _this = this;
                 var _this = this;
                 var promises = new Array();
                 var promises = new Array();
-                var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
-                node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
                 promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(function (babylonVertexData) {
                 promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(function (babylonVertexData) {
                     new BABYLON.Geometry(babylonMesh.name, _this._babylonScene, babylonVertexData, false, babylonMesh);
                     new BABYLON.Geometry(babylonMesh.name, _this._babylonScene, babylonVertexData, false, babylonMesh);
@@ -4042,12 +4052,11 @@ var BABYLON;
                     babylonMesh.material = this._getDefaultMaterial();
                     babylonMesh.material = this._getDefaultMaterial();
                 }
                 }
                 else {
                 else {
-                    var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
+                    var material = GLTFLoader._GetProperty(context + "/material}", this._gltf.materials, primitive.material);
                     promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
                     promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
                         babylonMesh.material = babylonMaterial;
                         babylonMesh.material = babylonMaterial;
                     }));
                     }));
                 }
                 }
-                this.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
@@ -4062,7 +4071,7 @@ var BABYLON;
                 }
                 }
                 if (primitive.mode != undefined && primitive.mode !== 4 /* TRIANGLES */) {
                 if (primitive.mode != undefined && primitive.mode !== 4 /* TRIANGLES */) {
                     // TODO: handle other primitive modes
                     // TODO: handle other primitive modes
-                    throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
+                    throw new Error(context + ": Mode (" + primitive.mode + ") is not currently supported");
                 }
                 }
                 var promises = new Array();
                 var promises = new Array();
                 var babylonVertexData = new BABYLON.VertexData();
                 var babylonVertexData = new BABYLON.VertexData();
@@ -4090,15 +4099,15 @@ var BABYLON;
                     if (babylonMesh._delayInfo.indexOf(kind) === -1) {
                     if (babylonMesh._delayInfo.indexOf(kind) === -1) {
                         babylonMesh._delayInfo.push(kind);
                         babylonMesh._delayInfo.push(kind);
                     }
                     }
+                    if (attribute === "COLOR_0") {
+                        // Assume vertex color has alpha on the mesh. The alphaMode of the material controls whether the material should use alpha or not.
+                        babylonMesh.hasVertexAlpha = true;
+                    }
                     var accessor = GLTFLoader._GetProperty(context + "/attributes/" + attribute, _this._gltf.accessors, attributes[attribute]);
                     var accessor = GLTFLoader._GetProperty(context + "/attributes/" + attribute, _this._gltf.accessors, attributes[attribute]);
                     promises.push(_this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(function (data) {
                     promises.push(_this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(function (data) {
                         var attributeData = GLTFLoader._ConvertToFloat32Array(context, accessor, data);
                         var attributeData = GLTFLoader._ConvertToFloat32Array(context, accessor, data);
-                        if (attribute === "COLOR_0") {
-                            // Assume vertex color has alpha on the mesh. The alphaMode of the material controls whether the material should use alpha or not.
-                            babylonMesh.hasVertexAlpha = true;
-                            if (accessor.type === "VEC3") {
-                                attributeData = GLTFLoader._ConvertVec3ToVec4(context, attributeData);
-                            }
+                        if (attribute === "COLOR_0" && accessor.type === "VEC3") {
+                            attributeData = GLTFLoader._ConvertVec3ToVec4(context, attributeData);
                         }
                         }
                         babylonVertexData.set(attributeData, kind);
                         babylonVertexData.set(attributeData, kind);
                     }));
                     }));
@@ -4204,7 +4213,7 @@ var BABYLON;
                             break;
                             break;
                         }
                         }
                         default: {
                         default: {
-                            throw new Error(context + ": Invalid component type " + accessor.componentType);
+                            throw new Error(context + ": Invalid component type (" + accessor.componentType + ")");
                         }
                         }
                     }
                     }
                 }
                 }
@@ -4250,10 +4259,9 @@ var BABYLON;
             GLTFLoader.prototype._loadSkinAsync = function (context, node, mesh, skin) {
             GLTFLoader.prototype._loadSkinAsync = function (context, node, mesh, skin) {
                 var _this = this;
                 var _this = this;
                 var assignSkeleton = function () {
                 var assignSkeleton = function () {
-                    for (var _i = 0, _a = node._primitiveBabylonMeshes; _i < _a.length; _i++) {
-                        var babylonMesh = _a[_i];
+                    _this._forEachNodeMesh(node, function (babylonMesh) {
                         babylonMesh.skeleton = skin._babylonSkeleton;
                         babylonMesh.skeleton = skin._babylonSkeleton;
-                    }
+                    });
                     node._babylonMesh.parent = _this._rootBabylonMesh;
                     node._babylonMesh.parent = _this._rootBabylonMesh;
                     node._babylonMesh.position = BABYLON.Vector3.Zero();
                     node._babylonMesh.position = BABYLON.Vector3.Zero();
                     node._babylonMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
                     node._babylonMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
@@ -4349,6 +4357,7 @@ var BABYLON;
                 });
                 });
             };
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
+                var _this = this;
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
                 if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                 if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                     return Promise.resolve();
@@ -4379,7 +4388,7 @@ var BABYLON;
                             break;
                             break;
                         }
                         }
                         default: {
                         default: {
-                            throw new Error(context + ": Invalid target path " + channel.target.path);
+                            throw new Error(context + ": Invalid target path (" + channel.target.path + ")");
                         }
                         }
                     }
                     }
                     var outputBufferOffset = 0;
                     var outputBufferOffset = 0;
@@ -4471,11 +4480,10 @@ var BABYLON;
                                 value: key.value[targetIndex],
                                 value: key.value[targetIndex],
                                 outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                                 outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                             }); }));
                             }); }));
-                            for (var _i = 0, _a = targetNode._primitiveBabylonMeshes; _i < _a.length; _i++) {
-                                var babylonMesh = _a[_i];
+                            _this._forEachNodeMesh(targetNode, function (babylonMesh) {
                                 var morphTarget = babylonMesh.morphTargetManager.getTarget(targetIndex);
                                 var morphTarget = babylonMesh.morphTargetManager.getTarget(targetIndex);
                                 babylonAnimationGroup.addTargetedAnimation(babylonAnimation, morphTarget);
                                 babylonAnimationGroup.addTargetedAnimation(babylonAnimation, morphTarget);
-                            }
+                            });
                         };
                         };
                         for (var targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) {
                         for (var targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) {
                             _loop_1(targetIndex);
                             _loop_1(targetIndex);
@@ -4506,7 +4514,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error(context + ": Invalid interpolation " + sampler.interpolation);
+                        throw new Error(context + ": Invalid interpolation (" + sampler.interpolation + ")");
                     }
                     }
                 }
                 }
                 var inputData;
                 var inputData;
@@ -4591,12 +4599,12 @@ var BABYLON;
                                 return _this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
                                 return _this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
                             }
                             }
                             default: {
                             default: {
-                                throw new Error(context + ": Invalid component type " + accessor.componentType);
+                                throw new Error(context + ": Invalid component type (" + accessor.componentType + ")");
                             }
                             }
                         }
                         }
                     }
                     }
                     catch (e) {
                     catch (e) {
-                        throw new Error(context + ": " + e);
+                        throw new Error(context + ": " + e.messsage);
                     }
                     }
                 });
                 });
                 return accessor._data;
                 return accessor._data;
@@ -4760,7 +4768,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error(context + ": Invalid alpha mode " + material.alphaMode);
+                        throw new Error(context + ": Invalid alpha mode (" + material.alphaMode + ")");
                     }
                     }
                 }
                 }
             };
             };
@@ -4778,7 +4786,7 @@ var BABYLON;
                     }
                     }
                 }, function (message, exception) {
                 }, function (message, exception) {
                     if (!_this._disposed) {
                     if (!_this._disposed) {
-                        deferred.reject(new Error(context + ": " + (exception && exception.message) ? exception.message : message || "Failed to load texture"));
+                        deferred.reject(new Error(context + ": " + ((exception && exception.message) ? exception.message : message || "Failed to load texture")));
                     }
                     }
                 });
                 });
                 promises.push(deferred.promise);
                 promises.push(deferred.promise);
@@ -4882,7 +4890,7 @@ var BABYLON;
             };
             };
             GLTFLoader._GetProperty = function (context, array, index) {
             GLTFLoader._GetProperty = function (context, array, index) {
                 if (!array || index == undefined || !array[index]) {
                 if (!array || index == undefined || !array[index]) {
-                    throw new Error(context + ": Failed to find index " + index);
+                    throw new Error(context + ": Failed to find index (" + index + ")");
                 }
                 }
                 return array[index];
                 return array[index];
             };
             };
@@ -4894,7 +4902,7 @@ var BABYLON;
                     case 33648 /* MIRRORED_REPEAT */: return BABYLON.Texture.MIRROR_ADDRESSMODE;
                     case 33648 /* MIRRORED_REPEAT */: return BABYLON.Texture.MIRROR_ADDRESSMODE;
                     case 10497 /* REPEAT */: return BABYLON.Texture.WRAP_ADDRESSMODE;
                     case 10497 /* REPEAT */: return BABYLON.Texture.WRAP_ADDRESSMODE;
                     default:
                     default:
-                        BABYLON.Tools.Warn(context + ": Invalid texture wrap mode " + mode);
+                        BABYLON.Tools.Warn(context + ": Invalid texture wrap mode (" + mode + ")");
                         return BABYLON.Texture.WRAP_ADDRESSMODE;
                         return BABYLON.Texture.WRAP_ADDRESSMODE;
                 }
                 }
             };
             };
@@ -4911,13 +4919,13 @@ var BABYLON;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_NEAREST_MIPLINEAR;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_NEAREST_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                         default:
                         default:
-                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter " + minFilter);
+                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter (" + minFilter + ")");
                             return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                             return BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR;
                     }
                     }
                 }
                 }
                 else {
                 else {
                     if (magFilter !== 9728 /* NEAREST */) {
                     if (magFilter !== 9728 /* NEAREST */) {
-                        BABYLON.Tools.Warn(context + ": Invalid texture magnification filter " + magFilter);
+                        BABYLON.Tools.Warn(context + ": Invalid texture magnification filter (" + magFilter + ")");
                     }
                     }
                     switch (minFilter) {
                     switch (minFilter) {
                         case 9728 /* NEAREST */: return BABYLON.Texture.NEAREST_NEAREST;
                         case 9728 /* NEAREST */: return BABYLON.Texture.NEAREST_NEAREST;
@@ -4927,7 +4935,7 @@ var BABYLON;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_NEAREST_MIPLINEAR;
                         case 9986 /* NEAREST_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_NEAREST_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_LINEAR_MIPLINEAR;
                         case 9987 /* LINEAR_MIPMAP_LINEAR */: return BABYLON.Texture.NEAREST_LINEAR_MIPLINEAR;
                         default:
                         default:
-                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter " + minFilter);
+                            BABYLON.Tools.Warn(context + ": Invalid texture minification filter (" + minFilter + ")");
                             return BABYLON.Texture.NEAREST_NEAREST_MIPNEAREST;
                             return BABYLON.Texture.NEAREST_NEAREST_MIPNEAREST;
                     }
                     }
                 }
                 }
@@ -4942,7 +4950,7 @@ var BABYLON;
                     case "MAT3": return 9;
                     case "MAT3": return 9;
                     case "MAT4": return 16;
                     case "MAT4": return 16;
                 }
                 }
-                throw new Error(context + ": Invalid type " + type);
+                throw new Error(context + ": Invalid type (" + type + ")");
             };
             };
             GLTFLoader._ValidateUri = function (uri) {
             GLTFLoader._ValidateUri = function (uri) {
                 return (BABYLON.Tools.IsBase64(uri) || uri.indexOf("..") === -1);
                 return (BABYLON.Tools.IsBase64(uri) || uri.indexOf("..") === -1);
@@ -4957,6 +4965,8 @@ var BABYLON;
                         if (babylonMaterial && babylonMeshes) {
                         if (babylonMaterial && babylonMeshes) {
                             for (var _b = 0, babylonMeshes_1 = babylonMeshes; _b < babylonMeshes_1.length; _b++) {
                             for (var _b = 0, babylonMeshes_1 = babylonMeshes; _b < babylonMeshes_1.length; _b++) {
                                 var babylonMesh = babylonMeshes_1[_b];
                                 var babylonMesh = babylonMeshes_1[_b];
+                                // Ensure nonUniformScaling is set if necessary.
+                                babylonMesh.computeWorldMatrix(true);
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                 if (this.useClipPlane) {
                                 if (this.useClipPlane) {
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
@@ -5147,7 +5157,10 @@ var BABYLON;
                             var promise = _this._loader._loadNodeAsync("#/nodes/" + nodeLOD._index, nodeLOD).then(function () {
                             var promise = _this._loader._loadNodeAsync("#/nodes/" + nodeLOD._index, nodeLOD).then(function () {
                                 if (indexLOD !== 0) {
                                 if (indexLOD !== 0) {
                                     var previousNodeLOD = nodeLODs[indexLOD - 1];
                                     var previousNodeLOD = nodeLODs[indexLOD - 1];
-                                    previousNodeLOD._babylonMesh.setEnabled(false);
+                                    if (previousNodeLOD._babylonMesh) {
+                                        previousNodeLOD._babylonMesh.dispose();
+                                        delete previousNodeLOD._babylonMesh;
+                                    }
                                 }
                                 }
                                 if (indexLOD !== nodeLODs.length - 1) {
                                 if (indexLOD !== nodeLODs.length - 1) {
                                     var nodeIndex = nodeLODs[indexLOD + 1]._index;
                                     var nodeIndex = nodeLODs[indexLOD + 1]._index;
@@ -5191,6 +5204,11 @@ var BABYLON;
                             var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
                             var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
                                 if (indexLOD !== 0) {
                                 if (indexLOD !== 0) {
                                     assign(materialLOD._babylonMaterial);
                                     assign(materialLOD._babylonMaterial);
+                                    var previousMaterialLOD = materialLODs[indexLOD - 1];
+                                    if (previousMaterialLOD._babylonMaterial) {
+                                        previousMaterialLOD._babylonMaterial.dispose();
+                                        delete previousMaterialLOD._babylonMaterial;
+                                    }
                                 }
                                 }
                                 if (indexLOD !== materialLODs.length - 1) {
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
@@ -5479,7 +5497,7 @@ var BABYLON;
                                 break;
                                 break;
                             }
                             }
                             default: {
                             default: {
-                                throw new Error(context + ": Invalid light type " + light.type);
+                                throw new Error(context + ": Invalid light type (" + light.type + ")");
                             }
                             }
                         }
                         }
                         babylonLight.diffuse = light.color ? BABYLON.Color3.FromArray(light.color) : BABYLON.Color3.White();
                         babylonLight.diffuse = light.color ? BABYLON.Color3.FromArray(light.color) : BABYLON.Color3.White();
@@ -5492,7 +5510,7 @@ var BABYLON;
                     get: function () {
                     get: function () {
                         var extensions = this._loader._gltf.extensions;
                         var extensions = this._loader._gltf.extensions;
                         if (!extensions || !extensions[this.name]) {
                         if (!extensions || !extensions[this.name]) {
-                            throw new Error("#/extensions: " + this.name + " not found");
+                            throw new Error("#/extensions: '" + this.name + "' not found");
                         }
                         }
                         var extension = extensions[this.name];
                         var extension = extensions[this.name];
                         return extension.lights;
                         return extension.lights;

Plik diff jest za duży
+ 2 - 2
dist/preview release/loaders/babylonjs.loaders.min.js


+ 5 - 4
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -218,7 +218,7 @@ declare module BABYLON {
         private _onMaterialLoadedObserver;
         private _onMaterialLoadedObserver;
         onMaterialLoaded: (material: Material) => void;
         onMaterialLoaded: (material: Material) => void;
         /**
         /**
-         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
          */
          */
         readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         private _onAnimationGroupLoadedObserver;
         private _onAnimationGroupLoadedObserver;
@@ -245,7 +245,7 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
         /**
-         * Gets a promise that resolves when the asset to be completely loaded.
+         * Gets a promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          * @returns A promise that resolves when the asset is completely loaded.
          */
          */
         whenCompleteAsync(): Promise<void>;
         whenCompleteAsync(): Promise<void>;
@@ -979,12 +979,13 @@ declare module BABYLON.GLTF2 {
         private _createRootNode();
         private _createRootNode();
         private _loadNodesAsync(nodes);
         private _loadNodesAsync(nodes);
         _loadSceneAsync(context: string, scene: ILoaderScene): Promise<void>;
         _loadSceneAsync(context: string, scene: ILoaderScene): Promise<void>;
+        private _forEachNodeMesh(node, callback);
         private _getMeshes();
         private _getMeshes();
         private _getSkeletons();
         private _getSkeletons();
         private _startAnimations();
         private _startAnimations();
         _loadNodeAsync(context: string, node: ILoaderNode): Promise<void>;
         _loadNodeAsync(context: string, node: ILoaderNode): Promise<void>;
-        private _loadMeshAsync(context, node, mesh);
-        private _loadPrimitiveAsync(context, node, mesh, primitive);
+        private _loadMeshAsync(context, node, mesh, babylonMesh);
+        private _loadPrimitiveAsync(context, node, mesh, primitive, babylonMesh);
         private _loadVertexDataAsync(context, primitive, babylonMesh);
         private _loadVertexDataAsync(context, primitive, babylonMesh);
         private _createMorphTargets(context, node, mesh, primitive, babylonMesh);
         private _createMorphTargets(context, node, mesh, primitive, babylonMesh);
         private _loadMorphTargetsAsync(context, primitive, babylonMesh, babylonVertexData);
         private _loadMorphTargetsAsync(context, primitive, babylonMesh, babylonVertexData);

+ 4 - 282
dist/preview release/materialsLibrary/babylon.customMaterial.d.ts

@@ -1,277 +1,5 @@
 
 
 declare module BABYLON {
 declare module BABYLON {
-    class StandardMaterialDefines_OldVer extends MaterialDefines implements IImageProcessingConfigurationDefines {
-        DIFFUSE: boolean;
-        AMBIENT: boolean;
-        OPACITY: boolean;
-        OPACITYRGB: boolean;
-        REFLECTION: boolean;
-        EMISSIVE: boolean;
-        SPECULAR: boolean;
-        BUMP: boolean;
-        PARALLAX: boolean;
-        PARALLAXOCCLUSION: boolean;
-        SPECULAROVERALPHA: boolean;
-        CLIPPLANE: boolean;
-        ALPHATEST: boolean;
-        ALPHAFROMDIFFUSE: boolean;
-        POINTSIZE: boolean;
-        FOG: boolean;
-        SPECULARTERM: boolean;
-        DIFFUSEFRESNEL: boolean;
-        OPACITYFRESNEL: boolean;
-        REFLECTIONFRESNEL: boolean;
-        REFRACTIONFRESNEL: boolean;
-        EMISSIVEFRESNEL: boolean;
-        FRESNEL: boolean;
-        NORMAL: boolean;
-        UV1: boolean;
-        UV2: boolean;
-        VERTEXCOLOR: boolean;
-        VERTEXALPHA: boolean;
-        NUM_BONE_INFLUENCERS: number;
-        BonesPerMesh: number;
-        INSTANCES: boolean;
-        GLOSSINESS: boolean;
-        ROUGHNESS: boolean;
-        EMISSIVEASILLUMINATION: boolean;
-        LINKEMISSIVEWITHDIFFUSE: boolean;
-        REFLECTIONFRESNELFROMSPECULAR: boolean;
-        LIGHTMAP: boolean;
-        USELIGHTMAPASSHADOWMAP: boolean;
-        REFLECTIONMAP_3D: boolean;
-        REFLECTIONMAP_SPHERICAL: boolean;
-        REFLECTIONMAP_PLANAR: boolean;
-        REFLECTIONMAP_CUBIC: boolean;
-        REFLECTIONMAP_PROJECTION: boolean;
-        REFLECTIONMAP_SKYBOX: boolean;
-        REFLECTIONMAP_EXPLICIT: boolean;
-        REFLECTIONMAP_EQUIRECTANGULAR: boolean;
-        REFLECTIONMAP_EQUIRECTANGULAR_FIXED: boolean;
-        REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED: boolean;
-        INVERTCUBICMAP: boolean;
-        LOGARITHMICDEPTH: boolean;
-        REFRACTION: boolean;
-        REFRACTIONMAP_3D: boolean;
-        REFLECTIONOVERALPHA: boolean;
-        TWOSIDEDLIGHTING: boolean;
-        SHADOWFLOAT: boolean;
-        MORPHTARGETS: boolean;
-        MORPHTARGETS_NORMAL: boolean;
-        MORPHTARGETS_TANGENT: boolean;
-        NUM_MORPH_INFLUENCERS: number;
-        IMAGEPROCESSING: boolean;
-        VIGNETTE: boolean;
-        VIGNETTEBLENDMODEMULTIPLY: boolean;
-        VIGNETTEBLENDMODEOPAQUE: boolean;
-        TONEMAPPING: boolean;
-        CONTRAST: boolean;
-        COLORCURVES: boolean;
-        COLORGRADING: boolean;
-        COLORGRADING3D: boolean;
-        SAMPLER3DGREENDEPTH: boolean;
-        SAMPLER3DBGRMAP: boolean;
-        IMAGEPROCESSINGPOSTPROCESS: boolean;
-        EXPOSURE: boolean;
-        GRAIN: boolean;
-        constructor();
-        setReflectionMode(modeToEnable: string): void;
-    }
-    class StandardMaterial_OldVer extends PushMaterial {
-        private _diffuseTexture;
-        diffuseTexture: BaseTexture;
-        private _ambientTexture;
-        ambientTexture: BaseTexture;
-        private _opacityTexture;
-        opacityTexture: BaseTexture;
-        private _reflectionTexture;
-        reflectionTexture: BaseTexture;
-        private _emissiveTexture;
-        emissiveTexture: BaseTexture;
-        private _specularTexture;
-        specularTexture: BaseTexture;
-        private _bumpTexture;
-        bumpTexture: BaseTexture;
-        private _lightmapTexture;
-        lightmapTexture: BaseTexture;
-        private _refractionTexture;
-        refractionTexture: BaseTexture;
-        ambientColor: Color3;
-        diffuseColor: Color3;
-        specularColor: Color3;
-        emissiveColor: Color3;
-        specularPower: number;
-        private _useAlphaFromDiffuseTexture;
-        useAlphaFromDiffuseTexture: boolean;
-        private _useEmissiveAsIllumination;
-        useEmissiveAsIllumination: boolean;
-        private _linkEmissiveWithDiffuse;
-        linkEmissiveWithDiffuse: boolean;
-        private _useSpecularOverAlpha;
-        useSpecularOverAlpha: boolean;
-        private _useReflectionOverAlpha;
-        useReflectionOverAlpha: boolean;
-        private _disableLighting;
-        disableLighting: boolean;
-        private _useParallax;
-        useParallax: boolean;
-        private _useParallaxOcclusion;
-        useParallaxOcclusion: boolean;
-        parallaxScaleBias: number;
-        private _roughness;
-        roughness: number;
-        indexOfRefraction: number;
-        invertRefractionY: boolean;
-        private _useLightmapAsShadowmap;
-        useLightmapAsShadowmap: boolean;
-        private _diffuseFresnelParameters;
-        diffuseFresnelParameters: FresnelParameters;
-        private _opacityFresnelParameters;
-        opacityFresnelParameters: FresnelParameters;
-        private _reflectionFresnelParameters;
-        reflectionFresnelParameters: FresnelParameters;
-        private _refractionFresnelParameters;
-        refractionFresnelParameters: FresnelParameters;
-        private _emissiveFresnelParameters;
-        emissiveFresnelParameters: FresnelParameters;
-        private _useReflectionFresnelFromSpecular;
-        useReflectionFresnelFromSpecular: boolean;
-        private _useGlossinessFromSpecularMapAlpha;
-        useGlossinessFromSpecularMapAlpha: boolean;
-        private _maxSimultaneousLights;
-        maxSimultaneousLights: number;
-        /**
-         * If sets to true, x component of normal map value will invert (x = 1.0 - x).
-         */
-        private _invertNormalMapX;
-        invertNormalMapX: boolean;
-        /**
-         * If sets to true, y component of normal map value will invert (y = 1.0 - y).
-         */
-        private _invertNormalMapY;
-        invertNormalMapY: boolean;
-        /**
-         * If sets to true and backfaceCulling is false, normals will be flipped on the backside.
-         */
-        private _twoSidedLighting;
-        twoSidedLighting: boolean;
-        /**
-         * Default configuration related to image processing available in the standard Material.
-         */
-        protected _imageProcessingConfiguration: ImageProcessingConfiguration;
-        /**
-         * Gets the image processing configuration used either in this material.
-         */
-        /**
-         * Sets the Default image processing configuration used either in the this material.
-         *
-         * If sets to null, the scene one is in use.
-         */
-        imageProcessingConfiguration: ImageProcessingConfiguration;
-        /**
-         * Keep track of the image processing observer to allow dispose and replace.
-         */
-        private _imageProcessingObserver;
-        /**
-         * Attaches a new image processing configuration to the Standard Material.
-         * @param configuration
-         */
-        protected _attachImageProcessingConfiguration(configuration: Nullable<ImageProcessingConfiguration>): void;
-        /**
-         * Gets wether the color curves effect is enabled.
-         */
-        /**
-         * Sets wether the color curves effect is enabled.
-         */
-        cameraColorCurvesEnabled: boolean;
-        /**
-         * Gets wether the color grading effect is enabled.
-         */
-        /**
-         * Gets wether the color grading effect is enabled.
-         */
-        cameraColorGradingEnabled: boolean;
-        /**
-         * Gets wether tonemapping is enabled or not.
-         */
-        /**
-         * Sets wether tonemapping is enabled or not
-         */
-        cameraToneMappingEnabled: boolean;
-        /**
-         * The camera exposure used on this material.
-         * This property is here and not in the camera to allow controlling exposure without full screen post process.
-         * This corresponds to a photographic exposure.
-         */
-        /**
-         * The camera exposure used on this material.
-         * This property is here and not in the camera to allow controlling exposure without full screen post process.
-         * This corresponds to a photographic exposure.
-         */
-        cameraExposure: number;
-        /**
-         * Gets The camera contrast used on this material.
-         */
-        /**
-         * Sets The camera contrast used on this material.
-         */
-        cameraContrast: number;
-        /**
-         * Gets the Color Grading 2D Lookup Texture.
-         */
-        /**
-         * Sets the Color Grading 2D Lookup Texture.
-         */
-        cameraColorGradingTexture: Nullable<BaseTexture>;
-        protected _shouldTurnAlphaTestOn(mesh: AbstractMesh): boolean;
-        customShaderNameResolve: (shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: StandardMaterialDefines_OldVer) => string;
-        protected _renderTargets: SmartArray<RenderTargetTexture>;
-        protected _worldViewProjectionMatrix: Matrix;
-        protected _globalAmbientColor: Color3;
-        protected _useLogarithmicDepth: boolean;
-        constructor(name: string, scene: Scene);
-        getClassName(): string;
-        useLogarithmicDepth: boolean;
-        needAlphaBlending(): boolean;
-        needAlphaTesting(): boolean;
-        protected _shouldUseAlphaFromDiffuseTexture(): boolean;
-        getAlphaTestTexture(): BaseTexture;
-        /**
-         * Child classes can use it to update shaders
-         */
-        isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean;
-        buildUniformLayout(): void;
-        unbind(): void;
-        bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void;
-        getAnimatables(): IAnimatable[];
-        getActiveTextures(): BaseTexture[];
-        dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void;
-        clone(name: string): StandardMaterial_OldVer;
-        serialize(): any;
-        static Parse(source: any, scene: Scene, rootUrl: string): StandardMaterial_OldVer;
-        static _DiffuseTextureEnabled: boolean;
-        static DiffuseTextureEnabled: boolean;
-        static _AmbientTextureEnabled: boolean;
-        static AmbientTextureEnabled: boolean;
-        static _OpacityTextureEnabled: boolean;
-        static OpacityTextureEnabled: boolean;
-        static _ReflectionTextureEnabled: boolean;
-        static ReflectionTextureEnabled: boolean;
-        static _EmissiveTextureEnabled: boolean;
-        static EmissiveTextureEnabled: boolean;
-        static _SpecularTextureEnabled: boolean;
-        static SpecularTextureEnabled: boolean;
-        static _BumpTextureEnabled: boolean;
-        static BumpTextureEnabled: boolean;
-        static _LightmapTextureEnabled: boolean;
-        static LightmapTextureEnabled: boolean;
-        static _RefractionTextureEnabled: boolean;
-        static RefractionTextureEnabled: boolean;
-        static _ColorGradingTextureEnabled: boolean;
-        static ColorGradingTextureEnabled: boolean;
-        static _FresnelEnabled: boolean;
-        static FresnelEnabled: boolean;
-    }
     class CustomShaderStructure {
     class CustomShaderStructure {
         FragmentStore: string;
         FragmentStore: string;
         VertexStore: string;
         VertexStore: string;
@@ -291,26 +19,20 @@ declare module BABYLON {
         Vertex_Before_PositionUpdated: string;
         Vertex_Before_PositionUpdated: string;
         Vertex_Before_NormalUpdated: string;
         Vertex_Before_NormalUpdated: string;
     }
     }
-    class ShaderForVer3_0 extends CustomShaderStructure {
-        constructor();
-    }
-    class StandardShaderVersions {
-        static Ver3_0: string;
-    }
-    class CustomMaterial extends StandardMaterial_OldVer {
+    class CustomMaterial extends StandardMaterial {
         static ShaderIndexer: number;
         static ShaderIndexer: number;
         CustomParts: ShaderSpecialParts;
         CustomParts: ShaderSpecialParts;
-        ShaderVersion: CustomShaderStructure;
         _isCreatedShader: boolean;
         _isCreatedShader: boolean;
         _createdShaderName: string;
         _createdShaderName: string;
         _customUniform: string[];
         _customUniform: string[];
         _newUniforms: string[];
         _newUniforms: string[];
         _newUniformInstances: any[];
         _newUniformInstances: any[];
         _newSamplerInstances: Texture[];
         _newSamplerInstances: Texture[];
+        FragmentShader: string;
+        VertexShader: string;
         AttachAfterBind(mesh: Mesh, effect: Effect): void;
         AttachAfterBind(mesh: Mesh, effect: Effect): void;
         ReviewUniform(name: string, arr: string[]): string[];
         ReviewUniform(name: string, arr: string[]): string[];
-        Builder(shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: StandardMaterialDefines_OldVer): string;
-        SelectVersion(ver: string): void;
+        Builder(shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: StandardMaterialDefines): string;
         constructor(name: string, scene: Scene);
         constructor(name: string, scene: Scene);
         AddUniform(name: string, kind: string, param: any): CustomMaterial;
         AddUniform(name: string, kind: string, param: any): CustomMaterial;
         Fragment_Begin(shaderPart: string): CustomMaterial;
         Fragment_Begin(shaderPart: string): CustomMaterial;

Plik diff jest za duży
+ 19 - 1908
dist/preview release/materialsLibrary/babylon.customMaterial.js


Plik diff jest za duży
+ 1 - 2
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


Plik diff jest za duży
+ 22 - 1906
dist/preview release/materialsLibrary/babylonjs.materials.js


Plik diff jest za duży
+ 3 - 5
dist/preview release/materialsLibrary/babylonjs.materials.min.js


+ 4 - 282
dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts

@@ -506,278 +506,6 @@ declare module BABYLON {
 
 
 
 
 declare module BABYLON {
 declare module BABYLON {
-    class StandardMaterialDefines_OldVer extends MaterialDefines implements IImageProcessingConfigurationDefines {
-        DIFFUSE: boolean;
-        AMBIENT: boolean;
-        OPACITY: boolean;
-        OPACITYRGB: boolean;
-        REFLECTION: boolean;
-        EMISSIVE: boolean;
-        SPECULAR: boolean;
-        BUMP: boolean;
-        PARALLAX: boolean;
-        PARALLAXOCCLUSION: boolean;
-        SPECULAROVERALPHA: boolean;
-        CLIPPLANE: boolean;
-        ALPHATEST: boolean;
-        ALPHAFROMDIFFUSE: boolean;
-        POINTSIZE: boolean;
-        FOG: boolean;
-        SPECULARTERM: boolean;
-        DIFFUSEFRESNEL: boolean;
-        OPACITYFRESNEL: boolean;
-        REFLECTIONFRESNEL: boolean;
-        REFRACTIONFRESNEL: boolean;
-        EMISSIVEFRESNEL: boolean;
-        FRESNEL: boolean;
-        NORMAL: boolean;
-        UV1: boolean;
-        UV2: boolean;
-        VERTEXCOLOR: boolean;
-        VERTEXALPHA: boolean;
-        NUM_BONE_INFLUENCERS: number;
-        BonesPerMesh: number;
-        INSTANCES: boolean;
-        GLOSSINESS: boolean;
-        ROUGHNESS: boolean;
-        EMISSIVEASILLUMINATION: boolean;
-        LINKEMISSIVEWITHDIFFUSE: boolean;
-        REFLECTIONFRESNELFROMSPECULAR: boolean;
-        LIGHTMAP: boolean;
-        USELIGHTMAPASSHADOWMAP: boolean;
-        REFLECTIONMAP_3D: boolean;
-        REFLECTIONMAP_SPHERICAL: boolean;
-        REFLECTIONMAP_PLANAR: boolean;
-        REFLECTIONMAP_CUBIC: boolean;
-        REFLECTIONMAP_PROJECTION: boolean;
-        REFLECTIONMAP_SKYBOX: boolean;
-        REFLECTIONMAP_EXPLICIT: boolean;
-        REFLECTIONMAP_EQUIRECTANGULAR: boolean;
-        REFLECTIONMAP_EQUIRECTANGULAR_FIXED: boolean;
-        REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED: boolean;
-        INVERTCUBICMAP: boolean;
-        LOGARITHMICDEPTH: boolean;
-        REFRACTION: boolean;
-        REFRACTIONMAP_3D: boolean;
-        REFLECTIONOVERALPHA: boolean;
-        TWOSIDEDLIGHTING: boolean;
-        SHADOWFLOAT: boolean;
-        MORPHTARGETS: boolean;
-        MORPHTARGETS_NORMAL: boolean;
-        MORPHTARGETS_TANGENT: boolean;
-        NUM_MORPH_INFLUENCERS: number;
-        IMAGEPROCESSING: boolean;
-        VIGNETTE: boolean;
-        VIGNETTEBLENDMODEMULTIPLY: boolean;
-        VIGNETTEBLENDMODEOPAQUE: boolean;
-        TONEMAPPING: boolean;
-        CONTRAST: boolean;
-        COLORCURVES: boolean;
-        COLORGRADING: boolean;
-        COLORGRADING3D: boolean;
-        SAMPLER3DGREENDEPTH: boolean;
-        SAMPLER3DBGRMAP: boolean;
-        IMAGEPROCESSINGPOSTPROCESS: boolean;
-        EXPOSURE: boolean;
-        GRAIN: boolean;
-        constructor();
-        setReflectionMode(modeToEnable: string): void;
-    }
-    class StandardMaterial_OldVer extends PushMaterial {
-        private _diffuseTexture;
-        diffuseTexture: BaseTexture;
-        private _ambientTexture;
-        ambientTexture: BaseTexture;
-        private _opacityTexture;
-        opacityTexture: BaseTexture;
-        private _reflectionTexture;
-        reflectionTexture: BaseTexture;
-        private _emissiveTexture;
-        emissiveTexture: BaseTexture;
-        private _specularTexture;
-        specularTexture: BaseTexture;
-        private _bumpTexture;
-        bumpTexture: BaseTexture;
-        private _lightmapTexture;
-        lightmapTexture: BaseTexture;
-        private _refractionTexture;
-        refractionTexture: BaseTexture;
-        ambientColor: Color3;
-        diffuseColor: Color3;
-        specularColor: Color3;
-        emissiveColor: Color3;
-        specularPower: number;
-        private _useAlphaFromDiffuseTexture;
-        useAlphaFromDiffuseTexture: boolean;
-        private _useEmissiveAsIllumination;
-        useEmissiveAsIllumination: boolean;
-        private _linkEmissiveWithDiffuse;
-        linkEmissiveWithDiffuse: boolean;
-        private _useSpecularOverAlpha;
-        useSpecularOverAlpha: boolean;
-        private _useReflectionOverAlpha;
-        useReflectionOverAlpha: boolean;
-        private _disableLighting;
-        disableLighting: boolean;
-        private _useParallax;
-        useParallax: boolean;
-        private _useParallaxOcclusion;
-        useParallaxOcclusion: boolean;
-        parallaxScaleBias: number;
-        private _roughness;
-        roughness: number;
-        indexOfRefraction: number;
-        invertRefractionY: boolean;
-        private _useLightmapAsShadowmap;
-        useLightmapAsShadowmap: boolean;
-        private _diffuseFresnelParameters;
-        diffuseFresnelParameters: FresnelParameters;
-        private _opacityFresnelParameters;
-        opacityFresnelParameters: FresnelParameters;
-        private _reflectionFresnelParameters;
-        reflectionFresnelParameters: FresnelParameters;
-        private _refractionFresnelParameters;
-        refractionFresnelParameters: FresnelParameters;
-        private _emissiveFresnelParameters;
-        emissiveFresnelParameters: FresnelParameters;
-        private _useReflectionFresnelFromSpecular;
-        useReflectionFresnelFromSpecular: boolean;
-        private _useGlossinessFromSpecularMapAlpha;
-        useGlossinessFromSpecularMapAlpha: boolean;
-        private _maxSimultaneousLights;
-        maxSimultaneousLights: number;
-        /**
-         * If sets to true, x component of normal map value will invert (x = 1.0 - x).
-         */
-        private _invertNormalMapX;
-        invertNormalMapX: boolean;
-        /**
-         * If sets to true, y component of normal map value will invert (y = 1.0 - y).
-         */
-        private _invertNormalMapY;
-        invertNormalMapY: boolean;
-        /**
-         * If sets to true and backfaceCulling is false, normals will be flipped on the backside.
-         */
-        private _twoSidedLighting;
-        twoSidedLighting: boolean;
-        /**
-         * Default configuration related to image processing available in the standard Material.
-         */
-        protected _imageProcessingConfiguration: ImageProcessingConfiguration;
-        /**
-         * Gets the image processing configuration used either in this material.
-         */
-        /**
-         * Sets the Default image processing configuration used either in the this material.
-         *
-         * If sets to null, the scene one is in use.
-         */
-        imageProcessingConfiguration: ImageProcessingConfiguration;
-        /**
-         * Keep track of the image processing observer to allow dispose and replace.
-         */
-        private _imageProcessingObserver;
-        /**
-         * Attaches a new image processing configuration to the Standard Material.
-         * @param configuration
-         */
-        protected _attachImageProcessingConfiguration(configuration: Nullable<ImageProcessingConfiguration>): void;
-        /**
-         * Gets wether the color curves effect is enabled.
-         */
-        /**
-         * Sets wether the color curves effect is enabled.
-         */
-        cameraColorCurvesEnabled: boolean;
-        /**
-         * Gets wether the color grading effect is enabled.
-         */
-        /**
-         * Gets wether the color grading effect is enabled.
-         */
-        cameraColorGradingEnabled: boolean;
-        /**
-         * Gets wether tonemapping is enabled or not.
-         */
-        /**
-         * Sets wether tonemapping is enabled or not
-         */
-        cameraToneMappingEnabled: boolean;
-        /**
-         * The camera exposure used on this material.
-         * This property is here and not in the camera to allow controlling exposure without full screen post process.
-         * This corresponds to a photographic exposure.
-         */
-        /**
-         * The camera exposure used on this material.
-         * This property is here and not in the camera to allow controlling exposure without full screen post process.
-         * This corresponds to a photographic exposure.
-         */
-        cameraExposure: number;
-        /**
-         * Gets The camera contrast used on this material.
-         */
-        /**
-         * Sets The camera contrast used on this material.
-         */
-        cameraContrast: number;
-        /**
-         * Gets the Color Grading 2D Lookup Texture.
-         */
-        /**
-         * Sets the Color Grading 2D Lookup Texture.
-         */
-        cameraColorGradingTexture: Nullable<BaseTexture>;
-        protected _shouldTurnAlphaTestOn(mesh: AbstractMesh): boolean;
-        customShaderNameResolve: (shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: StandardMaterialDefines_OldVer) => string;
-        protected _renderTargets: SmartArray<RenderTargetTexture>;
-        protected _worldViewProjectionMatrix: Matrix;
-        protected _globalAmbientColor: Color3;
-        protected _useLogarithmicDepth: boolean;
-        constructor(name: string, scene: Scene);
-        getClassName(): string;
-        useLogarithmicDepth: boolean;
-        needAlphaBlending(): boolean;
-        needAlphaTesting(): boolean;
-        protected _shouldUseAlphaFromDiffuseTexture(): boolean;
-        getAlphaTestTexture(): BaseTexture;
-        /**
-         * Child classes can use it to update shaders
-         */
-        isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean;
-        buildUniformLayout(): void;
-        unbind(): void;
-        bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void;
-        getAnimatables(): IAnimatable[];
-        getActiveTextures(): BaseTexture[];
-        dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void;
-        clone(name: string): StandardMaterial_OldVer;
-        serialize(): any;
-        static Parse(source: any, scene: Scene, rootUrl: string): StandardMaterial_OldVer;
-        static _DiffuseTextureEnabled: boolean;
-        static DiffuseTextureEnabled: boolean;
-        static _AmbientTextureEnabled: boolean;
-        static AmbientTextureEnabled: boolean;
-        static _OpacityTextureEnabled: boolean;
-        static OpacityTextureEnabled: boolean;
-        static _ReflectionTextureEnabled: boolean;
-        static ReflectionTextureEnabled: boolean;
-        static _EmissiveTextureEnabled: boolean;
-        static EmissiveTextureEnabled: boolean;
-        static _SpecularTextureEnabled: boolean;
-        static SpecularTextureEnabled: boolean;
-        static _BumpTextureEnabled: boolean;
-        static BumpTextureEnabled: boolean;
-        static _LightmapTextureEnabled: boolean;
-        static LightmapTextureEnabled: boolean;
-        static _RefractionTextureEnabled: boolean;
-        static RefractionTextureEnabled: boolean;
-        static _ColorGradingTextureEnabled: boolean;
-        static ColorGradingTextureEnabled: boolean;
-        static _FresnelEnabled: boolean;
-        static FresnelEnabled: boolean;
-    }
     class CustomShaderStructure {
     class CustomShaderStructure {
         FragmentStore: string;
         FragmentStore: string;
         VertexStore: string;
         VertexStore: string;
@@ -797,26 +525,20 @@ declare module BABYLON {
         Vertex_Before_PositionUpdated: string;
         Vertex_Before_PositionUpdated: string;
         Vertex_Before_NormalUpdated: string;
         Vertex_Before_NormalUpdated: string;
     }
     }
-    class ShaderForVer3_0 extends CustomShaderStructure {
-        constructor();
-    }
-    class StandardShaderVersions {
-        static Ver3_0: string;
-    }
-    class CustomMaterial extends StandardMaterial_OldVer {
+    class CustomMaterial extends StandardMaterial {
         static ShaderIndexer: number;
         static ShaderIndexer: number;
         CustomParts: ShaderSpecialParts;
         CustomParts: ShaderSpecialParts;
-        ShaderVersion: CustomShaderStructure;
         _isCreatedShader: boolean;
         _isCreatedShader: boolean;
         _createdShaderName: string;
         _createdShaderName: string;
         _customUniform: string[];
         _customUniform: string[];
         _newUniforms: string[];
         _newUniforms: string[];
         _newUniformInstances: any[];
         _newUniformInstances: any[];
         _newSamplerInstances: Texture[];
         _newSamplerInstances: Texture[];
+        FragmentShader: string;
+        VertexShader: string;
         AttachAfterBind(mesh: Mesh, effect: Effect): void;
         AttachAfterBind(mesh: Mesh, effect: Effect): void;
         ReviewUniform(name: string, arr: string[]): string[];
         ReviewUniform(name: string, arr: string[]): string[];
-        Builder(shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: StandardMaterialDefines_OldVer): string;
-        SelectVersion(ver: string): void;
+        Builder(shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: StandardMaterialDefines): string;
         constructor(name: string, scene: Scene);
         constructor(name: string, scene: Scene);
         AddUniform(name: string, kind: string, param: any): CustomMaterial;
         AddUniform(name: string, kind: string, param: any): CustomMaterial;
         Fragment_Begin(shaderPart: string): CustomMaterial;
         Fragment_Begin(shaderPart: string): CustomMaterial;

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

@@ -290,23 +290,33 @@ declare module BABYLON.GLTF2 {
         /**
         /**
          * Represents the dielectric specular values for R, G and B.
          * Represents the dielectric specular values for R, G and B.
          */
          */
-        private static readonly dielectricSpecular;
+        private static readonly _dielectricSpecular;
         /**
         /**
          * Allows the maximum specular power to be defined for material calculations.
          * Allows the maximum specular power to be defined for material calculations.
          */
          */
-        private static maxSpecularPower;
-        private static epsilon;
+        private static _maxSpecularPower;
+        /**
+         * Numeric tolerance value
+         */
+        private static _epsilon;
+        /**
+         * Specifies if two colors are approximately equal in value.
+         * @param color1 - first color to compare to.
+         * @param color2 - second color to compare to.
+         * @param epsilon - threshold value
+         */
+        private static FuzzyEquals(color1, color2, epsilon);
         /**
         /**
          * Gets the materials from a Babylon scene and converts them to glTF materials.
          * 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
-         */
-        static ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
+         * @param scene - babylonjs scene.
+         * @param mimeType - texture mime type.
+         * @param images - array of images.
+         * @param textures - array of textures.
+         * @param materials - array of materials.
+         * @param imageData - mapping of texture names to base64 textures
+         * @param hasTextureCoords - specifies if texture coordinates are present on the material.
+         */
+        static _ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
             [fileName: string]: {
             [fileName: string]: {
                 data: Uint8Array;
                 data: Uint8Array;
                 mimeType: ImageMimeType;
                 mimeType: ImageMimeType;
@@ -317,19 +327,19 @@ declare module BABYLON.GLTF2 {
          * @param originalMaterial - original glTF material.
          * @param originalMaterial - original glTF material.
          * @returns glTF material without texture parameters
          * @returns glTF material without texture parameters
          */
          */
-        static StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
+        static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
         /**
         /**
          * Specifies if the material has any texture parameters present.
          * Specifies if the material has any texture parameters present.
          * @param material - glTF Material.
          * @param material - glTF Material.
          * @returns boolean specifying if texture parameters are present
          * @returns boolean specifying if texture parameters are present
          */
          */
-        static HasTexturesPresent(material: IMaterial): boolean;
+        static _HasTexturesPresent(material: IMaterial): boolean;
         /**
         /**
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
          * @param babylonStandardMaterial
          * @param babylonStandardMaterial
          * @returns - glTF Metallic Roughness Material representation
          * @returns - glTF Metallic Roughness Material representation
          */
          */
-        static ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
+        static _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
         /**
         /**
          * Computes the metallic factor
          * Computes the metallic factor
          * @param diffuse - diffused value
          * @param diffuse - diffused value
@@ -337,13 +347,13 @@ declare module BABYLON.GLTF2 {
          * @param oneMinusSpecularStrength - one minus the specular strength
          * @param oneMinusSpecularStrength - one minus the specular strength
          * @returns - metallic value
          * @returns - metallic value
          */
          */
-        static SolveMetallic(diffuse: number, specular: number, oneMinusSpecularStrength: number): number;
+        static _SolveMetallic(diffuse: number, specular: number, oneMinusSpecularStrength: number): number;
         /**
         /**
          * Gets the glTF alpha mode from the Babylon Material
          * Gets the glTF alpha mode from the Babylon Material
          * @param babylonMaterial - Babylon Material
          * @param babylonMaterial - Babylon Material
          * @returns - The Babylon alpha mode value
          * @returns - The Babylon alpha mode value
          */
          */
-        static GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
+        static _GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
         /**
         /**
          * Converts a Babylon Standard Material to a glTF Material.
          * Converts a Babylon Standard Material to a glTF Material.
          * @param babylonStandardMaterial - BJS Standard Material.
          * @param babylonStandardMaterial - BJS Standard Material.
@@ -354,7 +364,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData - map of image file name to data.
          * @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.
          * @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: {
+        static _ConvertStandardMaterial(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
             [fileName: string]: {
             [fileName: string]: {
                 data: Uint8Array;
                 data: Uint8Array;
                 mimeType: ImageMimeType;
                 mimeType: ImageMimeType;
@@ -370,25 +380,66 @@ declare module BABYLON.GLTF2 {
          * @param imageData - map of image file name to data.
          * @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.
          * @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: {
+        static _ConvertPBRMetallicRoughnessMaterial(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
             [fileName: string]: {
             [fileName: string]: {
                 data: Uint8Array;
                 data: Uint8Array;
                 mimeType: ImageMimeType;
                 mimeType: ImageMimeType;
             };
             };
         }, hasTextureCoords: boolean): void;
         }, hasTextureCoords: boolean): void;
         /**
         /**
+         * Converts an image typed array buffer to a base64 image.
+         * @param buffer - typed array buffer.
+         * @param width - width of the image.
+         * @param height - height of the image.
+         * @param mimeType - mimetype of the image.
+         * @returns - base64 image string.
+         */
+        private static _CreateBase64FromCanvas(buffer, width, height, mimeType);
+        /**
+         * Generates a white texture based on the specified width and height.
+         * @param width - width of the texture in pixels.
+         * @param height - height of the texture in pixels.
+         * @param scene - babylonjs scene.
+         * @returns - white texture.
+         */
+        private static _CreateWhiteTexture(width, height, scene);
+        /**
+         * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null.
+         * @param texture1 - first texture to resize.
+         * @param texture2 - second texture to resize.
+         * @param scene - babylonjs scene.
+         * @returns resized textures or null.
+         */
+        private static _ResizeTexturesToSameDimensions(texture1, texture2, scene);
+        /**
+         * Convert Specular Glossiness Textures to Metallic Roughness.
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
          * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
          * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
+         * @param diffuseTexture - texture used to store diffuse information.
+         * @param specularGlossinessTexture - texture used to store specular and glossiness information.
+         * @param factors - specular glossiness material factors.
+         * @param mimeType - the mime type to use for the texture.
+         * @returns pbr metallic roughness interface or null.
+         */
+        private static _ConvertSpecularGlossinessTexturesToMetallicRoughness(diffuseTexture, specularGlossinessTexture, factors, mimeType);
+        /**
+         * Converts specular glossiness material properties to metallic roughness.
+         * @param specularGlossiness - interface with specular glossiness material properties.
+         * @returns - interface with metallic roughness material properties.
+         */
+        private static _ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+        /**
+         * Calculates the surface reflectance, independent of lighting conditions.
          * @param color - Color source to calculate brightness from.
          * @param color - Color source to calculate brightness from.
          * @returns number representing the perceived brightness, or zero if color is undefined.
          * @returns number representing the perceived brightness, or zero if color is undefined.
          */
          */
-        static GetPerceivedBrightness(color: Color3): number;
+        private static _GetPerceivedBrightness(color);
         /**
         /**
          * Returns the maximum color component value.
          * Returns the maximum color component value.
          * @param color
          * @param color
          * @returns maximum color component value, or zero if color is null or undefined.
          * @returns maximum color component value, or zero if color is null or undefined.
          */
          */
-        static GetMaxComponent(color: Color3): number;
+        private static _GetMaxComponent(color);
         /**
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
          * @param babylonPBRMaterial - BJS PBR Metallic Roughness Material.
          * @param babylonPBRMaterial - BJS PBR Metallic Roughness Material.
@@ -399,7 +450,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData - map of image file name to data.
          * @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.
          * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
          */
          */
-        static ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
+        static _ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
             [fileName: string]: {
             [fileName: string]: {
                 data: Uint8Array;
                 data: Uint8Array;
                 mimeType: ImageMimeType;
                 mimeType: ImageMimeType;
@@ -412,13 +463,19 @@ declare module BABYLON.GLTF2 {
          * @param images - Array of glTF images.
          * @param images - Array of glTF images.
          * @param textures - Array of glTF textures.
          * @param textures - Array of glTF textures.
          * @param imageData - map of image file name and data.
          * @param imageData - map of image file name and data.
-         * @return - glTF texture, or null if the texture format is not supported.
+         * @return - glTF texture info, or null if the texture format is not supported.
          */
          */
-        static ExportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }): Nullable<ITextureInfo>;
+        private static _ExportTexture(babylonTexture, mimeType, images, textures, imageData);
+        /**
+         * Builds a texture from base64 string.
+         * @param base64Texture - base64 texture string.
+         * @param textureName - Name to use for the texture.
+         * @param mimeType - image mime type for the texture.
+         * @param images - array of images.
+         * @param textures - array of textures.
+         * @param imageData - map of image data.
+         * @returns - glTF texture info, or null if the texture format is not supported.
+         */
+        private static _GetTextureInfoFromBase64(base64Texture, textureName, mimeType, images, textures, imageData);
     }
     }
 }
 }

+ 385 - 158
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -613,13 +613,13 @@ var BABYLON;
                                 else {
                                 else {
                                     BABYLON.Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
                                     BABYLON.Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
                                 }
                                 }
-                                if (materialIndex != null) {
-                                    if (uvCoordsPresent || !GLTF2._GLTFMaterial.HasTexturesPresent(this.materials[materialIndex])) {
+                                if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
+                                    if (uvCoordsPresent || !GLTF2._GLTFMaterial._HasTexturesPresent(this.materials[materialIndex])) {
                                         meshPrimitive.material = materialIndex;
                                         meshPrimitive.material = materialIndex;
                                     }
                                     }
                                     else {
                                     else {
                                         // If no texture coordinate information is present, make a copy of the material without the textures to be glTF compliant.
                                         // If no texture coordinate information is present, make a copy of the material without the textures to be glTF compliant.
-                                        var newMat = GLTF2._GLTFMaterial.StripTexturesFromMaterial(this.materials[materialIndex]);
+                                        var newMat = GLTF2._GLTFMaterial._StripTexturesFromMaterial(this.materials[materialIndex]);
                                         this.materials.push(newMat);
                                         this.materials.push(newMat);
                                         meshPrimitive.material = this.materials.length - 1;
                                         meshPrimitive.material = this.materials.length - 1;
                                     }
                                     }
@@ -642,7 +642,7 @@ var BABYLON;
                 if (babylonScene.meshes.length) {
                 if (babylonScene.meshes.length) {
                     var babylonMeshes = babylonScene.meshes;
                     var babylonMeshes = babylonScene.meshes;
                     var scene = { nodes: new Array() };
                     var scene = { nodes: new Array() };
-                    GLTF2._GLTFMaterial.ConvertMaterialsToGLTF(babylonScene.materials, "image/jpeg" /* JPEG */, this.images, this.textures, this.materials, this.imageData, true);
+                    GLTF2._GLTFMaterial._ConvertMaterialsToGLTF(babylonScene.materials, "image/png" /* PNG */, this.images, this.textures, this.materials, this.imageData, true);
                     var result = this.createNodeMap(babylonScene, byteOffset);
                     var result = this.createNodeMap(babylonScene, byteOffset);
                     this.nodeMap = result.nodeMap;
                     this.nodeMap = result.nodeMap;
                     this.totalByteLength = result.byteOffset;
                     this.totalByteLength = result.byteOffset;
@@ -799,29 +799,40 @@ var BABYLON;
             function _GLTFMaterial() {
             function _GLTFMaterial() {
             }
             }
             /**
             /**
+             * Specifies if two colors are approximately equal in value.
+             * @param color1 - first color to compare to.
+             * @param color2 - second color to compare to.
+             * @param epsilon - threshold value
+             */
+            _GLTFMaterial.FuzzyEquals = function (color1, color2, epsilon) {
+                return BABYLON.Scalar.WithinEpsilon(color1.r, color2.r, epsilon) &&
+                    BABYLON.Scalar.WithinEpsilon(color1.g, color2.g, epsilon) &&
+                    BABYLON.Scalar.WithinEpsilon(color1.b, color2.b, epsilon);
+            };
+            /**
              * Gets the materials from a Babylon scene and converts them to glTF materials.
              * Gets the materials from a Babylon scene and converts them to glTF materials.
-             * @param scene
-             * @param mimeType
-             * @param images
-             * @param textures
-             * @param materials
-             * @param imageData
-             * @param hasTextureCoords
+             * @param scene - babylonjs scene.
+             * @param mimeType - texture mime type.
+             * @param images - array of images.
+             * @param textures - array of textures.
+             * @param materials - array of materials.
+             * @param imageData - mapping of texture names to base64 textures
+             * @param hasTextureCoords - specifies if texture coordinates are present on the material.
              */
              */
-            _GLTFMaterial.ConvertMaterialsToGLTF = function (babylonMaterials, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+            _GLTFMaterial._ConvertMaterialsToGLTF = function (babylonMaterials, mimeType, images, textures, materials, imageData, hasTextureCoords) {
                 for (var i = 0; i < babylonMaterials.length; ++i) {
                 for (var i = 0; i < babylonMaterials.length; ++i) {
                     var babylonMaterial = babylonMaterials[i];
                     var babylonMaterial = babylonMaterials[i];
                     if (babylonMaterial instanceof BABYLON.StandardMaterial) {
                     if (babylonMaterial instanceof BABYLON.StandardMaterial) {
-                        _GLTFMaterial.ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                        _GLTFMaterial._ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                     }
                     }
                     else if (babylonMaterial instanceof BABYLON.PBRMetallicRoughnessMaterial) {
                     else if (babylonMaterial instanceof BABYLON.PBRMetallicRoughnessMaterial) {
-                        _GLTFMaterial.ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                        _GLTFMaterial._ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                     }
                     }
                     else if (babylonMaterial instanceof BABYLON.PBRMaterial) {
                     else if (babylonMaterial instanceof BABYLON.PBRMaterial) {
-                        _GLTFMaterial.ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                        _GLTFMaterial._ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                     }
                     }
                     else {
                     else {
-                        BABYLON.Tools.Error("Unsupported material type: " + babylonMaterial.name);
+                        throw new Error("Unsupported material type: " + babylonMaterial.name);
                     }
                     }
                 }
                 }
             };
             };
@@ -830,7 +841,7 @@ var BABYLON;
              * @param originalMaterial - original glTF material.
              * @param originalMaterial - original glTF material.
              * @returns glTF material without texture parameters
              * @returns glTF material without texture parameters
              */
              */
-            _GLTFMaterial.StripTexturesFromMaterial = function (originalMaterial) {
+            _GLTFMaterial._StripTexturesFromMaterial = function (originalMaterial) {
                 var newMaterial = {};
                 var newMaterial = {};
                 if (originalMaterial) {
                 if (originalMaterial) {
                     newMaterial.name = originalMaterial.name;
                     newMaterial.name = originalMaterial.name;
@@ -853,7 +864,7 @@ var BABYLON;
              * @param material - glTF Material.
              * @param material - glTF Material.
              * @returns boolean specifying if texture parameters are present
              * @returns boolean specifying if texture parameters are present
              */
              */
-            _GLTFMaterial.HasTexturesPresent = function (material) {
+            _GLTFMaterial._HasTexturesPresent = function (material) {
                 if (material.emissiveTexture || material.normalTexture || material.occlusionTexture) {
                 if (material.emissiveTexture || material.normalTexture || material.occlusionTexture) {
                     return true;
                     return true;
                 }
                 }
@@ -870,7 +881,7 @@ var BABYLON;
              * @param babylonStandardMaterial
              * @param babylonStandardMaterial
              * @returns - glTF Metallic Roughness Material representation
              * @returns - glTF Metallic Roughness Material representation
              */
              */
-            _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness = function (babylonStandardMaterial) {
+            _GLTFMaterial._ConvertToGLTFPBRMetallicRoughness = function (babylonStandardMaterial) {
                 var P0 = new BABYLON.Vector2(0, 1);
                 var P0 = new BABYLON.Vector2(0, 1);
                 var P1 = new BABYLON.Vector2(0, 0.1);
                 var P1 = new BABYLON.Vector2(0, 0.1);
                 var P2 = new BABYLON.Vector2(0, 0.1);
                 var P2 = new BABYLON.Vector2(0, 0.1);
@@ -884,7 +895,7 @@ var BABYLON;
                  * @param p3 - fourth control point.
                  * @param p3 - fourth control point.
                  * @returns - number result of cubic bezier curve at the specified t.
                  * @returns - number result of cubic bezier curve at the specified t.
                  */
                  */
-                function cubicBezierCurve(t, p0, p1, p2, p3) {
+                function _cubicBezierCurve(t, p0, p1, p2, p3) {
                     return ((1 - t) * (1 - t) * (1 - t) * p0 +
                     return ((1 - t) * (1 - t) * (1 - t) * p0 +
                         3 * (1 - t) * (1 - t) * t * p1 +
                         3 * (1 - t) * (1 - t) * t * p1 +
                         3 * (1 - t) * t * t * p2 +
                         3 * (1 - t) * t * t * p2 +
@@ -897,14 +908,14 @@ var BABYLON;
                  * @param specularPower - specular power of standard material.
                  * @param specularPower - specular power of standard material.
                  * @returns - Number representing the roughness value.
                  * @returns - Number representing the roughness value.
                  */
                  */
-                function solveForRoughness(specularPower) {
+                function _solveForRoughness(specularPower) {
                     var t = Math.pow(specularPower / P3.x, 0.333333);
                     var t = Math.pow(specularPower / P3.x, 0.333333);
-                    return cubicBezierCurve(t, P0.y, P1.y, P2.y, P3.y);
+                    return _cubicBezierCurve(t, P0.y, P1.y, P2.y, P3.y);
                 }
                 }
                 var diffuse = babylonStandardMaterial.diffuseColor.toLinearSpace().scale(0.5);
                 var diffuse = babylonStandardMaterial.diffuseColor.toLinearSpace().scale(0.5);
                 var opacity = babylonStandardMaterial.alpha;
                 var opacity = babylonStandardMaterial.alpha;
-                var specularPower = BABYLON.Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this.maxSpecularPower);
-                var roughness = solveForRoughness(specularPower);
+                var specularPower = BABYLON.Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this._maxSpecularPower);
+                var roughness = _solveForRoughness(specularPower);
                 var glTFPbrMetallicRoughness = {
                 var glTFPbrMetallicRoughness = {
                     baseColorFactor: [
                     baseColorFactor: [
                         diffuse.r,
                         diffuse.r,
@@ -924,14 +935,14 @@ var BABYLON;
              * @param oneMinusSpecularStrength - one minus the specular strength
              * @param oneMinusSpecularStrength - one minus the specular strength
              * @returns - metallic value
              * @returns - metallic value
              */
              */
-            _GLTFMaterial.SolveMetallic = function (diffuse, specular, oneMinusSpecularStrength) {
-                if (specular < _GLTFMaterial.dielectricSpecular.r) {
-                    _GLTFMaterial.dielectricSpecular;
+            _GLTFMaterial._SolveMetallic = function (diffuse, specular, oneMinusSpecularStrength) {
+                if (specular < _GLTFMaterial._dielectricSpecular.r) {
+                    _GLTFMaterial._dielectricSpecular;
                     return 0;
                     return 0;
                 }
                 }
-                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 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;
                 var D = b * b - 4.0 * a * c;
                 return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a), 0, 1);
                 return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a), 0, 1);
             };
             };
@@ -940,7 +951,7 @@ var BABYLON;
              * @param babylonMaterial - Babylon Material
              * @param babylonMaterial - Babylon Material
              * @returns - The Babylon alpha mode value
              * @returns - The Babylon alpha mode value
              */
              */
-            _GLTFMaterial.GetAlphaMode = function (babylonMaterial) {
+            _GLTFMaterial._GetAlphaMode = function (babylonMaterial) {
                 if (babylonMaterial instanceof BABYLON.StandardMaterial) {
                 if (babylonMaterial instanceof BABYLON.StandardMaterial) {
                     var babylonStandardMaterial = babylonMaterial;
                     var babylonStandardMaterial = babylonMaterial;
                     if ((babylonStandardMaterial.alpha != 1.0) ||
                     if ((babylonStandardMaterial.alpha != 1.0) ||
@@ -1008,11 +1019,11 @@ var BABYLON;
              * @param imageData - map of image file name to data.
              * @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.
              * @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) {
+            _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");
                 BABYLON.Tools.Warn(babylonStandardMaterial.name + ": Standard Material is currently not fully supported/implemented in glTF serializer");
-                var glTFPbrMetallicRoughness = _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
+                var glTFPbrMetallicRoughness = _GLTFMaterial._ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
                 var glTFMaterial = { name: babylonStandardMaterial.name };
                 var glTFMaterial = { name: babylonStandardMaterial.name };
-                if (babylonStandardMaterial.backFaceCulling) {
+                if (babylonStandardMaterial.backFaceCulling != null && !babylonStandardMaterial.backFaceCulling) {
                     if (!babylonStandardMaterial.twoSidedLighting) {
                     if (!babylonStandardMaterial.twoSidedLighting) {
                         BABYLON.Tools.Warn(babylonStandardMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
                         BABYLON.Tools.Warn(babylonStandardMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
                     }
                     }
@@ -1020,26 +1031,26 @@ var BABYLON;
                 }
                 }
                 if (hasTextureCoords) {
                 if (hasTextureCoords) {
                     if (babylonStandardMaterial.diffuseTexture) {
                     if (babylonStandardMaterial.diffuseTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, imageData);
                         if (glTFTexture != null) {
                         if (glTFTexture != null) {
                             glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                             glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                         }
                         }
                     }
                     }
                     if (babylonStandardMaterial.bumpTexture) {
                     if (babylonStandardMaterial.bumpTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, imageData);
                         if (glTFTexture) {
                         if (glTFTexture) {
                             glTFMaterial.normalTexture = glTFTexture;
                             glTFMaterial.normalTexture = glTFTexture;
                         }
                         }
                     }
                     }
                     if (babylonStandardMaterial.emissiveTexture) {
                     if (babylonStandardMaterial.emissiveTexture) {
-                        var glTFEmissiveTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, imageData);
+                        var glTFEmissiveTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, imageData);
                         if (glTFEmissiveTexture) {
                         if (glTFEmissiveTexture) {
                             glTFMaterial.emissiveTexture = glTFEmissiveTexture;
                             glTFMaterial.emissiveTexture = glTFEmissiveTexture;
                         }
                         }
                         glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
                         glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
                     }
                     }
                     if (babylonStandardMaterial.ambientTexture) {
                     if (babylonStandardMaterial.ambientTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
                         if (glTFTexture) {
                         if (glTFTexture) {
                             var occlusionTexture = {
                             var occlusionTexture = {
                                 index: glTFTexture.index
                                 index: glTFTexture.index
@@ -1057,7 +1068,7 @@ var BABYLON;
                         BABYLON.Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
                         BABYLON.Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
                     }
                     }
                 }
                 }
-                if (babylonStandardMaterial.emissiveColor) {
+                if (babylonStandardMaterial.emissiveColor && !this.FuzzyEquals(babylonStandardMaterial.emissiveColor, BABYLON.Color3.Black(), this._epsilon)) {
                     glTFMaterial.emissiveFactor = babylonStandardMaterial.emissiveColor.asArray();
                     glTFMaterial.emissiveFactor = babylonStandardMaterial.emissiveColor.asArray();
                 }
                 }
                 glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
                 glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
@@ -1073,7 +1084,7 @@ var BABYLON;
              * @param imageData - map of image file name to data.
              * @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.
              * @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) {
+            _GLTFMaterial._ConvertPBRMetallicRoughnessMaterial = function (babylonPBRMetalRoughMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
                 var glTFPbrMetallicRoughness = {};
                 var glTFPbrMetallicRoughness = {};
                 if (babylonPBRMetalRoughMaterial.baseColor) {
                 if (babylonPBRMetalRoughMaterial.baseColor) {
                     glTFPbrMetallicRoughness.baseColorFactor = [
                     glTFPbrMetallicRoughness.baseColorFactor = [
@@ -1083,10 +1094,10 @@ var BABYLON;
                         babylonPBRMetalRoughMaterial.alpha
                         babylonPBRMetalRoughMaterial.alpha
                     ];
                     ];
                 }
                 }
-                if (babylonPBRMetalRoughMaterial.metallic != null) {
+                if (babylonPBRMetalRoughMaterial.metallic != null && babylonPBRMetalRoughMaterial.metallic !== 1) {
                     glTFPbrMetallicRoughness.metallicFactor = babylonPBRMetalRoughMaterial.metallic;
                     glTFPbrMetallicRoughness.metallicFactor = babylonPBRMetalRoughMaterial.metallic;
                 }
                 }
-                if (babylonPBRMetalRoughMaterial.roughness != null) {
+                if (babylonPBRMetalRoughMaterial.roughness != null && babylonPBRMetalRoughMaterial.roughness !== 1) {
                     glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMetalRoughMaterial.roughness;
                     glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMetalRoughMaterial.roughness;
                 }
                 }
                 var glTFMaterial = {
                 var glTFMaterial = {
@@ -1097,19 +1108,19 @@ var BABYLON;
                 }
                 }
                 if (hasTextureCoords) {
                 if (hasTextureCoords) {
                     if (babylonPBRMetalRoughMaterial.baseTexture != null) {
                     if (babylonPBRMetalRoughMaterial.baseTexture != null) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, imageData);
                         if (glTFTexture != null) {
                         if (glTFTexture != null) {
                             glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                             glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                         }
                         }
                     }
                     }
                     if (babylonPBRMetalRoughMaterial.normalTexture) {
                     if (babylonPBRMetalRoughMaterial.normalTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, imageData);
                         if (glTFTexture) {
                         if (glTFTexture) {
                             glTFMaterial.normalTexture = glTFTexture;
                             glTFMaterial.normalTexture = glTFTexture;
                         }
                         }
                     }
                     }
                     if (babylonPBRMetalRoughMaterial.occlusionTexture) {
                     if (babylonPBRMetalRoughMaterial.occlusionTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
                         if (glTFTexture) {
                         if (glTFTexture) {
                             glTFMaterial.occlusionTexture = glTFTexture;
                             glTFMaterial.occlusionTexture = glTFTexture;
                             if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
                             if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
@@ -1118,17 +1129,17 @@ var BABYLON;
                         }
                         }
                     }
                     }
                     if (babylonPBRMetalRoughMaterial.emissiveTexture) {
                     if (babylonPBRMetalRoughMaterial.emissiveTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, imageData);
                         if (glTFTexture != null) {
                         if (glTFTexture != null) {
                             glTFMaterial.emissiveTexture = glTFTexture;
                             glTFMaterial.emissiveTexture = glTFTexture;
                         }
                         }
                     }
                     }
                 }
                 }
-                if (babylonPBRMetalRoughMaterial.emissiveColor.equalsFloats(0.0, 0.0, 0.0)) {
+                if (this.FuzzyEquals(babylonPBRMetalRoughMaterial.emissiveColor, BABYLON.Color3.Black(), this._epsilon)) {
                     glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
                     glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
                 }
                 }
                 if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
                 if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
-                    var alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMetalRoughMaterial);
+                    var alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMetalRoughMaterial);
                     if (alphaMode !== "OPAQUE" /* OPAQUE */) {
                     if (alphaMode !== "OPAQUE" /* OPAQUE */) {
                         glTFMaterial.alphaMode = alphaMode;
                         glTFMaterial.alphaMode = alphaMode;
                         if (alphaMode === "BLEND" /* BLEND */) {
                         if (alphaMode === "BLEND" /* BLEND */) {
@@ -1140,12 +1151,213 @@ var BABYLON;
                 materials.push(glTFMaterial);
                 materials.push(glTFMaterial);
             };
             };
             /**
             /**
+             * Converts an image typed array buffer to a base64 image.
+             * @param buffer - typed array buffer.
+             * @param width - width of the image.
+             * @param height - height of the image.
+             * @param mimeType - mimetype of the image.
+             * @returns - base64 image string.
+             */
+            _GLTFMaterial._CreateBase64FromCanvas = function (buffer, width, height, mimeType) {
+                var imageCanvas = document.createElement('canvas');
+                imageCanvas.id = "WriteCanvas";
+                var ctx = imageCanvas.getContext('2d');
+                imageCanvas.width = width;
+                imageCanvas.height = height;
+                var imgData = ctx.createImageData(width, height);
+                imgData.data.set(buffer);
+                ctx.putImageData(imgData, 0, 0);
+                return imageCanvas.toDataURL(mimeType);
+            };
+            /**
+             * Generates a white texture based on the specified width and height.
+             * @param width - width of the texture in pixels.
+             * @param height - height of the texture in pixels.
+             * @param scene - babylonjs scene.
+             * @returns - white texture.
+             */
+            _GLTFMaterial._CreateWhiteTexture = function (width, height, scene) {
+                var data = new Uint8Array(width * height * 4);
+                for (var i = 0; i < data.length; ++i) {
+                    data[i] = 255;
+                }
+                var rawTexture = BABYLON.RawTexture.CreateRGBATexture(data, width, height, scene);
+                return rawTexture;
+            };
+            /**
+             * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null.
+             * @param texture1 - first texture to resize.
+             * @param texture2 - second texture to resize.
+             * @param scene - babylonjs scene.
+             * @returns resized textures or null.
+             */
+            _GLTFMaterial._ResizeTexturesToSameDimensions = function (texture1, texture2, scene) {
+                var texture1Size = texture1 ? texture1.getSize() : { width: 0, height: 0 };
+                var texture2Size = texture2 ? texture2.getSize() : { width: 0, height: 0 };
+                var resizedTexture1;
+                var resizedTexture2;
+                if (texture1Size.width < texture2Size.width) {
+                    if (texture1) {
+                        resizedTexture1 = BABYLON.TextureTools.CreateResizedCopy(texture1, texture2Size.width, texture2Size.height, true);
+                    }
+                    else {
+                        resizedTexture1 = this._CreateWhiteTexture(texture2Size.width, texture2Size.height, scene);
+                    }
+                    resizedTexture2 = texture2;
+                }
+                else if (texture1Size.width > texture2Size.width) {
+                    if (texture2) {
+                        resizedTexture2 = BABYLON.TextureTools.CreateResizedCopy(texture2, texture1Size.width, texture1Size.height, true);
+                    }
+                    else {
+                        resizedTexture2 = this._CreateWhiteTexture(texture1Size.width, texture1Size.height, scene);
+                    }
+                    resizedTexture1 = texture1;
+                }
+                else {
+                    resizedTexture1 = texture1;
+                    resizedTexture2 = texture2;
+                }
+                return {
+                    "texture1": resizedTexture1,
+                    "texture2": resizedTexture2
+                };
+            };
+            /**
+             * Convert Specular Glossiness Textures to Metallic Roughness.
              * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
              * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
              * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
              * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
+             * @param diffuseTexture - texture used to store diffuse information.
+             * @param specularGlossinessTexture - texture used to store specular and glossiness information.
+             * @param factors - specular glossiness material factors.
+             * @param mimeType - the mime type to use for the texture.
+             * @returns pbr metallic roughness interface or null.
+             */
+            _GLTFMaterial._ConvertSpecularGlossinessTexturesToMetallicRoughness = function (diffuseTexture, specularGlossinessTexture, factors, mimeType) {
+                if (!(diffuseTexture || specularGlossinessTexture)) {
+                    return null;
+                }
+                var scene = diffuseTexture ? diffuseTexture.getScene() : specularGlossinessTexture.getScene();
+                if (!scene) {
+                    throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Scene from textures is missing!");
+                }
+                var resizedTextures = this._ResizeTexturesToSameDimensions(diffuseTexture, specularGlossinessTexture, scene);
+                var diffuseSize = resizedTextures.texture1.getSize();
+                var diffuseBuffer;
+                var specularGlossinessBuffer;
+                var width = diffuseSize.width;
+                var height = diffuseSize.height;
+                var pixels = (resizedTextures.texture1.readPixels());
+                if (pixels instanceof Uint8Array) {
+                    diffuseBuffer = (resizedTextures.texture1.readPixels());
+                }
+                else {
+                    throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture1.name);
+                }
+                pixels = resizedTextures.texture2.readPixels();
+                if (pixels instanceof Uint8Array) {
+                    specularGlossinessBuffer = (resizedTextures.texture2.readPixels());
+                }
+                else {
+                    throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture2.name);
+                }
+                var byteLength = specularGlossinessBuffer.byteLength;
+                var metallicRoughnessBuffer = new Uint8Array(byteLength);
+                var baseColorBuffer = new Uint8Array(byteLength);
+                var strideSize = 4;
+                var maxBaseColor = BABYLON.Color3.Black();
+                var maxMetallic = 0;
+                var maxRoughness = 0;
+                for (var h = 0; h < height; ++h) {
+                    for (var w = 0; w < width; ++w) {
+                        var offset = (width * h + w) * strideSize;
+                        var diffuseColor = BABYLON.Color3.FromInts(diffuseBuffer[offset], diffuseBuffer[offset + 1], diffuseBuffer[offset + 2]).multiply(factors.diffuseColor);
+                        var specularColor = BABYLON.Color3.FromInts(specularGlossinessBuffer[offset], specularGlossinessBuffer[offset + 1], specularGlossinessBuffer[offset + 2]).multiply(factors.specularColor);
+                        var glossiness = (specularGlossinessBuffer[offset + 3] / 255) * factors.glossiness;
+                        var specularGlossiness = {
+                            diffuseColor: diffuseColor,
+                            specularColor: specularColor,
+                            glossiness: glossiness
+                        };
+                        var metallicRoughness = this._ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+                        maxBaseColor.r = Math.max(maxBaseColor.r, metallicRoughness.baseColor.r);
+                        maxBaseColor.g = Math.max(maxBaseColor.g, metallicRoughness.baseColor.g);
+                        maxBaseColor.b = Math.max(maxBaseColor.b, metallicRoughness.baseColor.b);
+                        maxMetallic = Math.max(maxMetallic, metallicRoughness.metallic);
+                        maxRoughness = Math.max(maxRoughness, metallicRoughness.roughness);
+                        baseColorBuffer[offset] = metallicRoughness.baseColor.r * 255;
+                        baseColorBuffer[offset + 1] = metallicRoughness.baseColor.g * 255;
+                        baseColorBuffer[offset + 2] = metallicRoughness.baseColor.b * 255;
+                        baseColorBuffer[offset + 3] = resizedTextures.texture1.hasAlpha ? diffuseBuffer[offset + 3] : 255;
+                        metallicRoughnessBuffer[offset] = 255;
+                        metallicRoughnessBuffer[offset + 1] = metallicRoughness.roughness * 255;
+                        metallicRoughnessBuffer[offset + 2] = metallicRoughness.metallic * 255;
+                        metallicRoughnessBuffer[offset + 3] = 255;
+                    }
+                }
+                // Retrieves the metallic roughness factors from the maximum texture values.
+                var metallicRoughnessFactors = {
+                    baseColor: maxBaseColor,
+                    metallic: maxMetallic,
+                    roughness: maxRoughness
+                };
+                var writeOutMetallicRoughnessTexture = false;
+                var writeOutBaseColorTexture = false;
+                for (var h = 0; h < height; ++h) {
+                    for (var w = 0; w < width; ++w) {
+                        var destinationOffset = (width * h + w) * strideSize;
+                        baseColorBuffer[destinationOffset] /= metallicRoughnessFactors.baseColor.r > this._epsilon ? metallicRoughnessFactors.baseColor.r : 1;
+                        baseColorBuffer[destinationOffset + 1] /= metallicRoughnessFactors.baseColor.g > this._epsilon ? metallicRoughnessFactors.baseColor.g : 1;
+                        baseColorBuffer[destinationOffset + 2] /= metallicRoughnessFactors.baseColor.b > this._epsilon ? metallicRoughnessFactors.baseColor.b : 1;
+                        var baseColorPixel = new BABYLON.Color3(baseColorBuffer[destinationOffset], baseColorBuffer[destinationOffset + 1], baseColorBuffer[destinationOffset + 2]);
+                        if (!this.FuzzyEquals(baseColorPixel, BABYLON.Color3.White(), this._epsilon)) {
+                            writeOutBaseColorTexture = true;
+                        }
+                        metallicRoughnessBuffer[destinationOffset + 1] /= metallicRoughnessFactors.roughness > this._epsilon ? metallicRoughnessFactors.roughness : 1;
+                        metallicRoughnessBuffer[destinationOffset + 2] /= metallicRoughnessFactors.metallic > this._epsilon ? metallicRoughnessFactors.metallic : 1;
+                        var metallicRoughnessPixel = new BABYLON.Color3(metallicRoughnessBuffer[destinationOffset], metallicRoughnessBuffer[destinationOffset + 1], metallicRoughnessBuffer[destinationOffset + 2]);
+                        if (!this.FuzzyEquals(metallicRoughnessPixel, BABYLON.Color3.White(), this._epsilon)) {
+                            writeOutMetallicRoughnessTexture = true;
+                        }
+                    }
+                }
+                if (writeOutMetallicRoughnessTexture) {
+                    var metallicRoughnessBase64 = this._CreateBase64FromCanvas(metallicRoughnessBuffer, width, height, mimeType);
+                    metallicRoughnessFactors.metallicRoughnessTextureBase64 = metallicRoughnessBase64;
+                }
+                if (writeOutBaseColorTexture) {
+                    var baseColorBase64 = this._CreateBase64FromCanvas(baseColorBuffer, width, height, mimeType);
+                    metallicRoughnessFactors.baseColorTextureBase64 = baseColorBase64;
+                }
+                return metallicRoughnessFactors;
+            };
+            /**
+             * Converts specular glossiness material properties to metallic roughness.
+             * @param specularGlossiness - interface with specular glossiness material properties.
+             * @returns - interface with metallic roughness material properties.
+             */
+            _GLTFMaterial._ConvertSpecularGlossinessToMetallicRoughness = function (specularGlossiness) {
+                var diffusePerceivedBrightness = _GLTFMaterial._GetPerceivedBrightness(specularGlossiness.diffuseColor);
+                var specularPerceivedBrightness = _GLTFMaterial._GetPerceivedBrightness(specularGlossiness.specularColor);
+                var oneMinusSpecularStrength = 1 - _GLTFMaterial._GetMaxComponent(specularGlossiness.specularColor);
+                var metallic = _GLTFMaterial._SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
+                var baseColorFromDiffuse = specularGlossiness.diffuseColor.scale(oneMinusSpecularStrength / (1.0 - this._dielectricSpecular.r) / Math.max(1 - metallic, this._epsilon));
+                var baseColorFromSpecular = specularGlossiness.specularColor.subtract(this._dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this._epsilon));
+                var baseColor = BABYLON.Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
+                baseColor = baseColor.clampToRef(0, 1, baseColor);
+                var metallicRoughness = {
+                    baseColor: baseColor,
+                    metallic: metallic,
+                    roughness: 1 - specularGlossiness.glossiness
+                };
+                return metallicRoughness;
+            };
+            /**
+             * Calculates the surface reflectance, independent of lighting conditions.
              * @param color - Color source to calculate brightness from.
              * @param color - Color source to calculate brightness from.
              * @returns number representing the perceived brightness, or zero if color is undefined.
              * @returns number representing the perceived brightness, or zero if color is undefined.
              */
              */
-            _GLTFMaterial.GetPerceivedBrightness = function (color) {
+            _GLTFMaterial._GetPerceivedBrightness = function (color) {
                 if (color) {
                 if (color) {
                     return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
                     return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
                 }
                 }
@@ -1156,7 +1368,7 @@ var BABYLON;
              * @param color
              * @param color
              * @returns maximum color component value, or zero if color is null or undefined.
              * @returns maximum color component value, or zero if color is null or undefined.
              */
              */
-            _GLTFMaterial.GetMaxComponent = function (color) {
+            _GLTFMaterial._GetMaxComponent = function (color) {
                 if (color) {
                 if (color) {
                     return Math.max(color.r, Math.max(color.g, color.b));
                     return Math.max(color.r, Math.max(color.g, color.b));
                 }
                 }
@@ -1172,110 +1384,117 @@ var BABYLON;
              * @param imageData - map of image file name to data.
              * @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.
              * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
              */
              */
-            _GLTFMaterial.ConvertPBRMaterial = function (babylonPBRMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+            _GLTFMaterial._ConvertPBRMaterial = function (babylonPBRMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
                 var glTFPbrMetallicRoughness = {};
                 var glTFPbrMetallicRoughness = {};
+                var metallicRoughness;
                 var glTFMaterial = {
                 var glTFMaterial = {
                     name: babylonPBRMaterial.name
                     name: babylonPBRMaterial.name
                 };
                 };
                 var useMetallicRoughness = babylonPBRMaterial.isMetallicWorkflow();
                 var useMetallicRoughness = babylonPBRMaterial.isMetallicWorkflow();
-                if (babylonPBRMaterial) {
-                    if (useMetallicRoughness) {
-                        glTFPbrMetallicRoughness.baseColorFactor = [
-                            babylonPBRMaterial.albedoColor.r,
-                            babylonPBRMaterial.albedoColor.g,
-                            babylonPBRMaterial.albedoColor.b,
-                            babylonPBRMaterial.alpha
-                        ];
-                        if (babylonPBRMaterial.metallic != null) {
-                            if (babylonPBRMaterial.metallic !== 1) {
-                                glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
+                if (!useMetallicRoughness) {
+                    var specGloss = {
+                        diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
+                        specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
+                        glossiness: babylonPBRMaterial.microSurface || 1,
+                    };
+                    if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
+                        throw new Error("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture currently not supported");
+                    }
+                    metallicRoughness = this._ConvertSpecularGlossinessTexturesToMetallicRoughness(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType);
+                    if (!metallicRoughness) {
+                        metallicRoughness = this._ConvertSpecularGlossinessToMetallicRoughness(specGloss);
+                    }
+                    else {
+                        if (hasTextureCoords) {
+                            if (metallicRoughness.baseColorTextureBase64) {
+                                var glTFBaseColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughness.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, images, textures, imageData);
+                                if (glTFBaseColorTexture != null) {
+                                    glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                                }
                             }
                             }
-                        }
-                        if (babylonPBRMaterial.roughness != null) {
-                            if (babylonPBRMaterial.roughness !== 1) {
-                                glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
+                            if (metallicRoughness.metallicRoughnessTextureBase64) {
+                                var glTFMRColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughness.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, images, textures, imageData);
+                                if (glTFMRColorTexture != null) {
+                                    glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                                }
                             }
                             }
                         }
                         }
                     }
                     }
-                    else {
-                        var diffuseColor = babylonPBRMaterial.albedoColor || BABYLON.Color3.Black();
-                        var specularColor = babylonPBRMaterial.reflectionColor || BABYLON.Color3.Black();
-                        var diffusePerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(diffuseColor);
-                        var specularPerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(specularColor);
-                        var oneMinusSpecularStrength = 1 - _GLTFMaterial.GetMaxComponent(babylonPBRMaterial.reflectionColor);
-                        var metallic = _GLTFMaterial.SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
-                        var glossiness = babylonPBRMaterial.microSurface || 0;
-                        var baseColorFromDiffuse = diffuseColor.scale(oneMinusSpecularStrength / (1.0 - this.dielectricSpecular.r) / Math.max(1 - metallic, this.epsilon));
-                        var baseColorFromSpecular = specularColor.subtract(this.dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this.epsilon));
-                        var baseColor = BABYLON.Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
-                        baseColor = baseColor.clampToRef(0, 1, baseColor);
-                        glTFPbrMetallicRoughness.baseColorFactor = [
-                            baseColor.r,
-                            baseColor.g,
-                            baseColor.b,
-                            babylonPBRMaterial.alpha
-                        ];
-                        if (metallic !== 1) {
-                            glTFPbrMetallicRoughness.metallicFactor = metallic;
-                        }
-                        if (glossiness) {
-                            glTFPbrMetallicRoughness.roughnessFactor = 1 - glossiness;
-                        }
+                }
+                else {
+                    metallicRoughness = {
+                        baseColor: babylonPBRMaterial.albedoColor,
+                        metallic: babylonPBRMaterial.metallic,
+                        roughness: babylonPBRMaterial.roughness
+                    };
+                }
+                if (!(this.FuzzyEquals(metallicRoughness.baseColor, BABYLON.Color3.White(), this._epsilon) && babylonPBRMaterial.alpha >= this._epsilon)) {
+                    glTFPbrMetallicRoughness.baseColorFactor = [
+                        metallicRoughness.baseColor.r,
+                        metallicRoughness.baseColor.g,
+                        metallicRoughness.baseColor.b,
+                        babylonPBRMaterial.alpha
+                    ];
+                }
+                if (metallicRoughness.metallic != null && metallicRoughness.metallic !== 1) {
+                    glTFPbrMetallicRoughness.metallicFactor = metallicRoughness.metallic;
+                }
+                if (metallicRoughness.roughness != null && metallicRoughness.roughness !== 1) {
+                    glTFPbrMetallicRoughness.roughnessFactor = metallicRoughness.roughness;
+                }
+                if (babylonPBRMaterial.backFaceCulling != null && !babylonPBRMaterial.backFaceCulling) {
+                    if (!babylonPBRMaterial.twoSidedLighting) {
+                        BABYLON.Tools.Warn(babylonPBRMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
                     }
                     }
-                    if (babylonPBRMaterial.backFaceCulling) {
-                        if (!babylonPBRMaterial.twoSidedLighting) {
-                            BABYLON.Tools.Warn(babylonPBRMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
+                    glTFMaterial.doubleSided = true;
+                }
+                if (hasTextureCoords) {
+                    if (useMetallicRoughness && babylonPBRMaterial.albedoTexture) {
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                         }
                         }
-                        glTFMaterial.doubleSided = true;
                     }
                     }
-                    if (hasTextureCoords) {
-                        if (babylonPBRMaterial.albedoTexture) {
-                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, imageData);
-                            if (glTFTexture) {
-                                glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                            }
-                        }
-                        if (babylonPBRMaterial.bumpTexture) {
-                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, imageData);
-                            if (glTFTexture) {
-                                glTFMaterial.normalTexture = glTFTexture;
-                            }
-                        }
-                        if (babylonPBRMaterial.ambientTexture) {
-                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, imageData);
-                            if (glTFTexture) {
-                                var occlusionTexture = {
-                                    index: glTFTexture.index
-                                };
-                                glTFMaterial.occlusionTexture = occlusionTexture;
-                                if (babylonPBRMaterial.ambientTextureStrength) {
-                                    occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
-                                }
-                            }
+                    if (babylonPBRMaterial.bumpTexture) {
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            glTFMaterial.normalTexture = glTFTexture;
                         }
                         }
-                        if (babylonPBRMaterial.emissiveTexture) {
-                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, imageData);
-                            if (glTFTexture != null) {
-                                glTFMaterial.emissiveTexture = glTFTexture;
+                    }
+                    if (babylonPBRMaterial.ambientTexture) {
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            var occlusionTexture = {
+                                index: glTFTexture.index
+                            };
+                            glTFMaterial.occlusionTexture = occlusionTexture;
+                            if (babylonPBRMaterial.ambientTextureStrength) {
+                                occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
                             }
                             }
                         }
                         }
-                        if (babylonPBRMaterial.metallicTexture) {
-                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, imageData);
-                            if (glTFTexture != null) {
-                                glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
-                            }
+                    }
+                    if (babylonPBRMaterial.emissiveTexture) {
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture != null) {
+                            glTFMaterial.emissiveTexture = glTFTexture;
                         }
                         }
                     }
                     }
-                    if (!babylonPBRMaterial.emissiveColor.equalsFloats(0.0, 0.0, 0.0)) {
-                        glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                    if (babylonPBRMaterial.metallicTexture) {
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture != null) {
+                            glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
+                        }
                     }
                     }
-                    if (babylonPBRMaterial.transparencyMode != null) {
-                        var alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
-                        if (alphaMode !== "OPAQUE" /* OPAQUE */) {
-                            glTFMaterial.alphaMode = alphaMode;
-                            if (alphaMode === "BLEND" /* BLEND */) {
-                                glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
-                            }
+                }
+                if (!this.FuzzyEquals(babylonPBRMaterial.emissiveColor, BABYLON.Color3.Black(), this._epsilon)) {
+                    glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                }
+                if (babylonPBRMaterial.transparencyMode != null) {
+                    var alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMaterial);
+                    if (alphaMode !== "OPAQUE" /* OPAQUE */) {
+                        glTFMaterial.alphaMode = alphaMode;
+                        if (alphaMode === "BLEND" /* BLEND */) {
+                            glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
                         }
                         }
                     }
                     }
                 }
                 }
@@ -1289,17 +1508,13 @@ var BABYLON;
              * @param images - Array of glTF images.
              * @param images - Array of glTF images.
              * @param textures - Array of glTF textures.
              * @param textures - Array of glTF textures.
              * @param imageData - map of image file name and data.
              * @param imageData - map of image file name and data.
-             * @return - glTF texture, or null if the texture format is not supported.
+             * @return - glTF texture info, or null if the texture format is not supported.
              */
              */
-            _GLTFMaterial.ExportTexture = function (babylonTexture, mimeType, images, textures, imageData) {
-                var textureInfo = null;
-                var glTFTexture = {
-                    source: images.length
-                };
+            _GLTFMaterial._ExportTexture = function (babylonTexture, mimeType, images, textures, imageData) {
                 var textureName = "texture_" + (textures.length - 1).toString();
                 var textureName = "texture_" + (textures.length - 1).toString();
                 var textureData = babylonTexture.getInternalTexture();
                 var textureData = babylonTexture.getInternalTexture();
                 if (textureData != null) {
                 if (textureData != null) {
-                    textureName = textureData.url;
+                    textureName = textureData.url || textureName;
                 }
                 }
                 textureName = BABYLON.Tools.GetFilename(textureName);
                 textureName = BABYLON.Tools.GetFilename(textureName);
                 var baseFile = textureName.split('.')[0];
                 var baseFile = textureName.split('.')[0];
@@ -1311,28 +1526,37 @@ var BABYLON;
                     extension = ".png";
                     extension = ".png";
                 }
                 }
                 else {
                 else {
-                    BABYLON.Tools.Error("Unsupported mime type " + mimeType);
+                    throw new Error("Unsupported mime type " + mimeType);
                 }
                 }
                 textureName = baseFile + extension;
                 textureName = baseFile + extension;
                 var pixels = babylonTexture.readPixels();
                 var pixels = babylonTexture.readPixels();
-                var imageCanvas = document.createElement('canvas');
-                imageCanvas.id = "ImageCanvas";
-                var ctx = imageCanvas.getContext('2d');
                 var size = babylonTexture.getSize();
                 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 base64Data = this._CreateBase64FromCanvas(pixels, size.width, size.height, mimeType);
+                return this._GetTextureInfoFromBase64(base64Data, textureName, mimeType, images, textures, imageData);
+            };
+            /**
+             * Builds a texture from base64 string.
+             * @param base64Texture - base64 texture string.
+             * @param textureName - Name to use for the texture.
+             * @param mimeType - image mime type for the texture.
+             * @param images - array of images.
+             * @param textures - array of textures.
+             * @param imageData - map of image data.
+             * @returns - glTF texture info, or null if the texture format is not supported.
+             */
+            _GLTFMaterial._GetTextureInfoFromBase64 = function (base64Texture, textureName, mimeType, images, textures, imageData) {
+                var textureInfo = null;
+                var glTFTexture = {
+                    source: images.length
+                };
+                var binStr = atob(base64Texture.split(',')[1]);
                 var arr = new Uint8Array(binStr.length);
                 var arr = new Uint8Array(binStr.length);
                 for (var i = 0; i < binStr.length; ++i) {
                 for (var i = 0; i < binStr.length; ++i) {
                     arr[i] = binStr.charCodeAt(i);
                     arr[i] = binStr.charCodeAt(i);
                 }
                 }
                 var imageValues = { data: arr, mimeType: mimeType };
                 var imageValues = { data: arr, mimeType: mimeType };
                 imageData[textureName] = imageValues;
                 imageData[textureName] = imageValues;
-                if (mimeType === "image/jpeg" /* JPEG */) {
+                if (mimeType === "image/jpeg" /* JPEG */ || mimeType === "image/png" /* PNG */) {
                     var glTFImage = {
                     var glTFImage = {
                         uri: textureName
                         uri: textureName
                     };
                     };
@@ -1365,12 +1589,15 @@ var BABYLON;
             /**
             /**
              * Represents the dielectric specular values for R, G and B.
              * Represents the dielectric specular values for R, G and B.
              */
              */
-            _GLTFMaterial.dielectricSpecular = new BABYLON.Color3(0.04, 0.04, 0.04);
+            _GLTFMaterial._dielectricSpecular = new BABYLON.Color3(0.04, 0.04, 0.04);
             /**
             /**
              * Allows the maximum specular power to be defined for material calculations.
              * Allows the maximum specular power to be defined for material calculations.
              */
              */
-            _GLTFMaterial.maxSpecularPower = 1024;
-            _GLTFMaterial.epsilon = 1e-6;
+            _GLTFMaterial._maxSpecularPower = 1024;
+            /**
+             * Numeric tolerance value
+             */
+            _GLTFMaterial._epsilon = 1e-6;
             return _GLTFMaterial;
             return _GLTFMaterial;
         }());
         }());
         GLTF2._GLTFMaterial = _GLTFMaterial;
         GLTF2._GLTFMaterial = _GLTFMaterial;

Plik diff jest za duży
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 385 - 158
dist/preview release/serializers/babylonjs.serializers.js

@@ -767,13 +767,13 @@ var BABYLON;
                                 else {
                                 else {
                                     BABYLON.Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
                                     BABYLON.Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
                                 }
                                 }
-                                if (materialIndex != null) {
-                                    if (uvCoordsPresent || !GLTF2._GLTFMaterial.HasTexturesPresent(this.materials[materialIndex])) {
+                                if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
+                                    if (uvCoordsPresent || !GLTF2._GLTFMaterial._HasTexturesPresent(this.materials[materialIndex])) {
                                         meshPrimitive.material = materialIndex;
                                         meshPrimitive.material = materialIndex;
                                     }
                                     }
                                     else {
                                     else {
                                         // If no texture coordinate information is present, make a copy of the material without the textures to be glTF compliant.
                                         // If no texture coordinate information is present, make a copy of the material without the textures to be glTF compliant.
-                                        var newMat = GLTF2._GLTFMaterial.StripTexturesFromMaterial(this.materials[materialIndex]);
+                                        var newMat = GLTF2._GLTFMaterial._StripTexturesFromMaterial(this.materials[materialIndex]);
                                         this.materials.push(newMat);
                                         this.materials.push(newMat);
                                         meshPrimitive.material = this.materials.length - 1;
                                         meshPrimitive.material = this.materials.length - 1;
                                     }
                                     }
@@ -796,7 +796,7 @@ var BABYLON;
                 if (babylonScene.meshes.length) {
                 if (babylonScene.meshes.length) {
                     var babylonMeshes = babylonScene.meshes;
                     var babylonMeshes = babylonScene.meshes;
                     var scene = { nodes: new Array() };
                     var scene = { nodes: new Array() };
-                    GLTF2._GLTFMaterial.ConvertMaterialsToGLTF(babylonScene.materials, "image/jpeg" /* JPEG */, this.images, this.textures, this.materials, this.imageData, true);
+                    GLTF2._GLTFMaterial._ConvertMaterialsToGLTF(babylonScene.materials, "image/png" /* PNG */, this.images, this.textures, this.materials, this.imageData, true);
                     var result = this.createNodeMap(babylonScene, byteOffset);
                     var result = this.createNodeMap(babylonScene, byteOffset);
                     this.nodeMap = result.nodeMap;
                     this.nodeMap = result.nodeMap;
                     this.totalByteLength = result.byteOffset;
                     this.totalByteLength = result.byteOffset;
@@ -953,29 +953,40 @@ var BABYLON;
             function _GLTFMaterial() {
             function _GLTFMaterial() {
             }
             }
             /**
             /**
+             * Specifies if two colors are approximately equal in value.
+             * @param color1 - first color to compare to.
+             * @param color2 - second color to compare to.
+             * @param epsilon - threshold value
+             */
+            _GLTFMaterial.FuzzyEquals = function (color1, color2, epsilon) {
+                return BABYLON.Scalar.WithinEpsilon(color1.r, color2.r, epsilon) &&
+                    BABYLON.Scalar.WithinEpsilon(color1.g, color2.g, epsilon) &&
+                    BABYLON.Scalar.WithinEpsilon(color1.b, color2.b, epsilon);
+            };
+            /**
              * Gets the materials from a Babylon scene and converts them to glTF materials.
              * Gets the materials from a Babylon scene and converts them to glTF materials.
-             * @param scene
-             * @param mimeType
-             * @param images
-             * @param textures
-             * @param materials
-             * @param imageData
-             * @param hasTextureCoords
+             * @param scene - babylonjs scene.
+             * @param mimeType - texture mime type.
+             * @param images - array of images.
+             * @param textures - array of textures.
+             * @param materials - array of materials.
+             * @param imageData - mapping of texture names to base64 textures
+             * @param hasTextureCoords - specifies if texture coordinates are present on the material.
              */
              */
-            _GLTFMaterial.ConvertMaterialsToGLTF = function (babylonMaterials, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+            _GLTFMaterial._ConvertMaterialsToGLTF = function (babylonMaterials, mimeType, images, textures, materials, imageData, hasTextureCoords) {
                 for (var i = 0; i < babylonMaterials.length; ++i) {
                 for (var i = 0; i < babylonMaterials.length; ++i) {
                     var babylonMaterial = babylonMaterials[i];
                     var babylonMaterial = babylonMaterials[i];
                     if (babylonMaterial instanceof BABYLON.StandardMaterial) {
                     if (babylonMaterial instanceof BABYLON.StandardMaterial) {
-                        _GLTFMaterial.ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                        _GLTFMaterial._ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                     }
                     }
                     else if (babylonMaterial instanceof BABYLON.PBRMetallicRoughnessMaterial) {
                     else if (babylonMaterial instanceof BABYLON.PBRMetallicRoughnessMaterial) {
-                        _GLTFMaterial.ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                        _GLTFMaterial._ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                     }
                     }
                     else if (babylonMaterial instanceof BABYLON.PBRMaterial) {
                     else if (babylonMaterial instanceof BABYLON.PBRMaterial) {
-                        _GLTFMaterial.ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                        _GLTFMaterial._ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                     }
                     }
                     else {
                     else {
-                        BABYLON.Tools.Error("Unsupported material type: " + babylonMaterial.name);
+                        throw new Error("Unsupported material type: " + babylonMaterial.name);
                     }
                     }
                 }
                 }
             };
             };
@@ -984,7 +995,7 @@ var BABYLON;
              * @param originalMaterial - original glTF material.
              * @param originalMaterial - original glTF material.
              * @returns glTF material without texture parameters
              * @returns glTF material without texture parameters
              */
              */
-            _GLTFMaterial.StripTexturesFromMaterial = function (originalMaterial) {
+            _GLTFMaterial._StripTexturesFromMaterial = function (originalMaterial) {
                 var newMaterial = {};
                 var newMaterial = {};
                 if (originalMaterial) {
                 if (originalMaterial) {
                     newMaterial.name = originalMaterial.name;
                     newMaterial.name = originalMaterial.name;
@@ -1007,7 +1018,7 @@ var BABYLON;
              * @param material - glTF Material.
              * @param material - glTF Material.
              * @returns boolean specifying if texture parameters are present
              * @returns boolean specifying if texture parameters are present
              */
              */
-            _GLTFMaterial.HasTexturesPresent = function (material) {
+            _GLTFMaterial._HasTexturesPresent = function (material) {
                 if (material.emissiveTexture || material.normalTexture || material.occlusionTexture) {
                 if (material.emissiveTexture || material.normalTexture || material.occlusionTexture) {
                     return true;
                     return true;
                 }
                 }
@@ -1024,7 +1035,7 @@ var BABYLON;
              * @param babylonStandardMaterial
              * @param babylonStandardMaterial
              * @returns - glTF Metallic Roughness Material representation
              * @returns - glTF Metallic Roughness Material representation
              */
              */
-            _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness = function (babylonStandardMaterial) {
+            _GLTFMaterial._ConvertToGLTFPBRMetallicRoughness = function (babylonStandardMaterial) {
                 var P0 = new BABYLON.Vector2(0, 1);
                 var P0 = new BABYLON.Vector2(0, 1);
                 var P1 = new BABYLON.Vector2(0, 0.1);
                 var P1 = new BABYLON.Vector2(0, 0.1);
                 var P2 = new BABYLON.Vector2(0, 0.1);
                 var P2 = new BABYLON.Vector2(0, 0.1);
@@ -1038,7 +1049,7 @@ var BABYLON;
                  * @param p3 - fourth control point.
                  * @param p3 - fourth control point.
                  * @returns - number result of cubic bezier curve at the specified t.
                  * @returns - number result of cubic bezier curve at the specified t.
                  */
                  */
-                function cubicBezierCurve(t, p0, p1, p2, p3) {
+                function _cubicBezierCurve(t, p0, p1, p2, p3) {
                     return ((1 - t) * (1 - t) * (1 - t) * p0 +
                     return ((1 - t) * (1 - t) * (1 - t) * p0 +
                         3 * (1 - t) * (1 - t) * t * p1 +
                         3 * (1 - t) * (1 - t) * t * p1 +
                         3 * (1 - t) * t * t * p2 +
                         3 * (1 - t) * t * t * p2 +
@@ -1051,14 +1062,14 @@ var BABYLON;
                  * @param specularPower - specular power of standard material.
                  * @param specularPower - specular power of standard material.
                  * @returns - Number representing the roughness value.
                  * @returns - Number representing the roughness value.
                  */
                  */
-                function solveForRoughness(specularPower) {
+                function _solveForRoughness(specularPower) {
                     var t = Math.pow(specularPower / P3.x, 0.333333);
                     var t = Math.pow(specularPower / P3.x, 0.333333);
-                    return cubicBezierCurve(t, P0.y, P1.y, P2.y, P3.y);
+                    return _cubicBezierCurve(t, P0.y, P1.y, P2.y, P3.y);
                 }
                 }
                 var diffuse = babylonStandardMaterial.diffuseColor.toLinearSpace().scale(0.5);
                 var diffuse = babylonStandardMaterial.diffuseColor.toLinearSpace().scale(0.5);
                 var opacity = babylonStandardMaterial.alpha;
                 var opacity = babylonStandardMaterial.alpha;
-                var specularPower = BABYLON.Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this.maxSpecularPower);
-                var roughness = solveForRoughness(specularPower);
+                var specularPower = BABYLON.Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this._maxSpecularPower);
+                var roughness = _solveForRoughness(specularPower);
                 var glTFPbrMetallicRoughness = {
                 var glTFPbrMetallicRoughness = {
                     baseColorFactor: [
                     baseColorFactor: [
                         diffuse.r,
                         diffuse.r,
@@ -1078,14 +1089,14 @@ var BABYLON;
              * @param oneMinusSpecularStrength - one minus the specular strength
              * @param oneMinusSpecularStrength - one minus the specular strength
              * @returns - metallic value
              * @returns - metallic value
              */
              */
-            _GLTFMaterial.SolveMetallic = function (diffuse, specular, oneMinusSpecularStrength) {
-                if (specular < _GLTFMaterial.dielectricSpecular.r) {
-                    _GLTFMaterial.dielectricSpecular;
+            _GLTFMaterial._SolveMetallic = function (diffuse, specular, oneMinusSpecularStrength) {
+                if (specular < _GLTFMaterial._dielectricSpecular.r) {
+                    _GLTFMaterial._dielectricSpecular;
                     return 0;
                     return 0;
                 }
                 }
-                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 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;
                 var D = b * b - 4.0 * a * c;
                 return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a), 0, 1);
                 return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a), 0, 1);
             };
             };
@@ -1094,7 +1105,7 @@ var BABYLON;
              * @param babylonMaterial - Babylon Material
              * @param babylonMaterial - Babylon Material
              * @returns - The Babylon alpha mode value
              * @returns - The Babylon alpha mode value
              */
              */
-            _GLTFMaterial.GetAlphaMode = function (babylonMaterial) {
+            _GLTFMaterial._GetAlphaMode = function (babylonMaterial) {
                 if (babylonMaterial instanceof BABYLON.StandardMaterial) {
                 if (babylonMaterial instanceof BABYLON.StandardMaterial) {
                     var babylonStandardMaterial = babylonMaterial;
                     var babylonStandardMaterial = babylonMaterial;
                     if ((babylonStandardMaterial.alpha != 1.0) ||
                     if ((babylonStandardMaterial.alpha != 1.0) ||
@@ -1162,11 +1173,11 @@ var BABYLON;
              * @param imageData - map of image file name to data.
              * @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.
              * @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) {
+            _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");
                 BABYLON.Tools.Warn(babylonStandardMaterial.name + ": Standard Material is currently not fully supported/implemented in glTF serializer");
-                var glTFPbrMetallicRoughness = _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
+                var glTFPbrMetallicRoughness = _GLTFMaterial._ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
                 var glTFMaterial = { name: babylonStandardMaterial.name };
                 var glTFMaterial = { name: babylonStandardMaterial.name };
-                if (babylonStandardMaterial.backFaceCulling) {
+                if (babylonStandardMaterial.backFaceCulling != null && !babylonStandardMaterial.backFaceCulling) {
                     if (!babylonStandardMaterial.twoSidedLighting) {
                     if (!babylonStandardMaterial.twoSidedLighting) {
                         BABYLON.Tools.Warn(babylonStandardMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
                         BABYLON.Tools.Warn(babylonStandardMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
                     }
                     }
@@ -1174,26 +1185,26 @@ var BABYLON;
                 }
                 }
                 if (hasTextureCoords) {
                 if (hasTextureCoords) {
                     if (babylonStandardMaterial.diffuseTexture) {
                     if (babylonStandardMaterial.diffuseTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, imageData);
                         if (glTFTexture != null) {
                         if (glTFTexture != null) {
                             glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                             glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                         }
                         }
                     }
                     }
                     if (babylonStandardMaterial.bumpTexture) {
                     if (babylonStandardMaterial.bumpTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, imageData);
                         if (glTFTexture) {
                         if (glTFTexture) {
                             glTFMaterial.normalTexture = glTFTexture;
                             glTFMaterial.normalTexture = glTFTexture;
                         }
                         }
                     }
                     }
                     if (babylonStandardMaterial.emissiveTexture) {
                     if (babylonStandardMaterial.emissiveTexture) {
-                        var glTFEmissiveTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, imageData);
+                        var glTFEmissiveTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, imageData);
                         if (glTFEmissiveTexture) {
                         if (glTFEmissiveTexture) {
                             glTFMaterial.emissiveTexture = glTFEmissiveTexture;
                             glTFMaterial.emissiveTexture = glTFEmissiveTexture;
                         }
                         }
                         glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
                         glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
                     }
                     }
                     if (babylonStandardMaterial.ambientTexture) {
                     if (babylonStandardMaterial.ambientTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
                         if (glTFTexture) {
                         if (glTFTexture) {
                             var occlusionTexture = {
                             var occlusionTexture = {
                                 index: glTFTexture.index
                                 index: glTFTexture.index
@@ -1211,7 +1222,7 @@ var BABYLON;
                         BABYLON.Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
                         BABYLON.Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
                     }
                     }
                 }
                 }
-                if (babylonStandardMaterial.emissiveColor) {
+                if (babylonStandardMaterial.emissiveColor && !this.FuzzyEquals(babylonStandardMaterial.emissiveColor, BABYLON.Color3.Black(), this._epsilon)) {
                     glTFMaterial.emissiveFactor = babylonStandardMaterial.emissiveColor.asArray();
                     glTFMaterial.emissiveFactor = babylonStandardMaterial.emissiveColor.asArray();
                 }
                 }
                 glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
                 glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
@@ -1227,7 +1238,7 @@ var BABYLON;
              * @param imageData - map of image file name to data.
              * @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.
              * @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) {
+            _GLTFMaterial._ConvertPBRMetallicRoughnessMaterial = function (babylonPBRMetalRoughMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
                 var glTFPbrMetallicRoughness = {};
                 var glTFPbrMetallicRoughness = {};
                 if (babylonPBRMetalRoughMaterial.baseColor) {
                 if (babylonPBRMetalRoughMaterial.baseColor) {
                     glTFPbrMetallicRoughness.baseColorFactor = [
                     glTFPbrMetallicRoughness.baseColorFactor = [
@@ -1237,10 +1248,10 @@ var BABYLON;
                         babylonPBRMetalRoughMaterial.alpha
                         babylonPBRMetalRoughMaterial.alpha
                     ];
                     ];
                 }
                 }
-                if (babylonPBRMetalRoughMaterial.metallic != null) {
+                if (babylonPBRMetalRoughMaterial.metallic != null && babylonPBRMetalRoughMaterial.metallic !== 1) {
                     glTFPbrMetallicRoughness.metallicFactor = babylonPBRMetalRoughMaterial.metallic;
                     glTFPbrMetallicRoughness.metallicFactor = babylonPBRMetalRoughMaterial.metallic;
                 }
                 }
-                if (babylonPBRMetalRoughMaterial.roughness != null) {
+                if (babylonPBRMetalRoughMaterial.roughness != null && babylonPBRMetalRoughMaterial.roughness !== 1) {
                     glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMetalRoughMaterial.roughness;
                     glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMetalRoughMaterial.roughness;
                 }
                 }
                 var glTFMaterial = {
                 var glTFMaterial = {
@@ -1251,19 +1262,19 @@ var BABYLON;
                 }
                 }
                 if (hasTextureCoords) {
                 if (hasTextureCoords) {
                     if (babylonPBRMetalRoughMaterial.baseTexture != null) {
                     if (babylonPBRMetalRoughMaterial.baseTexture != null) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, imageData);
                         if (glTFTexture != null) {
                         if (glTFTexture != null) {
                             glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                             glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                         }
                         }
                     }
                     }
                     if (babylonPBRMetalRoughMaterial.normalTexture) {
                     if (babylonPBRMetalRoughMaterial.normalTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, imageData);
                         if (glTFTexture) {
                         if (glTFTexture) {
                             glTFMaterial.normalTexture = glTFTexture;
                             glTFMaterial.normalTexture = glTFTexture;
                         }
                         }
                     }
                     }
                     if (babylonPBRMetalRoughMaterial.occlusionTexture) {
                     if (babylonPBRMetalRoughMaterial.occlusionTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
                         if (glTFTexture) {
                         if (glTFTexture) {
                             glTFMaterial.occlusionTexture = glTFTexture;
                             glTFMaterial.occlusionTexture = glTFTexture;
                             if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
                             if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
@@ -1272,17 +1283,17 @@ var BABYLON;
                         }
                         }
                     }
                     }
                     if (babylonPBRMetalRoughMaterial.emissiveTexture) {
                     if (babylonPBRMetalRoughMaterial.emissiveTexture) {
-                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, imageData);
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, imageData);
                         if (glTFTexture != null) {
                         if (glTFTexture != null) {
                             glTFMaterial.emissiveTexture = glTFTexture;
                             glTFMaterial.emissiveTexture = glTFTexture;
                         }
                         }
                     }
                     }
                 }
                 }
-                if (babylonPBRMetalRoughMaterial.emissiveColor.equalsFloats(0.0, 0.0, 0.0)) {
+                if (this.FuzzyEquals(babylonPBRMetalRoughMaterial.emissiveColor, BABYLON.Color3.Black(), this._epsilon)) {
                     glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
                     glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
                 }
                 }
                 if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
                 if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
-                    var alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMetalRoughMaterial);
+                    var alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMetalRoughMaterial);
                     if (alphaMode !== "OPAQUE" /* OPAQUE */) {
                     if (alphaMode !== "OPAQUE" /* OPAQUE */) {
                         glTFMaterial.alphaMode = alphaMode;
                         glTFMaterial.alphaMode = alphaMode;
                         if (alphaMode === "BLEND" /* BLEND */) {
                         if (alphaMode === "BLEND" /* BLEND */) {
@@ -1294,12 +1305,213 @@ var BABYLON;
                 materials.push(glTFMaterial);
                 materials.push(glTFMaterial);
             };
             };
             /**
             /**
+             * Converts an image typed array buffer to a base64 image.
+             * @param buffer - typed array buffer.
+             * @param width - width of the image.
+             * @param height - height of the image.
+             * @param mimeType - mimetype of the image.
+             * @returns - base64 image string.
+             */
+            _GLTFMaterial._CreateBase64FromCanvas = function (buffer, width, height, mimeType) {
+                var imageCanvas = document.createElement('canvas');
+                imageCanvas.id = "WriteCanvas";
+                var ctx = imageCanvas.getContext('2d');
+                imageCanvas.width = width;
+                imageCanvas.height = height;
+                var imgData = ctx.createImageData(width, height);
+                imgData.data.set(buffer);
+                ctx.putImageData(imgData, 0, 0);
+                return imageCanvas.toDataURL(mimeType);
+            };
+            /**
+             * Generates a white texture based on the specified width and height.
+             * @param width - width of the texture in pixels.
+             * @param height - height of the texture in pixels.
+             * @param scene - babylonjs scene.
+             * @returns - white texture.
+             */
+            _GLTFMaterial._CreateWhiteTexture = function (width, height, scene) {
+                var data = new Uint8Array(width * height * 4);
+                for (var i = 0; i < data.length; ++i) {
+                    data[i] = 255;
+                }
+                var rawTexture = BABYLON.RawTexture.CreateRGBATexture(data, width, height, scene);
+                return rawTexture;
+            };
+            /**
+             * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null.
+             * @param texture1 - first texture to resize.
+             * @param texture2 - second texture to resize.
+             * @param scene - babylonjs scene.
+             * @returns resized textures or null.
+             */
+            _GLTFMaterial._ResizeTexturesToSameDimensions = function (texture1, texture2, scene) {
+                var texture1Size = texture1 ? texture1.getSize() : { width: 0, height: 0 };
+                var texture2Size = texture2 ? texture2.getSize() : { width: 0, height: 0 };
+                var resizedTexture1;
+                var resizedTexture2;
+                if (texture1Size.width < texture2Size.width) {
+                    if (texture1) {
+                        resizedTexture1 = BABYLON.TextureTools.CreateResizedCopy(texture1, texture2Size.width, texture2Size.height, true);
+                    }
+                    else {
+                        resizedTexture1 = this._CreateWhiteTexture(texture2Size.width, texture2Size.height, scene);
+                    }
+                    resizedTexture2 = texture2;
+                }
+                else if (texture1Size.width > texture2Size.width) {
+                    if (texture2) {
+                        resizedTexture2 = BABYLON.TextureTools.CreateResizedCopy(texture2, texture1Size.width, texture1Size.height, true);
+                    }
+                    else {
+                        resizedTexture2 = this._CreateWhiteTexture(texture1Size.width, texture1Size.height, scene);
+                    }
+                    resizedTexture1 = texture1;
+                }
+                else {
+                    resizedTexture1 = texture1;
+                    resizedTexture2 = texture2;
+                }
+                return {
+                    "texture1": resizedTexture1,
+                    "texture2": resizedTexture2
+                };
+            };
+            /**
+             * Convert Specular Glossiness Textures to Metallic Roughness.
              * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
              * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
              * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
              * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
+             * @param diffuseTexture - texture used to store diffuse information.
+             * @param specularGlossinessTexture - texture used to store specular and glossiness information.
+             * @param factors - specular glossiness material factors.
+             * @param mimeType - the mime type to use for the texture.
+             * @returns pbr metallic roughness interface or null.
+             */
+            _GLTFMaterial._ConvertSpecularGlossinessTexturesToMetallicRoughness = function (diffuseTexture, specularGlossinessTexture, factors, mimeType) {
+                if (!(diffuseTexture || specularGlossinessTexture)) {
+                    return null;
+                }
+                var scene = diffuseTexture ? diffuseTexture.getScene() : specularGlossinessTexture.getScene();
+                if (!scene) {
+                    throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Scene from textures is missing!");
+                }
+                var resizedTextures = this._ResizeTexturesToSameDimensions(diffuseTexture, specularGlossinessTexture, scene);
+                var diffuseSize = resizedTextures.texture1.getSize();
+                var diffuseBuffer;
+                var specularGlossinessBuffer;
+                var width = diffuseSize.width;
+                var height = diffuseSize.height;
+                var pixels = (resizedTextures.texture1.readPixels());
+                if (pixels instanceof Uint8Array) {
+                    diffuseBuffer = (resizedTextures.texture1.readPixels());
+                }
+                else {
+                    throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture1.name);
+                }
+                pixels = resizedTextures.texture2.readPixels();
+                if (pixels instanceof Uint8Array) {
+                    specularGlossinessBuffer = (resizedTextures.texture2.readPixels());
+                }
+                else {
+                    throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture2.name);
+                }
+                var byteLength = specularGlossinessBuffer.byteLength;
+                var metallicRoughnessBuffer = new Uint8Array(byteLength);
+                var baseColorBuffer = new Uint8Array(byteLength);
+                var strideSize = 4;
+                var maxBaseColor = BABYLON.Color3.Black();
+                var maxMetallic = 0;
+                var maxRoughness = 0;
+                for (var h = 0; h < height; ++h) {
+                    for (var w = 0; w < width; ++w) {
+                        var offset = (width * h + w) * strideSize;
+                        var diffuseColor = BABYLON.Color3.FromInts(diffuseBuffer[offset], diffuseBuffer[offset + 1], diffuseBuffer[offset + 2]).multiply(factors.diffuseColor);
+                        var specularColor = BABYLON.Color3.FromInts(specularGlossinessBuffer[offset], specularGlossinessBuffer[offset + 1], specularGlossinessBuffer[offset + 2]).multiply(factors.specularColor);
+                        var glossiness = (specularGlossinessBuffer[offset + 3] / 255) * factors.glossiness;
+                        var specularGlossiness = {
+                            diffuseColor: diffuseColor,
+                            specularColor: specularColor,
+                            glossiness: glossiness
+                        };
+                        var metallicRoughness = this._ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+                        maxBaseColor.r = Math.max(maxBaseColor.r, metallicRoughness.baseColor.r);
+                        maxBaseColor.g = Math.max(maxBaseColor.g, metallicRoughness.baseColor.g);
+                        maxBaseColor.b = Math.max(maxBaseColor.b, metallicRoughness.baseColor.b);
+                        maxMetallic = Math.max(maxMetallic, metallicRoughness.metallic);
+                        maxRoughness = Math.max(maxRoughness, metallicRoughness.roughness);
+                        baseColorBuffer[offset] = metallicRoughness.baseColor.r * 255;
+                        baseColorBuffer[offset + 1] = metallicRoughness.baseColor.g * 255;
+                        baseColorBuffer[offset + 2] = metallicRoughness.baseColor.b * 255;
+                        baseColorBuffer[offset + 3] = resizedTextures.texture1.hasAlpha ? diffuseBuffer[offset + 3] : 255;
+                        metallicRoughnessBuffer[offset] = 255;
+                        metallicRoughnessBuffer[offset + 1] = metallicRoughness.roughness * 255;
+                        metallicRoughnessBuffer[offset + 2] = metallicRoughness.metallic * 255;
+                        metallicRoughnessBuffer[offset + 3] = 255;
+                    }
+                }
+                // Retrieves the metallic roughness factors from the maximum texture values.
+                var metallicRoughnessFactors = {
+                    baseColor: maxBaseColor,
+                    metallic: maxMetallic,
+                    roughness: maxRoughness
+                };
+                var writeOutMetallicRoughnessTexture = false;
+                var writeOutBaseColorTexture = false;
+                for (var h = 0; h < height; ++h) {
+                    for (var w = 0; w < width; ++w) {
+                        var destinationOffset = (width * h + w) * strideSize;
+                        baseColorBuffer[destinationOffset] /= metallicRoughnessFactors.baseColor.r > this._epsilon ? metallicRoughnessFactors.baseColor.r : 1;
+                        baseColorBuffer[destinationOffset + 1] /= metallicRoughnessFactors.baseColor.g > this._epsilon ? metallicRoughnessFactors.baseColor.g : 1;
+                        baseColorBuffer[destinationOffset + 2] /= metallicRoughnessFactors.baseColor.b > this._epsilon ? metallicRoughnessFactors.baseColor.b : 1;
+                        var baseColorPixel = new BABYLON.Color3(baseColorBuffer[destinationOffset], baseColorBuffer[destinationOffset + 1], baseColorBuffer[destinationOffset + 2]);
+                        if (!this.FuzzyEquals(baseColorPixel, BABYLON.Color3.White(), this._epsilon)) {
+                            writeOutBaseColorTexture = true;
+                        }
+                        metallicRoughnessBuffer[destinationOffset + 1] /= metallicRoughnessFactors.roughness > this._epsilon ? metallicRoughnessFactors.roughness : 1;
+                        metallicRoughnessBuffer[destinationOffset + 2] /= metallicRoughnessFactors.metallic > this._epsilon ? metallicRoughnessFactors.metallic : 1;
+                        var metallicRoughnessPixel = new BABYLON.Color3(metallicRoughnessBuffer[destinationOffset], metallicRoughnessBuffer[destinationOffset + 1], metallicRoughnessBuffer[destinationOffset + 2]);
+                        if (!this.FuzzyEquals(metallicRoughnessPixel, BABYLON.Color3.White(), this._epsilon)) {
+                            writeOutMetallicRoughnessTexture = true;
+                        }
+                    }
+                }
+                if (writeOutMetallicRoughnessTexture) {
+                    var metallicRoughnessBase64 = this._CreateBase64FromCanvas(metallicRoughnessBuffer, width, height, mimeType);
+                    metallicRoughnessFactors.metallicRoughnessTextureBase64 = metallicRoughnessBase64;
+                }
+                if (writeOutBaseColorTexture) {
+                    var baseColorBase64 = this._CreateBase64FromCanvas(baseColorBuffer, width, height, mimeType);
+                    metallicRoughnessFactors.baseColorTextureBase64 = baseColorBase64;
+                }
+                return metallicRoughnessFactors;
+            };
+            /**
+             * Converts specular glossiness material properties to metallic roughness.
+             * @param specularGlossiness - interface with specular glossiness material properties.
+             * @returns - interface with metallic roughness material properties.
+             */
+            _GLTFMaterial._ConvertSpecularGlossinessToMetallicRoughness = function (specularGlossiness) {
+                var diffusePerceivedBrightness = _GLTFMaterial._GetPerceivedBrightness(specularGlossiness.diffuseColor);
+                var specularPerceivedBrightness = _GLTFMaterial._GetPerceivedBrightness(specularGlossiness.specularColor);
+                var oneMinusSpecularStrength = 1 - _GLTFMaterial._GetMaxComponent(specularGlossiness.specularColor);
+                var metallic = _GLTFMaterial._SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
+                var baseColorFromDiffuse = specularGlossiness.diffuseColor.scale(oneMinusSpecularStrength / (1.0 - this._dielectricSpecular.r) / Math.max(1 - metallic, this._epsilon));
+                var baseColorFromSpecular = specularGlossiness.specularColor.subtract(this._dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this._epsilon));
+                var baseColor = BABYLON.Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
+                baseColor = baseColor.clampToRef(0, 1, baseColor);
+                var metallicRoughness = {
+                    baseColor: baseColor,
+                    metallic: metallic,
+                    roughness: 1 - specularGlossiness.glossiness
+                };
+                return metallicRoughness;
+            };
+            /**
+             * Calculates the surface reflectance, independent of lighting conditions.
              * @param color - Color source to calculate brightness from.
              * @param color - Color source to calculate brightness from.
              * @returns number representing the perceived brightness, or zero if color is undefined.
              * @returns number representing the perceived brightness, or zero if color is undefined.
              */
              */
-            _GLTFMaterial.GetPerceivedBrightness = function (color) {
+            _GLTFMaterial._GetPerceivedBrightness = function (color) {
                 if (color) {
                 if (color) {
                     return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
                     return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
                 }
                 }
@@ -1310,7 +1522,7 @@ var BABYLON;
              * @param color
              * @param color
              * @returns maximum color component value, or zero if color is null or undefined.
              * @returns maximum color component value, or zero if color is null or undefined.
              */
              */
-            _GLTFMaterial.GetMaxComponent = function (color) {
+            _GLTFMaterial._GetMaxComponent = function (color) {
                 if (color) {
                 if (color) {
                     return Math.max(color.r, Math.max(color.g, color.b));
                     return Math.max(color.r, Math.max(color.g, color.b));
                 }
                 }
@@ -1326,110 +1538,117 @@ var BABYLON;
              * @param imageData - map of image file name to data.
              * @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.
              * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
              */
              */
-            _GLTFMaterial.ConvertPBRMaterial = function (babylonPBRMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+            _GLTFMaterial._ConvertPBRMaterial = function (babylonPBRMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
                 var glTFPbrMetallicRoughness = {};
                 var glTFPbrMetallicRoughness = {};
+                var metallicRoughness;
                 var glTFMaterial = {
                 var glTFMaterial = {
                     name: babylonPBRMaterial.name
                     name: babylonPBRMaterial.name
                 };
                 };
                 var useMetallicRoughness = babylonPBRMaterial.isMetallicWorkflow();
                 var useMetallicRoughness = babylonPBRMaterial.isMetallicWorkflow();
-                if (babylonPBRMaterial) {
-                    if (useMetallicRoughness) {
-                        glTFPbrMetallicRoughness.baseColorFactor = [
-                            babylonPBRMaterial.albedoColor.r,
-                            babylonPBRMaterial.albedoColor.g,
-                            babylonPBRMaterial.albedoColor.b,
-                            babylonPBRMaterial.alpha
-                        ];
-                        if (babylonPBRMaterial.metallic != null) {
-                            if (babylonPBRMaterial.metallic !== 1) {
-                                glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
+                if (!useMetallicRoughness) {
+                    var specGloss = {
+                        diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
+                        specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
+                        glossiness: babylonPBRMaterial.microSurface || 1,
+                    };
+                    if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
+                        throw new Error("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture currently not supported");
+                    }
+                    metallicRoughness = this._ConvertSpecularGlossinessTexturesToMetallicRoughness(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType);
+                    if (!metallicRoughness) {
+                        metallicRoughness = this._ConvertSpecularGlossinessToMetallicRoughness(specGloss);
+                    }
+                    else {
+                        if (hasTextureCoords) {
+                            if (metallicRoughness.baseColorTextureBase64) {
+                                var glTFBaseColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughness.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, images, textures, imageData);
+                                if (glTFBaseColorTexture != null) {
+                                    glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                                }
                             }
                             }
-                        }
-                        if (babylonPBRMaterial.roughness != null) {
-                            if (babylonPBRMaterial.roughness !== 1) {
-                                glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
+                            if (metallicRoughness.metallicRoughnessTextureBase64) {
+                                var glTFMRColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughness.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, images, textures, imageData);
+                                if (glTFMRColorTexture != null) {
+                                    glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                                }
                             }
                             }
                         }
                         }
                     }
                     }
-                    else {
-                        var diffuseColor = babylonPBRMaterial.albedoColor || BABYLON.Color3.Black();
-                        var specularColor = babylonPBRMaterial.reflectionColor || BABYLON.Color3.Black();
-                        var diffusePerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(diffuseColor);
-                        var specularPerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(specularColor);
-                        var oneMinusSpecularStrength = 1 - _GLTFMaterial.GetMaxComponent(babylonPBRMaterial.reflectionColor);
-                        var metallic = _GLTFMaterial.SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
-                        var glossiness = babylonPBRMaterial.microSurface || 0;
-                        var baseColorFromDiffuse = diffuseColor.scale(oneMinusSpecularStrength / (1.0 - this.dielectricSpecular.r) / Math.max(1 - metallic, this.epsilon));
-                        var baseColorFromSpecular = specularColor.subtract(this.dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this.epsilon));
-                        var baseColor = BABYLON.Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
-                        baseColor = baseColor.clampToRef(0, 1, baseColor);
-                        glTFPbrMetallicRoughness.baseColorFactor = [
-                            baseColor.r,
-                            baseColor.g,
-                            baseColor.b,
-                            babylonPBRMaterial.alpha
-                        ];
-                        if (metallic !== 1) {
-                            glTFPbrMetallicRoughness.metallicFactor = metallic;
-                        }
-                        if (glossiness) {
-                            glTFPbrMetallicRoughness.roughnessFactor = 1 - glossiness;
-                        }
+                }
+                else {
+                    metallicRoughness = {
+                        baseColor: babylonPBRMaterial.albedoColor,
+                        metallic: babylonPBRMaterial.metallic,
+                        roughness: babylonPBRMaterial.roughness
+                    };
+                }
+                if (!(this.FuzzyEquals(metallicRoughness.baseColor, BABYLON.Color3.White(), this._epsilon) && babylonPBRMaterial.alpha >= this._epsilon)) {
+                    glTFPbrMetallicRoughness.baseColorFactor = [
+                        metallicRoughness.baseColor.r,
+                        metallicRoughness.baseColor.g,
+                        metallicRoughness.baseColor.b,
+                        babylonPBRMaterial.alpha
+                    ];
+                }
+                if (metallicRoughness.metallic != null && metallicRoughness.metallic !== 1) {
+                    glTFPbrMetallicRoughness.metallicFactor = metallicRoughness.metallic;
+                }
+                if (metallicRoughness.roughness != null && metallicRoughness.roughness !== 1) {
+                    glTFPbrMetallicRoughness.roughnessFactor = metallicRoughness.roughness;
+                }
+                if (babylonPBRMaterial.backFaceCulling != null && !babylonPBRMaterial.backFaceCulling) {
+                    if (!babylonPBRMaterial.twoSidedLighting) {
+                        BABYLON.Tools.Warn(babylonPBRMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
                     }
                     }
-                    if (babylonPBRMaterial.backFaceCulling) {
-                        if (!babylonPBRMaterial.twoSidedLighting) {
-                            BABYLON.Tools.Warn(babylonPBRMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
+                    glTFMaterial.doubleSided = true;
+                }
+                if (hasTextureCoords) {
+                    if (useMetallicRoughness && babylonPBRMaterial.albedoTexture) {
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                         }
                         }
-                        glTFMaterial.doubleSided = true;
                     }
                     }
-                    if (hasTextureCoords) {
-                        if (babylonPBRMaterial.albedoTexture) {
-                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, imageData);
-                            if (glTFTexture) {
-                                glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                            }
-                        }
-                        if (babylonPBRMaterial.bumpTexture) {
-                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, imageData);
-                            if (glTFTexture) {
-                                glTFMaterial.normalTexture = glTFTexture;
-                            }
-                        }
-                        if (babylonPBRMaterial.ambientTexture) {
-                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, imageData);
-                            if (glTFTexture) {
-                                var occlusionTexture = {
-                                    index: glTFTexture.index
-                                };
-                                glTFMaterial.occlusionTexture = occlusionTexture;
-                                if (babylonPBRMaterial.ambientTextureStrength) {
-                                    occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
-                                }
-                            }
+                    if (babylonPBRMaterial.bumpTexture) {
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            glTFMaterial.normalTexture = glTFTexture;
                         }
                         }
-                        if (babylonPBRMaterial.emissiveTexture) {
-                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, imageData);
-                            if (glTFTexture != null) {
-                                glTFMaterial.emissiveTexture = glTFTexture;
+                    }
+                    if (babylonPBRMaterial.ambientTexture) {
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            var occlusionTexture = {
+                                index: glTFTexture.index
+                            };
+                            glTFMaterial.occlusionTexture = occlusionTexture;
+                            if (babylonPBRMaterial.ambientTextureStrength) {
+                                occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
                             }
                             }
                         }
                         }
-                        if (babylonPBRMaterial.metallicTexture) {
-                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, imageData);
-                            if (glTFTexture != null) {
-                                glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
-                            }
+                    }
+                    if (babylonPBRMaterial.emissiveTexture) {
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture != null) {
+                            glTFMaterial.emissiveTexture = glTFTexture;
                         }
                         }
                     }
                     }
-                    if (!babylonPBRMaterial.emissiveColor.equalsFloats(0.0, 0.0, 0.0)) {
-                        glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                    if (babylonPBRMaterial.metallicTexture) {
+                        var glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture != null) {
+                            glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
+                        }
                     }
                     }
-                    if (babylonPBRMaterial.transparencyMode != null) {
-                        var alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
-                        if (alphaMode !== "OPAQUE" /* OPAQUE */) {
-                            glTFMaterial.alphaMode = alphaMode;
-                            if (alphaMode === "BLEND" /* BLEND */) {
-                                glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
-                            }
+                }
+                if (!this.FuzzyEquals(babylonPBRMaterial.emissiveColor, BABYLON.Color3.Black(), this._epsilon)) {
+                    glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                }
+                if (babylonPBRMaterial.transparencyMode != null) {
+                    var alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMaterial);
+                    if (alphaMode !== "OPAQUE" /* OPAQUE */) {
+                        glTFMaterial.alphaMode = alphaMode;
+                        if (alphaMode === "BLEND" /* BLEND */) {
+                            glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
                         }
                         }
                     }
                     }
                 }
                 }
@@ -1443,17 +1662,13 @@ var BABYLON;
              * @param images - Array of glTF images.
              * @param images - Array of glTF images.
              * @param textures - Array of glTF textures.
              * @param textures - Array of glTF textures.
              * @param imageData - map of image file name and data.
              * @param imageData - map of image file name and data.
-             * @return - glTF texture, or null if the texture format is not supported.
+             * @return - glTF texture info, or null if the texture format is not supported.
              */
              */
-            _GLTFMaterial.ExportTexture = function (babylonTexture, mimeType, images, textures, imageData) {
-                var textureInfo = null;
-                var glTFTexture = {
-                    source: images.length
-                };
+            _GLTFMaterial._ExportTexture = function (babylonTexture, mimeType, images, textures, imageData) {
                 var textureName = "texture_" + (textures.length - 1).toString();
                 var textureName = "texture_" + (textures.length - 1).toString();
                 var textureData = babylonTexture.getInternalTexture();
                 var textureData = babylonTexture.getInternalTexture();
                 if (textureData != null) {
                 if (textureData != null) {
-                    textureName = textureData.url;
+                    textureName = textureData.url || textureName;
                 }
                 }
                 textureName = BABYLON.Tools.GetFilename(textureName);
                 textureName = BABYLON.Tools.GetFilename(textureName);
                 var baseFile = textureName.split('.')[0];
                 var baseFile = textureName.split('.')[0];
@@ -1465,28 +1680,37 @@ var BABYLON;
                     extension = ".png";
                     extension = ".png";
                 }
                 }
                 else {
                 else {
-                    BABYLON.Tools.Error("Unsupported mime type " + mimeType);
+                    throw new Error("Unsupported mime type " + mimeType);
                 }
                 }
                 textureName = baseFile + extension;
                 textureName = baseFile + extension;
                 var pixels = babylonTexture.readPixels();
                 var pixels = babylonTexture.readPixels();
-                var imageCanvas = document.createElement('canvas');
-                imageCanvas.id = "ImageCanvas";
-                var ctx = imageCanvas.getContext('2d');
                 var size = babylonTexture.getSize();
                 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 base64Data = this._CreateBase64FromCanvas(pixels, size.width, size.height, mimeType);
+                return this._GetTextureInfoFromBase64(base64Data, textureName, mimeType, images, textures, imageData);
+            };
+            /**
+             * Builds a texture from base64 string.
+             * @param base64Texture - base64 texture string.
+             * @param textureName - Name to use for the texture.
+             * @param mimeType - image mime type for the texture.
+             * @param images - array of images.
+             * @param textures - array of textures.
+             * @param imageData - map of image data.
+             * @returns - glTF texture info, or null if the texture format is not supported.
+             */
+            _GLTFMaterial._GetTextureInfoFromBase64 = function (base64Texture, textureName, mimeType, images, textures, imageData) {
+                var textureInfo = null;
+                var glTFTexture = {
+                    source: images.length
+                };
+                var binStr = atob(base64Texture.split(',')[1]);
                 var arr = new Uint8Array(binStr.length);
                 var arr = new Uint8Array(binStr.length);
                 for (var i = 0; i < binStr.length; ++i) {
                 for (var i = 0; i < binStr.length; ++i) {
                     arr[i] = binStr.charCodeAt(i);
                     arr[i] = binStr.charCodeAt(i);
                 }
                 }
                 var imageValues = { data: arr, mimeType: mimeType };
                 var imageValues = { data: arr, mimeType: mimeType };
                 imageData[textureName] = imageValues;
                 imageData[textureName] = imageValues;
-                if (mimeType === "image/jpeg" /* JPEG */) {
+                if (mimeType === "image/jpeg" /* JPEG */ || mimeType === "image/png" /* PNG */) {
                     var glTFImage = {
                     var glTFImage = {
                         uri: textureName
                         uri: textureName
                     };
                     };
@@ -1519,12 +1743,15 @@ var BABYLON;
             /**
             /**
              * Represents the dielectric specular values for R, G and B.
              * Represents the dielectric specular values for R, G and B.
              */
              */
-            _GLTFMaterial.dielectricSpecular = new BABYLON.Color3(0.04, 0.04, 0.04);
+            _GLTFMaterial._dielectricSpecular = new BABYLON.Color3(0.04, 0.04, 0.04);
             /**
             /**
              * Allows the maximum specular power to be defined for material calculations.
              * Allows the maximum specular power to be defined for material calculations.
              */
              */
-            _GLTFMaterial.maxSpecularPower = 1024;
-            _GLTFMaterial.epsilon = 1e-6;
+            _GLTFMaterial._maxSpecularPower = 1024;
+            /**
+             * Numeric tolerance value
+             */
+            _GLTFMaterial._epsilon = 1e-6;
             return _GLTFMaterial;
             return _GLTFMaterial;
         }());
         }());
         GLTF2._GLTFMaterial = _GLTFMaterial;
         GLTF2._GLTFMaterial = _GLTFMaterial;

Plik diff jest za duży
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


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

@@ -305,23 +305,33 @@ declare module BABYLON.GLTF2 {
         /**
         /**
          * Represents the dielectric specular values for R, G and B.
          * Represents the dielectric specular values for R, G and B.
          */
          */
-        private static readonly dielectricSpecular;
+        private static readonly _dielectricSpecular;
         /**
         /**
          * Allows the maximum specular power to be defined for material calculations.
          * Allows the maximum specular power to be defined for material calculations.
          */
          */
-        private static maxSpecularPower;
-        private static epsilon;
+        private static _maxSpecularPower;
+        /**
+         * Numeric tolerance value
+         */
+        private static _epsilon;
+        /**
+         * Specifies if two colors are approximately equal in value.
+         * @param color1 - first color to compare to.
+         * @param color2 - second color to compare to.
+         * @param epsilon - threshold value
+         */
+        private static FuzzyEquals(color1, color2, epsilon);
         /**
         /**
          * Gets the materials from a Babylon scene and converts them to glTF materials.
          * 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
-         */
-        static ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
+         * @param scene - babylonjs scene.
+         * @param mimeType - texture mime type.
+         * @param images - array of images.
+         * @param textures - array of textures.
+         * @param materials - array of materials.
+         * @param imageData - mapping of texture names to base64 textures
+         * @param hasTextureCoords - specifies if texture coordinates are present on the material.
+         */
+        static _ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
             [fileName: string]: {
             [fileName: string]: {
                 data: Uint8Array;
                 data: Uint8Array;
                 mimeType: ImageMimeType;
                 mimeType: ImageMimeType;
@@ -332,19 +342,19 @@ declare module BABYLON.GLTF2 {
          * @param originalMaterial - original glTF material.
          * @param originalMaterial - original glTF material.
          * @returns glTF material without texture parameters
          * @returns glTF material without texture parameters
          */
          */
-        static StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
+        static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
         /**
         /**
          * Specifies if the material has any texture parameters present.
          * Specifies if the material has any texture parameters present.
          * @param material - glTF Material.
          * @param material - glTF Material.
          * @returns boolean specifying if texture parameters are present
          * @returns boolean specifying if texture parameters are present
          */
          */
-        static HasTexturesPresent(material: IMaterial): boolean;
+        static _HasTexturesPresent(material: IMaterial): boolean;
         /**
         /**
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
          * @param babylonStandardMaterial
          * @param babylonStandardMaterial
          * @returns - glTF Metallic Roughness Material representation
          * @returns - glTF Metallic Roughness Material representation
          */
          */
-        static ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
+        static _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness;
         /**
         /**
          * Computes the metallic factor
          * Computes the metallic factor
          * @param diffuse - diffused value
          * @param diffuse - diffused value
@@ -352,13 +362,13 @@ declare module BABYLON.GLTF2 {
          * @param oneMinusSpecularStrength - one minus the specular strength
          * @param oneMinusSpecularStrength - one minus the specular strength
          * @returns - metallic value
          * @returns - metallic value
          */
          */
-        static SolveMetallic(diffuse: number, specular: number, oneMinusSpecularStrength: number): number;
+        static _SolveMetallic(diffuse: number, specular: number, oneMinusSpecularStrength: number): number;
         /**
         /**
          * Gets the glTF alpha mode from the Babylon Material
          * Gets the glTF alpha mode from the Babylon Material
          * @param babylonMaterial - Babylon Material
          * @param babylonMaterial - Babylon Material
          * @returns - The Babylon alpha mode value
          * @returns - The Babylon alpha mode value
          */
          */
-        static GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
+        static _GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode;
         /**
         /**
          * Converts a Babylon Standard Material to a glTF Material.
          * Converts a Babylon Standard Material to a glTF Material.
          * @param babylonStandardMaterial - BJS Standard Material.
          * @param babylonStandardMaterial - BJS Standard Material.
@@ -369,7 +379,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData - map of image file name to data.
          * @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.
          * @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: {
+        static _ConvertStandardMaterial(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
             [fileName: string]: {
             [fileName: string]: {
                 data: Uint8Array;
                 data: Uint8Array;
                 mimeType: ImageMimeType;
                 mimeType: ImageMimeType;
@@ -385,25 +395,66 @@ declare module BABYLON.GLTF2 {
          * @param imageData - map of image file name to data.
          * @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.
          * @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: {
+        static _ConvertPBRMetallicRoughnessMaterial(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
             [fileName: string]: {
             [fileName: string]: {
                 data: Uint8Array;
                 data: Uint8Array;
                 mimeType: ImageMimeType;
                 mimeType: ImageMimeType;
             };
             };
         }, hasTextureCoords: boolean): void;
         }, hasTextureCoords: boolean): void;
         /**
         /**
+         * Converts an image typed array buffer to a base64 image.
+         * @param buffer - typed array buffer.
+         * @param width - width of the image.
+         * @param height - height of the image.
+         * @param mimeType - mimetype of the image.
+         * @returns - base64 image string.
+         */
+        private static _CreateBase64FromCanvas(buffer, width, height, mimeType);
+        /**
+         * Generates a white texture based on the specified width and height.
+         * @param width - width of the texture in pixels.
+         * @param height - height of the texture in pixels.
+         * @param scene - babylonjs scene.
+         * @returns - white texture.
+         */
+        private static _CreateWhiteTexture(width, height, scene);
+        /**
+         * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null.
+         * @param texture1 - first texture to resize.
+         * @param texture2 - second texture to resize.
+         * @param scene - babylonjs scene.
+         * @returns resized textures or null.
+         */
+        private static _ResizeTexturesToSameDimensions(texture1, texture2, scene);
+        /**
+         * Convert Specular Glossiness Textures to Metallic Roughness.
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
          * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
          * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
+         * @param diffuseTexture - texture used to store diffuse information.
+         * @param specularGlossinessTexture - texture used to store specular and glossiness information.
+         * @param factors - specular glossiness material factors.
+         * @param mimeType - the mime type to use for the texture.
+         * @returns pbr metallic roughness interface or null.
+         */
+        private static _ConvertSpecularGlossinessTexturesToMetallicRoughness(diffuseTexture, specularGlossinessTexture, factors, mimeType);
+        /**
+         * Converts specular glossiness material properties to metallic roughness.
+         * @param specularGlossiness - interface with specular glossiness material properties.
+         * @returns - interface with metallic roughness material properties.
+         */
+        private static _ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+        /**
+         * Calculates the surface reflectance, independent of lighting conditions.
          * @param color - Color source to calculate brightness from.
          * @param color - Color source to calculate brightness from.
          * @returns number representing the perceived brightness, or zero if color is undefined.
          * @returns number representing the perceived brightness, or zero if color is undefined.
          */
          */
-        static GetPerceivedBrightness(color: Color3): number;
+        private static _GetPerceivedBrightness(color);
         /**
         /**
          * Returns the maximum color component value.
          * Returns the maximum color component value.
          * @param color
          * @param color
          * @returns maximum color component value, or zero if color is null or undefined.
          * @returns maximum color component value, or zero if color is null or undefined.
          */
          */
-        static GetMaxComponent(color: Color3): number;
+        private static _GetMaxComponent(color);
         /**
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
          * @param babylonPBRMaterial - BJS PBR Metallic Roughness Material.
          * @param babylonPBRMaterial - BJS PBR Metallic Roughness Material.
@@ -414,7 +465,7 @@ declare module BABYLON.GLTF2 {
          * @param imageData - map of image file name to data.
          * @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.
          * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
          */
          */
-        static ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
+        static _ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
             [fileName: string]: {
             [fileName: string]: {
                 data: Uint8Array;
                 data: Uint8Array;
                 mimeType: ImageMimeType;
                 mimeType: ImageMimeType;
@@ -427,13 +478,19 @@ declare module BABYLON.GLTF2 {
          * @param images - Array of glTF images.
          * @param images - Array of glTF images.
          * @param textures - Array of glTF textures.
          * @param textures - Array of glTF textures.
          * @param imageData - map of image file name and data.
          * @param imageData - map of image file name and data.
-         * @return - glTF texture, or null if the texture format is not supported.
+         * @return - glTF texture info, or null if the texture format is not supported.
          */
          */
-        static ExportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], imageData: {
-            [fileName: string]: {
-                data: Uint8Array;
-                mimeType: ImageMimeType;
-            };
-        }): Nullable<ITextureInfo>;
+        private static _ExportTexture(babylonTexture, mimeType, images, textures, imageData);
+        /**
+         * Builds a texture from base64 string.
+         * @param base64Texture - base64 texture string.
+         * @param textureName - Name to use for the texture.
+         * @param mimeType - image mime type for the texture.
+         * @param images - array of images.
+         * @param textures - array of textures.
+         * @param imageData - map of image data.
+         * @returns - glTF texture info, or null if the texture format is not supported.
+         */
+        private static _GetTextureInfoFromBase64(base64Texture, textureName, mimeType, images, textures, imageData);
     }
     }
 }
 }

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

@@ -1,7 +1,7 @@
 {
 {
-  "errors": 7193,
+  "errors": 7189,
   "babylon.typedoc.json": {
   "babylon.typedoc.json": {
-    "errors": 7193,
+    "errors": 7189,
     "AnimationKeyInterpolation": {
     "AnimationKeyInterpolation": {
       "Enumeration": {
       "Enumeration": {
         "Comments": {
         "Comments": {
@@ -16462,28 +16462,6 @@
             }
             }
           }
           }
         },
         },
-        "Lerp": {
-          "Comments": {
-            "MissingReturn": true
-          },
-          "Parameter": {
-            "startValue": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "endValue": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "gradient": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
         "LookAtLH": {
         "LookAtLH": {
           "Comments": {
           "Comments": {
             "MissingReturn": true
             "MissingReturn": true

+ 80 - 24
dist/preview release/viewer/babylon.viewer.d.ts

@@ -224,6 +224,11 @@ declare module BabylonViewer {
         subtitle?: string;
         subtitle?: string;
         thumbnail?: string; // URL or data-url
         thumbnail?: string; // URL or data-url
 
 
+        animation?: {
+            autoStart?: boolean | string;
+            playOnce?: boolean;
+        }
+
         // [propName: string]: any; // further configuration, like title and creator
         // [propName: string]: any; // further configuration, like title and creator
     }
     }
 
 
@@ -400,57 +405,108 @@ declare module BabylonViewer {
     }
     }
     /////>configuration
     /////>configuration
 
 
+    export enum AnimationPlayMode {
+        ONCE = 0,
+        LOOP = 1,
+    }
+    export enum AnimationState {
+        INIT = 0,
+        PLAYING = 1,
+        PAUSED = 2,
+        STOPPED = 3,
+        ENDED = 4,
+    }
+    interface IModelAnimation extends BABYLON.IDisposable {
+        readonly state: AnimationState;
+        readonly name: string;
+        readonly frames: number;
+        readonly currentFrame: number;
+        readonly fps: number;
+        speedRatio: number;
+        playMode: AnimationPlayMode;
+        start(): any;
+        stop(): any;
+        pause(): any;
+        reset(): any;
+        restart(): any;
+        goToFrame(frameNumber: number): any;
+    }
+
+    class ViewerModel implements BABYLON.IDisposable {
+        loader: BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync;
+        meshes: Array<BABYLON.AbstractMesh>;
+        particleSystems: Array<BABYLON.ParticleSystem>;
+        skeletons: Array<BABYLON.Skeleton>;
+        currentAnimation: IModelAnimation;
+        onLoadedObservable: BABYLON.Observable<ViewerModel>;
+        onLoadProgressObservable: BABYLON.Observable<BABYLON.SceneLoaderProgressEvent>;
+        onLoadErrorObservable: BABYLON.Observable<{
+            message: string;
+            exception: any;
+        }>;
+        constructor(_modelConfiguration: IModelConfiguration, _scene: BABYLON.Scene, disableAutoLoad?: boolean);
+        load(): void;
+        getAnimationNames(): string[];
+        protected _getAnimationByName(name: string): BABYLON.Nullable<IModelAnimation>;
+        playAnimation(name: string): IModelAnimation;
+        dispose(): void;
+    }
+
     /////<viewer
     /////<viewer
     export abstract class AbstractViewer {
     export abstract class AbstractViewer {
         containerElement: HTMLElement;
         containerElement: HTMLElement;
         templateManager: TemplateManager;
         templateManager: TemplateManager;
-        camera: BABYLON.ArcRotateCamera;
         engine: BABYLON.Engine;
         engine: BABYLON.Engine;
         scene: BABYLON.Scene;
         scene: BABYLON.Scene;
+        camera: BABYLON.ArcRotateCamera;
+        sceneOptimizer: BABYLON.SceneOptimizer;
         baseId: string;
         baseId: string;
-        canvas: HTMLCanvasElement;
+        models: Array<ViewerModel>;
+        lastUsedLoader: BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync;
         protected configuration: ViewerConfiguration;
         protected configuration: ViewerConfiguration;
         environmentHelper: BABYLON.EnvironmentHelper;
         environmentHelper: BABYLON.EnvironmentHelper;
         protected defaultHighpTextureType: number;
         protected defaultHighpTextureType: number;
         protected shadowGeneratorBias: number;
         protected shadowGeneratorBias: number;
         protected defaultPipelineTextureType: number;
         protected defaultPipelineTextureType: number;
         protected maxShadows: number;
         protected maxShadows: number;
+        readonly isHdrSupported: boolean;
         onSceneInitObservable: BABYLON.Observable<BABYLON.Scene>;
         onSceneInitObservable: BABYLON.Observable<BABYLON.Scene>;
         onEngineInitObservable: BABYLON.Observable<BABYLON.Engine>;
         onEngineInitObservable: BABYLON.Observable<BABYLON.Engine>;
-        onModelLoadedObservable: BABYLON.Observable<BABYLON.AbstractMesh[]>;
+        onModelLoadedObservable: BABYLON.Observable<ViewerModel>;
         onModelLoadProgressObservable: BABYLON.Observable<BABYLON.SceneLoaderProgressEvent>;
         onModelLoadProgressObservable: BABYLON.Observable<BABYLON.SceneLoaderProgressEvent>;
-        onModelLoadErrorObservable: BABYLON.Observable<{ message: string; exception: any }>;
+        onModelLoadErrorObservable: BABYLON.Observable<{
+            message: string;
+            exception: any;
+        }>;
         onLoaderInitObservable: BABYLON.Observable<BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync>;
         onLoaderInitObservable: BABYLON.Observable<BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync>;
         onInitDoneObservable: BABYLON.Observable<AbstractViewer>;
         onInitDoneObservable: BABYLON.Observable<AbstractViewer>;
+        canvas: HTMLCanvasElement;
+        protected registeredOnBeforerenderFunctions: Array<() => void>;
         constructor(containerElement: HTMLElement, initialConfiguration?: ViewerConfiguration);
         constructor(containerElement: HTMLElement, initialConfiguration?: ViewerConfiguration);
         getBaseId(): string;
         getBaseId(): string;
-        protected abstract prepareContainerElement(): any;
-        protected onTemplatesLoaded(): Promise<AbstractViewer>;
-        protected initEngine(): Promise<BABYLON.Engine>;
-        protected initScene(): Promise<BABYLON.Scene>;
-        dispose(): void;
-        loadModel(model?: any, clearScene?: boolean): Promise<BABYLON.Scene>;
-        lastUsedLoader: BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync;
-        sceneOptimizer: BABYLON.SceneOptimizer;
-        protected registeredOnBeforerenderFunctions: Array<() => void>;
         isCanvasInDOM(): boolean;
         isCanvasInDOM(): boolean;
         protected resize: () => void;
         protected resize: () => void;
         protected render: () => void;
         protected render: () => void;
-        updateConfiguration(newConfiguration: Partial<ViewerConfiguration>): void;
-        protected configureEnvironment(skyboxConifguration?: ISkyboxConfiguration | boolean, groundConfiguration?: IGroundConfiguration | boolean): void;
+        updateConfiguration(newConfiguration?: Partial<ViewerConfiguration>): void;
+        protected configureEnvironment(skyboxConifguration?: ISkyboxConfiguration | boolean, groundConfiguration?: IGroundConfiguration | boolean): Promise<BABYLON.Scene> | undefined;
         protected configureScene(sceneConfig: ISceneConfiguration, optimizerConfig?: ISceneOptimizerConfiguration): void;
         protected configureScene(sceneConfig: ISceneConfiguration, optimizerConfig?: ISceneOptimizerConfiguration): void;
         protected configureOptimizer(optimizerConfig: ISceneOptimizerConfiguration | boolean): void;
         protected configureOptimizer(optimizerConfig: ISceneOptimizerConfiguration | boolean): void;
         protected configureObservers(observersConfiguration: IObserversConfiguration): void;
         protected configureObservers(observersConfiguration: IObserversConfiguration): void;
-        protected configureCamera(cameraConfig: ICameraConfiguration, focusMeshes: Array<BABYLON.AbstractMesh>): void;
-        protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean }, focusMeshes: Array<BABYLON.AbstractMesh>): void;
-        protected configureModel(modelConfiguration: Partial<IModelConfiguration>, focusMeshes: Array<BABYLON.AbstractMesh>): void;
+        protected configureCamera(cameraConfig: ICameraConfiguration, model?: ViewerModel): void;
+        protected configureLights(lightsConfiguration?: {
+            [name: string]: ILightConfiguration | boolean;
+        }, model?: ViewerModel): void;
+        protected configureModel(modelConfiguration: Partial<IModelConfiguration>, model?: ViewerModel): void;
         dispose(): void;
         dispose(): void;
-        protected initEnvironment(focusMeshes: Array<BABYLON.AbstractMesh>): Promise<BABYLON.Scene>;
+        protected abstract prepareContainerElement(): any;
+        protected onTemplatesLoaded(): Promise<AbstractViewer>;
+        protected initEngine(): Promise<BABYLON.Engine>;
+        protected initScene(): Promise<BABYLON.Scene>;
+        loadModel(modelConfig?: any, clearScene?: boolean): Promise<ViewerModel>;
+        protected initEnvironment(viewerModel?: ViewerModel): Promise<BABYLON.Scene>;
+        protected handleHardwareLimitations(): void;
         protected injectCustomShaders(): void;
         protected injectCustomShaders(): void;
         protected extendClassWithConfig(object: any, config: any): void;
         protected extendClassWithConfig(object: any, config: any): void;
-        protected handleHardwareLimitations(): void;
-
-
     }
     }
 
 
     export class DefaultViewer extends AbstractViewer {
     export class DefaultViewer extends AbstractViewer {
@@ -460,8 +516,8 @@ declare module BabylonViewer {
         initScene(): Promise<BABYLON.Scene>;
         initScene(): Promise<BABYLON.Scene>;
         protected onTemplatesLoaded(): Promise<AbstractViewer>;
         protected onTemplatesLoaded(): Promise<AbstractViewer>;
         protected prepareContainerElement(): void;
         protected prepareContainerElement(): void;
-        loadModel(model?: any): Promise<BABYLON.Scene>;
-        initEnvironment(focusMeshes?: Array<BABYLON.AbstractMesh>): Promise<BABYLON.Scene>;
+        loadModel(model?: any): Promise<ViewerModel>;
+        initEnvironment(viewerModel?: ViewerModel): Promise<BABYLON.Scene>;
         showOverlayScreen(subScreen: string): Promise<Template>;
         showOverlayScreen(subScreen: string): Promise<Template>;
         hideOverlayScreen(): Promise<Template>;
         hideOverlayScreen(): Promise<Template>;
         showLoadingScreen(): Promise<Template>;
         showLoadingScreen(): Promise<Template>;

Plik diff jest za duży
+ 68 - 68
dist/preview release/viewer/babylon.viewer.js


Plik diff jest za duży
+ 1493 - 736
dist/preview release/viewer/babylon.viewer.max.js


+ 7 - 2
dist/preview release/what's new.md

@@ -14,11 +14,13 @@
 - Added [VideoDome](http://doc.babylonjs.com/how_to/360videodome) class to easily support 360 videos ([DavidHGillen](https://github.com/DavidHGillen))
 - 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))
 - Added [GlowLayer](https://doc.babylonjs.com/how_to/glow_layer) to easily support glow from emissive materials ([sebavan](https://github.com/sebavan))
 - New [AssetContainer](http://doc.babylonjs.com/how_to/how_to_use_assetcontainer) Class and loading methods ([trevordev](https://github.com/trevordev))
 - New [AssetContainer](http://doc.babylonjs.com/how_to/how_to_use_assetcontainer) Class and loading methods ([trevordev](https://github.com/trevordev))
-- Added depth of field, MSAA, sharpening, grain and chromatic aberration effect to the default pipeline ([trevordev](https://github.com/trevordev))
+- Added depth of field, MSAA, sharpening, chromatic aberration and grain effect to the default pipeline ([trevordev](https://github.com/trevordev))
+- Particle System SubEmitters- Spawn new Sub emitter when particles dies. Cone/Sphere shapes emitters ([IbraheemOsama](https://github.com/IbraheemOsama))
 
 
 ## Updates
 ## Updates
 
 
 - Tons of functions and classes received the code comments they deserved (All the community)
 - Tons of functions and classes received the code comments they deserved (All the community)
+- Added support for 16bits TGA ([deltakosh](https://github.com/deltakosh))
 - New `AnimationPropertiesOverride` class used to simplify setting animation properties on child animations. [Documentation](http://doc.babylonjs.com/babylon101/animations#overriding-properties) ([deltakosh](https://github.com/deltakosh))
 - New `AnimationPropertiesOverride` class used to simplify setting animation properties on child animations. [Documentation](http://doc.babylonjs.com/babylon101/animations#overriding-properties) ([deltakosh](https://github.com/deltakosh))
 - New `Texture.UseSerializedUrlIfAny` static property to let textures serialize complete URL instead of using side by side loading ([deltakosh](https://github.com/deltakosh))
 - New `Texture.UseSerializedUrlIfAny` static property to let textures serialize complete URL instead of using side by side loading ([deltakosh](https://github.com/deltakosh))
 - Added `particleSystem.reset()` to clear a particle system ([deltakosh](https://github.com/deltakosh))
 - Added `particleSystem.reset()` to clear a particle system ([deltakosh](https://github.com/deltakosh))
@@ -85,6 +87,8 @@
 - Default fragment shader will clamp negative values to avoid underflow, webVR post processing will render to eye texture size ([trevordev](https://github.com/trevordev))
 - Default fragment shader will clamp negative values to avoid underflow, webVR post processing will render to eye texture size ([trevordev](https://github.com/trevordev))
 - Supports Environment Drag and Drop in Sandbox ([sebavan](https://github.com/sebavan))
 - Supports Environment Drag and Drop in Sandbox ([sebavan](https://github.com/sebavan))
 - EnvironmentHelper has no an onError observable to handle errors when loading the textures ([RaananW](https://github.com/RaananW))
 - EnvironmentHelper has no an onError observable to handle errors when loading the textures ([RaananW](https://github.com/RaananW))
+- (Viewer) Viewer supports model animations ([RaananW](https://github.com/RaananW))
+- Tests for sharpen, chromatic aberration and enable/disable post processes ([trevordev](https://github.com/trevordev))
 
 
 ## Bug fixes
 ## Bug fixes
 
 
@@ -102,10 +106,11 @@
 - postMessage calls in webworkers were fixed. ([RaananW](https://github.com/RaananW))
 - postMessage calls in webworkers were fixed. ([RaananW](https://github.com/RaananW))
 - Fixed WebCam Texture on Firefox and Edge - [#3825](https://github.com/BabylonJS/Babylon.js/issues/3825) ([sebavan](https://github.com/sebavan))
 - Fixed WebCam Texture on Firefox and Edge - [#3825](https://github.com/BabylonJS/Babylon.js/issues/3825) ([sebavan](https://github.com/sebavan))
 - Add onLoadObservable on VideoTexture - [#3845](https://github.com/BabylonJS/Babylon.js/issues/3845) ([sebavan](https://github.com/sebavan))
 - Add onLoadObservable on VideoTexture - [#3845](https://github.com/BabylonJS/Babylon.js/issues/3845) ([sebavan](https://github.com/sebavan))
+- beforeRender is now triggered after the camera updated its state - [#3873](https://github.com/BabylonJS/Babylon.js/issues/3873) ([RaananW](https://github.com/RaananW))
 
 
 ## Breaking changes
 ## Breaking changes
 
 
 - Removed the unused PostProcessRenderPass class and extended postProcessingRenderingEffect to support multiple PostProcesses ([trevordev](https://github.com/trevordev))
 - 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)]
 - 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 now creates a mesh for each primitive instead of merging the primitives together into one mesh. If a mesh only has one primitive, the behavior is the same as before. This change only affects meshes that have multiple primitives. ([bghgary](https://github.com/bghgary)]
 - Engine's onCanvasPointerOutObservable will now return a PointerEvent instead of the Engine. ([trevordev](https://github.com/trevordev))
 - Engine's onCanvasPointerOutObservable will now return a PointerEvent instead of the Engine. ([trevordev](https://github.com/trevordev))

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

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

+ 2 - 2
loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts

@@ -56,13 +56,13 @@ module BABYLON.GLTF2.Extensions {
             babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
             babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
 
 
             if (properties.diffuseTexture) {
             if (properties.diffuseTexture) {
-                promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, texture => {
+                promises.push(this._loader._loadTextureAsync(`${context}/diffuseTexture`, properties.diffuseTexture, texture => {
                     babylonMaterial.albedoTexture = texture;
                     babylonMaterial.albedoTexture = texture;
                 }));
                 }));
             }
             }
 
 
             if (properties.specularGlossinessTexture) {
             if (properties.specularGlossinessTexture) {
-                promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, texture => {
+                promises.push(this._loader._loadTextureAsync(`${context}/specularGlossinessTexture`, properties.specularGlossinessTexture, texture => {
                     babylonMaterial.reflectivityTexture = texture;
                     babylonMaterial.reflectivityTexture = texture;
                 }));
                 }));
 
 

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

@@ -39,10 +39,13 @@ module BABYLON.GLTF2.Extensions {
                         }
                         }
                     }
                     }
 
 
-                    const promise = this._loader._loadNodeAsync("#/nodes/" + nodeLOD._index, nodeLOD).then(() => {
+                    const promise = this._loader._loadNodeAsync(`#/nodes/${nodeLOD._index}`, nodeLOD).then(() => {
                         if (indexLOD !== 0) {
                         if (indexLOD !== 0) {
                             const previousNodeLOD = nodeLODs[indexLOD - 1];
                             const previousNodeLOD = nodeLODs[indexLOD - 1];
-                            previousNodeLOD._babylonMesh!.setEnabled(false);
+                            if (previousNodeLOD._babylonMesh) {
+                                previousNodeLOD._babylonMesh.dispose();
+                                delete previousNodeLOD._babylonMesh;
+                            }
                         }
                         }
 
 
                         if (indexLOD !== nodeLODs.length - 1) {
                         if (indexLOD !== nodeLODs.length - 1) {
@@ -89,9 +92,15 @@ module BABYLON.GLTF2.Extensions {
                         }
                         }
                     }
                     }
 
 
-                    const promise = this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : () => {}).then(() => {
+                    const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD._index}`, materialLOD, babylonMesh, indexLOD === 0 ? assign : () => {}).then(() => {
                         if (indexLOD !== 0) {
                         if (indexLOD !== 0) {
                             assign(materialLOD._babylonMaterial!);
                             assign(materialLOD._babylonMaterial!);
+
+                            const previousMaterialLOD = materialLODs[indexLOD - 1];
+                            if (previousMaterialLOD._babylonMaterial) {
+                                previousMaterialLOD._babylonMaterial.dispose();
+                                delete previousMaterialLOD._babylonMaterial;
+                            }
                         }
                         }
 
 
                         if (indexLOD !== materialLODs.length - 1) {
                         if (indexLOD !== materialLODs.length - 1) {
@@ -145,7 +154,7 @@ module BABYLON.GLTF2.Extensions {
             const properties = new Array<T>();
             const properties = new Array<T>();
 
 
             for (let i = ids.length - 1; i >= 0; i--) {
             for (let i = ids.length - 1; i >= 0; i--) {
-                properties.push(GLTFLoader._GetProperty(context + "/ids/" + ids[i], array, ids[i]));
+                properties.push(GLTFLoader._GetProperty(`${context}/ids/${ids[i]}`, array, ids[i]));
                 if (properties.length === this.maxLODsToLoad) {
                 if (properties.length === this.maxLODsToLoad) {
                     return properties;
                     return properties;
                 }
                 }

+ 138 - 127
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -34,7 +34,7 @@ module BABYLON.GLTF2 {
         private static _Factories: { [name: string]: (loader: GLTFLoader) => GLTFLoaderExtension } = {};
         private static _Factories: { [name: string]: (loader: GLTFLoader) => GLTFLoaderExtension } = {};
         public static _Register(name: string, factory: (loader: GLTFLoader) => GLTFLoaderExtension): void {
         public static _Register(name: string, factory: (loader: GLTFLoader) => GLTFLoaderExtension): void {
             if (GLTFLoader._Factories[name]) {
             if (GLTFLoader._Factories[name]) {
-                Tools.Error("Extension with the name '" + name + "' already exists");
+                Tools.Error(`Extension with the name '${name}' already exists`);
                 return;
                 return;
             }
             }
 
 
@@ -93,7 +93,7 @@ module BABYLON.GLTF2 {
                     nodes = names.map(name => {
                     nodes = names.map(name => {
                         const node = nodeMap[name];
                         const node = nodeMap[name];
                         if (!node) {
                         if (!node) {
-                            throw new Error("Failed to find node " + name);
+                            throw new Error(`Failed to find node '${name}'`);
                         }
                         }
 
 
                         return node;
                         return node;
@@ -132,8 +132,8 @@ module BABYLON.GLTF2 {
                     promises.push(this._loadNodesAsync(nodes));
                     promises.push(this._loadNodesAsync(nodes));
                 }
                 }
                 else {
                 else {
-                    const scene = GLTFLoader._GetProperty("#/scene", this._gltf.scenes, this._gltf.scene || 0);
-                    promises.push(this._loadSceneAsync("#/scenes/" + scene._index, scene));
+                    const scene = GLTFLoader._GetProperty(`#/scene`, this._gltf.scenes, this._gltf.scene || 0);
+                    promises.push(this._loadSceneAsync(`#/scenes/${scene._index}`, scene));
                 }
                 }
 
 
                 if (this.compileMaterials) {
                 if (this.compileMaterials) {
@@ -160,7 +160,7 @@ module BABYLON.GLTF2 {
                                 this.onCompleteObservable.clear();
                                 this.onCompleteObservable.clear();
                                 this._clear();
                                 this._clear();
                             }).catch(error => {
                             }).catch(error => {
-                                Tools.Error("glTF Loader: " + error.message);
+                                Tools.Error(`glTF Loader: ${error.message}`);
                                 this._clear();
                                 this._clear();
                             });
                             });
                         }
                         }
@@ -169,7 +169,7 @@ module BABYLON.GLTF2 {
 
 
                 return resultPromise;
                 return resultPromise;
             }).catch(error => {
             }).catch(error => {
-                Tools.Error("glTF Loader: " + error.message);
+                Tools.Error(`glTF Loader: ${error.message}`);
                 this._clear();
                 this._clear();
                 throw error;
                 throw error;
             });
             });
@@ -195,7 +195,7 @@ module BABYLON.GLTF2 {
                 if (buffers && buffers[0] && !buffers[0].uri) {
                 if (buffers && buffers[0] && !buffers[0].uri) {
                     const binaryBuffer = buffers[0];
                     const binaryBuffer = buffers[0];
                     if (binaryBuffer.byteLength < data.bin.byteLength - 3 || binaryBuffer.byteLength > data.bin.byteLength) {
                     if (binaryBuffer.byteLength < data.bin.byteLength - 3 || binaryBuffer.byteLength > data.bin.byteLength) {
-                        Tools.Warn("Binary buffer length (" + binaryBuffer.byteLength + ") from JSON does not match chunk length (" + data.bin.byteLength + ")");
+                        Tools.Warn(`Binary buffer length (${binaryBuffer.byteLength}) from JSON does not match chunk length (${data.bin.byteLength})`);
                     }
                     }
 
 
                     binaryBuffer._data = Promise.resolve(data.bin);
                     binaryBuffer._data = Promise.resolve(data.bin);
@@ -269,7 +269,7 @@ module BABYLON.GLTF2 {
                     break;
                     break;
                 }
                 }
                 default: {
                 default: {
-                    throw new Error("Invalid coordinate system mode " + this.coordinateSystemMode);
+                    throw new Error(`Invalid coordinate system mode (${this.coordinateSystemMode})`);
                 }
                 }
             }
             }
 
 
@@ -281,7 +281,7 @@ module BABYLON.GLTF2 {
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
             for (let node of nodes) {
             for (let node of nodes) {
-                promises.push(this._loadNodeAsync("#/nodes/" + node._index, node));
+                promises.push(this._loadNodeAsync(`#/nodes/${node._index}`, node));
             }
             }
 
 
             promises.push(this._loadAnimationsAsync());
             promises.push(this._loadAnimationsAsync());
@@ -298,8 +298,8 @@ module BABYLON.GLTF2 {
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
             for (let index of scene.nodes) {
             for (let index of scene.nodes) {
-                const node = GLTFLoader._GetProperty(context + "/nodes/" + index, this._gltf.nodes, index);
-                promises.push(this._loadNodeAsync("#/nodes/" + node._index, node));
+                const node = GLTFLoader._GetProperty(`${context}/nodes/${index}`, this._gltf.nodes, index);
+                promises.push(this._loadNodeAsync(`#/nodes/${node._index}`, node));
             }
             }
 
 
             promises.push(this._loadAnimationsAsync());
             promises.push(this._loadAnimationsAsync());
@@ -307,8 +307,20 @@ module BABYLON.GLTF2 {
             return Promise.all(promises).then(() => {});
             return Promise.all(promises).then(() => {});
         }
         }
 
 
-        private _getMeshes(): AbstractMesh[] {
-            const meshes = new Array<AbstractMesh>();
+        private _forEachNodeMesh(node: ILoaderNode, callback: (babylonMesh: Mesh) => void): void {
+            if (node._babylonMesh) {
+                callback(node._babylonMesh);
+            }
+
+            if (node._primitiveBabylonMeshes) {
+                for (const babylonMesh of node._primitiveBabylonMeshes) {
+                    callback(babylonMesh);
+                }
+            }
+        }
+
+        private _getMeshes(): Mesh[] {
+            const meshes = new Array<Mesh>();
 
 
             // Root mesh is always first.
             // Root mesh is always first.
             meshes.push(this._rootBabylonMesh);
             meshes.push(this._rootBabylonMesh);
@@ -316,15 +328,9 @@ module BABYLON.GLTF2 {
             const nodes = this._gltf.nodes;
             const nodes = this._gltf.nodes;
             if (nodes) {
             if (nodes) {
                 for (const node of nodes) {
                 for (const node of nodes) {
-                    if (node._babylonMesh) {
-                        meshes.push(node._babylonMesh);
-                    }
-
-                    if (node._primitiveBabylonMeshes) {
-                        for (const babylonMesh of node._primitiveBabylonMeshes) {
-                            meshes.push(babylonMesh);
-                        }
-                    }
+                    this._forEachNodeMesh(node, mesh => {
+                        meshes.push(mesh);
+                    });
                 }
                 }
             }
             }
 
 
@@ -369,7 +375,7 @@ module BABYLON.GLTF2 {
                     break;
                     break;
                 }
                 }
                 default: {
                 default: {
-                    Tools.Error("Invalid animation start mode " + this.animationStartMode);
+                    Tools.Error(`Invalid animation start mode (${this.animationStartMode})`);
                     return;
                     return;
                 }
                 }
             }
             }
@@ -382,12 +388,12 @@ module BABYLON.GLTF2 {
             }
             }
 
 
             if (node._babylonMesh) {
             if (node._babylonMesh) {
-                throw new Error(context + ": Invalid recursive node hierarchy");
+                throw new Error(`${context}: Invalid recursive node hierarchy`);
             }
             }
 
 
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
-            const babylonMesh = new Mesh(node.name || "node" + node._index, this._babylonScene, node._parent._babylonMesh);
+            const babylonMesh = new Mesh(node.name || `node${node._index}`, this._babylonScene, node._parent._babylonMesh);
             node._babylonMesh = babylonMesh;
             node._babylonMesh = babylonMesh;
 
 
             node._babylonAnimationTargets = node._babylonAnimationTargets || [];
             node._babylonAnimationTargets = node._babylonAnimationTargets || [];
@@ -396,14 +402,14 @@ module BABYLON.GLTF2 {
             GLTFLoader._LoadTransform(node, babylonMesh);
             GLTFLoader._LoadTransform(node, babylonMesh);
 
 
             if (node.mesh != undefined) {
             if (node.mesh != undefined) {
-                const mesh = GLTFLoader._GetProperty(context + "/mesh", this._gltf.meshes, node.mesh);
-                promises.push(this._loadMeshAsync("#/meshes/" + mesh._index, node, mesh));
+                const mesh = GLTFLoader._GetProperty(`${context}/mesh`, this._gltf.meshes, node.mesh);
+                promises.push(this._loadMeshAsync(`#/meshes/${mesh._index}`, node, mesh, babylonMesh));
             }
             }
 
 
             if (node.children) {
             if (node.children) {
                 for (const index of node.children) {
                 for (const index of node.children) {
-                    const childNode = GLTFLoader._GetProperty(context + "/children/" + index, this._gltf.nodes, index);
-                    promises.push(this._loadNodeAsync("#/nodes/" + index, childNode));
+                    const childNode = GLTFLoader._GetProperty(`${context}/children/${index}`, this._gltf.nodes, index);
+                    promises.push(this._loadNodeAsync(`#/nodes/${index}`, childNode));
                 }
                 }
             }
             }
 
 
@@ -412,43 +418,46 @@ module BABYLON.GLTF2 {
             return Promise.all(promises).then(() => {});
             return Promise.all(promises).then(() => {});
         }
         }
 
 
-        private _loadMeshAsync(context: string, node: ILoaderNode, mesh: ILoaderMesh): Promise<void> {
+        private _loadMeshAsync(context: string, node: ILoaderNode, mesh: ILoaderMesh, babylonMesh: Mesh): Promise<void> {
             // TODO: instancing
             // TODO: instancing
 
 
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
             const primitives = mesh.primitives;
             const primitives = mesh.primitives;
             if (!primitives || primitives.length === 0) {
             if (!primitives || primitives.length === 0) {
-                throw new Error(context + ": Primitives are missing");
+                throw new Error(`${context}: Primitives are missing`);
             }
             }
 
 
             ArrayItem.Assign(primitives);
             ArrayItem.Assign(primitives);
-            for (const primitive of primitives) {
-                promises.push(this._loadPrimitiveAsync(context + "/primitives/" + primitive._index, node, mesh, primitive));
+            if (primitives.length === 1) {
+                const primitive = primitives[0];
+                promises.push(this._loadPrimitiveAsync(`${context}/primitives/${primitive._index}`, node, mesh, primitive, babylonMesh));
+            }
+            else {
+                node._primitiveBabylonMeshes = [];
+                for (const primitive of primitives) {
+                    const primitiveBabylonMesh = new Mesh(`${mesh.name || babylonMesh.name}_${primitive._index}`, this._babylonScene, babylonMesh);
+                    node._primitiveBabylonMeshes.push(babylonMesh);
+                    promises.push(this._loadPrimitiveAsync(`${context}/primitives/${primitive._index}`, node, mesh, primitive, primitiveBabylonMesh));
+                    this.onMeshLoadedObservable.notifyObservers(babylonMesh);
+                }
             }
             }
 
 
             if (node.skin != undefined) {
             if (node.skin != undefined) {
-                const skin = GLTFLoader._GetProperty(context + "/skin", this._gltf.skins, node.skin);
-                promises.push(this._loadSkinAsync("#/skins/" + skin._index, node, mesh, skin));
+                const skin = GLTFLoader._GetProperty(`${context}/skin`, this._gltf.skins, node.skin);
+                promises.push(this._loadSkinAsync(`#/skins/${skin._index}`, node, mesh, skin));
             }
             }
 
 
             return Promise.all(promises).then(() => {
             return Promise.all(promises).then(() => {
-                if (node._primitiveBabylonMeshes) {
-                    for (const primitiveBabylonMesh of node._primitiveBabylonMeshes) {
-                        primitiveBabylonMesh._refreshBoundingInfo(true);
-                    }
-                }
+                this._forEachNodeMesh(node, babylonMesh => {
+                    babylonMesh._refreshBoundingInfo(true);
+                });
             });
             });
         }
         }
 
 
-        private _loadPrimitiveAsync(context: string, node: ILoaderNode, mesh: ILoaderMesh, primitive: ILoaderMeshPrimitive): Promise<void> {
+        private _loadPrimitiveAsync(context: string, node: ILoaderNode, mesh: ILoaderMesh, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Promise<void> {
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
-            const babylonMesh = new Mesh((mesh.name || node._babylonMesh!.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-
-            node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
-            node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
-
             this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
             this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
 
 
             promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(babylonVertexData => {
             promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(babylonVertexData => {
@@ -460,14 +469,12 @@ module BABYLON.GLTF2 {
                 babylonMesh.material = this._getDefaultMaterial();
                 babylonMesh.material = this._getDefaultMaterial();
             }
             }
             else {
             else {
-                const material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, babylonMaterial => {
+                const material = GLTFLoader._GetProperty(`${context}/material}`, this._gltf.materials, primitive.material);
+                promises.push(this._loadMaterialAsync(`#/materials/${material._index}`, material, babylonMesh, babylonMaterial => {
                     babylonMesh.material = babylonMaterial;
                     babylonMesh.material = babylonMaterial;
                 }));
                 }));
             }
             }
 
 
-            this.onMeshLoadedObservable.notifyObservers(babylonMesh);
-
             return Promise.all(promises).then(() => {});
             return Promise.all(promises).then(() => {});
         }
         }
 
 
@@ -479,12 +486,12 @@ module BABYLON.GLTF2 {
 
 
             const attributes = primitive.attributes;
             const attributes = primitive.attributes;
             if (!attributes) {
             if (!attributes) {
-                throw new Error(context + ": Attributes are missing");
+                throw new Error(`${context}: Attributes are missing`);
             }
             }
 
 
             if (primitive.mode != undefined && primitive.mode !== MeshPrimitiveMode.TRIANGLES) {
             if (primitive.mode != undefined && primitive.mode !== MeshPrimitiveMode.TRIANGLES) {
                 // TODO: handle other primitive modes
                 // TODO: handle other primitive modes
-                throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
+                throw new Error(`${context}: Mode (${primitive.mode}) is not currently supported`);
             }
             }
 
 
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
@@ -494,7 +501,7 @@ module BABYLON.GLTF2 {
             if (primitive.indices == undefined) {
             if (primitive.indices == undefined) {
                 const positionAccessorIndex = attributes["POSITION"];
                 const positionAccessorIndex = attributes["POSITION"];
                 if (positionAccessorIndex != undefined) {
                 if (positionAccessorIndex != undefined) {
-                    const accessor = GLTFLoader._GetProperty(context + "/attributes/POSITION", this._gltf.accessors, positionAccessorIndex);
+                    const accessor = GLTFLoader._GetProperty(`${context}/attributes/POSITION`, this._gltf.accessors, positionAccessorIndex);
                     babylonVertexData.indices = new Uint32Array(accessor.count);
                     babylonVertexData.indices = new Uint32Array(accessor.count);
                     for (let i = 0; i < babylonVertexData.indices.length; i++) {
                     for (let i = 0; i < babylonVertexData.indices.length; i++) {
                         babylonVertexData.indices[i] = i;
                         babylonVertexData.indices[i] = i;
@@ -502,8 +509,8 @@ module BABYLON.GLTF2 {
                 }
                 }
             }
             }
             else {
             else {
-                const indicesAccessor = GLTFLoader._GetProperty(context + "/indices", this._gltf.accessors, primitive.indices);
-                promises.push(this._loadAccessorAsync("#/accessors/" + indicesAccessor._index, indicesAccessor).then(data => {
+                const indicesAccessor = GLTFLoader._GetProperty(`${context}/indices`, this._gltf.accessors, primitive.indices);
+                promises.push(this._loadAccessorAsync(`#/accessors/${indicesAccessor._index}`, indicesAccessor).then(data => {
                     babylonVertexData.indices = data as IndicesArray;
                     babylonVertexData.indices = data as IndicesArray;
                 }));
                 }));
             }
             }
@@ -518,17 +525,18 @@ module BABYLON.GLTF2 {
                     babylonMesh._delayInfo.push(kind);
                     babylonMesh._delayInfo.push(kind);
                 }
                 }
 
 
-                const accessor = GLTFLoader._GetProperty(context + "/attributes/" + attribute, this._gltf.accessors, attributes[attribute]);
+                if (attribute === "COLOR_0") {
+                    // Assume vertex color has alpha on the mesh. The alphaMode of the material controls whether the material should use alpha or not.
+                    babylonMesh.hasVertexAlpha = true;
+                }
 
 
-                promises.push(this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(data => {
+                const accessor = GLTFLoader._GetProperty(`${context}/attributes/${attribute}`, this._gltf.accessors, attributes[attribute]);
+
+                promises.push(this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
                     let attributeData = GLTFLoader._ConvertToFloat32Array(context, accessor, data);
                     let attributeData = GLTFLoader._ConvertToFloat32Array(context, accessor, data);
 
 
-                    if (attribute === "COLOR_0") {
-                        // Assume vertex color has alpha on the mesh. The alphaMode of the material controls whether the material should use alpha or not.
-                        babylonMesh.hasVertexAlpha = true;
-                        if (accessor.type === "VEC3") {
-                            attributeData = GLTFLoader._ConvertVec3ToVec4(context, attributeData);
-                        }
+                    if (attribute === "COLOR_0" && accessor.type === "VEC3") {
+                        attributeData = GLTFLoader._ConvertVec3ToVec4(context, attributeData);
                     }
                     }
 
 
                     babylonVertexData.set(attributeData, kind);
                     babylonVertexData.set(attributeData, kind);
@@ -558,13 +566,13 @@ module BABYLON.GLTF2 {
                 node._numMorphTargets = primitive.targets.length;
                 node._numMorphTargets = primitive.targets.length;
             }
             }
             else if (primitive.targets.length !== node._numMorphTargets) {
             else if (primitive.targets.length !== node._numMorphTargets) {
-                throw new Error(context + ": Primitives do not have the same number of targets");
+                throw new Error(`${context}: Primitives do not have the same number of targets`);
             }
             }
 
 
             babylonMesh.morphTargetManager = new MorphTargetManager();
             babylonMesh.morphTargetManager = new MorphTargetManager();
             for (let index = 0; index < primitive.targets.length; index++) {
             for (let index = 0; index < primitive.targets.length; index++) {
                 const weight = node.weights ? node.weights[index] : mesh.weights ? mesh.weights[index] : 0;
                 const weight = node.weights ? node.weights[index] : mesh.weights ? mesh.weights[index] : 0;
-                babylonMesh.morphTargetManager.addTarget(new MorphTarget("morphTarget" + index, weight));
+                babylonMesh.morphTargetManager.addTarget(new MorphTarget(`morphTarget${index}`, weight));
                 // TODO: tell the target whether it has positions, normals, tangents
                 // TODO: tell the target whether it has positions, normals, tangents
             }
             }
         }
         }
@@ -579,7 +587,7 @@ module BABYLON.GLTF2 {
             const morphTargetManager = babylonMesh.morphTargetManager!;
             const morphTargetManager = babylonMesh.morphTargetManager!;
             for (let index = 0; index < morphTargetManager.numTargets; index++) {
             for (let index = 0; index < morphTargetManager.numTargets; index++) {
                 const babylonMorphTarget = morphTargetManager.getTarget(index);
                 const babylonMorphTarget = morphTargetManager.getTarget(index);
-                promises.push(this._loadMorphTargetVertexDataAsync(context + "/targets/" + index, babylonVertexData, primitive.targets[index], babylonMorphTarget));
+                promises.push(this._loadMorphTargetVertexDataAsync(`${context}/targets/${index}`, babylonVertexData, primitive.targets[index], babylonMorphTarget));
             }
             }
 
 
             return Promise.all(promises).then(() => {});
             return Promise.all(promises).then(() => {});
@@ -593,8 +601,8 @@ module BABYLON.GLTF2 {
                     return;
                     return;
                 }
                 }
 
 
-                const accessor = GLTFLoader._GetProperty(context + "/" + attribute, this._gltf.accessors, attributes[attribute]);
-                promises.push(this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(data => {
+                const accessor = GLTFLoader._GetProperty(`${context}/${attribute}`, this._gltf.accessors, attributes[attribute]);
+                promises.push(this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
                     setData(data as Float32Array);
                     setData(data as Float32Array);
                 }));
                 }));
             };
             };
@@ -652,7 +660,7 @@ module BABYLON.GLTF2 {
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error(context + ": Invalid component type " + accessor.componentType);
+                        throw new Error(`${context}: Invalid component type (${accessor.componentType})`);
                     }
                     }
                 }
                 }
             }
             }
@@ -703,9 +711,9 @@ module BABYLON.GLTF2 {
 
 
         private _loadSkinAsync(context: string, node: ILoaderNode, mesh: ILoaderMesh, skin: ILoaderSkin): Promise<void> {
         private _loadSkinAsync(context: string, node: ILoaderNode, mesh: ILoaderMesh, skin: ILoaderSkin): Promise<void> {
             const assignSkeleton = () => {
             const assignSkeleton = () => {
-                for (const babylonMesh of node._primitiveBabylonMeshes!) {
+                this._forEachNodeMesh(node, babylonMesh => {
                     babylonMesh.skeleton = skin._babylonSkeleton;
                     babylonMesh.skeleton = skin._babylonSkeleton;
-                }
+                });
 
 
                 node._babylonMesh!.parent = this._rootBabylonMesh;
                 node._babylonMesh!.parent = this._rootBabylonMesh;
                 node._babylonMesh!.position = Vector3.Zero();
                 node._babylonMesh!.position = Vector3.Zero();
@@ -722,7 +730,7 @@ module BABYLON.GLTF2 {
             // TODO: split into two parts so that bones are created before inverseBindMatricesData is loaded (for compiling materials).
             // TODO: split into two parts so that bones are created before inverseBindMatricesData is loaded (for compiling materials).
 
 
             return (skin._loaded = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(inverseBindMatricesData => {
             return (skin._loaded = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(inverseBindMatricesData => {
-                const skeletonId = "skeleton" + skin._index;
+                const skeletonId = `skeleton${skin._index}`;
                 const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
                 const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
                 skin._babylonSkeleton = babylonSkeleton;
                 skin._babylonSkeleton = babylonSkeleton;
                 this._loadBones(context, skin, inverseBindMatricesData);
                 this._loadBones(context, skin, inverseBindMatricesData);
@@ -735,14 +743,14 @@ module BABYLON.GLTF2 {
                 return Promise.resolve(null);
                 return Promise.resolve(null);
             }
             }
 
 
-            const accessor = GLTFLoader._GetProperty(context + "/inverseBindMatrices", this._gltf.accessors, skin.inverseBindMatrices);
-            return this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(data => {
+            const accessor = GLTFLoader._GetProperty(`${context}/inverseBindMatrices`, this._gltf.accessors, skin.inverseBindMatrices);
+            return this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
                 return data as Float32Array;
                 return data as Float32Array;
             });
             });
         }
         }
 
 
         private _createBone(node: ILoaderNode, skin: ILoaderSkin, parent: Nullable<Bone>, localMatrix: Matrix, baseMatrix: Matrix, index: number): Bone {
         private _createBone(node: ILoaderNode, skin: ILoaderSkin, parent: Nullable<Bone>, localMatrix: Matrix, baseMatrix: Matrix, index: number): Bone {
-            const babylonBone = new Bone(node.name || "joint" + node._index, skin._babylonSkeleton!, parent, localMatrix, null, baseMatrix, index);
+            const babylonBone = new Bone(node.name || `joint${node._index}`, skin._babylonSkeleton!, parent, localMatrix, null, baseMatrix, index);
 
 
             node._babylonAnimationTargets = node._babylonAnimationTargets || [];
             node._babylonAnimationTargets = node._babylonAnimationTargets || [];
             node._babylonAnimationTargets.push(babylonBone);
             node._babylonAnimationTargets.push(babylonBone);
@@ -753,7 +761,7 @@ module BABYLON.GLTF2 {
         private _loadBones(context: string, skin: ILoaderSkin, inverseBindMatricesData: Nullable<Float32Array>): void {
         private _loadBones(context: string, skin: ILoaderSkin, inverseBindMatricesData: Nullable<Float32Array>): void {
             const babylonBones: { [index: number]: Bone } = {};
             const babylonBones: { [index: number]: Bone } = {};
             for (const index of skin.joints) {
             for (const index of skin.joints) {
-                const node = GLTFLoader._GetProperty(context + "/joints/" + index, this._gltf.nodes, index);
+                const node = GLTFLoader._GetProperty(`${context}/joints/${index}`, this._gltf.nodes, index);
                 this._loadBone(node, skin, inverseBindMatricesData, babylonBones);
                 this._loadBone(node, skin, inverseBindMatricesData, babylonBones);
             }
             }
         }
         }
@@ -802,14 +810,14 @@ module BABYLON.GLTF2 {
 
 
             for (let index = 0; index < animations.length; index++) {
             for (let index = 0; index < animations.length; index++) {
                 const animation = animations[index];
                 const animation = animations[index];
-                promises.push(this._loadAnimationAsync("#/animations/" + index, animation));
+                promises.push(this._loadAnimationAsync(`#/animations/${index}`, animation));
             }
             }
 
 
             return Promise.all(promises).then(() => {});
             return Promise.all(promises).then(() => {});
         }
         }
 
 
         private _loadAnimationAsync(context: string, animation: ILoaderAnimation): Promise<void> {
         private _loadAnimationAsync(context: string, animation: ILoaderAnimation): Promise<void> {
-            const babylonAnimationGroup = new AnimationGroup(animation.name || "animation" + animation._index, this._babylonScene);
+            const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation._index}`, this._babylonScene);
             animation._babylonAnimationGroup = babylonAnimationGroup;
             animation._babylonAnimationGroup = babylonAnimationGroup;
 
 
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
@@ -818,7 +826,7 @@ module BABYLON.GLTF2 {
             ArrayItem.Assign(animation.samplers);
             ArrayItem.Assign(animation.samplers);
 
 
             for (const channel of animation.channels) {
             for (const channel of animation.channels) {
-                promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
+                promises.push(this._loadAnimationChannelAsync(`${context}/channels/${channel._index}`, context, animation, channel, babylonAnimationGroup));
             }
             }
 
 
             this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
             this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
@@ -829,13 +837,13 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         private _loadAnimationChannelAsync(context: string, animationContext: string, animation: ILoaderAnimation, channel: ILoaderAnimationChannel, babylonAnimationGroup: AnimationGroup): Promise<void> {
         private _loadAnimationChannelAsync(context: string, animationContext: string, animation: ILoaderAnimation, channel: ILoaderAnimationChannel, babylonAnimationGroup: AnimationGroup): Promise<void> {
-            const targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
+            const targetNode = GLTFLoader._GetProperty(`${context}/target/node`, this._gltf.nodes, channel.target.node);
             if (!targetNode._babylonMesh || targetNode.skin != undefined) {
             if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                 return Promise.resolve();
                 return Promise.resolve();
             }
             }
 
 
-            const sampler = GLTFLoader._GetProperty(context + "/sampler", animation.samplers, channel.sampler);
-            return this._loadAnimationSamplerAsync(animationContext + "/samplers/" + channel.sampler, sampler).then(data => {
+            const sampler = GLTFLoader._GetProperty(`${context}/sampler`, animation.samplers, channel.sampler);
+            return this._loadAnimationSamplerAsync(`${animationContext}/samplers/${channel.sampler}`, sampler).then(data => {
                 let targetPath: string;
                 let targetPath: string;
                 let animationType: number;
                 let animationType: number;
                 switch (channel.target.path) {
                 switch (channel.target.path) {
@@ -860,7 +868,7 @@ module BABYLON.GLTF2 {
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        throw new Error(context + ": Invalid target path " + channel.target.path);
+                        throw new Error(`${context}: Invalid target path (${channel.target.path})`);
                     }
                     }
                 }
                 }
 
 
@@ -948,7 +956,7 @@ module BABYLON.GLTF2 {
 
 
                 if (targetPath === "influence") {
                 if (targetPath === "influence") {
                     for (let targetIndex = 0; targetIndex < targetNode._numMorphTargets!; targetIndex++) {
                     for (let targetIndex = 0; targetIndex < targetNode._numMorphTargets!; targetIndex++) {
-                        const animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
+                        const animationName = `${babylonAnimationGroup.name}_channel${babylonAnimationGroup.targetedAnimations.length}`;
                         const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                         const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys.map(key => ({
                         babylonAnimation.setKeys(keys.map(key => ({
                             frame: key.frame,
                             frame: key.frame,
@@ -957,14 +965,14 @@ module BABYLON.GLTF2 {
                             outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                             outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                         })));
                         })));
 
 
-                        for (const babylonMesh of targetNode._primitiveBabylonMeshes!) {
+                        this._forEachNodeMesh(targetNode, babylonMesh => {
                             const morphTarget = babylonMesh.morphTargetManager!.getTarget(targetIndex);
                             const morphTarget = babylonMesh.morphTargetManager!.getTarget(targetIndex);
                             babylonAnimationGroup.addTargetedAnimation(babylonAnimation, morphTarget);
                             babylonAnimationGroup.addTargetedAnimation(babylonAnimation, morphTarget);
-                        }
+                        });
                     }
                     }
                 }
                 }
                 else {
                 else {
-                    const animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
+                    const animationName = `${babylonAnimationGroup.name}_channel${babylonAnimationGroup.targetedAnimations.length}`;
                     const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                     const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                     babylonAnimation.setKeys(keys);
                     babylonAnimation.setKeys(keys);
 
 
@@ -990,21 +998,21 @@ module BABYLON.GLTF2 {
                     break;
                     break;
                 }
                 }
                 default: {
                 default: {
-                    throw new Error(context + ": Invalid interpolation " + sampler.interpolation);
+                    throw new Error(`${context}: Invalid interpolation (${sampler.interpolation})`);
                 }
                 }
             }
             }
 
 
             let inputData: Nullable<Float32Array>;
             let inputData: Nullable<Float32Array>;
             let outputData: Nullable<Float32Array>;
             let outputData: Nullable<Float32Array>;
 
 
-            const inputAccessor = GLTFLoader._GetProperty(context + "/input", this._gltf.accessors, sampler.input);
-            const outputAccessor = GLTFLoader._GetProperty(context + "/output", this._gltf.accessors, sampler.output);
+            const inputAccessor = GLTFLoader._GetProperty(`${context}/input`, this._gltf.accessors, sampler.input);
+            const outputAccessor = GLTFLoader._GetProperty(`${context}/output`, this._gltf.accessors, sampler.output);
 
 
             sampler._data = Promise.all([
             sampler._data = Promise.all([
-                this._loadAccessorAsync("#/accessors/" + inputAccessor._index, inputAccessor).then(data => {
+                this._loadAccessorAsync(`#/accessors/${inputAccessor._index}`, inputAccessor).then(data => {
                     inputData = data as Float32Array;
                     inputData = data as Float32Array;
                 }),
                 }),
-                this._loadAccessorAsync("#/accessors/" + outputAccessor._index, outputAccessor).then(data => {
+                this._loadAccessorAsync(`#/accessors/${outputAccessor._index}`, outputAccessor).then(data => {
                     outputData = data as Float32Array;
                     outputData = data as Float32Array;
                 })
                 })
             ]).then(() => {
             ]).then(() => {
@@ -1024,7 +1032,7 @@ module BABYLON.GLTF2 {
             }
             }
 
 
             if (!buffer.uri) {
             if (!buffer.uri) {
-                throw new Error(context + ": Uri is missing");
+                throw new Error(`${context}: Uri is missing`);
             }
             }
 
 
             buffer._data = this._loadUriAsync(context, buffer.uri);
             buffer._data = this._loadUriAsync(context, buffer.uri);
@@ -1037,13 +1045,13 @@ module BABYLON.GLTF2 {
                 return bufferView._data;
                 return bufferView._data;
             }
             }
 
 
-            const buffer = GLTFLoader._GetProperty(context + "/buffer", this._gltf.buffers, bufferView.buffer);
-            bufferView._data = this._loadBufferAsync("#/buffers/" + buffer._index, buffer).then(bufferData => {
+            const buffer = GLTFLoader._GetProperty(`${context}/buffer`, this._gltf.buffers, bufferView.buffer);
+            bufferView._data = this._loadBufferAsync(`#/buffers/${buffer._index}`, buffer).then(bufferData => {
                 try {
                 try {
                     return new Uint8Array(bufferData.buffer, bufferData.byteOffset + (bufferView.byteOffset || 0), bufferView.byteLength);
                     return new Uint8Array(bufferData.buffer, bufferData.byteOffset + (bufferView.byteOffset || 0), bufferView.byteLength);
                 }
                 }
                 catch (e) {
                 catch (e) {
-                    throw new Error(context + ": " + e.message);
+                    throw new Error(`${context}: ${e.message}`);
                 }
                 }
             });
             });
 
 
@@ -1052,21 +1060,21 @@ module BABYLON.GLTF2 {
 
 
         private _loadAccessorAsync(context: string, accessor: ILoaderAccessor): Promise<TypedArray> {
         private _loadAccessorAsync(context: string, accessor: ILoaderAccessor): Promise<TypedArray> {
             if (accessor.sparse) {
             if (accessor.sparse) {
-                throw new Error(context + ": Sparse accessors are not currently supported");
+                throw new Error(`${context}: Sparse accessors are not currently supported`);
             }
             }
 
 
             if (accessor._data) {
             if (accessor._data) {
                 return accessor._data;
                 return accessor._data;
             }
             }
 
 
-            const bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, accessor.bufferView);
-            accessor._data = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView).then(bufferViewData => {
+            const bufferView = GLTFLoader._GetProperty(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
+            accessor._data = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView).then(bufferViewData => {
                 const numComponents = GLTFLoader._GetNumComponents(context, accessor.type);
                 const numComponents = GLTFLoader._GetNumComponents(context, accessor.type);
                 const byteOffset = accessor.byteOffset || 0;
                 const byteOffset = accessor.byteOffset || 0;
                 const byteStride = bufferView.byteStride;
                 const byteStride = bufferView.byteStride;
 
 
                 if (byteStride === 0) {
                 if (byteStride === 0) {
-                    Tools.Warn(context + ": Byte stride of 0 is not valid");
+                    Tools.Warn(`${context}: Byte stride of 0 is not valid`);
                 }
                 }
 
 
                 try {
                 try {
@@ -1090,12 +1098,12 @@ module BABYLON.GLTF2 {
                             return this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
                             return this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
                         }
                         }
                         default: {
                         default: {
-                            throw new Error(context + ": Invalid component type " + accessor.componentType);
+                            throw new Error(`${context}: Invalid component type (${accessor.componentType})`);
                         }
                         }
                     }
                     }
                 }
                 }
                 catch (e) {
                 catch (e) {
-                    throw new Error(context + ": " + e);
+                    throw new Error(`${context}: ${e.messsage}`);
                 }
                 }
             });
             });
 
 
@@ -1166,13 +1174,13 @@ module BABYLON.GLTF2 {
                 babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
                 babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
 
 
                 if (properties.baseColorTexture) {
                 if (properties.baseColorTexture) {
-                    promises.push(this._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, texture => {
+                    promises.push(this._loadTextureAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
                         babylonMaterial.albedoTexture = texture;
                         babylonMaterial.albedoTexture = texture;
                     }));
                     }));
                 }
                 }
 
 
                 if (properties.metallicRoughnessTexture) {
                 if (properties.metallicRoughnessTexture) {
-                    promises.push(this._loadTextureAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, texture => {
+                    promises.push(this._loadTextureAsync(`${context}/metallicRoughnessTexture`, properties.metallicRoughnessTexture, texture => {
                         babylonMaterial.metallicTexture = texture;
                         babylonMaterial.metallicTexture = texture;
                     }));
                     }));
 
 
@@ -1214,7 +1222,7 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         public _createMaterial(material: ILoaderMaterial): PBRMaterial {
         public _createMaterial(material: ILoaderMaterial): PBRMaterial {
-            const babylonMaterial = new PBRMaterial(material.name || "material" + material._index, this._babylonScene);
+            const babylonMaterial = new PBRMaterial(material.name || `material${material._index}`, this._babylonScene);
             babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
             babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
             return babylonMaterial;
             return babylonMaterial;
         }
         }
@@ -1231,7 +1239,7 @@ module BABYLON.GLTF2 {
             }
             }
 
 
             if (material.normalTexture) {
             if (material.normalTexture) {
-                promises.push(this._loadTextureAsync(context + "/normalTexture", material.normalTexture, texture => {
+                promises.push(this._loadTextureAsync(`${context}/normalTexture`, material.normalTexture, texture => {
                     babylonMaterial.bumpTexture = texture;
                     babylonMaterial.bumpTexture = texture;
                 }));
                 }));
 
 
@@ -1243,7 +1251,7 @@ module BABYLON.GLTF2 {
             }
             }
 
 
             if (material.occlusionTexture) {
             if (material.occlusionTexture) {
-                promises.push(this._loadTextureAsync(context + "/occlusionTexture", material.occlusionTexture, texture => {
+                promises.push(this._loadTextureAsync(`${context}/occlusionTexture`, material.occlusionTexture, texture => {
                     babylonMaterial.ambientTexture = texture;
                     babylonMaterial.ambientTexture = texture;
                 }));
                 }));
 
 
@@ -1254,7 +1262,7 @@ module BABYLON.GLTF2 {
             }
             }
 
 
             if (material.emissiveTexture) {
             if (material.emissiveTexture) {
-                promises.push(this._loadTextureAsync(context + "/emissiveTexture", material.emissiveTexture, texture => {
+                promises.push(this._loadTextureAsync(`${context}/emissiveTexture`, material.emissiveTexture, texture => {
                     babylonMaterial.emissiveTexture = texture;
                     babylonMaterial.emissiveTexture = texture;
                 }));
                 }));
             }
             }
@@ -1302,19 +1310,19 @@ module BABYLON.GLTF2 {
                     break;
                     break;
                 }
                 }
                 default: {
                 default: {
-                    throw new Error(context + ": Invalid alpha mode " + material.alphaMode);
+                    throw new Error(`${context}: Invalid alpha mode (${material.alphaMode})`);
                 }
                 }
             }
             }
         }
         }
 
 
         public _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void> {
         public _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void> {
-            const texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
-            context = "#/textures/" + textureInfo.index;
+            const texture = GLTFLoader._GetProperty(`${context}/index`, this._gltf.textures, textureInfo.index);
+            context = `#/textures/${textureInfo.index}`;
 
 
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
-            const sampler = (texture.sampler == undefined ? this._defaultSampler : GLTFLoader._GetProperty(context + "/sampler", this._gltf.samplers, texture.sampler));
-            const samplerData = this._loadSampler("#/samplers/" + sampler._index, sampler);
+            const sampler = (texture.sampler == undefined ? this._defaultSampler : GLTFLoader._GetProperty(`${context}/sampler`, this._gltf.samplers, texture.sampler));
+            const samplerData = this._loadSampler(`#/samplers/${sampler._index}`, sampler);
 
 
             const deferred = new Deferred<void>();
             const deferred = new Deferred<void>();
             const babylonTexture = new Texture(null, this._babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, () => {
             const babylonTexture = new Texture(null, this._babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, () => {
@@ -1323,18 +1331,18 @@ module BABYLON.GLTF2 {
                 }
                 }
             }, (message, exception) => {
             }, (message, exception) => {
                 if (!this._disposed) {
                 if (!this._disposed) {
-                    deferred.reject(new Error(context + ": " + (exception && exception.message) ? exception.message : message || "Failed to load texture"));
+                    deferred.reject(new Error(`${context}: ${(exception && exception.message) ? exception.message : message || "Failed to load texture"}`));
                 }
                 }
             });
             });
             promises.push(deferred.promise);
             promises.push(deferred.promise);
 
 
-            babylonTexture.name = texture.name || "texture" + texture._index;
+            babylonTexture.name = texture.name || `texture${texture._index}`;
             babylonTexture.wrapU = samplerData.wrapU;
             babylonTexture.wrapU = samplerData.wrapU;
             babylonTexture.wrapV = samplerData.wrapV;
             babylonTexture.wrapV = samplerData.wrapV;
             babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
             babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
 
 
-            const image = GLTFLoader._GetProperty(context + "/source", this._gltf.images, texture.source);
-            promises.push(this._loadImageAsync("#/images/" + image._index, image).then(objectURL => {
+            const image = GLTFLoader._GetProperty(`${context}/source`, this._gltf.images, texture.source);
+            promises.push(this._loadImageAsync(`#/images/${image._index}`, image).then(objectURL => {
                 babylonTexture.updateURL(objectURL);
                 babylonTexture.updateURL(objectURL);
             }));
             }));
 
 
@@ -1367,8 +1375,8 @@ module BABYLON.GLTF2 {
                 promise = this._loadUriAsync(context, image.uri);
                 promise = this._loadUriAsync(context, image.uri);
             }
             }
             else {
             else {
-                const bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, image.bufferView);
-                promise = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
+                const bufferView = GLTFLoader._GetProperty(`${context}/bufferView`, this._gltf.bufferViews, image.bufferView);
+                promise = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView);
             }
             }
 
 
             image._objectURL = promise.then(data => {
             image._objectURL = promise.then(data => {
@@ -1385,7 +1393,7 @@ module BABYLON.GLTF2 {
             }
             }
 
 
             if (!GLTFLoader._ValidateUri(uri)) {
             if (!GLTFLoader._ValidateUri(uri)) {
-                throw new Error(context + ": Uri '" + uri + "' is invalid");
+                throw new Error(`${context}: Uri '${uri}' is invalid`);
             }
             }
 
 
             if (Tools.IsBase64(uri)) {
             if (Tools.IsBase64(uri)) {
@@ -1413,7 +1421,7 @@ module BABYLON.GLTF2 {
                     }
                     }
                 }, this._babylonScene.database, true, (request, exception) => {
                 }, this._babylonScene.database, true, (request, exception) => {
                     if (!this._disposed) {
                     if (!this._disposed) {
-                        reject(new LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request));
+                        reject(new LoadFileError(`${context}: Failed to load '${uri}'${request ? ": " + request.status + " " + request.statusText : ""}`, request));
                     }
                     }
                 }) as IFileRequestInfo;
                 }) as IFileRequestInfo;
 
 
@@ -1444,7 +1452,7 @@ module BABYLON.GLTF2 {
 
 
         public static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T {
         public static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T {
             if (!array || index == undefined || !array[index]) {
             if (!array || index == undefined || !array[index]) {
-                throw new Error(context + ": Failed to find index " + index);
+                throw new Error(`${context}: Failed to find index (${index})`);
             }
             }
 
 
             return array[index];
             return array[index];
@@ -1459,7 +1467,7 @@ module BABYLON.GLTF2 {
                 case TextureWrapMode.MIRRORED_REPEAT: return Texture.MIRROR_ADDRESSMODE;
                 case TextureWrapMode.MIRRORED_REPEAT: return Texture.MIRROR_ADDRESSMODE;
                 case TextureWrapMode.REPEAT: return Texture.WRAP_ADDRESSMODE;
                 case TextureWrapMode.REPEAT: return Texture.WRAP_ADDRESSMODE;
                 default:
                 default:
-                    Tools.Warn(context + ": Invalid texture wrap mode " + mode);
+                    Tools.Warn(`${context}: Invalid texture wrap mode (${mode})`);
                     return Texture.WRAP_ADDRESSMODE;
                     return Texture.WRAP_ADDRESSMODE;
             }
             }
         }
         }
@@ -1478,13 +1486,13 @@ module BABYLON.GLTF2 {
                     case TextureMinFilter.NEAREST_MIPMAP_LINEAR: return Texture.LINEAR_NEAREST_MIPLINEAR;
                     case TextureMinFilter.NEAREST_MIPMAP_LINEAR: return Texture.LINEAR_NEAREST_MIPLINEAR;
                     case TextureMinFilter.LINEAR_MIPMAP_LINEAR: return Texture.LINEAR_LINEAR_MIPLINEAR;
                     case TextureMinFilter.LINEAR_MIPMAP_LINEAR: return Texture.LINEAR_LINEAR_MIPLINEAR;
                     default:
                     default:
-                        Tools.Warn(context + ": Invalid texture minification filter " + minFilter);
+                        Tools.Warn(`${context}: Invalid texture minification filter (${minFilter})`);
                         return Texture.LINEAR_LINEAR_MIPLINEAR;
                         return Texture.LINEAR_LINEAR_MIPLINEAR;
                 }
                 }
             }
             }
             else {
             else {
                 if (magFilter !== TextureMagFilter.NEAREST) {
                 if (magFilter !== TextureMagFilter.NEAREST) {
-                    Tools.Warn(context + ": Invalid texture magnification filter " + magFilter);
+                    Tools.Warn(`${context}: Invalid texture magnification filter (${magFilter})`);
                 }
                 }
 
 
                 switch (minFilter) {
                 switch (minFilter) {
@@ -1495,7 +1503,7 @@ module BABYLON.GLTF2 {
                     case TextureMinFilter.NEAREST_MIPMAP_LINEAR: return Texture.NEAREST_NEAREST_MIPLINEAR;
                     case TextureMinFilter.NEAREST_MIPMAP_LINEAR: return Texture.NEAREST_NEAREST_MIPLINEAR;
                     case TextureMinFilter.LINEAR_MIPMAP_LINEAR: return Texture.NEAREST_LINEAR_MIPLINEAR;
                     case TextureMinFilter.LINEAR_MIPMAP_LINEAR: return Texture.NEAREST_LINEAR_MIPLINEAR;
                     default:
                     default:
-                        Tools.Warn(context + ": Invalid texture minification filter " + minFilter);
+                        Tools.Warn(`${context}: Invalid texture minification filter (${minFilter})`);
                         return Texture.NEAREST_NEAREST_MIPNEAREST;
                         return Texture.NEAREST_NEAREST_MIPNEAREST;
                 }
                 }
             }
             }
@@ -1512,7 +1520,7 @@ module BABYLON.GLTF2 {
                 case "MAT4": return 16;
                 case "MAT4": return 16;
             }
             }
 
 
-            throw new Error(context + ": Invalid type " + type);
+            throw new Error(`${context}: Invalid type (${type})`);
         }
         }
 
 
         private static _ValidateUri(uri: string): boolean {
         private static _ValidateUri(uri: string): boolean {
@@ -1528,6 +1536,9 @@ module BABYLON.GLTF2 {
                     const babylonMeshes = material._babylonMeshes;
                     const babylonMeshes = material._babylonMeshes;
                     if (babylonMaterial && babylonMeshes) {
                     if (babylonMaterial && babylonMeshes) {
                         for (const babylonMesh of babylonMeshes) {
                         for (const babylonMesh of babylonMeshes) {
+                            // Ensure nonUniformScaling is set if necessary.
+                            babylonMesh.computeWorldMatrix(true);
+
                             promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                             promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                             if (this.useClipPlane) {
                             if (this.useClipPlane) {
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));

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

@@ -51,7 +51,7 @@ module BABYLON.GLTF2 {
             delete extensions[this.name];
             delete extensions[this.name];
 
 
             try {
             try {
-                return actionAsync(context + "/extensions/" + this.name, extension);
+                return actionAsync(`${context}/extensions/${this.name}`, extension);
             }
             }
             finally {
             finally {
                 // Restore the extension after executing the action.
                 // Restore the extension after executing the action.

Plik diff jest za duży
+ 35 - 1973
materialsLibrary/src/custom/babylon.customMaterial.ts


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

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

+ 475 - 158
serializers/src/glTF/2.0/babylon.glTFMaterial.ts

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

+ 1 - 0
src/Behaviors/Cameras/babylon.framingBehavior.ts

@@ -328,6 +328,7 @@ module BABYLON {
 
 
             animatable = Animation.TransitionTo("radius", radius, this._attachedCamera, this._attachedCamera.getScene(),
             animatable = Animation.TransitionTo("radius", radius, this._attachedCamera, this._attachedCamera.getScene(),
                 60, this._radiusTransition, this._framingTime, () => {
                 60, this._radiusTransition, this._framingTime, () => {
+                    this.stopAllAnimations();
                     if (onAnimationEnd) {
                     if (onAnimationEnd) {
                         onAnimationEnd();
                         onAnimationEnd();
                     }
                     }

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

@@ -157,6 +157,7 @@ module BABYLON {
             makeNotPick(mesh);
             makeNotPick(mesh);
             var childMeshes = mesh.getChildMeshes();
             var childMeshes = mesh.getChildMeshes();
 
 
+            this.webVRController._pointingPoseNode = null;
             for (var i = 0; i < childMeshes.length; i++) {
             for (var i = 0; i < childMeshes.length; i++) {
                 if (childMeshes[i].name && childMeshes[i].name.indexOf(PoseEnabledController.POINTING_POSE) >= 0) {
                 if (childMeshes[i].name && childMeshes[i].name.indexOf(PoseEnabledController.POINTING_POSE) >= 0) {
                     mesh = childMeshes[i];
                     mesh = childMeshes[i];

+ 11 - 7
src/Cameras/babylon.arcRotateCamera.ts

@@ -338,6 +338,8 @@
 
 
         protected _targetBoundingCenter: Nullable<Vector3>;
         protected _targetBoundingCenter: Nullable<Vector3>;
 
 
+        private _computationVector: Vector3 = Vector3.Zero();
+
         constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, scene: Scene) {
         constructor(name: string, alpha: number, beta: number, radius: number, target: Vector3, scene: Scene) {
             super(name, Vector3.Zero(), scene);
             super(name, Vector3.Zero(), scene);
 
 
@@ -584,22 +586,22 @@
         }
         }
 
 
         public rebuildAnglesAndRadius() {
         public rebuildAnglesAndRadius() {
-            var radiusv3 = this.position.subtract(this._getTargetPosition());
-            this.radius = radiusv3.length();
+            this.position.subtractToRef(this._getTargetPosition(), this._computationVector);
+            this.radius = this._computationVector.length();
 
 
             if (this.radius === 0) {
             if (this.radius === 0) {
                 this.radius = 0.0001; // Just to avoid division by zero
                 this.radius = 0.0001; // Just to avoid division by zero
             }
             }
 
 
             // Alpha
             // Alpha
-            this.alpha = Math.acos(radiusv3.x / Math.sqrt(Math.pow(radiusv3.x, 2) + Math.pow(radiusv3.z, 2)));
+            this.alpha = Math.acos(this._computationVector.x / Math.sqrt(Math.pow(this._computationVector.x, 2) + Math.pow(this._computationVector.z, 2)));
 
 
-            if (radiusv3.z < 0) {
+            if (this._computationVector.z < 0) {
                 this.alpha = 2 * Math.PI - this.alpha;
                 this.alpha = 2 * Math.PI - this.alpha;
             }
             }
 
 
             // Beta
             // Beta
-            this.beta = Math.acos(radiusv3.y / this.radius);
+            this.beta = Math.acos(this._computationVector.y / this.radius);
 
 
             this._checkLimits();
             this._checkLimits();
         }
         }
@@ -652,7 +654,8 @@
             }
             }
 
 
             var target = this._getTargetPosition();
             var target = this._getTargetPosition();
-            target.addToRef(new Vector3(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb), this._newPosition);
+            this._computationVector.copyFromFloats(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb);
+            target.addToRef(this._computationVector, this._newPosition);
             if (this.getScene().collisionsEnabled && this.checkCollisions) {
             if (this.getScene().collisionsEnabled && this.checkCollisions) {
                 if (!this._collider) {
                 if (!this._collider) {
                     this._collider = new Collider();
                     this._collider = new Collider();
@@ -709,7 +712,8 @@
             }
             }
 
 
             var target = this._getTargetPosition();
             var target = this._getTargetPosition();
-            target.addToRef(new Vector3(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb), this._newPosition);
+            this._computationVector.copyFromFloats(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb);
+            target.addToRef(this._computationVector, this._newPosition);
             this.position.copyFrom(this._newPosition);
             this.position.copyFrom(this._newPosition);
 
 
             var up = this.upVector;
             var up = this.upVector;

+ 32 - 8
src/Cameras/babylon.camera.ts

@@ -161,7 +161,7 @@
         public _projectionMatrix = new Matrix();
         public _projectionMatrix = new Matrix();
         private _doNotComputeProjectionMatrix = false;
         private _doNotComputeProjectionMatrix = false;
         private _worldMatrix: Matrix;
         private _worldMatrix: Matrix;
-        public _postProcesses = new Array<PostProcess>();
+        public _postProcesses = new Array<Nullable<PostProcess>>();
         private _transformMatrix = Matrix.Zero();
         private _transformMatrix = Matrix.Zero();
 
 
         public _activeMeshes = new SmartArray<AbstractMesh>(256);
         public _activeMeshes = new SmartArray<AbstractMesh>(256);
@@ -352,10 +352,24 @@
             return this._rigPostProcess;
             return this._rigPostProcess;
         }
         }
 
 
+        /**
+         * Internal, gets the first post proces.
+         * @returns the first post process to be run on this camera.
+         */
+        public _getFirstPostProcess():Nullable<PostProcess>{
+            for(var pp in this._postProcesses){
+                if(this._postProcesses[pp] !== null){
+                    return this._postProcesses[pp];
+                }
+            }
+            return null;
+        }
+
         private _cascadePostProcessesToRigCams(): void {
         private _cascadePostProcessesToRigCams(): void {
             // invalidate framebuffer
             // invalidate framebuffer
-            if (this._postProcesses.length > 0) {
-                this._postProcesses[0].markTextureDirty();
+            var firstPostProcess = this._getFirstPostProcess();
+            if (firstPostProcess) {
+                firstPostProcess.markTextureDirty();
             }
             }
 
 
             // glue the rigPostProcess to the end of the user postprocesses & assign to each sub-camera
             // glue the rigPostProcess to the end of the user postprocesses & assign to each sub-camera
@@ -387,7 +401,9 @@
 
 
             if (insertAt == null || insertAt < 0) {
             if (insertAt == null || insertAt < 0) {
                 this._postProcesses.push(postProcess);
                 this._postProcesses.push(postProcess);
-            } else {
+            } else if(this._postProcesses[insertAt] === null) {
+                this._postProcesses[insertAt] = postProcess;
+            }else{
                 this._postProcesses.splice(insertAt, 0, postProcess);
                 this._postProcesses.splice(insertAt, 0, postProcess);
             }
             }
             this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
             this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
@@ -397,7 +413,7 @@
         public detachPostProcess(postProcess: PostProcess): void {
         public detachPostProcess(postProcess: PostProcess): void {
             var idx = this._postProcesses.indexOf(postProcess);
             var idx = this._postProcesses.indexOf(postProcess);
             if (idx !== -1) {
             if (idx !== -1) {
-                this._postProcesses.splice(idx, 1);
+                this._postProcesses[idx] = null;
             }
             }
             this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
             this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
         }
         }
@@ -589,7 +605,12 @@
             return new Ray(origin, direction, length);
             return new Ray(origin, direction, length);
         }
         }
 
 
-        public dispose(): void {
+        /**
+         * Releases resources associated with this node.
+         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
+         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
+         */
+        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
             // Observables
             // Observables
             this.onViewMatrixChangedObservable.clear();
             this.onViewMatrixChangedObservable.clear();
             this.onProjectionMatrixChangedObservable.clear();
             this.onProjectionMatrixChangedObservable.clear();
@@ -625,7 +646,10 @@
             } else {
             } else {
                 var i = this._postProcesses.length;
                 var i = this._postProcesses.length;
                 while (--i >= 0) {
                 while (--i >= 0) {
-                    this._postProcesses[i].dispose(this);
+                    var postProcess = this._postProcesses[i]
+                    if(postProcess){
+                        postProcess.dispose(this);
+                    }
                 }
                 }
             }
             }
 
 
@@ -639,7 +663,7 @@
             // Active Meshes
             // Active Meshes
             this._activeMeshes.dispose();
             this._activeMeshes.dispose();
 
 
-            super.dispose();
+            super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
         }
 
 
         // ---- Camera rigs section ----
         // ---- Camera rigs section ----

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

@@ -5631,7 +5631,7 @@
                 if (this._renderingCanvas) {
                 if (this._renderingCanvas) {
                     this._renderingCanvas.removeEventListener("focus", this._onCanvasFocus);
                     this._renderingCanvas.removeEventListener("focus", this._onCanvasFocus);
                     this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);
                     this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);
-                    this._renderingCanvas.removeEventListener("pointerout", this._onCanvasBlur);
+                    this._renderingCanvas.removeEventListener("pointerout", this._onCanvasPointerOut);
 
 
                     if (!this._doNotHandleContextLost) {
                     if (!this._doNotHandleContextLost) {
                         this._renderingCanvas.removeEventListener("webglcontextlost", this._onContextLost);
                         this._renderingCanvas.removeEventListener("webglcontextlost", this._onContextLost);
@@ -5673,6 +5673,7 @@
             this._currentBufferPointers = [];
             this._currentBufferPointers = [];
             this._renderingCanvas = null;
             this._renderingCanvas = null;
             this._currentProgram = null;
             this._currentProgram = null;
+            this._bindedRenderFunction = null;
 
 
             this.onResizeObservable.clear();
             this.onResizeObservable.clear();
             this.onCanvasBlurObservable.clear();
             this.onCanvasBlurObservable.clear();

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

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

+ 5 - 6
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -639,8 +639,8 @@
         private _viewMatrix = Matrix.Zero();
         private _viewMatrix = Matrix.Zero();
         private _projectionMatrix = Matrix.Zero();
         private _projectionMatrix = Matrix.Zero();
         private _transformMatrix = Matrix.Zero();
         private _transformMatrix = Matrix.Zero();
-        private _cachedPosition: Vector3;
-        private _cachedDirection: Vector3;
+        private _cachedPosition: Vector3 = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+        private _cachedDirection: Vector3 = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
         private _cachedDefines: string;
         private _cachedDefines: string;
         private _currentRenderID: number;
         private _currentRenderID: number;
         private _boxBlurPostprocess: Nullable<PostProcess>;
         private _boxBlurPostprocess: Nullable<PostProcess>;
@@ -745,10 +745,9 @@
                 }
                 }
             });
             });
 
 
+            // Clear according to the chosen filter.
             var clearZero = new Color4(0, 0, 0, 0);
             var clearZero = new Color4(0, 0, 0, 0);
             var clearOne = new Color4(1.0, 1.0, 1.0, 1.0);
             var clearOne = new Color4(1.0, 1.0, 1.0, 1.0);
-
-            // Clear according to the chosen filter.
             this._shadowMap.onClearObservable.add((engine: Engine) => {
             this._shadowMap.onClearObservable.add((engine: Engine) => {
                 if (this._filter === ShadowGenerator.FILTER_PCF) {
                 if (this._filter === ShadowGenerator.FILTER_PCF) {
                     engine.clear(clearOne, false, true, false);
                     engine.clear(clearOne, false, true, false);
@@ -1206,8 +1205,8 @@
 
 
             if (this._light.needProjectionMatrixCompute() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !this._lightDirection.equals(this._cachedDirection)) {
             if (this._light.needProjectionMatrixCompute() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !this._lightDirection.equals(this._cachedDirection)) {
 
 
-                this._cachedPosition = lightPosition.clone();
-                this._cachedDirection = this._lightDirection.clone();
+                this._cachedPosition.copyFrom(lightPosition);
+                this._cachedDirection.copyFrom(this._lightDirection);
 
 
                 Matrix.LookAtLHToRef(lightPosition, lightPosition.add(this._lightDirection), Vector3.Up(), this._viewMatrix);
                 Matrix.LookAtLHToRef(lightPosition, lightPosition.add(this._lightDirection), Vector3.Up(), this._viewMatrix);
 
 

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

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

+ 9 - 0
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -781,5 +781,14 @@
                 this._postProcessManager._rebuild();
                 this._postProcessManager._rebuild();
             }
             }
         }
         }
+
+        /**
+         * Clear the info related to rendering groups preventing retention point in material dispose.
+         */
+        public freeRenderingGroups(): void {
+            if (this._renderingManager) {
+                this._renderingManager.freeRenderingGroups();
+            }
+        }
     }
     }
 }
 }

+ 53 - 0
src/Materials/babylon.effect.ts

@@ -288,6 +288,11 @@
 
 
             this.uniqueId = Effect._uniqueIdSeed++;
             this.uniqueId = Effect._uniqueIdSeed++;
 
 
+            if (this._getFromCache(baseName)) {
+                this._prepareEffect();
+                return;
+            }
+
             var vertexSource: any;
             var vertexSource: any;
             var fragmentSource: any;
             var fragmentSource: any;
 
 
@@ -336,10 +341,58 @@
                     this._vertexSourceCode = finalVertexCode;
                     this._vertexSourceCode = finalVertexCode;
                     this._fragmentSourceCode = migratedFragmentCode;
                     this._fragmentSourceCode = migratedFragmentCode;
                 }
                 }
+
+                this._setInCache(baseName);
                 this._prepareEffect(); 
                 this._prepareEffect(); 
             });
             });
         }
         }
 
 
+        private static _sourceCache: { [baseName: string]: { vertex: string, fragment: string } } = { };
+
+        private _getSourceCacheKey(baseName: string): string {
+            let cacheKey: string = baseName;
+            if (this._indexParameters) {
+                for (let key in this._indexParameters) {
+                    if (this._indexParameters.hasOwnProperty(key)) {
+                        cacheKey += "|";
+                        cacheKey += key
+                        cacheKey += "_";
+                        cacheKey += this._indexParameters[key];
+                    }
+                }
+            }
+
+            return cacheKey;
+        }
+
+        private _getFromCache(baseName: string): boolean {
+            if (typeof baseName !== "string") {
+                return false;
+            }
+
+            let cacheKey = this._getSourceCacheKey(baseName);
+            let sources = Effect._sourceCache[cacheKey];
+            if (!sources) {
+                return false;
+            }
+
+            this._vertexSourceCode = sources.vertex;
+            this._fragmentSourceCode = sources.fragment;
+            return true;
+        }
+
+        private _setInCache(baseName: string): void {
+            if (typeof baseName !== "string") {
+                return;
+            }
+
+            let cacheKey = this._getSourceCacheKey(baseName);
+            Effect._sourceCache[cacheKey] = {
+                vertex: this._vertexSourceCode,
+                fragment: this._fragmentSourceCode
+            };
+        }
+
         /**
         /**
          * Unique key for this effect
          * Unique key for this effect
          */
          */

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

@@ -1212,6 +1212,7 @@
         public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
         public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
             // Animations
             // Animations
             this.getScene().stopAnimation(this);
             this.getScene().stopAnimation(this);
+            this.getScene().freeProcessedMaterials();
 
 
             // Remove from scene
             // Remove from scene
             var index = this._scene.materials.indexOf(this);
             var index = this._scene.materials.indexOf(this);

+ 19 - 3
src/Math/babylon.math.ts

@@ -4369,17 +4369,33 @@
                 0.0, 0.0, 1.0, 0.0,
                 0.0, 0.0, 1.0, 0.0,
                 x, y, z, 1.0, result);
                 x, y, z, 1.0, result);
         }
         }
+
         /**
         /**
-         * Returns a new Matrix whose values are the interpolated values for "gradien" (float) between the ones of the matrices "startValue" and "endValue".
+         * Returns a new Matrix whose values are the interpolated values for "gradient" (float) between the ones of the matrices "startValue" and "endValue".
+         * @param startValue defines the start value
+         * @param endValue defines the end value
+         * @param gradient defines the gradient factor
+         * @returns the new matrix
          */
          */
         public static Lerp(startValue: Matrix, endValue: Matrix, gradient: number): Matrix {
         public static Lerp(startValue: Matrix, endValue: Matrix, gradient: number): Matrix {
             var result = Matrix.Zero();
             var result = Matrix.Zero();
+            Matrix.LerpToRef(startValue, endValue, gradient, result);
+            return result;
+        }
+
+        /**
+         * Set the passed matrix "result" as the interpolated values for "gradient" (float) between the ones of the matrices "startValue" and "endValue".
+         * @param startValue defines the start value
+         * @param endValue defines the end value
+         * @param gradient defines the gradient factor
+         * @param result defines the Matrix object where to store data
+         */
+        public static LerpToRef(startValue: Matrix, endValue: Matrix, gradient: number, result: Matrix): void {
             for (var index = 0; index < 16; index++) {
             for (var index = 0; index < 16; index++) {
                 result.m[index] = startValue.m[index] * (1.0 - gradient) + endValue.m[index] * gradient;
                 result.m[index] = startValue.m[index] * (1.0 - gradient) + endValue.m[index] * gradient;
             }
             }
             result._markAsUpdated();
             result._markAsUpdated();
-            return result;
-        }
+        }        
 
 
         /**
         /**
          * Returns a new Matrix whose values are computed by : 
          * Returns a new Matrix whose values are computed by : 

+ 4 - 0
src/Mesh/Compression/babylon.dracoCompression.ts

@@ -188,6 +188,10 @@ module BABYLON {
         }
         }
 
 
         private static _GetDefaultDecoderUrl(): Nullable<string> {
         private static _GetDefaultDecoderUrl(): Nullable<string> {
+            if (!Tools.IsWindowObjectExist) {
+                return null;
+            }
+
             for (let i = 0; i < document.scripts.length; i++) {
             for (let i = 0; i < document.scripts.length; i++) {
                 if (document.scripts[i].type === "text/x-draco-decoder") {
                 if (document.scripts[i].type === "text/x-draco-decoder") {
                     return document.scripts[i].src;
                     return document.scripts[i].src;

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

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

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

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

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

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

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

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

+ 16 - 3
src/Particles/babylon.gpuParticleSystem.ts

@@ -241,6 +241,12 @@
         }
         }
 
 
         /**
         /**
+         * Forces the particle to write their depth information to the depth buffer. This can help preventing other draw calls
+         * to override the particles.
+         */
+        public forceDepthWrite = false;        
+
+        /**
          * Gets or set the number of active particles
          * Gets or set the number of active particles
          */
          */
         public get activeParticleCount(): number {
         public get activeParticleCount(): number {
@@ -323,7 +329,7 @@
             this._updateEffectOptions = {
             this._updateEffectOptions = {
                 attributes: ["position", "age", "life", "seed", "size", "color", "direction"],
                 attributes: ["position", "age", "life", "seed", "size", "color", "direction"],
                 uniformsNames: ["currentCount", "timeDelta", "generalRandoms", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "gravity", "emitPower",
                 uniformsNames: ["currentCount", "timeDelta", "generalRandoms", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "gravity", "emitPower",
-                                "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "angle"],
+                                "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "angle", "stopFactor"],
                 uniformBuffersNames: [],
                 uniformBuffersNames: [],
                 samplers:["randomSampler"],
                 samplers:["randomSampler"],
                 defines: "",
                 defines: "",
@@ -484,8 +490,6 @@
 
 
                 if (this.targetStopDuration && this._actualFrame >= this.targetStopDuration)
                 if (this.targetStopDuration && this._actualFrame >= this.targetStopDuration)
                     this.stop();
                     this.stop();
-            } else {
-                this._timeDelta = 0;
             }             
             }             
         }        
         }        
 
 
@@ -523,6 +527,7 @@
             
             
             this._updateEffect.setFloat("currentCount", this._currentActiveCount);
             this._updateEffect.setFloat("currentCount", this._currentActiveCount);
             this._updateEffect.setFloat("timeDelta", this._timeDelta);
             this._updateEffect.setFloat("timeDelta", this._timeDelta);
+            this._updateEffect.setFloat("stopFactor", this._stopped ? 0 : 1);
             this._updateEffect.setFloat3("generalRandoms", Math.random(), Math.random(), Math.random());
             this._updateEffect.setFloat3("generalRandoms", Math.random(), Math.random(), Math.random());
             this._updateEffect.setTexture("randomSampler", this._randomTexture);
             this._updateEffect.setTexture("randomSampler", this._randomTexture);
             this._updateEffect.setFloat2("lifeTime", this.minLifeTime, this.maxLifeTime);
             this._updateEffect.setFloat2("lifeTime", this.minLifeTime, this.maxLifeTime);
@@ -582,6 +587,10 @@
                 this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
                 this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
             }            
             }            
 
 
+            if (this.forceDepthWrite) {
+                this._engine.setDepthWrite(true);
+            }
+
             // Bind source VAO
             // Bind source VAO
             this._engine.bindVertexArrayObject(this._renderVAO[this._targetIndex], null);
             this._engine.bindVertexArrayObject(this._renderVAO[this._targetIndex], null);
 
 
@@ -626,6 +635,10 @@
         }
         }
 
 
         private _releaseVAOs() {
         private _releaseVAOs() {
+            if (!this._updateVAO) {
+                return;
+            }
+            
             for (var index = 0; index < this._updateVAO.length; index++) {
             for (var index = 0; index < this._updateVAO.length; index++) {
                 this._engine.releaseVertexArrayObject(this._updateVAO[index]);
                 this._engine.releaseVertexArrayObject(this._updateVAO[index]);
             }
             }

+ 13 - 5
src/Particles/babylon.particle.ts

@@ -61,18 +61,26 @@
          * Creates a new instance of @see Particle
          * Creates a new instance of @see Particle
          * @param particleSystem the particle system the particle belongs to
          * @param particleSystem the particle system the particle belongs to
          */
          */
-        constructor(private particleSystem: ParticleSystem) {
+        constructor(
+            /**
+             * particleSystem the particle system the particle belongs to.
+             */
+            public particleSystem: ParticleSystem) {
             if (!this.particleSystem.isAnimationSheetEnabled) {
             if (!this.particleSystem.isAnimationSheetEnabled) {
                 return;
                 return;
             }
             }
 
 
+            this.updateCellInfoFromSystem();
+        }
+
+        private updateCellInfoFromSystem(): void {
             this.cellIndex = this.particleSystem.startSpriteCellID;
             this.cellIndex = this.particleSystem.startSpriteCellID;
 
 
             if (this.particleSystem.spriteCellChangeSpeed == 0) {
             if (this.particleSystem.spriteCellChangeSpeed == 0) {
-                this.updateCellIndex = this.updateCellIndexWithSpeedCalculated;
+                this.updateCellIndex = this._updateCellIndexWithSpeedCalculated;
             }
             }
             else {
             else {
-                this.updateCellIndex = this.updateCellIndexWithCustomSpeed;
+                this.updateCellIndex = this._updateCellIndexWithCustomSpeed;
             }
             }
         }
         }
 
 
@@ -82,7 +90,7 @@
          */
          */
         public updateCellIndex: (scaledUpdateSpeed: number) => void;
         public updateCellIndex: (scaledUpdateSpeed: number) => void;
 
 
-        private updateCellIndexWithSpeedCalculated(scaledUpdateSpeed: number): void {
+        private _updateCellIndexWithSpeedCalculated(scaledUpdateSpeed: number): void {
             //   (ageOffset / scaledUpdateSpeed) / available cells
             //   (ageOffset / scaledUpdateSpeed) / available cells
             var numberOfScaledUpdatesPerCell = ((this.lifeTime - this.age) / scaledUpdateSpeed) / (this.particleSystem.endSpriteCellID + 1 - this.cellIndex);
             var numberOfScaledUpdatesPerCell = ((this.lifeTime - this.age) / scaledUpdateSpeed) / (this.particleSystem.endSpriteCellID + 1 - this.cellIndex);
 
 
@@ -96,7 +104,7 @@
             }
             }
         }
         }
 
 
-        private updateCellIndexWithCustomSpeed(): void {
+        private _updateCellIndexWithCustomSpeed(): void {
             if (this._currentFrameCounter >= this.particleSystem.spriteCellChangeSpeed) {
             if (this._currentFrameCounter >= this.particleSystem.spriteCellChangeSpeed) {
                 this.cellIndex++;
                 this.cellIndex++;
                 this._currentFrameCounter = 0;
                 this._currentFrameCounter = 0;

+ 99 - 22
src/Particles/babylon.particleSystem.ts

@@ -325,6 +325,7 @@
         private _scaledGravity = Vector3.Zero();
         private _scaledGravity = Vector3.Zero();
         private _currentRenderId = -1;
         private _currentRenderId = -1;
         private _alive: boolean;
         private _alive: boolean;
+
         private _started = false;
         private _started = false;
         private _stopped = false;
         private _stopped = false;
         private _actualFrame = 0;
         private _actualFrame = 0;
@@ -332,6 +333,21 @@
         private _vertexBufferSize = 11;
         private _vertexBufferSize = 11;
         private _isAnimationSheetEnabled: boolean;
         private _isAnimationSheetEnabled: boolean;
 
 
+        // end of sheet animation
+
+        // Sub-emitters
+        /**
+         * this is the Sub-emitters templates that will be used to generate particle system when the particle dies, this property is used by the root particle system only.
+         */
+        public subEmitters: ParticleSystem[];
+        /**
+        * The current active Sub-systems, this property is used by the root particle system only.
+        */
+        public activeSubSystems: Array<ParticleSystem>;
+        
+        private _rootParticleSystem: ParticleSystem;
+        //end of Sub-emitter
+
         /**
         /**
          * Gets the current list of active particles
          * Gets the current list of active particles
          */
          */
@@ -403,6 +419,7 @@
                     particle.age += this._scaledUpdateSpeed;
                     particle.age += this._scaledUpdateSpeed;
 
 
                     if (particle.age >= particle.lifeTime) { // Recycle by swapping with last particle
                     if (particle.age >= particle.lifeTime) { // Recycle by swapping with last particle
+                        this._emitFromParticle(particle);
                         this.recycleParticle(particle);
                         this.recycleParticle(particle);
                         index--;
                         index--;
                         continue;
                         continue;
@@ -447,20 +464,6 @@
         }
         }
 
 
         /**
         /**
-         * "Recycles" one of the particle by copying it back to the "stock" of particles and removing it from the active list.
-         * Its lifetime will start back at 0.
-         * @param particle The particle to recycle
-         */
-        public recycleParticle(particle: Particle): void {
-            var lastParticle = <Particle>this._particles.pop();
-
-            if (lastParticle !== particle) {
-                lastParticle.copyTo(particle);
-                this._stockParticles.push(lastParticle);
-            }
-        }
-
-        /**
          * Gets the maximum number of particles active at the same time.
          * Gets the maximum number of particles active at the same time.
          * @returns The max number of active particles.
          * @returns The max number of active particles.
          */
          */
@@ -491,15 +494,25 @@
             this._started = true;
             this._started = true;
             this._stopped = false;
             this._stopped = false;
             this._actualFrame = 0;
             this._actualFrame = 0;
+            if (this.subEmitters && this.subEmitters.length != 0) {
+                this.activeSubSystems = new Array<ParticleSystem>();
+            }
         }
         }
 
 
         /**
         /**
          * Stops the particle system.
          * Stops the particle system.
+         * @param stopSubEmitters if true it will stop the current system and all created sub-Systems if false it will stop the current root system only, this param is used by the root particle system only. the default value is true.
          */
          */
-        public stop(): void {
+        public stop(stopSubEmitters = true): void {
             this._stopped = true;
             this._stopped = true;
+
+            if (stopSubEmitters) {
+                this._stopSubEmitters();
+            }
         }
         }
 
 
+        // animation sheet
+
         /**
         /**
          * Remove all active particles
          * Remove all active particles
          */
          */
@@ -555,6 +568,69 @@
             this._vertexData[offset + 11] = particle.cellIndex;
             this._vertexData[offset + 11] = particle.cellIndex;
         }
         }
 
 
+        // start of sub system methods
+
+        /**
+         * "Recycles" one of the particle by copying it back to the "stock" of particles and removing it from the active list.
+         * Its lifetime will start back at 0.
+         */
+        public recycleParticle: (particle: Particle) => void = (particle) => {
+            var lastParticle = <Particle>this._particles.pop();
+
+            if (lastParticle !== particle) {
+                lastParticle.copyTo(particle);
+            }
+            this._stockParticles.push(lastParticle);
+        };
+
+        private _stopSubEmitters(): void {
+            if (!this.activeSubSystems) {
+                return;
+            }
+            this.activeSubSystems.forEach(subSystem => {
+                subSystem.stop(true);
+            });
+            this.activeSubSystems = new Array<ParticleSystem>();
+        }
+
+        private _createParticle: () => Particle = () => {
+            var particle: Particle;
+            if (this._stockParticles.length !== 0) {
+                particle = <Particle>this._stockParticles.pop();
+                particle.age = 0;
+                particle.cellIndex = this.startSpriteCellID;
+            } else {
+                particle = new Particle(this);
+            }
+            return particle;
+        }
+
+        private _removeFromRoot(): void {
+            if (!this._rootParticleSystem){
+                return;
+            }
+            
+            let index = this._rootParticleSystem.activeSubSystems.indexOf(this);
+            if (index !== -1) {
+                this._rootParticleSystem.activeSubSystems.splice(index, 1);
+            }
+        }
+
+        private _emitFromParticle: (particle: Particle) => void = (particle) => {
+            if (!this.subEmitters || this.subEmitters.length === 0) {
+                return;
+            }
+
+            var templateIndex = Math.floor(Math.random() * this.subEmitters.length);
+
+            var subSystem = this.subEmitters[templateIndex].clone(this.name + "_sub", particle.position.clone());
+            subSystem._rootParticleSystem = this;
+            this.activeSubSystems.push(subSystem);
+            subSystem.start();
+        }
+
+        // end of sub system methods
+
         private _update(newParticles: number): void {
         private _update(newParticles: number): void {
             // Update current
             // Update current
             this._alive = this._particles.length > 0;
             this._alive = this._particles.length > 0;
@@ -578,13 +654,7 @@
                     break;
                     break;
                 }
                 }
 
 
-                if (this._stockParticles.length !== 0) {
-                    particle = <Particle>this._stockParticles.pop();
-                    particle.age = 0;
-                    particle.cellIndex = this.startSpriteCellID;
-                } else {
-                    particle = new Particle(this);
-                }
+                particle = this._createParticle();
 
 
                 this._particles.push(particle);
                 this._particles.push(particle);
 
 
@@ -743,6 +813,10 @@
             if (this._vertexBuffer) {
             if (this._vertexBuffer) {
                 this._vertexBuffer.update(this._vertexData);
                 this._vertexBuffer.update(this._vertexData);
             }
             }
+
+            if (this.manualEmitCount === 0 && this.disposeOnStop) {
+                this.stop();
+            }
         }
         }
 
 
         private _appendParticleVertexes: Nullable<(offset: number, particle: Particle) => void> = null;
         private _appendParticleVertexes: Nullable<(offset: number, particle: Particle) => void> = null;
@@ -850,6 +924,8 @@
                 this.particleTexture = null;
                 this.particleTexture = null;
             }
             }
 
 
+            this._removeFromRoot();
+
             // Remove from scene
             // Remove from scene
             var index = this._scene.particleSystems.indexOf(this);
             var index = this._scene.particleSystems.indexOf(this);
             if (index > -1) {
             if (index > -1) {
@@ -916,6 +992,7 @@
             return particleEmitter;
             return particleEmitter;
         }
         }
 
 
+        // Clone
         /**
         /**
          * Clones the particle system.
          * Clones the particle system.
          * @param name The name of the cloned object
          * @param name The name of the cloned object

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

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

+ 1 - 1
src/PostProcess/RenderPipeline/babylon.postProcessRenderEffect.ts

@@ -177,7 +177,7 @@ module BABYLON {
                 var cameraName = camera.name;
                 var cameraName = camera.name;
 
 
                 for (var j = 0; j < this._indicesForCamera[cameraName].length; j++) {
                 for (var j = 0; j < this._indicesForCamera[cameraName].length; j++) {
-                    if (camera._postProcesses[this._indicesForCamera[cameraName][j]] === undefined) {
+                    if (camera._postProcesses[this._indicesForCamera[cameraName][j]] === undefined || camera._postProcesses[this._indicesForCamera[cameraName][j]] === null) {
                         this._postProcesses[this._singleInstance ? 0 : cameraName].forEach((postProcess)=>{
                         this._postProcesses[this._singleInstance ? 0 : cameraName].forEach((postProcess)=>{
                             cams![i].attachPostProcess(postProcess, this._indicesForCamera[cameraName][j]);
                             cams![i].attachPostProcess(postProcess, this._indicesForCamera[cameraName][j]);
                         });
                         });

+ 25 - 6
src/PostProcess/babylon.blurPostProcess.ts

@@ -19,7 +19,9 @@
 			v = Math.max(v, 1);
 			v = Math.max(v, 1);
 			this._idealKernel = v;
 			this._idealKernel = v;
 			this._kernel = this._nearestBestKernel(v);
 			this._kernel = this._nearestBestKernel(v);
-            this._updateParameters();
+			if(!this.blockCompilation){
+				this._updateParameters();
+			}
 		}
 		}
 
 
 		/**
 		/**
@@ -37,7 +39,9 @@
 				return;
 				return;
 			}
 			}
 			this._packedFloat = v;
 			this._packedFloat = v;
-            this._updateParameters();
+            if(!this.blockCompilation){
+				this._updateParameters();
+			}
 		}
 		}
 
 
 		/**
 		/**
@@ -58,8 +62,9 @@
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param textureType Type of textures used when performing the post process. (default: 0)
          * @param textureType Type of textures used when performing the post process. (default: 0)
+		 * @param blockCompilation If compilation of the shader should not be done in the constructor. The updateEffect method can be used to compile the shader at a later time. (default: false)
          */
          */
-        constructor(name: string, /** The direction in which to blur the image. */ public direction: Vector2, kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, defines = "") {
+        constructor(name: string, /** The direction in which to blur the image. */ public direction: Vector2, kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, defines = "", private blockCompilation = false) {
 			super(name, "kernelBlur", ["delta", "direction", "cameraMinMaxZ"], ["circleOfConfusionSampler"], options, camera, samplingMode, engine, reusable, null, textureType, "kernelBlur", {varyingCount: 0, depCount: 0}, true);
 			super(name, "kernelBlur", ["delta", "direction", "cameraMinMaxZ"], ["circleOfConfusionSampler"], options, camera, samplingMode, engine, reusable, null, textureType, "kernelBlur", {varyingCount: 0, depCount: 0}, true);
 			this._staticDefines = defines;
 			this._staticDefines = defines;
 			this.onApplyObservable.add((effect: Effect) => {
 			this.onApplyObservable.add((effect: Effect) => {
@@ -69,7 +74,21 @@
             this.kernel = kernel;
             this.kernel = kernel;
         }
         }
 
 
-        protected _updateParameters(): void {
+		/**
+         * Updates the effect with the current post process compile time values and recompiles the shader.
+         * @param defines Define statements that should be added at the beginning of the shader. (default: null)
+         * @param uniforms Set of uniform variables that will be passed to the shader. (default: null)
+         * @param samplers Set of Texture2D variables that will be passed to the shader. (default: null)
+         * @param indexParameters The index parameters to be used for babylons include syntax "#include<kernelBlurVaryingDeclaration>[0..varyingCount]". (default: undefined) See usage in babylon.blurPostProcess.ts and kernelBlur.vertex.fx
+         * @param onCompiled Called when the shader has been compiled.
+         * @param onError Called if there is an error when compiling a shader.
+         */
+        public updateEffect(defines: Nullable<string> = null, uniforms: Nullable<string[]> = null, samplers: Nullable<string[]> = null, indexParameters?: any,
+            onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void) {
+            this._updateParameters(onCompiled, onError);
+        }
+
+        protected _updateParameters(onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void): void {
             // Generate sampling offsets and weights
             // Generate sampling offsets and weights
 			let N = this._kernel;
 			let N = this._kernel;
 			let centerIndex = (N - 1) / 2;
 			let centerIndex = (N - 1) / 2;
@@ -155,10 +174,10 @@
 				defines += `#define PACKEDFLOAT 1`;
 				defines += `#define PACKEDFLOAT 1`;
 			}
 			}
 
 
-            this.updateEffect(defines, null, null, {
+            super.updateEffect(defines, null, null, {
 				varyingCount: varyingCount,
 				varyingCount: varyingCount,
 				depCount: depCount
 				depCount: depCount
-			});
+			}, onCompiled, onError);
         }
         }
 
 
         /**
         /**

+ 3 - 2
src/PostProcess/babylon.chromaticAberrationPostProcess.ts

@@ -34,9 +34,10 @@ module BABYLON {
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param textureType Type of textures used when performing the post process. (default: 0)
          * @param textureType Type of textures used when performing the post process. (default: 0)
+         * @param blockCompilation If compilation of the shader should not be done in the constructor. The updateEffect method can be used to compile the shader at a later time. (default: false)
          */
          */
-        constructor(name: string, screenWidth:number, screenHeight:number, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
-            super(name, "chromaticAberration", ["chromatic_aberration", "screen_width", "screen_height", "direction", "radialIntensity", "centerPosition"], [], options, camera, samplingMode, engine, reusable, null, textureType);
+        constructor(name: string, screenWidth:number, screenHeight:number, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false) {
+            super(name, "chromaticAberration", ["chromatic_aberration", "screen_width", "screen_height", "direction", "radialIntensity", "centerPosition"], [], options, camera, samplingMode, engine, reusable, null, textureType, undefined, null, blockCompilation);
             this.onApplyObservable.add((effect: Effect) => {
             this.onApplyObservable.add((effect: Effect) => {
                 effect.setFloat('chromatic_aberration', this.aberrationAmount);
                 effect.setFloat('chromatic_aberration', this.aberrationAmount);
                 effect.setFloat('screen_width', screenWidth);
                 effect.setFloat('screen_width', screenWidth);

+ 23 - 9
src/PostProcess/babylon.circleOfConfusionPostProcess.ts

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

+ 3 - 2
src/PostProcess/babylon.depthOfFieldBlurPostProcess.ts

@@ -20,9 +20,10 @@ module BABYLON {
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param textureType Type of textures used when performing the post process. (default: 0)
          * @param textureType Type of textures used when performing the post process. (default: 0)
+         * @param blockCompilation If compilation of the shader should not be done in the constructor. The updateEffect method can be used to compile the shader at a later time. (default: false)
          */
          */
-        constructor(name: string, scene: Scene, public direction: Vector2, kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, circleOfConfusion:PostProcess, imageToBlur:Nullable<PostProcess> = null, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
-            super(name, direction, kernel, options, camera, samplingMode = Texture.BILINEAR_SAMPLINGMODE, engine, reusable, textureType = Engine.TEXTURETYPE_UNSIGNED_INT, `#define DOF 1\r\n`);
+        constructor(name: string, scene: Scene, public direction: Vector2, kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, circleOfConfusion:PostProcess, imageToBlur:Nullable<PostProcess> = null, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false) {
+            super(name, direction, kernel, options, camera, samplingMode = Texture.BILINEAR_SAMPLINGMODE, engine, reusable, textureType = Engine.TEXTURETYPE_UNSIGNED_INT, `#define DOF 1\r\n`, blockCompilation);
 			
 			
 			this.onApplyObservable.add((effect: Effect) => {
 			this.onApplyObservable.add((effect: Effect) => {
                 if(imageToBlur != null){
                 if(imageToBlur != null){

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

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

+ 21 - 2
src/PostProcess/babylon.depthOfFieldMergePostProcess.ts

@@ -15,9 +15,10 @@ module BABYLON {
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param textureType Type of textures used when performing the post process. (default: 0)
          * @param textureType Type of textures used when performing the post process. (default: 0)
+         * @param blockCompilation If compilation of the shader should not be done in the constructor. The updateEffect method can be used to compile the shader at a later time. (default: false)
          */
          */
-        constructor(name: string, original: PostProcess, circleOfConfusion: PostProcess, blurSteps: Array<PostProcess>, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
-            super(name, "depthOfFieldMerge", [], ["circleOfConfusionSampler", "originalSampler", "blurStep1", "blurStep2"], options, camera, samplingMode, engine, reusable, "#define BLUR_LEVEL "+blurSteps.length+"\n", textureType);
+        constructor(name: string, original: PostProcess, circleOfConfusion: PostProcess, private blurSteps: Array<PostProcess>, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false) {
+            super(name, "depthOfFieldMerge", [], ["circleOfConfusionSampler", "originalSampler", "blurStep1", "blurStep2"], options, camera, samplingMode, engine, reusable, null, textureType, undefined, null, true);
             this.onApplyObservable.add((effect: Effect) => {
             this.onApplyObservable.add((effect: Effect) => {
                 effect.setTextureFromPostProcess("circleOfConfusionSampler", circleOfConfusion);
                 effect.setTextureFromPostProcess("circleOfConfusionSampler", circleOfConfusion);
                 effect.setTextureFromPostProcess("originalSampler", original);
                 effect.setTextureFromPostProcess("originalSampler", original);
@@ -25,6 +26,24 @@ module BABYLON {
                     effect.setTextureFromPostProcess("blurStep"+(index+1), step);
                     effect.setTextureFromPostProcess("blurStep"+(index+1), step);
                 });
                 });
             });
             });
+
+            if(!blockCompilation){
+                this.updateEffect();
+            }
+        }
+
+        /**
+         * Updates the effect with the current post process compile time values and recompiles the shader.
+         * @param defines Define statements that should be added at the beginning of the shader. (default: null)
+         * @param uniforms Set of uniform variables that will be passed to the shader. (default: null)
+         * @param samplers Set of Texture2D variables that will be passed to the shader. (default: null)
+         * @param indexParameters The index parameters to be used for babylons include syntax "#include<kernelBlurVaryingDeclaration>[0..varyingCount]". (default: undefined) See usage in babylon.blurPostProcess.ts and kernelBlur.vertex.fx
+         * @param onCompiled Called when the shader has been compiled.
+         * @param onError Called if there is an error when compiling a shader.
+         */
+        public updateEffect(defines: Nullable<string> = null, uniforms: Nullable<string[]> = null, samplers: Nullable<string[]> = null, indexParameters?: any,
+            onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void) {
+            super.updateEffect(defines ? defines : "#define BLUR_LEVEL "+this.blurSteps.length+"\n", uniforms, samplers, indexParameters, onCompiled, onError);
         }
         }
     }
     }
 }
 }

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

@@ -272,6 +272,45 @@
             this.imageProcessingConfiguration.vignetteEnabled = value;
             this.imageProcessingConfiguration.vignetteEnabled = value;
         }
         }
 
 
+        /**
+         * Gets wether the grain effect is enabled.
+         */
+        public get grainEnabled(): boolean {
+            return this.imageProcessingConfiguration.grainEnabled;
+        }
+        /**
+         * Sets wether the grain effect is enabled.
+         */
+        public set grainEnabled(value: boolean) {
+            this.imageProcessingConfiguration.grainEnabled = value;
+        }
+
+        /**
+         * Gets the grain effect's intensity.
+         */
+        public get grainIntensity(): number {
+            return this.imageProcessingConfiguration.grainIntensity;
+        }
+        /**
+         * Sets the grain effect's intensity.
+         */
+        public set grainIntensity(value: number) {
+            this.imageProcessingConfiguration.grainIntensity = value;
+        }
+
+        /**
+         * Gets wether the grain effect is animated.
+         */
+        public get grainAnimated(): boolean {
+            return this.imageProcessingConfiguration.grainAnimated;
+        }
+        /**
+         * Sets wether the grain effect is animated.
+         */
+        public set grainAnimated(value: boolean) {
+            this.imageProcessingConfiguration.grainAnimated = value;
+        }
+
         @serialize()
         @serialize()
         private _fromLinearSpace = true;
         private _fromLinearSpace = true;
         /**
         /**

+ 2 - 2
src/PostProcess/babylon.passPostProcess.ts

@@ -1,7 +1,7 @@
 module BABYLON {
 module BABYLON {
     export class PassPostProcess extends PostProcess {    
     export class PassPostProcess extends PostProcess {    
-        constructor(name: string, options: number | PostProcessOptions, camera: Nullable<Camera> = null, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
-            super(name, "pass", null, null, options, camera, samplingMode, engine, reusable, undefined, textureType);
+        constructor(name: string, options: number | PostProcessOptions, camera: Nullable<Camera> = null, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false) {
+            super(name, "pass", null, null, options, camera, samplingMode, engine, reusable, undefined, textureType, undefined, null, blockCompilation);
         }
         }
     }
     }
 } 
 } 

+ 17 - 2
src/PostProcess/babylon.postProcess.ts

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

+ 3 - 3
src/PostProcess/babylon.postProcessManager.ts

@@ -73,7 +73,7 @@
                 return false;
                 return false;
             }
             }
 
 
-            var postProcesses = postProcesses || (<Nullable<PostProcess[]>>camera._postProcesses);
+            var postProcesses = postProcesses || (<Nullable<PostProcess[]>>camera._postProcesses.filter((pp)=>{return pp != null;}));
 
 
             if (!postProcesses || postProcesses.length === 0 || !this._scene.postProcessesEnabled) {
             if (!postProcesses || postProcesses.length === 0 || !this._scene.postProcessesEnabled) {
                 return false;
                 return false;
@@ -133,14 +133,14 @@
          * @param postProcesses The array of post processes to render.
          * @param postProcesses The array of post processes to render.
          * @param forceFullscreenViewport force gl.viewport to be full screen eg. 0,0,textureWidth,textureHeight (default: false)
          * @param forceFullscreenViewport force gl.viewport to be full screen eg. 0,0,textureWidth,textureHeight (default: false)
          */
          */
-        public _finalizeFrame(doNotPresent?: boolean, targetTexture?: InternalTexture, faceIndex?: number, postProcesses?: PostProcess[], forceFullscreenViewport = false): void {
+        public _finalizeFrame(doNotPresent?: boolean, targetTexture?: InternalTexture, faceIndex?: number, postProcesses?: Array<PostProcess>, forceFullscreenViewport = false): void {
             let camera = this._scene.activeCamera;
             let camera = this._scene.activeCamera;
 
 
             if (!camera) {
             if (!camera) {
                 return;
                 return;
             }
             }
 
 
-            postProcesses = postProcesses || camera._postProcesses;
+            postProcesses = postProcesses || <Array<PostProcess>>camera._postProcesses.filter((pp)=>{return pp != null;});
             if (postProcesses.length === 0 || !this._scene.postProcessesEnabled) {
             if (postProcesses.length === 0 || !this._scene.postProcessesEnabled) {
                 return;
                 return;
             }
             }

+ 3 - 2
src/PostProcess/babylon.sharpenPostProcess.ts

@@ -21,9 +21,10 @@ module BABYLON {
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param textureType Type of textures used when performing the post process. (default: 0)
          * @param textureType Type of textures used when performing the post process. (default: 0)
+         * @param blockCompilation If compilation of the shader should not be done in the constructor. The updateEffect method can be used to compile the shader at a later time. (default: false)
          */
          */
-        constructor(name: string, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
-            super(name, "sharpen", ["sharpnessAmounts", "screenSize"], null, options, camera, samplingMode, engine, reusable, null, textureType);
+        constructor(name: string, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false) {
+            super(name, "sharpen", ["sharpnessAmounts", "screenSize"], null, options, camera, samplingMode, engine, reusable, null, textureType, undefined, null, blockCompilation);
 
 
             this.onApply = (effect: Effect) => {
             this.onApply = (effect: Effect) => {
                 effect.setFloat2("screenSize", this.width, this.height);
                 effect.setFloat2("screenSize", this.width, this.height);

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

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

+ 8 - 2
src/Rendering/babylon.renderingManager.ts

@@ -131,14 +131,20 @@
         }
         }
 
 
         public dispose(): void {
         public dispose(): void {
+            this.freeRenderingGroups();
+            this._renderingGroups.length = 0;
+        }
+
+        /**
+         * Clear the info related to rendering groups preventing retention points during dispose.
+         */
+        public freeRenderingGroups(): void {
             for (let index = RenderingManager.MIN_RENDERINGGROUPS; index < RenderingManager.MAX_RENDERINGGROUPS; index++) {
             for (let index = RenderingManager.MIN_RENDERINGGROUPS; index < RenderingManager.MAX_RENDERINGGROUPS; index++) {
                 var renderingGroup = this._renderingGroups[index];
                 var renderingGroup = this._renderingGroups[index];
                 if (renderingGroup) {
                 if (renderingGroup) {
                     renderingGroup.dispose();
                     renderingGroup.dispose();
                 }
                 }
             }
             }
-
-            this._renderingGroups.length = 0;
         }
         }
 
 
         private _prepareRenderingGroup(renderingGroupId: number): void {
         private _prepareRenderingGroup(renderingGroupId: number): void {

+ 1 - 1
src/Shaders/gpuRenderParticles.vertex.fx

@@ -30,7 +30,7 @@ void main() {
 
 
   // Expand position
   // Expand position
   vec4 viewPosition = view * vec4(position, 1.0);
   vec4 viewPosition = view * vec4(position, 1.0);
-  gl_Position = projection * (viewPosition + vec4(offset * size, 0, 1.0));
+  gl_Position = projection * (viewPosition + vec4(offset * size, 0., 0.));
 
 
 	// Clip plane
 	// Clip plane
 #ifdef CLIPPLANE
 #ifdef CLIPPLANE

+ 13 - 2
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -4,6 +4,7 @@
 
 
 uniform float currentCount;
 uniform float currentCount;
 uniform float timeDelta;
 uniform float timeDelta;
+uniform float stopFactor;
 uniform vec3 generalRandoms;
 uniform vec3 generalRandoms;
 uniform mat4 emitterWM;
 uniform mat4 emitterWM;
 uniform vec2 lifeTime;
 uniform vec2 lifeTime;
@@ -67,6 +68,16 @@ vec4 getRandomVec4(float offset) {
 
 
 void main() {
 void main() {
   if (age >= life) {
   if (age >= life) {
+    if (stopFactor == 0.) {
+      outPosition = position;
+      outAge = life;
+      outLife = life;
+      outSeed = seed;
+      outColor = vec4(0.,0.,0.,0.);
+      outSize = size;
+      outDirection = direction;
+      return;
+    }
     vec3 position;
     vec3 position;
     vec3 direction;
     vec3 direction;
 
 
@@ -151,12 +162,12 @@ void main() {
     outDirection = (emitterWM * vec4(direction * power, 0.)).xyz;
     outDirection = (emitterWM * vec4(direction * power, 0.)).xyz;
 
 
   } else {   
   } else {   
-    outPosition = position + (direction + gravity) * timeDelta;
+    outPosition = position + direction * timeDelta;
     outAge = age + timeDelta;
     outAge = age + timeDelta;
     outLife = life;
     outLife = life;
     outSeed = seed;
     outSeed = seed;
     outColor = color;
     outColor = color;
     outSize = size;
     outSize = size;
-    outDirection = direction;
+    outDirection = direction + gravity * timeDelta;
   }
   }
 }
 }

+ 7 - 3
src/Tools/babylon.tga.ts

@@ -225,9 +225,13 @@
             for (y = y_start; y !== y_end; y += y_step) {
             for (y = y_start; y !== y_end; y += y_step) {
                 for (x = x_start; x !== x_end; x += x_step, i += 2) {
                 for (x = x_start; x !== x_end; x += x_step, i += 2) {
                     color = image[i + 0] + (image[i + 1] << 8); // Inversed ?
                     color = image[i + 0] + (image[i + 1] << 8); // Inversed ?
-                    imageData[(x + width * y) * 4 + 0] = (color & 0x7C00) >> 7;
-                    imageData[(x + width * y) * 4 + 1] = (color & 0x03E0) >> 2;
-                    imageData[(x + width * y) * 4 + 2] = (color & 0x001F) >> 3;
+                    let r = (((color & 0x7C00) >> 10) * 255) / 0x1F | 0;
+                    let g = (((color & 0x03E0) >> 5) * 255) / 0x1F | 0;
+                    let b = ((color & 0x001F) * 255) / 0x1F | 0;
+
+                    imageData[(x + width * y) * 4 + 0] = r;
+                    imageData[(x + width * y) * 4 + 1] = g;
+                    imageData[(x + width * y) * 4 + 2] = b;
                     imageData[(x + width * y) * 4 + 3] = (color & 0x8000) ? 0 : 255;
                     imageData[(x + width * y) * 4 + 3] = (color & 0x8000) ? 0 : 255;
                 }
                 }
             }
             }

+ 19 - 4
src/babylon.node.ts

@@ -589,9 +589,24 @@
         }
         }
 
 
         /**
         /**
-         * Releases all associated resources
-         */
-        public dispose(): void {
+         * Releases resources associated with this node.
+         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
+         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
+         */
+        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
+            if (!doNotRecurse) {
+                const nodes = this.getDescendants(true);
+                for (const node of nodes) {
+                    node.dispose(doNotRecurse, disposeMaterialAndTextures);
+                }
+            } else {
+                const transformNodes = this.getChildTransformNodes(true);
+                for (const transformNode of transformNodes) {
+                    transformNode.parent = null;
+                    transformNode.computeWorldMatrix(true);
+                }
+            }
+
             this.parent = null;
             this.parent = null;
 
 
             // Callback
             // Callback
@@ -604,7 +619,7 @@
             }
             }
 
 
             this._behaviors = [];
             this._behaviors = [];
-            this._isDisposed = true;            
+            this._isDisposed = true;
         }
         }
 
 
         /**
         /**

+ 45 - 3
src/babylon.scene.ts

@@ -3170,6 +3170,48 @@
             }
             }
         }
         }
 
 
+        /**
+         * Clear the processed materials smart array preventing retention point in material dispose.
+         */
+        public freeProcessedMaterials(): void {
+            this._processedMaterials.dispose();
+        }
+
+        /**
+         * Clear the active meshes smart array preventing retention point in mesh dispose.
+         */
+        public freeActiveMeshes(): void {
+            this._activeMeshes.dispose();
+            if (this.activeCamera && this.activeCamera._activeMeshes) {
+                this.activeCamera._activeMeshes.dispose();
+            }
+            if (this.activeCameras) {
+                for (let i = 0; i < this.activeCameras.length; i++) {
+                    let activeCamera = this.activeCameras[i];
+                    if (activeCamera && activeCamera._activeMeshes) {
+                        activeCamera._activeMeshes.dispose();
+                    }
+                }
+            }
+        }
+
+        /**
+         * Clear the info related to rendering groups preventing retention points during dispose.
+         */
+        public freeRenderingGroups(): void {
+            if (this._renderingManager) {
+                this._renderingManager.freeRenderingGroups();
+            }
+            if (this.textures) {
+                for (let i = 0; i < this.textures.length; i++) {
+                    let texture = this.textures[i];
+                    if (texture && texture.isRenderTarget) {
+                        (<RenderTargetTexture>texture).freeRenderingGroups();
+                    }
+                }
+            }
+        }
+
         public _isInIntermediateRendering(): boolean {
         public _isInIntermediateRendering(): boolean {
             return this._intermediateRendering
             return this._intermediateRendering
         }
         }
@@ -3706,9 +3748,6 @@
                 this._gamepadManager._checkGamepadsStatus();
                 this._gamepadManager._checkGamepadsStatus();
             }
             }
 
 
-            // Before render
-            this.onBeforeRenderObservable.notifyObservers(this);
-
             // Update Cameras
             // Update Cameras
             if (this.activeCameras.length > 0) {
             if (this.activeCameras.length > 0) {
                 for (var cameraIndex = 0; cameraIndex < this.activeCameras.length; cameraIndex++) {
                 for (var cameraIndex = 0; cameraIndex < this.activeCameras.length; cameraIndex++) {
@@ -3731,6 +3770,9 @@
                 }
                 }
             }
             }
 
 
+            // Before render
+            this.onBeforeRenderObservable.notifyObservers(this);
+
             // Customs render targets
             // Customs render targets
             this.OnBeforeRenderTargetsRenderObservable.notifyObservers(this);
             this.OnBeforeRenderTargetsRenderObservable.notifyObservers(this);
             var engine = this.getEngine();
             var engine = this.getEngine();

+ 0 - 0
tests/unit/babylon/serializers/babylon.glTFSerializer.tests.ts


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików