Przeglądaj źródła

Merge branch 'rotationAlongDragPlaneBugFix' of https://github.com/TrevorDev/Babylon.js into rotationAlongDragPlaneBugFix

Trevor Baron 7 lat temu
rodzic
commit
6b408758b5
100 zmienionych plików z 25694 dodań i 18822 usunięć
  1. 6998 6279
      Playground/babylon.d.txt
  2. BIN
      Playground/scenes/Box/20140615_192225.png
  3. 16 0
      Playground/scenes/Box/Box.mtl
  4. 52 0
      Playground/scenes/Box/Box.obj
  5. 10 2
      Tools/Gulp/config.json
  6. 1 1
      Tools/Gulp/package.json
  7. BIN
      Viewer/dist/babylon.woff
  8. 2 2
      Viewer/src/configuration/configuration.ts
  9. 16 1
      Viewer/src/configuration/loader.ts
  10. 4 4
      Viewer/src/configuration/types/index.ts
  11. 2 1
      Viewer/src/index.ts
  12. 15 14
      Viewer/src/loader/modelLoader.ts
  13. 4 4
      Viewer/src/loader/plugins/applyMaterialConfig.ts
  14. 3 3
      Viewer/src/loader/plugins/extendedMaterialLoaderPlugin.ts
  15. 5 4
      Viewer/src/loader/plugins/msftLodLoaderPlugin.ts
  16. 9 2
      Viewer/src/managers/observablesManager.ts
  17. 1 1
      Viewer/src/model/modelAnimation.ts
  18. 8 3
      Viewer/src/templating/templateManager.ts
  19. 20 11
      Viewer/src/viewer/defaultViewer.ts
  20. 2 0
      Viewer/src/viewer/viewer.ts
  21. 3 0
      Viewer/tests/commons/helper.ts
  22. 1 1
      Viewer/tests/unit/src/configuration/updateConfiguration.ts
  23. 6949 6259
      dist/preview release/babylon.d.ts
  24. 57 56
      dist/preview release/babylon.js
  25. 1965 1071
      dist/preview release/babylon.max.js
  26. 1965 1071
      dist/preview release/babylon.no-module.max.js
  27. 57 56
      dist/preview release/babylon.worker.js
  28. 1967 1073
      dist/preview release/es6.js
  29. 1 1
      dist/preview release/gui/babylon.gui.js
  30. 1 1
      dist/preview release/gui/babylon.gui.min.js
  31. 34 6
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  32. 187 51
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  33. 3 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  34. 34 6
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  35. 185 51
      dist/preview release/loaders/babylon.glTFFileLoader.js
  36. 4 4
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  37. 34 6
      dist/preview release/loaders/babylonjs.loaders.d.ts
  38. 176 51
      dist/preview release/loaders/babylonjs.loaders.js
  39. 4 4
      dist/preview release/loaders/babylonjs.loaders.min.js
  40. 34 6
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  41. 16 15
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  42. 100 76
      dist/preview release/serializers/babylon.glTF2Serializer.js
  43. 2 2
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  44. 16 15
      dist/preview release/serializers/babylonjs.serializers.d.ts
  45. 100 76
      dist/preview release/serializers/babylonjs.serializers.js
  46. 2 2
      dist/preview release/serializers/babylonjs.serializers.min.js
  47. 16 15
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  48. 36 477
      dist/preview release/typedocValidationBaseline.json
  49. 28 22
      dist/preview release/viewer/babylon.viewer.d.ts
  50. 71 70
      dist/preview release/viewer/babylon.viewer.js
  51. 2366 1317
      dist/preview release/viewer/babylon.viewer.max.js
  52. 46 40
      dist/preview release/viewer/babylon.viewer.module.d.ts
  53. 12 4
      dist/preview release/what's new.md
  54. 1 1
      gui/src/2D/controls/radioButton.ts
  55. 122 0
      loaders/src/glTF/2.0/Extensions/EXT_lights_imageBased.ts
  56. 5 7
      loaders/src/glTF/2.0/Extensions/KHR_lights.ts
  57. 2 2
      loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts
  58. 1 1
      loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts
  59. 2 2
      loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts
  60. 51 43
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  61. 17 3
      loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts
  62. 1 1
      loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts
  63. BIN
      sandbox/Assets/environment.dds
  64. 11 5
      sandbox/index.js
  65. 16 16
      serializers/src/glTF/2.0/babylon.glTFAnimation.ts
  66. 32 13
      serializers/src/glTF/2.0/babylon.glTFExporter.ts
  67. 37 36
      serializers/src/glTF/2.0/babylon.glTFMaterialExporter.ts
  68. 21 14
      serializers/src/glTF/2.0/babylon.glTFUtilities.ts
  69. 3 3
      src/Actions/babylon.actionManager.ts
  70. 94 9
      src/Animations/babylon.animatable.ts
  71. 1 1
      src/Culling/babylon.boundingSphere.ts
  72. 17 12
      src/Engine/babylon.engine.ts
  73. 6 0
      src/Engine/babylon.nullEngine.ts
  74. 27 4
      src/Layer/babylon.effectLayer.ts
  75. 237 0
      src/Layer/babylon.effectLayerSceneComponent.ts
  76. 32 1
      src/Layer/babylon.glowLayer.ts
  77. 39 1
      src/Layer/babylon.highlightLayer.ts
  78. 5 0
      src/Layer/babylon.layer.ts
  79. 126 0
      src/Layer/babylon.layerSceneComponent.ts
  80. 7 1
      src/LensFlare/babylon.lensFlareSystem.ts
  81. 182 0
      src/LensFlare/babylon.lensFlareSystemSceneComponent.ts
  82. 3 19
      src/Loading/Plugins/babylon.babylonFileLoader.ts
  83. 1 1
      src/Materials/Textures/babylon.cubeTexture.ts
  84. 16 3
      src/Materials/Textures/babylon.internalTexture.ts
  85. 85 0
      src/Materials/Textures/babylon.rawCubeTexture.ts
  86. 228 79
      src/Math/babylon.sphericalPolynomial.ts
  87. 4 11
      src/Mesh/babylon.mesh.ts
  88. 44 0
      src/Particles/babylon.IParticleSystem.ts
  89. 223 71
      src/Particles/babylon.gpuParticleSystem.ts
  90. 25 1
      src/Particles/babylon.particle.ts
  91. 12 5
      src/Particles/babylon.particleHelper.ts
  92. 178 5
      src/Particles/babylon.particleSystem.ts
  93. 2 0
      src/Particles/babylon.particleSystemSet.ts
  94. 8 1
      src/Rendering/babylon.renderingManager.ts
  95. 7 7
      src/Shaders/gpuRenderParticles.vertex.fx
  96. 39 4
      src/Shaders/gpuUpdateParticles.vertex.fx
  97. 1 1
      src/Tools/HDR/babylon.cubemapToSphericalPolynomial.ts
  98. 63 195
      src/Tools/babylon.environmentTextureTools.ts
  99. 20 4
      src/Tools/babylon.observable.ts
  100. 0 0
      src/Tools/babylon.sceneSerializer.ts

Plik diff jest za duży
+ 6998 - 6279
Playground/babylon.d.txt


BIN
Playground/scenes/Box/20140615_192225.png


+ 16 - 0
Playground/scenes/Box/Box.mtl

@@ -0,0 +1,16 @@
+# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
+# File Created: 29.06.2018 00:36:27
+
+newmtl 01___Default
+	Ns 10.0000
+	Ni 1.5000
+	d 1.0000
+	Tr 0.0000
+	Tf 1.0000 1.0000 1.0000 
+	illum 2
+	Ka 0.5882 0.5882 0.5882
+	Kd 0.5882 0.5882 0.5882
+	Ks 0.0000 0.0000 0.0000
+	Ke 0.0000 0.0000 0.0000
+	map_Ka 20140615_192225.png
+	map_Kd 20140615_192225.png

+ 52 - 0
Playground/scenes/Box/Box.obj

@@ -0,0 +1,52 @@
+# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
+# File Created: 29.06.2018 00:36:27
+
+mtllib Box.mtl
+
+#
+# object Box001
+#
+
+v  -13.8390 0.0000 16.0985
+v  -13.8390 0.0000 -16.1733
+v  13.2327 0.0000 -16.1733
+v  13.2327 0.0000 16.0985
+v  -13.8390 23.5656 16.0985
+v  13.2327 23.5656 16.0985
+v  13.2327 23.5656 -16.1733
+v  -13.8390 23.5656 -16.1733
+# 8 vertices
+
+vn -0.5774 -0.5774 0.5774
+vn -0.5774 -0.5774 -0.5774
+vn 0.5774 -0.5774 -0.5774
+vn 0.5774 -0.5774 0.5774
+vn -0.5774 0.5774 0.5774
+vn 0.5774 0.5774 0.5774
+vn 0.5774 0.5774 -0.5774
+vn -0.5774 0.5774 -0.5774
+# 8 vertex normals
+
+vt 1.0000 0.0000 0.0000
+vt 1.0000 1.0000 0.0000
+vt 0.0000 1.0000 0.0000
+vt 0.0000 0.0000 0.0000
+# 4 texture coords
+
+o Box001
+g Box001
+usemtl 01___Default
+s 2
+f 1/1/1 2/2/2 3/3/3 4/4/4 
+s 4
+f 5/4/5 6/1/6 7/2/7 8/3/8 
+s 8
+f 1/4/1 4/1/4 6/2/6 5/3/5 
+s 16
+f 4/4/4 3/1/3 7/2/7 6/3/6 
+s 32
+f 3/4/3 2/1/2 8/2/8 7/3/7 
+s 64
+f 2/4/2 1/1/1 5/2/5 8/3/8 
+# 6 polygons
+

+ 10 - 2
Tools/Gulp/config.json

@@ -165,6 +165,8 @@
                 "../../src/Cameras/babylon.camera.js",
                 "../../src/Rendering/babylon.renderingManager.js",
                 "../../src/Rendering/babylon.renderingGroup.js",
+                "../../src/babylon.sceneComponent.js",
+                "../../src/babylon.abstractScene.js",
                 "../../src/babylon.scene.js",
                 "../../src/babylon.assetContainer.js",
                 "../../src/Mesh/babylon.buffer.js",
@@ -539,6 +541,7 @@
         "additionalTextures": {
             "files": [
                 "../../src/Materials/Textures/babylon.cubeTexture.js",
+                "../../src/Materials/Textures/babylon.rawCubeTexture.js",
                 "../../src/Materials/Textures/babylon.renderTargetTexture.js",
                 "../../src/Materials/Textures/babylon.multiRenderTarget.js",
                 "../../src/Materials/Textures/babylon.mirrorTexture.js",
@@ -1007,6 +1010,7 @@
         "lensFlares": {
             "files": [
                 "../../src/LensFlare/babylon.lensFlare.js",
+                "../../src/LensFlare/babylon.lensFlareSystemSceneComponent.js",
                 "../../src/LensFlare/babylon.lensFlareSystem.js"
             ],
             "dependUpon": [
@@ -1153,6 +1157,7 @@
             "files": [
                 "../../src/Rendering/babylon.outlineRenderer.js",
                 "../../src/Rendering/babylon.edgesRenderer.js",
+                "../../src/Layer/babylon.effectLayerSceneComponent.js",
                 "../../src/Layer/babylon.effectLayer.js",
                 "../../src/Layer/babylon.highlightLayer.js",
                 "../../src/Layer/babylon.glowLayer.js"
@@ -1198,6 +1203,7 @@
         },
         "layer": {
             "files": [
+                "../../src/Layer/babylon.layerSceneComponent.js",
                 "../../src/Layer/babylon.layer.js"
             ],
             "dependUpon": [
@@ -1633,7 +1639,8 @@
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_lights.ts",
-                    "../../loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts"
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts",
+                    "../../loaders/src/glTF/2.0/Extensions/EXT_lights_imageBased.ts"
                 ],
                 "doNotIncludeInBundle": true,
                 "output": "babylon.glTF2FileLoader.js"
@@ -1657,7 +1664,8 @@
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_lights.ts",
-                    "../../loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts"
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts",
+                    "../../loaders/src/glTF/2.0/Extensions/EXT_lights_imageBased.ts"
                 ],
                 "output": "babylon.glTFFileLoader.js"
             }

+ 1 - 1
Tools/Gulp/package.json

@@ -19,7 +19,7 @@
         "del": "2.2.2",
         "es6-promise": "^4.2.4",
         "exports-loader": "^0.6.4",
-        "gulp": "^3.8.11",
+        "gulp": "^3.9.1",
         "gulp-changed-in-place": "2.0.3",
         "gulp-clean-ts-extends": "~0.1.1",
         "gulp-concat": "~2.5.2",

BIN
Viewer/dist/babylon.woff


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

@@ -1,5 +1,5 @@
-import { EngineOptions, IGlowLayerOptions, DepthOfFieldEffectBlurLevel } from 'babylonjs';
-import { IVRConfiguration, IObserversConfiguration, IModelConfiguration, ISceneConfiguration, ISceneOptimizerConfiguration, ICameraConfiguration, ISkyboxConfiguration, IGroundConfiguration, ILightConfiguration, IDefaultRenderingPipelineConfiguration, ITemplateConfiguration } from './interfaces';
+import { EngineOptions } from 'babylonjs';
+import { ICameraConfiguration, IDefaultRenderingPipelineConfiguration, IGroundConfiguration, ILightConfiguration, IModelConfiguration, IObserversConfiguration, ISceneConfiguration, ISceneOptimizerConfiguration, ISkyboxConfiguration, ITemplateConfiguration, IVRConfiguration } from './interfaces';
 
 export function getConfigurationKey(key: string, configObject: any) {
     let splits = key.split('.');

+ 16 - 1
Viewer/src/configuration/loader.ts

@@ -33,8 +33,9 @@ export class ConfigurationLoader {
     public loadConfiguration(initConfig: ViewerConfiguration = {}, callback?: (config: ViewerConfiguration) => void): Promise<ViewerConfiguration> {
 
         let loadedConfig: ViewerConfiguration = deepmerge({}, initConfig);
+        this._processInitialConfiguration(loadedConfig);
 
-        let extendedConfiguration = getConfigurationType(loadedConfig.extends || "");
+        let extendedConfiguration = getConfigurationType(loadedConfig.extends || "extended");
 
         if (loadedConfig.configuration) {
 
@@ -94,6 +95,20 @@ export class ConfigurationLoader {
         this._loadRequests.length = 0;
     }
 
+    /**
+     * This function will process the initial configuration and make needed changes for the viewer to work.
+     * @param config the mutable(!) initial configuration to process
+     */
+    private _processInitialConfiguration(config: ViewerConfiguration) {
+        if (config.model) {
+            if (typeof config.model === "string") {
+                config.model = {
+                    url: config.model
+                }
+            }
+        }
+    }
+
     private _loadFile(url: string): Promise<any> {
         let cacheReference = this._configurationCache;
         if (this._enableCache && cacheReference[url]) {

+ 4 - 4
Viewer/src/configuration/types/index.ts

@@ -28,17 +28,17 @@ let getConfigurationType = function (types: string): ViewerConfiguration {
             case 'shadowSpotLight':
                 config = deepmerge(config, shadowSpotlLightConfiguration);
                 break;
-            case 'extended':
-                config = deepmerge(config, extendedConfiguration);
+            case 'default':
+                config = deepmerge(config, defaultConfiguration);
                 break;
             case 'minimal':
                 config = deepmerge(config, minimalConfiguration);
                 break;
             case 'none':
                 break;
-            case 'default':
+            case 'extended':
             default:
-                config = deepmerge(config, defaultConfiguration);
+                config = deepmerge(config, extendedConfiguration);
                 break;
         }
 

+ 2 - 1
Viewer/src/index.ts

@@ -8,6 +8,7 @@ import { ModelLoader } from './loader/modelLoader';
 import { ViewerModel, ModelState } from './model/viewerModel';
 import { AnimationPlayMode, AnimationState } from './model/modelAnimation';
 import { ILoaderPlugin } from './loader/plugins/loaderPlugin';
+import { AbstractViewerNavbarButton } from './templating/viewerTemplatePlugin';
 
 /**
  * BabylonJS Viewer
@@ -44,6 +45,6 @@ const Version = viewerGlobals.version;
 console.log("Babylon.js viewer (v" + Version + ")");
 
 // public API for initialization
-export { BABYLON, Version, InitTags, DefaultViewer, AbstractViewer, viewerGlobals, telemetryManager, disableInit, viewerManager, mapperManager, disposeAll, ModelLoader, ViewerModel, AnimationPlayMode, AnimationState, ModelState, ILoaderPlugin };
+export { BABYLON, Version, InitTags, DefaultViewer, AbstractViewer, viewerGlobals, telemetryManager, disableInit, viewerManager, mapperManager, disposeAll, ModelLoader, ViewerModel, AnimationPlayMode, AnimationState, ModelState, ILoaderPlugin, AbstractViewerNavbarButton };
 // export publicliy all configuration interfaces
 export * from './configuration';

+ 15 - 14
Viewer/src/loader/modelLoader.ts

@@ -1,10 +1,11 @@
-import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, Tools, SceneLoader, Tags } from "babylonjs";
-import { GLTFFileLoader, GLTFLoaderAnimationStartMode } from "babylonjs-loaders";
-import { IModelConfiguration } from "../configuration/interfaces/modelConfiguration";
-import { ViewerModel, ModelState } from "../model/viewerModel";
+import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, SceneLoader, Tags, Tools } from 'babylonjs';
+import { GLTFFileLoader, GLTFLoaderAnimationStartMode } from 'babylonjs-loaders';
+
+import { ConfigurationContainer } from '../configuration/configurationContainer';
+import { IModelConfiguration } from '../configuration/interfaces/modelConfiguration';
+import { ObservablesManager } from '../managers/observablesManager';
+import { ModelState, ViewerModel } from '../model/viewerModel';
 import { getLoaderPluginByName, ILoaderPlugin } from './plugins/';
-import { ObservablesManager } from "../managers/observablesManager";
-import { ConfigurationContainer } from "../configuration/configurationContainer";
 
 /**
  * An instance of the class is in charge of loading the model correctly.
@@ -61,24 +62,24 @@ export class ModelLoader {
 
         model.loadId = this._loadId++;
 
-        if (!modelConfiguration.url) {
-            model.state = ModelState.ERROR;
-            Tools.Error("No URL provided");
-            return model;
-        }
-
-        let base: string;
+        let base: string = "";
 
         let filename: any;
         if (modelConfiguration.file) {
             base = "file:";
             filename = modelConfiguration.file;
         }
-        else {
+        else if (modelConfiguration.url) {
             filename = Tools.GetFilename(modelConfiguration.url) || modelConfiguration.url;
             base = modelConfiguration.root || Tools.GetFolderPath(modelConfiguration.url);
         }
 
+        if (!filename || !base) {
+            model.state = ModelState.ERROR;
+            Tools.Error("No URL provided");
+            return model;
+        }
+
 
         let plugin = modelConfiguration.loader;
 

+ 4 - 4
Viewer/src/loader/plugins/applyMaterialConfig.ts

@@ -1,7 +1,7 @@
-import { ILoaderPlugin } from "./loaderPlugin";
-import { ViewerModel } from "../../model/viewerModel";
-import { Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Material } from "babylonjs";
-import { IGLTFLoaderData, GLTF2 } from "babylonjs-loaders";
+import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, Material } from 'babylonjs';
+
+import { ViewerModel } from '../../model/viewerModel';
+import { ILoaderPlugin } from './loaderPlugin';
 
 /**
  * Force-apply material configuration right after a material was loaded.

+ 3 - 3
Viewer/src/loader/plugins/extendedMaterialLoaderPlugin.ts

@@ -1,6 +1,6 @@
-import { ILoaderPlugin } from "./loaderPlugin";
-import { ViewerModel } from "../../model/viewerModel";
-import { Color3, Texture, BaseTexture, Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Material, PBRMaterial, Engine } from "babylonjs";
+import { Engine, Material, PBRMaterial } from 'babylonjs';
+
+import { ILoaderPlugin } from './loaderPlugin';
 
 /**
  * A (PBR) material will be extended using this function.

+ 5 - 4
Viewer/src/loader/plugins/msftLodLoaderPlugin.ts

@@ -1,7 +1,8 @@
-import { ILoaderPlugin } from "./loaderPlugin";
-import { ViewerModel } from "../../model/viewerModel";
-import { Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs";
-import { IGLTFLoaderExtension, GLTF2 } from "babylonjs-loaders";
+import { ISceneLoaderPlugin, ISceneLoaderPluginAsync } from 'babylonjs';
+import { GLTF2, IGLTFLoaderExtension } from 'babylonjs-loaders';
+
+import { ViewerModel } from '../../model/viewerModel';
+import { ILoaderPlugin } from './loaderPlugin';
 
 /**
  * A loder plugin to use MSFT_lod extension correctly (glTF)

+ 9 - 2
Viewer/src/managers/observablesManager.ts

@@ -1,5 +1,6 @@
-import { Observable, Scene, Engine, SceneLoaderProgressEvent, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs";
-import { ViewerModel } from "../model/viewerModel";
+import { Engine, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Observable, Scene, SceneLoaderProgressEvent } from 'babylonjs';
+
+import { ViewerModel } from '../model/viewerModel';
 
 export class ObservablesManager {
 
@@ -44,6 +45,11 @@ export class ObservablesManager {
     public onViewerInitDoneObservable: Observable<any>;
 
     /**
+     * Will notify when the viewer init started (after configuration was loaded)
+     */
+    public onViewerInitStartedObservable: Observable<any>;
+
+    /**
      * Functions added to this observable will be executed on each frame rendered.
      */
     public onFrameRenderedObservable: Observable<any>;
@@ -57,6 +63,7 @@ export class ObservablesManager {
         this.onModelAddedObservable = new Observable();
         this.onModelRemovedObservable = new Observable();
         this.onViewerInitDoneObservable = new Observable();
+        this.onViewerInitStartedObservable = new Observable();
         this.onLoaderInitObservable = new Observable();
         this.onFrameRenderedObservable = new Observable();
     }

+ 1 - 1
Viewer/src/model/modelAnimation.ts

@@ -1,4 +1,4 @@
-import { AnimationGroup, Animatable, Skeleton, Vector3 } from "babylonjs";
+import { Animatable, AnimationGroup, Vector3 } from 'babylonjs';
 
 /**
  * Animation play mode enum - is the animation looping or playing once

+ 8 - 3
Viewer/src/templating/templateManager.ts

@@ -80,8 +80,9 @@ export class TemplateManager {
             // register the observers
             //template.onLoaded.add(() => {
             let addToParent = () => {
-                let containingElement = parentTemplate && parentTemplate.parent.querySelector(camelToKebab(name)) || this.containerElement;
-                template.appendTo(containingElement);
+                let lastElements = parentTemplate && parentTemplate.parent.querySelectorAll(camelToKebab(name));
+                let containingElement = (lastElements && lastElements.length && lastElements.item(lastElements.length - 1)) || this.containerElement;
+                template.appendTo(<HTMLElement>containingElement);
                 this._checkLoadedState();
             }
 
@@ -379,6 +380,10 @@ export class Template {
         }
     }
 
+    public redraw() {
+        this.updateParams({});
+    }
+
     /**
      * Get the template'S configuration
      */
@@ -539,7 +544,7 @@ export class Template {
     private _getTemplateAsHtml(templateConfig: ITemplateConfiguration): Promise<string> {
         if (!templateConfig) {
             return Promise.reject('No templateConfig provided');
-        } else if (templateConfig.html) {
+        } else if (templateConfig.html && !templateConfig.location) {
             return Promise.resolve(templateConfig.html);
         } else {
             let location = this._getTemplateLocation(templateConfig);

+ 20 - 11
Viewer/src/viewer/defaultViewer.ts

@@ -35,6 +35,12 @@ export class DefaultViewer extends AbstractViewer {
                 this._configureLights();
             })
         });
+
+        this.onInitDoneObservable.add(() => {
+            if (!this.sceneManager.models.length) {
+                this.hideLoadingScreen();
+            }
+        })
     }
 
     private _registeredPlugins: Array<IViewerTemplatePlugin> = [];
@@ -51,8 +57,9 @@ export class DefaultViewer extends AbstractViewer {
         }
         if (plugin.addHTMLTemplate) {
             template.onHTMLRendered.add((tmpl) => {
-                plugin.addHTMLTemplate && plugin.addHTMLTemplate(tmpl);
+                plugin.addHTMLTemplate!(tmpl);
             });
+            template.redraw();
         }
 
         if (plugin.eventsToAttach) {
@@ -89,16 +96,18 @@ export class DefaultViewer extends AbstractViewer {
 
         if (this.configuration.templates && this.configuration.templates.viewer) {
             if (this.configuration.templates.viewer.params && this.configuration.templates.viewer.params.enableDragAndDrop) {
-                let filesInput = new FilesInput(this.engine, this.sceneManager.scene, () => {
-                }, () => {
-                }, () => {
-                }, () => {
-                }, function () {
-                }, (file: File) => {
-                    this.loadModel(file);
-                }, () => {
-                });
-                filesInput.monitorElementForDragNDrop(this.templateManager.getCanvas()!);
+                this.onSceneInitObservable.addOnce(() => {
+                    let filesInput = new FilesInput(this.engine, this.sceneManager.scene, () => {
+                    }, () => {
+                    }, () => {
+                    }, () => {
+                    }, function () {
+                    }, (file: File) => {
+                        this.loadModel(file);
+                    }, () => {
+                    });
+                    filesInput.monitorElementForDragNDrop(this.templateManager.getCanvas()!);
+                })
             }
         }
 

+ 2 - 0
Viewer/src/viewer/viewer.ts

@@ -372,6 +372,8 @@ export abstract class AbstractViewer {
         }
 
         this.templateManager = new TemplateManager(this.containerElement);
+
+        this.observablesManager.onViewerInitStartedObservable.notifyObservers(this);
     }
 
     /**

+ 3 - 0
Viewer/tests/commons/helper.ts

@@ -8,6 +8,9 @@ export const useNullEngine = true;
 export class Helper {
 
     public static getNewViewerInstance(element: HTMLElement = Helper.getViewerContainer(), configuration?: ViewerConfiguration, useAbstractViewer?: boolean) {
+        if (configuration) {
+            configuration.extends = configuration.extends || "default";
+        }
         if (useNullEngine) {
             if (useAbstractViewer) {
                 return new NullEngineAbstractViewer(element, configuration);

+ 1 - 1
Viewer/tests/unit/src/configuration/updateConfiguration.ts

@@ -36,7 +36,7 @@ describe(name + " scene", () => {
             }
         })
         viewer.onInitDoneObservable.add(() => {
-            assert.isUndefined(viewer.configuration.scene);
+            // assert.isUndefined(viewer.configuration.scene);
             assert.equal(showCalled, 0);
             assert.equal(hideCalled, 0);
             viewer.updateConfiguration({

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


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


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


Plik diff jest za duży
+ 1965 - 1071
dist/preview release/babylon.no-module.max.js


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


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


+ 1 - 1
dist/preview release/gui/babylon.gui.js

@@ -4401,7 +4401,7 @@ var BABYLON;
                     this._isChecked = value;
                     this._markAsDirty();
                     this.onIsCheckedChangedObservable.notifyObservers(value);
-                    if (this._isChecked) {
+                    if (this._isChecked && this._host) {
                         // Update all controls from same group
                         this._host.executeOnAllControls(function (control) {
                             if (control === _this) {

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


+ 34 - 6
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -368,7 +368,7 @@ declare module BABYLON.GLTF2 {
     }
     /** @hidden */
     interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
     /** @hidden */
     interface _ILoaderMaterial extends IMaterial, _IArrayItem {
@@ -441,6 +441,10 @@ declare module BABYLON.GLTF2 {
  */
 declare module BABYLON.GLTF2 {
     /** @hidden */
+    class _ArrayItem {
+        static Assign(values?: _IArrayItem[]): void;
+    }
+    /** @hidden */
     class GLTFLoader implements IGLTFLoader {
         _parent: GLTFFileLoader;
         _gltf: _ILoaderGLTF;
@@ -516,9 +520,10 @@ declare module BABYLON.GLTF2 {
         _createMaterial(name: string, drawMode: number): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): void;
-        _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void>;
+        _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Promise<void>;
+        _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Promise<void>;
         private _loadSampler(context, sampler);
-        private _loadImageAsync(context, image);
+        _loadImageAsync(context: string, image: _ILoaderImage): Promise<ArrayBufferView>;
         _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView>;
         private _onProgress();
         static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T;
@@ -584,10 +589,15 @@ declare module BABYLON.GLTF2 {
          */
         protected _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Override this method to modify the default behavior for loading texture infos.
+         * @hidden
+         */
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          */
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Override this method to modify the default behavior for loading uris.
          * @hidden
@@ -629,10 +639,15 @@ declare module BABYLON.GLTF2 {
          */
         static _LoadMaterialPropertiesAsync(loader: GLTFLoader, context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Helper method called by the loader to allow extensions to override loading texture infos.
+         * @hidden
+         */
+        static _LoadTextureInfoAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          */
-        static _LoadTextureAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        static _LoadTextureAsync(loader: GLTFLoader, context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Helper method called by the loader to allow extensions to override loading uris.
          * @hidden
@@ -764,6 +779,19 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_texture_transform extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
+    /**
+     * [Specification](TODO) (Experimental)
+     */
+    class EXT_lights_imageBased extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadSceneAsync(context: string, scene: _ILoaderScene): Nullable<Promise<void>>;
+        private _loadLightAsync(context, light);
+        private readonly _lights;
     }
 }

+ 187 - 51
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -677,6 +677,7 @@ var BABYLON;
 (function (BABYLON) {
     var GLTF2;
     (function (GLTF2) {
+        /** @hidden */
         var _ArrayItem = /** @class */ (function () {
             function _ArrayItem() {
             }
@@ -689,6 +690,7 @@ var BABYLON;
             };
             return _ArrayItem;
         }());
+        GLTF2._ArrayItem = _ArrayItem;
         /** @hidden */
         var GLTFLoader = /** @class */ (function () {
             function GLTFLoader(parent) {
@@ -941,9 +943,9 @@ var BABYLON;
                 return rootNode;
             };
             GLTFLoader.prototype._loadSceneAsync = function (context, scene) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (scene.name || ""));
@@ -1044,9 +1046,9 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._loadNodeAsync = function (context, node) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (node._babylonMesh) {
                     throw new Error(context + ": Invalid recursive node hierarchy");
@@ -1137,9 +1139,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var attributes = primitive.attributes;
                 if (!attributes) {
@@ -1746,12 +1748,12 @@ var BABYLON;
                     babylonMaterial.metallic = properties.metallicFactor == undefined ? 1 : properties.metallicFactor;
                     babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
                     if (properties.baseColorTexture) {
-                        promises.push(this._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.metallicRoughnessTexture) {
-                        promises.push(this._loadTextureAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
                             babylonMaterial.metallicTexture = texture;
                         }));
                         babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
@@ -1763,9 +1765,9 @@ var BABYLON;
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadMaterialAsync = function (context, material, mesh, babylonMesh, babylonDrawMode, assign) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 material._babylonData = material._babylonData || {};
                 var babylonData = material._babylonData[babylonDrawMode];
@@ -1793,9 +1795,9 @@ var BABYLON;
                 return babylonData.loaded;
             };
             GLTFLoader.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 promises.push(this._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
@@ -1819,7 +1821,7 @@ var BABYLON;
                     babylonMaterial.twoSidedLighting = true;
                 }
                 if (material.normalTexture) {
-                    promises.push(this._loadTextureAsync(context + "/normalTexture", material.normalTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/normalTexture", material.normalTexture, function (texture) {
                         babylonMaterial.bumpTexture = texture;
                     }));
                     babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
@@ -1829,7 +1831,7 @@ var BABYLON;
                     }
                 }
                 if (material.occlusionTexture) {
-                    promises.push(this._loadTextureAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
                         babylonMaterial.ambientTexture = texture;
                     }));
                     babylonMaterial.useAmbientInGrayScale = true;
@@ -1838,7 +1840,7 @@ var BABYLON;
                     }
                 }
                 if (material.emissiveTexture) {
-                    promises.push(this._loadTextureAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
                         babylonMaterial.emissiveTexture = texture;
                     }));
                 }
@@ -1872,15 +1874,26 @@ var BABYLON;
                     }
                 }
             };
-            GLTFLoader.prototype._loadTextureAsync = function (context, textureInfo, assign) {
-                var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, textureInfo, assign);
-                if (promise) {
-                    return promise;
+            GLTFLoader.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureInfoAsync(this, context, textureInfo, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 this._parent._logOpen("" + context);
                 var texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
-                context = "#/textures/" + textureInfo.index;
+                var promise = this._loadTextureAsync("#/textures/" + textureInfo.index, texture, function (babylonTexture) {
+                    babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
+                    assign(babylonTexture);
+                });
+                this._parent._logClose();
+                return promise;
+            };
+            GLTFLoader.prototype._loadTextureAsync = function (context, texture, assign) {
+                var _this = this;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, texture, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
+                }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (texture.name || ""));
                 var sampler = (texture.sampler == undefined ? this._defaultSampler : GLTFLoader._GetProperty(context + "/sampler", this._gltf.samplers, texture.sampler));
@@ -1899,16 +1912,14 @@ var BABYLON;
                 babylonTexture.name = texture.name || "texture" + texture._index;
                 babylonTexture.wrapU = samplerData.wrapU;
                 babylonTexture.wrapV = samplerData.wrapV;
-                babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
                 var image = GLTFLoader._GetProperty(context + "/source", this._gltf.images, texture.source);
-                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (blob) {
+                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (data) {
                     var dataUrl = "data:" + _this._rootUrl + (image.uri || "image" + image._index);
-                    babylonTexture.updateURL(dataUrl, blob);
+                    babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                 }));
                 assign(babylonTexture);
                 this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
                 this._parent._logClose();
-                this._parent._logClose();
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
@@ -1924,28 +1935,24 @@ var BABYLON;
                 return sampler._data;
             };
             GLTFLoader.prototype._loadImageAsync = function (context, image) {
-                if (!image._blob) {
+                if (!image._data) {
                     this._parent._logOpen(context + " " + (image.name || ""));
-                    var promise = void 0;
                     if (image.uri) {
-                        promise = this._loadUriAsync(context, image.uri);
+                        image._data = this._loadUriAsync(context, image.uri);
                     }
                     else {
                         var bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, image.bufferView);
-                        promise = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
+                        image._data = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
                     }
-                    image._blob = promise.then(function (data) {
-                        return new Blob([data], { type: image.mimeType });
-                    });
                     this._parent._logClose();
                 }
-                return image._blob;
+                return image._data;
             };
             GLTFLoader.prototype._loadUriAsync = function (context, uri) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (!GLTFLoader._ValidateUri(uri)) {
                     throw new Error(context + ": Uri '" + uri + "' is invalid");
@@ -2230,10 +2237,15 @@ var BABYLON;
              */
             GLTFLoaderExtension.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) { return null; };
             /**
+             * Override this method to modify the default behavior for loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) { return null; };
+            /**
              * Override this method to modify the default behavior for loading textures.
              * @hidden
              */
-            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, textureInfo, assign) { return null; };
+            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, texture, assign) { return null; };
             /**
              * Override this method to modify the default behavior for loading uris.
              * @hidden
@@ -2322,11 +2334,18 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadMaterialPropertiesAsync(context, material, babylonMaterial); });
             };
             /**
+             * Helper method called by the loader to allow extensions to override loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension._LoadTextureInfoAsync = function (loader, context, textureInfo, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureInfoAsync(context, textureInfo, assign); });
+            };
+            /**
              * Helper method called by the loader to allow extensions to override loading textures.
              * @hidden
              */
-            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, textureInfo, assign) {
-                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, textureInfo, assign); });
+            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, texture, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, texture, assign); });
             };
             /**
              * Helper method called by the loader to allow extensions to override loading uris.
@@ -2840,12 +2859,12 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
@@ -2914,7 +2933,7 @@ var BABYLON;
                             babylonMaterial.albedoColor = BABYLON.Color3.White();
                         }
                         if (properties.baseColorTexture) {
-                            promises.push(this._loader._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                            promises.push(this._loader._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                                 babylonMaterial.albedoTexture = texture;
                             }));
                         }
@@ -3003,10 +3022,9 @@ var BABYLON;
                                 break;
                             }
                             case LightType.SPOT: {
-                                var spotLight = light;
                                 // TODO: support inner and outer cone angles
                                 //const innerConeAngle = spotLight.innerConeAngle || 0;
-                                var outerConeAngle = spotLight.outerConeAngle || Math.PI / 4;
+                                var outerConeAngle = light.spot && light.spot.outerConeAngle || Math.PI / 4;
                                 babylonLight = new BABYLON.SpotLight(name, BABYLON.Vector3.Zero(), BABYLON.Vector3.Forward(), outerConeAngle, 2, _this._loader._babylonScene);
                                 break;
                             }
@@ -3070,10 +3088,10 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_texture_transform.prototype._loadTextureAsync = function (context, textureInfo, assign) {
+                KHR_texture_transform.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, textureInfo, function (extensionContext, extension) {
-                        return _this._loader._loadTextureAsync(context, textureInfo, function (babylonTexture) {
+                        return _this._loader._loadTextureInfoAsync(context, textureInfo, function (babylonTexture) {
                             if (extension.offset) {
                                 babylonTexture.uOffset = extension.offset[0];
                                 babylonTexture.vOffset = extension.offset[1];
@@ -3104,3 +3122,121 @@ var BABYLON;
 })(BABYLON || (BABYLON = {}));
 
 //# sourceMappingURL=KHR_texture_transform.js.map
+
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2;
+    (function (GLTF2) {
+        var Extensions;
+        (function (Extensions) {
+            var NAME = "EXT_lights_imageBased";
+            /**
+             * [Specification](TODO) (Experimental)
+             */
+            var EXT_lights_imageBased = /** @class */ (function (_super) {
+                __extends(EXT_lights_imageBased, _super);
+                function EXT_lights_imageBased() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
+                }
+                EXT_lights_imageBased.prototype._loadSceneAsync = function (context, scene) {
+                    var _this = this;
+                    return this._loadExtensionAsync(context, scene, function (extensionContext, extension) {
+                        var promises = new Array();
+                        promises.push(_this._loader._loadSceneAsync(context, scene));
+                        _this._loader._parent._logOpen("" + extensionContext);
+                        var light = GLTF2.GLTFLoader._GetProperty(extensionContext + "/light", _this._lights, extension.light);
+                        promises.push(_this._loadLightAsync("#/extensions/" + _this.name + "/lights/" + extension.light, light).then(function (texture) {
+                            _this._loader._babylonScene.environmentTexture = texture;
+                        }));
+                        _this._loader._parent._logClose();
+                        return Promise.all(promises).then(function () { });
+                    });
+                };
+                EXT_lights_imageBased.prototype._loadLightAsync = function (context, light) {
+                    var _this = this;
+                    if (!light._loaded) {
+                        var promises = new Array();
+                        this._loader._parent._logOpen("" + context);
+                        var imageData_1 = new Array(light.specularImages.length);
+                        var _loop_1 = function (mipmap) {
+                            var faces = light.specularImages[mipmap];
+                            imageData_1[mipmap] = new Array(faces.length);
+                            var _loop_2 = function (face) {
+                                var specularImageContext = context + "/specularImages/" + mipmap + "/" + face;
+                                this_1._loader._parent._logOpen("" + specularImageContext);
+                                var index = faces[face];
+                                var image = GLTF2.GLTFLoader._GetProperty(specularImageContext, this_1._loader._gltf.images, index);
+                                promises.push(this_1._loader._loadImageAsync("#/images/" + index, image).then(function (data) {
+                                    imageData_1[mipmap][face] = data;
+                                }));
+                                this_1._loader._parent._logClose();
+                            };
+                            for (var face = 0; face < faces.length; face++) {
+                                _loop_2(face);
+                            }
+                        };
+                        var this_1 = this;
+                        for (var mipmap = 0; mipmap < light.specularImages.length; mipmap++) {
+                            _loop_1(mipmap);
+                        }
+                        this._loader._parent._logClose();
+                        light._loaded = Promise.all(promises).then(function () {
+                            var size = Math.pow(2, imageData_1.length - 1);
+                            var babylonTexture = new BABYLON.RawCubeTexture(_this._loader._babylonScene, null, size);
+                            light._babylonTexture = babylonTexture;
+                            if (light.intensity != undefined) {
+                                babylonTexture.level = light.intensity;
+                            }
+                            if (light.rotation) {
+                                var rotation = BABYLON.Quaternion.FromArray(light.rotation);
+                                // Invert the rotation so that positive rotation is counter-clockwise.
+                                if (!_this._loader._babylonScene.useRightHandedSystem) {
+                                    rotation = BABYLON.Quaternion.Inverse(rotation);
+                                }
+                                BABYLON.Matrix.FromQuaternionToRef(rotation, babylonTexture.getReflectionTextureMatrix());
+                            }
+                            var sphericalHarmonics = BABYLON.SphericalHarmonics.FromArray(light.irradianceCoefficients);
+                            sphericalHarmonics.scale(light.intensity);
+                            sphericalHarmonics.convertIrradianceToLambertianRadiance();
+                            var sphericalPolynomial = BABYLON.SphericalPolynomial.FromHarmonics(sphericalHarmonics);
+                            return babylonTexture.updateRGBDAsync(imageData_1, sphericalPolynomial);
+                        });
+                    }
+                    return light._loaded.then(function () {
+                        return light._babylonTexture;
+                    });
+                };
+                Object.defineProperty(EXT_lights_imageBased.prototype, "_lights", {
+                    get: function () {
+                        var extensions = this._loader._gltf.extensions;
+                        if (!extensions || !extensions[this.name]) {
+                            throw new Error("#/extensions: '" + this.name + "' not found");
+                        }
+                        var extension = extensions[this.name];
+                        return extension.lights;
+                    },
+                    enumerable: true,
+                    configurable: true
+                });
+                return EXT_lights_imageBased;
+            }(GLTF2.GLTFLoaderExtension));
+            Extensions.EXT_lights_imageBased = EXT_lights_imageBased;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new EXT_lights_imageBased(loader); });
+        })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
+    })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=EXT_lights_imageBased.js.map

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


+ 34 - 6
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -930,7 +930,7 @@ declare module BABYLON.GLTF2 {
     }
     /** @hidden */
     interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
     /** @hidden */
     interface _ILoaderMaterial extends IMaterial, _IArrayItem {
@@ -1003,6 +1003,10 @@ declare module BABYLON.GLTF2 {
  */
 declare module BABYLON.GLTF2 {
     /** @hidden */
+    class _ArrayItem {
+        static Assign(values?: _IArrayItem[]): void;
+    }
+    /** @hidden */
     class GLTFLoader implements IGLTFLoader {
         _parent: GLTFFileLoader;
         _gltf: _ILoaderGLTF;
@@ -1078,9 +1082,10 @@ declare module BABYLON.GLTF2 {
         _createMaterial(name: string, drawMode: number): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): void;
-        _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void>;
+        _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Promise<void>;
+        _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Promise<void>;
         private _loadSampler(context, sampler);
-        private _loadImageAsync(context, image);
+        _loadImageAsync(context: string, image: _ILoaderImage): Promise<ArrayBufferView>;
         _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView>;
         private _onProgress();
         static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T;
@@ -1146,10 +1151,15 @@ declare module BABYLON.GLTF2 {
          */
         protected _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Override this method to modify the default behavior for loading texture infos.
+         * @hidden
+         */
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          */
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Override this method to modify the default behavior for loading uris.
          * @hidden
@@ -1191,10 +1201,15 @@ declare module BABYLON.GLTF2 {
          */
         static _LoadMaterialPropertiesAsync(loader: GLTFLoader, context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Helper method called by the loader to allow extensions to override loading texture infos.
+         * @hidden
+         */
+        static _LoadTextureInfoAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          */
-        static _LoadTextureAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        static _LoadTextureAsync(loader: GLTFLoader, context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Helper method called by the loader to allow extensions to override loading uris.
          * @hidden
@@ -1326,6 +1341,19 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_texture_transform extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
+    /**
+     * [Specification](TODO) (Experimental)
+     */
+    class EXT_lights_imageBased extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadSceneAsync(context: string, scene: _ILoaderScene): Nullable<Promise<void>>;
+        private _loadLightAsync(context, light);
+        private readonly _lights;
     }
 }

+ 185 - 51
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -2878,6 +2878,7 @@ var BABYLON;
 (function (BABYLON) {
     var GLTF2;
     (function (GLTF2) {
+        /** @hidden */
         var _ArrayItem = /** @class */ (function () {
             function _ArrayItem() {
             }
@@ -2890,6 +2891,7 @@ var BABYLON;
             };
             return _ArrayItem;
         }());
+        GLTF2._ArrayItem = _ArrayItem;
         /** @hidden */
         var GLTFLoader = /** @class */ (function () {
             function GLTFLoader(parent) {
@@ -3142,9 +3144,9 @@ var BABYLON;
                 return rootNode;
             };
             GLTFLoader.prototype._loadSceneAsync = function (context, scene) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (scene.name || ""));
@@ -3245,9 +3247,9 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._loadNodeAsync = function (context, node) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (node._babylonMesh) {
                     throw new Error(context + ": Invalid recursive node hierarchy");
@@ -3338,9 +3340,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var attributes = primitive.attributes;
                 if (!attributes) {
@@ -3947,12 +3949,12 @@ var BABYLON;
                     babylonMaterial.metallic = properties.metallicFactor == undefined ? 1 : properties.metallicFactor;
                     babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
                     if (properties.baseColorTexture) {
-                        promises.push(this._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.metallicRoughnessTexture) {
-                        promises.push(this._loadTextureAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
                             babylonMaterial.metallicTexture = texture;
                         }));
                         babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
@@ -3964,9 +3966,9 @@ var BABYLON;
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadMaterialAsync = function (context, material, mesh, babylonMesh, babylonDrawMode, assign) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 material._babylonData = material._babylonData || {};
                 var babylonData = material._babylonData[babylonDrawMode];
@@ -3994,9 +3996,9 @@ var BABYLON;
                 return babylonData.loaded;
             };
             GLTFLoader.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 promises.push(this._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
@@ -4020,7 +4022,7 @@ var BABYLON;
                     babylonMaterial.twoSidedLighting = true;
                 }
                 if (material.normalTexture) {
-                    promises.push(this._loadTextureAsync(context + "/normalTexture", material.normalTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/normalTexture", material.normalTexture, function (texture) {
                         babylonMaterial.bumpTexture = texture;
                     }));
                     babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
@@ -4030,7 +4032,7 @@ var BABYLON;
                     }
                 }
                 if (material.occlusionTexture) {
-                    promises.push(this._loadTextureAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
                         babylonMaterial.ambientTexture = texture;
                     }));
                     babylonMaterial.useAmbientInGrayScale = true;
@@ -4039,7 +4041,7 @@ var BABYLON;
                     }
                 }
                 if (material.emissiveTexture) {
-                    promises.push(this._loadTextureAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
                         babylonMaterial.emissiveTexture = texture;
                     }));
                 }
@@ -4073,15 +4075,26 @@ var BABYLON;
                     }
                 }
             };
-            GLTFLoader.prototype._loadTextureAsync = function (context, textureInfo, assign) {
-                var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, textureInfo, assign);
-                if (promise) {
-                    return promise;
+            GLTFLoader.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureInfoAsync(this, context, textureInfo, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 this._parent._logOpen("" + context);
                 var texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
-                context = "#/textures/" + textureInfo.index;
+                var promise = this._loadTextureAsync("#/textures/" + textureInfo.index, texture, function (babylonTexture) {
+                    babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
+                    assign(babylonTexture);
+                });
+                this._parent._logClose();
+                return promise;
+            };
+            GLTFLoader.prototype._loadTextureAsync = function (context, texture, assign) {
+                var _this = this;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, texture, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
+                }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (texture.name || ""));
                 var sampler = (texture.sampler == undefined ? this._defaultSampler : GLTFLoader._GetProperty(context + "/sampler", this._gltf.samplers, texture.sampler));
@@ -4100,16 +4113,14 @@ var BABYLON;
                 babylonTexture.name = texture.name || "texture" + texture._index;
                 babylonTexture.wrapU = samplerData.wrapU;
                 babylonTexture.wrapV = samplerData.wrapV;
-                babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
                 var image = GLTFLoader._GetProperty(context + "/source", this._gltf.images, texture.source);
-                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (blob) {
+                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (data) {
                     var dataUrl = "data:" + _this._rootUrl + (image.uri || "image" + image._index);
-                    babylonTexture.updateURL(dataUrl, blob);
+                    babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                 }));
                 assign(babylonTexture);
                 this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
                 this._parent._logClose();
-                this._parent._logClose();
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
@@ -4125,28 +4136,24 @@ var BABYLON;
                 return sampler._data;
             };
             GLTFLoader.prototype._loadImageAsync = function (context, image) {
-                if (!image._blob) {
+                if (!image._data) {
                     this._parent._logOpen(context + " " + (image.name || ""));
-                    var promise = void 0;
                     if (image.uri) {
-                        promise = this._loadUriAsync(context, image.uri);
+                        image._data = this._loadUriAsync(context, image.uri);
                     }
                     else {
                         var bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, image.bufferView);
-                        promise = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
+                        image._data = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
                     }
-                    image._blob = promise.then(function (data) {
-                        return new Blob([data], { type: image.mimeType });
-                    });
                     this._parent._logClose();
                 }
-                return image._blob;
+                return image._data;
             };
             GLTFLoader.prototype._loadUriAsync = function (context, uri) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (!GLTFLoader._ValidateUri(uri)) {
                     throw new Error(context + ": Uri '" + uri + "' is invalid");
@@ -4431,10 +4438,15 @@ var BABYLON;
              */
             GLTFLoaderExtension.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) { return null; };
             /**
+             * Override this method to modify the default behavior for loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) { return null; };
+            /**
              * Override this method to modify the default behavior for loading textures.
              * @hidden
              */
-            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, textureInfo, assign) { return null; };
+            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, texture, assign) { return null; };
             /**
              * Override this method to modify the default behavior for loading uris.
              * @hidden
@@ -4523,11 +4535,18 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadMaterialPropertiesAsync(context, material, babylonMaterial); });
             };
             /**
+             * Helper method called by the loader to allow extensions to override loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension._LoadTextureInfoAsync = function (loader, context, textureInfo, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureInfoAsync(context, textureInfo, assign); });
+            };
+            /**
              * Helper method called by the loader to allow extensions to override loading textures.
              * @hidden
              */
-            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, textureInfo, assign) {
-                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, textureInfo, assign); });
+            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, texture, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, texture, assign); });
             };
             /**
              * Helper method called by the loader to allow extensions to override loading uris.
@@ -5041,12 +5060,12 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
@@ -5115,7 +5134,7 @@ var BABYLON;
                             babylonMaterial.albedoColor = BABYLON.Color3.White();
                         }
                         if (properties.baseColorTexture) {
-                            promises.push(this._loader._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                            promises.push(this._loader._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                                 babylonMaterial.albedoTexture = texture;
                             }));
                         }
@@ -5204,10 +5223,9 @@ var BABYLON;
                                 break;
                             }
                             case LightType.SPOT: {
-                                var spotLight = light;
                                 // TODO: support inner and outer cone angles
                                 //const innerConeAngle = spotLight.innerConeAngle || 0;
-                                var outerConeAngle = spotLight.outerConeAngle || Math.PI / 4;
+                                var outerConeAngle = light.spot && light.spot.outerConeAngle || Math.PI / 4;
                                 babylonLight = new BABYLON.SpotLight(name, BABYLON.Vector3.Zero(), BABYLON.Vector3.Forward(), outerConeAngle, 2, _this._loader._babylonScene);
                                 break;
                             }
@@ -5269,10 +5287,10 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_texture_transform.prototype._loadTextureAsync = function (context, textureInfo, assign) {
+                KHR_texture_transform.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, textureInfo, function (extensionContext, extension) {
-                        return _this._loader._loadTextureAsync(context, textureInfo, function (babylonTexture) {
+                        return _this._loader._loadTextureInfoAsync(context, textureInfo, function (babylonTexture) {
                             if (extension.offset) {
                                 babylonTexture.uOffset = extension.offset[0];
                                 babylonTexture.vOffset = extension.offset[1];
@@ -5301,3 +5319,119 @@ var BABYLON;
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
+
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2;
+    (function (GLTF2) {
+        var Extensions;
+        (function (Extensions) {
+            var NAME = "EXT_lights_imageBased";
+            /**
+             * [Specification](TODO) (Experimental)
+             */
+            var EXT_lights_imageBased = /** @class */ (function (_super) {
+                __extends(EXT_lights_imageBased, _super);
+                function EXT_lights_imageBased() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
+                }
+                EXT_lights_imageBased.prototype._loadSceneAsync = function (context, scene) {
+                    var _this = this;
+                    return this._loadExtensionAsync(context, scene, function (extensionContext, extension) {
+                        var promises = new Array();
+                        promises.push(_this._loader._loadSceneAsync(context, scene));
+                        _this._loader._parent._logOpen("" + extensionContext);
+                        var light = GLTF2.GLTFLoader._GetProperty(extensionContext + "/light", _this._lights, extension.light);
+                        promises.push(_this._loadLightAsync("#/extensions/" + _this.name + "/lights/" + extension.light, light).then(function (texture) {
+                            _this._loader._babylonScene.environmentTexture = texture;
+                        }));
+                        _this._loader._parent._logClose();
+                        return Promise.all(promises).then(function () { });
+                    });
+                };
+                EXT_lights_imageBased.prototype._loadLightAsync = function (context, light) {
+                    var _this = this;
+                    if (!light._loaded) {
+                        var promises = new Array();
+                        this._loader._parent._logOpen("" + context);
+                        var imageData_1 = new Array(light.specularImages.length);
+                        var _loop_1 = function (mipmap) {
+                            var faces = light.specularImages[mipmap];
+                            imageData_1[mipmap] = new Array(faces.length);
+                            var _loop_2 = function (face) {
+                                var specularImageContext = context + "/specularImages/" + mipmap + "/" + face;
+                                this_1._loader._parent._logOpen("" + specularImageContext);
+                                var index = faces[face];
+                                var image = GLTF2.GLTFLoader._GetProperty(specularImageContext, this_1._loader._gltf.images, index);
+                                promises.push(this_1._loader._loadImageAsync("#/images/" + index, image).then(function (data) {
+                                    imageData_1[mipmap][face] = data;
+                                }));
+                                this_1._loader._parent._logClose();
+                            };
+                            for (var face = 0; face < faces.length; face++) {
+                                _loop_2(face);
+                            }
+                        };
+                        var this_1 = this;
+                        for (var mipmap = 0; mipmap < light.specularImages.length; mipmap++) {
+                            _loop_1(mipmap);
+                        }
+                        this._loader._parent._logClose();
+                        light._loaded = Promise.all(promises).then(function () {
+                            var size = Math.pow(2, imageData_1.length - 1);
+                            var babylonTexture = new BABYLON.RawCubeTexture(_this._loader._babylonScene, null, size);
+                            light._babylonTexture = babylonTexture;
+                            if (light.intensity != undefined) {
+                                babylonTexture.level = light.intensity;
+                            }
+                            if (light.rotation) {
+                                var rotation = BABYLON.Quaternion.FromArray(light.rotation);
+                                // Invert the rotation so that positive rotation is counter-clockwise.
+                                if (!_this._loader._babylonScene.useRightHandedSystem) {
+                                    rotation = BABYLON.Quaternion.Inverse(rotation);
+                                }
+                                BABYLON.Matrix.FromQuaternionToRef(rotation, babylonTexture.getReflectionTextureMatrix());
+                            }
+                            var sphericalHarmonics = BABYLON.SphericalHarmonics.FromArray(light.irradianceCoefficients);
+                            sphericalHarmonics.scale(light.intensity);
+                            sphericalHarmonics.convertIrradianceToLambertianRadiance();
+                            var sphericalPolynomial = BABYLON.SphericalPolynomial.FromHarmonics(sphericalHarmonics);
+                            return babylonTexture.updateRGBDAsync(imageData_1, sphericalPolynomial);
+                        });
+                    }
+                    return light._loaded.then(function () {
+                        return light._babylonTexture;
+                    });
+                };
+                Object.defineProperty(EXT_lights_imageBased.prototype, "_lights", {
+                    get: function () {
+                        var extensions = this._loader._gltf.extensions;
+                        if (!extensions || !extensions[this.name]) {
+                            throw new Error("#/extensions: '" + this.name + "' not found");
+                        }
+                        var extension = extensions[this.name];
+                        return extension.lights;
+                    },
+                    enumerable: true,
+                    configurable: true
+                });
+                return EXT_lights_imageBased;
+            }(GLTF2.GLTFLoaderExtension));
+            Extensions.EXT_lights_imageBased = EXT_lights_imageBased;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new EXT_lights_imageBased(loader); });
+        })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
+    })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
+})(BABYLON || (BABYLON = {}));

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


+ 34 - 6
dist/preview release/loaders/babylonjs.loaders.d.ts

@@ -1031,7 +1031,7 @@ declare module BABYLON.GLTF2 {
     }
     /** @hidden */
     interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
     /** @hidden */
     interface _ILoaderMaterial extends IMaterial, _IArrayItem {
@@ -1104,6 +1104,10 @@ declare module BABYLON.GLTF2 {
  */
 declare module BABYLON.GLTF2 {
     /** @hidden */
+    class _ArrayItem {
+        static Assign(values?: _IArrayItem[]): void;
+    }
+    /** @hidden */
     class GLTFLoader implements IGLTFLoader {
         _parent: GLTFFileLoader;
         _gltf: _ILoaderGLTF;
@@ -1179,9 +1183,10 @@ declare module BABYLON.GLTF2 {
         _createMaterial(name: string, drawMode: number): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): void;
-        _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void>;
+        _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Promise<void>;
+        _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Promise<void>;
         private _loadSampler(context, sampler);
-        private _loadImageAsync(context, image);
+        _loadImageAsync(context: string, image: _ILoaderImage): Promise<ArrayBufferView>;
         _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView>;
         private _onProgress();
         static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T;
@@ -1247,10 +1252,15 @@ declare module BABYLON.GLTF2 {
          */
         protected _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Override this method to modify the default behavior for loading texture infos.
+         * @hidden
+         */
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          */
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Override this method to modify the default behavior for loading uris.
          * @hidden
@@ -1292,10 +1302,15 @@ declare module BABYLON.GLTF2 {
          */
         static _LoadMaterialPropertiesAsync(loader: GLTFLoader, context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Helper method called by the loader to allow extensions to override loading texture infos.
+         * @hidden
+         */
+        static _LoadTextureInfoAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          */
-        static _LoadTextureAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        static _LoadTextureAsync(loader: GLTFLoader, context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Helper method called by the loader to allow extensions to override loading uris.
          * @hidden
@@ -1427,6 +1442,19 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_texture_transform extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
+    /**
+     * [Specification](TODO) (Experimental)
+     */
+    class EXT_lights_imageBased extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadSceneAsync(context: string, scene: _ILoaderScene): Nullable<Promise<void>>;
+        private _loadLightAsync(context, light);
+        private readonly _lights;
     }
 }

+ 176 - 51
dist/preview release/loaders/babylonjs.loaders.js

@@ -3879,6 +3879,7 @@ var BABYLON;
 (function (BABYLON) {
     var GLTF2;
     (function (GLTF2) {
+        /** @hidden */
         var _ArrayItem = /** @class */ (function () {
             function _ArrayItem() {
             }
@@ -3891,6 +3892,7 @@ var BABYLON;
             };
             return _ArrayItem;
         }());
+        GLTF2._ArrayItem = _ArrayItem;
         /** @hidden */
         var GLTFLoader = /** @class */ (function () {
             function GLTFLoader(parent) {
@@ -4143,9 +4145,9 @@ var BABYLON;
                 return rootNode;
             };
             GLTFLoader.prototype._loadSceneAsync = function (context, scene) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (scene.name || ""));
@@ -4246,9 +4248,9 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._loadNodeAsync = function (context, node) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (node._babylonMesh) {
                     throw new Error(context + ": Invalid recursive node hierarchy");
@@ -4339,9 +4341,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var attributes = primitive.attributes;
                 if (!attributes) {
@@ -4948,12 +4950,12 @@ var BABYLON;
                     babylonMaterial.metallic = properties.metallicFactor == undefined ? 1 : properties.metallicFactor;
                     babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
                     if (properties.baseColorTexture) {
-                        promises.push(this._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.metallicRoughnessTexture) {
-                        promises.push(this._loadTextureAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
                             babylonMaterial.metallicTexture = texture;
                         }));
                         babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
@@ -4965,9 +4967,9 @@ var BABYLON;
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadMaterialAsync = function (context, material, mesh, babylonMesh, babylonDrawMode, assign) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 material._babylonData = material._babylonData || {};
                 var babylonData = material._babylonData[babylonDrawMode];
@@ -4995,9 +4997,9 @@ var BABYLON;
                 return babylonData.loaded;
             };
             GLTFLoader.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 promises.push(this._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
@@ -5021,7 +5023,7 @@ var BABYLON;
                     babylonMaterial.twoSidedLighting = true;
                 }
                 if (material.normalTexture) {
-                    promises.push(this._loadTextureAsync(context + "/normalTexture", material.normalTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/normalTexture", material.normalTexture, function (texture) {
                         babylonMaterial.bumpTexture = texture;
                     }));
                     babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
@@ -5031,7 +5033,7 @@ var BABYLON;
                     }
                 }
                 if (material.occlusionTexture) {
-                    promises.push(this._loadTextureAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
                         babylonMaterial.ambientTexture = texture;
                     }));
                     babylonMaterial.useAmbientInGrayScale = true;
@@ -5040,7 +5042,7 @@ var BABYLON;
                     }
                 }
                 if (material.emissiveTexture) {
-                    promises.push(this._loadTextureAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
                         babylonMaterial.emissiveTexture = texture;
                     }));
                 }
@@ -5074,15 +5076,26 @@ var BABYLON;
                     }
                 }
             };
-            GLTFLoader.prototype._loadTextureAsync = function (context, textureInfo, assign) {
-                var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, textureInfo, assign);
-                if (promise) {
-                    return promise;
+            GLTFLoader.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureInfoAsync(this, context, textureInfo, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 this._parent._logOpen("" + context);
                 var texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
-                context = "#/textures/" + textureInfo.index;
+                var promise = this._loadTextureAsync("#/textures/" + textureInfo.index, texture, function (babylonTexture) {
+                    babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
+                    assign(babylonTexture);
+                });
+                this._parent._logClose();
+                return promise;
+            };
+            GLTFLoader.prototype._loadTextureAsync = function (context, texture, assign) {
+                var _this = this;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, texture, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
+                }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (texture.name || ""));
                 var sampler = (texture.sampler == undefined ? this._defaultSampler : GLTFLoader._GetProperty(context + "/sampler", this._gltf.samplers, texture.sampler));
@@ -5101,16 +5114,14 @@ var BABYLON;
                 babylonTexture.name = texture.name || "texture" + texture._index;
                 babylonTexture.wrapU = samplerData.wrapU;
                 babylonTexture.wrapV = samplerData.wrapV;
-                babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
                 var image = GLTFLoader._GetProperty(context + "/source", this._gltf.images, texture.source);
-                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (blob) {
+                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (data) {
                     var dataUrl = "data:" + _this._rootUrl + (image.uri || "image" + image._index);
-                    babylonTexture.updateURL(dataUrl, blob);
+                    babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                 }));
                 assign(babylonTexture);
                 this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
                 this._parent._logClose();
-                this._parent._logClose();
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
@@ -5126,28 +5137,24 @@ var BABYLON;
                 return sampler._data;
             };
             GLTFLoader.prototype._loadImageAsync = function (context, image) {
-                if (!image._blob) {
+                if (!image._data) {
                     this._parent._logOpen(context + " " + (image.name || ""));
-                    var promise = void 0;
                     if (image.uri) {
-                        promise = this._loadUriAsync(context, image.uri);
+                        image._data = this._loadUriAsync(context, image.uri);
                     }
                     else {
                         var bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, image.bufferView);
-                        promise = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
+                        image._data = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
                     }
-                    image._blob = promise.then(function (data) {
-                        return new Blob([data], { type: image.mimeType });
-                    });
                     this._parent._logClose();
                 }
-                return image._blob;
+                return image._data;
             };
             GLTFLoader.prototype._loadUriAsync = function (context, uri) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (!GLTFLoader._ValidateUri(uri)) {
                     throw new Error(context + ": Uri '" + uri + "' is invalid");
@@ -5432,10 +5439,15 @@ var BABYLON;
              */
             GLTFLoaderExtension.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) { return null; };
             /**
+             * Override this method to modify the default behavior for loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) { return null; };
+            /**
              * Override this method to modify the default behavior for loading textures.
              * @hidden
              */
-            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, textureInfo, assign) { return null; };
+            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, texture, assign) { return null; };
             /**
              * Override this method to modify the default behavior for loading uris.
              * @hidden
@@ -5524,11 +5536,18 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadMaterialPropertiesAsync(context, material, babylonMaterial); });
             };
             /**
+             * Helper method called by the loader to allow extensions to override loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension._LoadTextureInfoAsync = function (loader, context, textureInfo, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureInfoAsync(context, textureInfo, assign); });
+            };
+            /**
              * Helper method called by the loader to allow extensions to override loading textures.
              * @hidden
              */
-            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, textureInfo, assign) {
-                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, textureInfo, assign); });
+            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, texture, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, texture, assign); });
             };
             /**
              * Helper method called by the loader to allow extensions to override loading uris.
@@ -5997,12 +6016,12 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
@@ -6062,7 +6081,7 @@ var BABYLON;
                             babylonMaterial.albedoColor = BABYLON.Color3.White();
                         }
                         if (properties.baseColorTexture) {
-                            promises.push(this._loader._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                            promises.push(this._loader._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                                 babylonMaterial.albedoTexture = texture;
                             }));
                         }
@@ -6142,10 +6161,9 @@ var BABYLON;
                                 break;
                             }
                             case LightType.SPOT: {
-                                var spotLight = light;
                                 // TODO: support inner and outer cone angles
                                 //const innerConeAngle = spotLight.innerConeAngle || 0;
-                                var outerConeAngle = spotLight.outerConeAngle || Math.PI / 4;
+                                var outerConeAngle = light.spot && light.spot.outerConeAngle || Math.PI / 4;
                                 babylonLight = new BABYLON.SpotLight(name, BABYLON.Vector3.Zero(), BABYLON.Vector3.Forward(), outerConeAngle, 2, _this._loader._babylonScene);
                                 break;
                             }
@@ -6198,10 +6216,10 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_texture_transform.prototype._loadTextureAsync = function (context, textureInfo, assign) {
+                KHR_texture_transform.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, textureInfo, function (extensionContext, extension) {
-                        return _this._loader._loadTextureAsync(context, textureInfo, function (babylonTexture) {
+                        return _this._loader._loadTextureInfoAsync(context, textureInfo, function (babylonTexture) {
                             if (extension.offset) {
                                 babylonTexture.uOffset = extension.offset[0];
                                 babylonTexture.vOffset = extension.offset[1];
@@ -6231,6 +6249,113 @@ var BABYLON;
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
 
+
+
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2;
+    (function (GLTF2) {
+        var Extensions;
+        (function (Extensions) {
+            var NAME = "EXT_lights_imageBased";
+            /**
+             * [Specification](TODO) (Experimental)
+             */
+            var EXT_lights_imageBased = /** @class */ (function (_super) {
+                __extends(EXT_lights_imageBased, _super);
+                function EXT_lights_imageBased() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
+                }
+                EXT_lights_imageBased.prototype._loadSceneAsync = function (context, scene) {
+                    var _this = this;
+                    return this._loadExtensionAsync(context, scene, function (extensionContext, extension) {
+                        var promises = new Array();
+                        promises.push(_this._loader._loadSceneAsync(context, scene));
+                        _this._loader._parent._logOpen("" + extensionContext);
+                        var light = GLTF2.GLTFLoader._GetProperty(extensionContext + "/light", _this._lights, extension.light);
+                        promises.push(_this._loadLightAsync("#/extensions/" + _this.name + "/lights/" + extension.light, light).then(function (texture) {
+                            _this._loader._babylonScene.environmentTexture = texture;
+                        }));
+                        _this._loader._parent._logClose();
+                        return Promise.all(promises).then(function () { });
+                    });
+                };
+                EXT_lights_imageBased.prototype._loadLightAsync = function (context, light) {
+                    var _this = this;
+                    if (!light._loaded) {
+                        var promises = new Array();
+                        this._loader._parent._logOpen("" + context);
+                        var imageData_1 = new Array(light.specularImages.length);
+                        var _loop_1 = function (mipmap) {
+                            var faces = light.specularImages[mipmap];
+                            imageData_1[mipmap] = new Array(faces.length);
+                            var _loop_2 = function (face) {
+                                var specularImageContext = context + "/specularImages/" + mipmap + "/" + face;
+                                this_1._loader._parent._logOpen("" + specularImageContext);
+                                var index = faces[face];
+                                var image = GLTF2.GLTFLoader._GetProperty(specularImageContext, this_1._loader._gltf.images, index);
+                                promises.push(this_1._loader._loadImageAsync("#/images/" + index, image).then(function (data) {
+                                    imageData_1[mipmap][face] = data;
+                                }));
+                                this_1._loader._parent._logClose();
+                            };
+                            for (var face = 0; face < faces.length; face++) {
+                                _loop_2(face);
+                            }
+                        };
+                        var this_1 = this;
+                        for (var mipmap = 0; mipmap < light.specularImages.length; mipmap++) {
+                            _loop_1(mipmap);
+                        }
+                        this._loader._parent._logClose();
+                        light._loaded = Promise.all(promises).then(function () {
+                            var size = Math.pow(2, imageData_1.length - 1);
+                            var babylonTexture = new BABYLON.RawCubeTexture(_this._loader._babylonScene, null, size);
+                            light._babylonTexture = babylonTexture;
+                            if (light.intensity != undefined) {
+                                babylonTexture.level = light.intensity;
+                            }
+                            if (light.rotation) {
+                                var rotation = BABYLON.Quaternion.FromArray(light.rotation);
+                                // Invert the rotation so that positive rotation is counter-clockwise.
+                                if (!_this._loader._babylonScene.useRightHandedSystem) {
+                                    rotation = BABYLON.Quaternion.Inverse(rotation);
+                                }
+                                BABYLON.Matrix.FromQuaternionToRef(rotation, babylonTexture.getReflectionTextureMatrix());
+                            }
+                            var sphericalHarmonics = BABYLON.SphericalHarmonics.FromArray(light.irradianceCoefficients);
+                            sphericalHarmonics.scale(light.intensity);
+                            sphericalHarmonics.convertIrradianceToLambertianRadiance();
+                            var sphericalPolynomial = BABYLON.SphericalPolynomial.FromHarmonics(sphericalHarmonics);
+                            return babylonTexture.updateRGBDAsync(imageData_1, sphericalPolynomial);
+                        });
+                    }
+                    return light._loaded.then(function () {
+                        return light._babylonTexture;
+                    });
+                };
+                Object.defineProperty(EXT_lights_imageBased.prototype, "_lights", {
+                    get: function () {
+                        var extensions = this._loader._gltf.extensions;
+                        if (!extensions || !extensions[this.name]) {
+                            throw new Error("#/extensions: '" + this.name + "' not found");
+                        }
+                        var extension = extensions[this.name];
+                        return extension.lights;
+                    },
+                    enumerable: true,
+                    configurable: true
+                });
+                return EXT_lights_imageBased;
+            }(GLTF2.GLTFLoaderExtension));
+            Extensions.EXT_lights_imageBased = EXT_lights_imageBased;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new EXT_lights_imageBased(loader); });
+        })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
+    })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
+})(BABYLON || (BABYLON = {}));
+
     
 
     return BABYLON;

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


+ 34 - 6
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -1038,7 +1038,7 @@ declare module BABYLON.GLTF2 {
     }
     /** @hidden */
     interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
     /** @hidden */
     interface _ILoaderMaterial extends IMaterial, _IArrayItem {
@@ -1111,6 +1111,10 @@ declare module BABYLON.GLTF2 {
  */
 declare module BABYLON.GLTF2 {
     /** @hidden */
+    class _ArrayItem {
+        static Assign(values?: _IArrayItem[]): void;
+    }
+    /** @hidden */
     class GLTFLoader implements IGLTFLoader {
         _parent: GLTFFileLoader;
         _gltf: _ILoaderGLTF;
@@ -1186,9 +1190,10 @@ declare module BABYLON.GLTF2 {
         _createMaterial(name: string, drawMode: number): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): void;
-        _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void>;
+        _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Promise<void>;
+        _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Promise<void>;
         private _loadSampler(context, sampler);
-        private _loadImageAsync(context, image);
+        _loadImageAsync(context: string, image: _ILoaderImage): Promise<ArrayBufferView>;
         _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView>;
         private _onProgress();
         static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T;
@@ -1254,10 +1259,15 @@ declare module BABYLON.GLTF2 {
          */
         protected _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Override this method to modify the default behavior for loading texture infos.
+         * @hidden
+         */
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          */
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Override this method to modify the default behavior for loading uris.
          * @hidden
@@ -1299,10 +1309,15 @@ declare module BABYLON.GLTF2 {
          */
         static _LoadMaterialPropertiesAsync(loader: GLTFLoader, context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Helper method called by the loader to allow extensions to override loading texture infos.
+         * @hidden
+         */
+        static _LoadTextureInfoAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          */
-        static _LoadTextureAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        static _LoadTextureAsync(loader: GLTFLoader, context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Helper method called by the loader to allow extensions to override loading uris.
          * @hidden
@@ -1434,6 +1449,19 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_texture_transform extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
+    /**
+     * [Specification](TODO) (Experimental)
+     */
+    class EXT_lights_imageBased extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadSceneAsync(context: string, scene: _ILoaderScene): Nullable<Promise<void>>;
+        private _loadLightAsync(context, light);
+        private readonly _lights;
     }
 }

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

@@ -560,7 +560,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         private _getGLTFTextureSampler(texture);
         private _getGLTFTextureWrapMode(wrapMode);
         private _getGLTFTextureWrapModesSampler(texture);
@@ -575,7 +575,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -804,7 +804,7 @@ declare module BABYLON.GLTF2 {
          * @param name name of the buffer view
          * @returns bufferView for glTF
          */
-        static CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
+        static _CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex The index of the bufferview referenced by this accessor
@@ -817,7 +817,7 @@ declare module BABYLON.GLTF2 {
          * @param max Maximum value of each component in this attribute
          * @returns accessor for glTF
          */
-        static CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
+        static _CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
         /**
          * Calculates the minimum and maximum values of an array of position floats
          * @param positions Positions array of a mesh
@@ -825,7 +825,7 @@ declare module BABYLON.GLTF2 {
          * @param vertexCount Number of vertices to check for min and max values
          * @returns min number array and max number array
          */
-        static CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
+        static _CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
             min: number[];
             max: number[];
         };
@@ -834,52 +834,53 @@ declare module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedPositionVector3(vector: Vector3): Vector3;
+        static _GetRightHandedPositionVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedPositionVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedPositionVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedPositionArray3FromRef(vector: number[]): void;
+        static _GetRightHandedPositionArray3FromRef(vector: number[]): void;
         /**
          * Converts a new right-handed Vector3
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedNormalVector3(vector: Vector3): Vector3;
+        static _GetRightHandedNormalVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedNormalVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedNormalVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedNormalArray3FromRef(vector: number[]): void;
+        static _GetRightHandedNormalArray3FromRef(vector: number[]): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedVector4FromRef(vector: Vector4): void;
+        static _GetRightHandedVector4FromRef(vector: Vector4): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedArray4FromRef(vector: number[]): void;
+        static _GetRightHandedArray4FromRef(vector: number[]): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
+        static _GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _NormalizeTangentFromRef(tangent: Vector4): void;
     }
 }

+ 100 - 76
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -367,12 +367,26 @@ var BABYLON;
                     var vertex = vertices_1[_i];
                     if (this._convertToRightHandedSystem && !(vertexAttributeKind === BABYLON.VertexBuffer.ColorKind) && !(vertex instanceof BABYLON.Vector2)) {
                         if (vertex instanceof BABYLON.Vector3) {
-                            (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertex) : GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertex);
+                            if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertex);
+                            }
+                            else if (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertex);
+                            }
+                            else {
+                                BABYLON.Tools.Error('Unsupported vertex attribute kind!');
+                            }
                         }
                         else {
-                            GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertex);
+                            GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertex);
                         }
                     }
+                    if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                        vertex.normalize();
+                    }
+                    else if (vertexAttributeKind === BABYLON.VertexBuffer.TangentKind && vertex instanceof BABYLON.Vector4) {
+                        GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertex);
+                    }
                     for (var _a = 0, _b = vertex.asArray(); _a < _b.length; _a++) {
                         var component = _b[_a];
                         binaryWriter.setFloat32(component, byteOffset);
@@ -398,7 +412,7 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertexData);
                             }
                             vertexAttributes.push(vertexData.asArray());
                         }
@@ -409,8 +423,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertexData);
                             }
+                            vertexData.normalize();
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -420,8 +435,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector4.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertexData);
                             }
+                            GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertexData);
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -513,7 +529,7 @@ var BABYLON;
                             if (image.uri) {
                                 imageData = _this._imageData[image.uri];
                                 imageName = image.uri.split('.')[0] + " image";
-                                bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
+                                bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                                 byteOffset += imageData.data.buffer.byteLength;
                                 _this._bufferViews.push(bufferView);
                                 image.bufferView = _this._bufferViews.length - 1;
@@ -663,7 +679,7 @@ var BABYLON;
              */
             _Exporter.prototype.setNodeTransformation = function (node, babylonTransformNode) {
                 if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
                 }
                 if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
                     node.scale = babylonTransformNode.scaling.asArray();
@@ -674,7 +690,7 @@ var BABYLON;
                 }
                 if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
                     if (this._convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                     }
                     node.rotation = rotationQuaternion.normalize().asArray();
                 }
@@ -702,7 +718,7 @@ var BABYLON;
                     var vertexData = bufferMesh.getVerticesData(kind);
                     if (vertexData) {
                         var byteLength = vertexData.length * 4;
-                        var bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
+                        var bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                         this._bufferViews.push(bufferView);
                         this.writeAttributeData(kind, vertexData, byteStride, binaryWriter);
                     }
@@ -843,7 +859,7 @@ var BABYLON;
                         var indices = bufferMesh.getIndices();
                         if (indices) {
                             var byteLength = indices.length * 4;
-                            bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
+                            bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
                             this._bufferViews.push(bufferView);
                             indexBufferViewIndex = this._bufferViews.length - 1;
                             for (var k = 0, length_7 = indices.length; k < length_7; ++k) {
@@ -902,9 +918,9 @@ var BABYLON;
                                         if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                             minMax = { min: null, max: null };
                                             if (attributeKind == BABYLON.VertexBuffer.PositionKind) {
-                                                minMax = GLTF2._GLTFUtilities.CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
+                                                minMax = GLTF2._GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
                                             }
-                                            var accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
+                                            var accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
                                             this._accessors.push(accessor);
                                             this.setAttributeKind(meshPrimitive, attributeKind);
                                             if (meshPrimitive.attributes.TEXCOORD_0 != null || meshPrimitive.attributes.TEXCOORD_1 != null) {
@@ -916,7 +932,7 @@ var BABYLON;
                             }
                             if (indexBufferViewIndex) {
                                 // Create accessor
-                                var accessor = GLTF2._GLTFUtilities.CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
+                                var accessor = GLTF2._GLTFUtilities._CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
                                 this._accessors.push(accessor);
                                 meshPrimitive.indices = this._accessors.length - 1;
                             }
@@ -1970,7 +1986,7 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._gonvertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var promises = [];
                 var metallicRoughness = {
                     baseColor: babylonPBRMaterial.albedoColor,
@@ -2102,39 +2118,41 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughness = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var _this = this;
-                var samplers = this._exporter._samplers;
-                var textures = this._exporter._textures;
-                var specGloss = {
-                    diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
-                    specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
-                    glossiness: babylonPBRMaterial.microSurface || 1,
-                };
-                var samplerIndex = null;
-                var sampler = this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
-                if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
-                    samplers.push(sampler);
-                    samplerIndex = samplers.length - 1;
-                }
-                if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
-                    return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
-                }
-                return this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
-                    if (hasTextureCoords) {
-                        if (metallicRoughnessFactors.baseColorTextureBase64) {
-                            var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFBaseColorTexture) {
-                                glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                return Promise.resolve().then(function () {
+                    var samplers = _this._exporter._samplers;
+                    var textures = _this._exporter._textures;
+                    var specGloss = {
+                        diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
+                        specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
+                        glossiness: babylonPBRMaterial.microSurface || 1,
+                    };
+                    var samplerIndex = null;
+                    var sampler = _this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
+                    if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
+                        samplers.push(sampler);
+                        samplerIndex = samplers.length - 1;
+                    }
+                    if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
+                        return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
+                    }
+                    if ((babylonPBRMaterial.albedoTexture || babylonPBRMaterial.reflectivityTexture) && hasTextureCoords) {
+                        return _this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
+                            if (metallicRoughnessFactors.baseColorTextureBase64) {
+                                var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFBaseColorTexture) {
+                                    glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                                }
                             }
-                        }
-                        if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
-                            var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFMRColorTexture) {
-                                glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                            if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
+                                var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFMRColorTexture) {
+                                    glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                                }
                             }
-                        }
-                        return metallicRoughnessFactors;
+                            return metallicRoughnessFactors;
+                        });
                     }
                     else {
                         return _this._convertSpecularGlossinessToMetallicRoughness(specGloss);
@@ -2167,12 +2185,12 @@ var BABYLON;
                             babylonPBRMaterial.alpha
                         ];
                     }
-                    return this._gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
                 else {
-                    return this._convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
@@ -2598,26 +2616,26 @@ var BABYLON;
                     var nodeIndex = nodeMap[babylonTransformNode.uniqueId];
                     // Creates buffer view and accessor for key frames.
                     var byteLength = animationData.inputs.length * 4;
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
                     bufferViews.push(bufferView);
                     animationData.inputs.forEach(function (input) {
                         binaryWriter.setFloat32(input);
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
                     accessors.push(accessor);
                     keyframeAccessorIndex = accessors.length - 1;
                     // create bufferview and accessor for keyed values.
                     outputLength = animationData.outputs.length;
                     byteLength = dataAccessorType === "VEC3" /* VEC3 */ ? animationData.outputs.length * 12 : animationData.outputs.length * 16;
                     // check for in and out tangents
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
                     bufferViews.push(bufferView);
                     animationData.outputs.forEach(function (output) {
                         output.forEach(function (entry) {
                             binaryWriter.setFloat32(entry);
                         });
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
                     accessors.push(accessor);
                     dataAccessorIndex = accessors.length - 1;
                     // create sampler
@@ -2757,7 +2775,7 @@ var BABYLON;
                             BABYLON.Quaternion.RotationYawPitchRollToRef(cacheValue.y, cacheValue.x, cacheValue.z, quaternionCache);
                         }
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(quaternionCache);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(quaternionCache);
                             if (!babylonTransformNode.parent) {
                                 quaternionCache = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(quaternionCache);
                             }
@@ -2767,7 +2785,7 @@ var BABYLON;
                     else {
                         cacheValue = value;
                         if (convertToRightHandedSystem && (animationChannelTargetPath !== "scale" /* SCALE */)) {
-                            GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(cacheValue);
+                            GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(cacheValue);
                             if (!babylonTransformNode.parent) {
                                 cacheValue.x *= -1;
                                 cacheValue.z *= -1;
@@ -2822,7 +2840,7 @@ var BABYLON;
                         if (babylonTransformNode.rotationQuaternion) {
                             basePositionRotationOrScale = babylonTransformNode.rotationQuaternion.asArray();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     basePositionRotationOrScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(basePositionRotationOrScale)).asArray();
                                 }
@@ -2834,13 +2852,13 @@ var BABYLON;
                     }
                     else {
                         basePositionRotationOrScale = babylonTransformNode.rotation.asArray();
-                        GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                     basePositionRotationOrScale = babylonTransformNode.position.asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else { // scale
@@ -2868,7 +2886,7 @@ var BABYLON;
                         var array = BABYLON.Vector3.FromArray(value);
                         var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z);
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                             if (!babylonTransformNode.parent) {
                                 rotationQuaternion = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(rotationQuaternion);
                             }
@@ -2877,7 +2895,7 @@ var BABYLON;
                     }
                     else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(value);
+                            GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(value);
                             if (!babylonTransformNode.parent) {
                                 value[0] *= -1;
                                 value[2] *= -1;
@@ -2892,7 +2910,7 @@ var BABYLON;
                         if (animationChannelTargetPath === "rotation" /* ROTATION */) {
                             var posRotScale = useQuaternion ? newPositionRotationOrScale : BABYLON.Quaternion.RotationYawPitchRoll(newPositionRotationOrScale.y, newPositionRotationOrScale.x, newPositionRotationOrScale.z).normalize();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(posRotScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(posRotScale);
                                 if (!babylonTransformNode.parent) {
                                     posRotScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(posRotScale);
                                 }
@@ -2901,7 +2919,7 @@ var BABYLON;
                         }
                         else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     newPositionRotationOrScale.x *= -1;
                                     newPositionRotationOrScale.z *= -1;
@@ -2914,7 +2932,7 @@ var BABYLON;
                 else if (animationType === BABYLON.Animation.ANIMATIONTYPE_QUATERNION) {
                     value = keyFrame.value.normalize().asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(value);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(value);
                         if (!babylonTransformNode.parent) {
                             value = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(value)).asArray();
                         }
@@ -3002,7 +3020,7 @@ var BABYLON;
                                 tangent = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z).asArray();
                             }
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(tangent);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(tangent);
                                 if (!babylonTransformNode.parent) {
                                     tangent = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(tangent)).asArray();
                                 }
@@ -3017,7 +3035,7 @@ var BABYLON;
                             tangent = tangentValue.scale(frameDelta).asArray();
                             if (convertToRightHandedSystem) {
                                 if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
-                                    GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(tangent);
+                                    GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(tangent);
                                     if (!babylonTransformNode.parent) {
                                         tangent[0] *= -1; // x
                                         tangent[2] *= -1; // z
@@ -3074,7 +3092,7 @@ var BABYLON;
              * @param name name of the buffer view
              * @returns bufferView for glTF
              */
-            _GLTFUtilities.CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
+            _GLTFUtilities._CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
                 var bufferview = { buffer: bufferIndex, byteLength: byteLength };
                 if (byteOffset) {
                     bufferview.byteOffset = byteOffset;
@@ -3099,7 +3117,7 @@ var BABYLON;
              * @param max Maximum value of each component in this attribute
              * @returns accessor for glTF
              */
-            _GLTFUtilities.CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
+            _GLTFUtilities._CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
                 var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
                 if (min != null) {
                     accessor.min = min;
@@ -3119,7 +3137,7 @@ var BABYLON;
              * @param vertexCount Number of vertices to check for min and max values
              * @returns min number array and max number array
              */
-            _GLTFUtilities.CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
+            _GLTFUtilities._CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
                 var min = [Infinity, Infinity, Infinity];
                 var max = [-Infinity, -Infinity, -Infinity];
                 var positionStrideSize = 3;
@@ -3131,7 +3149,7 @@ var BABYLON;
                         indexOffset = positionStrideSize * i;
                         position = BABYLON.Vector3.FromArray(positions, indexOffset);
                         if (convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedPositionVector3FromRef(position);
+                            _GLTFUtilities._GetRightHandedPositionVector3FromRef(position);
                         }
                         vector = position.asArray();
                         for (var j = 0; j < positionStrideSize; ++j) {
@@ -3153,21 +3171,21 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedPositionVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
@@ -3175,28 +3193,28 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedNormalVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedVector4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedVector4FromRef = function (vector) {
                 vector.z *= -1;
                 vector.w *= -1;
             };
@@ -3204,7 +3222,7 @@ var BABYLON;
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedArray4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedArray4FromRef = function (vector) {
                 vector[2] *= -1;
                 vector[3] *= -1;
             };
@@ -3212,7 +3230,7 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionFromRef = function (quaternion) {
                 quaternion.x *= -1;
                 quaternion.y *= -1;
             };
@@ -3220,10 +3238,16 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionArrayFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionArrayFromRef = function (quaternion) {
                 quaternion[0] *= -1;
                 quaternion[1] *= -1;
             };
+            _GLTFUtilities._NormalizeTangentFromRef = function (tangent) {
+                var length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z + tangent.z);
+                tangent.x /= length;
+                tangent.y /= length;
+                tangent.z /= length;
+            };
             return _GLTFUtilities;
         }());
         GLTF2._GLTFUtilities = _GLTFUtilities;

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


+ 16 - 15
dist/preview release/serializers/babylonjs.serializers.d.ts

@@ -568,7 +568,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         private _getGLTFTextureSampler(texture);
         private _getGLTFTextureWrapMode(wrapMode);
         private _getGLTFTextureWrapModesSampler(texture);
@@ -583,7 +583,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -812,7 +812,7 @@ declare module BABYLON.GLTF2 {
          * @param name name of the buffer view
          * @returns bufferView for glTF
          */
-        static CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
+        static _CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex The index of the bufferview referenced by this accessor
@@ -825,7 +825,7 @@ declare module BABYLON.GLTF2 {
          * @param max Maximum value of each component in this attribute
          * @returns accessor for glTF
          */
-        static CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
+        static _CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
         /**
          * Calculates the minimum and maximum values of an array of position floats
          * @param positions Positions array of a mesh
@@ -833,7 +833,7 @@ declare module BABYLON.GLTF2 {
          * @param vertexCount Number of vertices to check for min and max values
          * @returns min number array and max number array
          */
-        static CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
+        static _CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
             min: number[];
             max: number[];
         };
@@ -842,52 +842,53 @@ declare module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedPositionVector3(vector: Vector3): Vector3;
+        static _GetRightHandedPositionVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedPositionVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedPositionVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedPositionArray3FromRef(vector: number[]): void;
+        static _GetRightHandedPositionArray3FromRef(vector: number[]): void;
         /**
          * Converts a new right-handed Vector3
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedNormalVector3(vector: Vector3): Vector3;
+        static _GetRightHandedNormalVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedNormalVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedNormalVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedNormalArray3FromRef(vector: number[]): void;
+        static _GetRightHandedNormalArray3FromRef(vector: number[]): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedVector4FromRef(vector: Vector4): void;
+        static _GetRightHandedVector4FromRef(vector: Vector4): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedArray4FromRef(vector: number[]): void;
+        static _GetRightHandedArray4FromRef(vector: number[]): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
+        static _GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _NormalizeTangentFromRef(tangent: Vector4): void;
     }
 }

+ 100 - 76
dist/preview release/serializers/babylonjs.serializers.js

@@ -517,12 +517,26 @@ var BABYLON;
                     var vertex = vertices_1[_i];
                     if (this._convertToRightHandedSystem && !(vertexAttributeKind === BABYLON.VertexBuffer.ColorKind) && !(vertex instanceof BABYLON.Vector2)) {
                         if (vertex instanceof BABYLON.Vector3) {
-                            (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertex) : GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertex);
+                            if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertex);
+                            }
+                            else if (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertex);
+                            }
+                            else {
+                                BABYLON.Tools.Error('Unsupported vertex attribute kind!');
+                            }
                         }
                         else {
-                            GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertex);
+                            GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertex);
                         }
                     }
+                    if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                        vertex.normalize();
+                    }
+                    else if (vertexAttributeKind === BABYLON.VertexBuffer.TangentKind && vertex instanceof BABYLON.Vector4) {
+                        GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertex);
+                    }
                     for (var _a = 0, _b = vertex.asArray(); _a < _b.length; _a++) {
                         var component = _b[_a];
                         binaryWriter.setFloat32(component, byteOffset);
@@ -548,7 +562,7 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertexData);
                             }
                             vertexAttributes.push(vertexData.asArray());
                         }
@@ -559,8 +573,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertexData);
                             }
+                            vertexData.normalize();
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -570,8 +585,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector4.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertexData);
                             }
+                            GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertexData);
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -663,7 +679,7 @@ var BABYLON;
                             if (image.uri) {
                                 imageData = _this._imageData[image.uri];
                                 imageName = image.uri.split('.')[0] + " image";
-                                bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
+                                bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                                 byteOffset += imageData.data.buffer.byteLength;
                                 _this._bufferViews.push(bufferView);
                                 image.bufferView = _this._bufferViews.length - 1;
@@ -813,7 +829,7 @@ var BABYLON;
              */
             _Exporter.prototype.setNodeTransformation = function (node, babylonTransformNode) {
                 if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
                 }
                 if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
                     node.scale = babylonTransformNode.scaling.asArray();
@@ -824,7 +840,7 @@ var BABYLON;
                 }
                 if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
                     if (this._convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                     }
                     node.rotation = rotationQuaternion.normalize().asArray();
                 }
@@ -852,7 +868,7 @@ var BABYLON;
                     var vertexData = bufferMesh.getVerticesData(kind);
                     if (vertexData) {
                         var byteLength = vertexData.length * 4;
-                        var bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
+                        var bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                         this._bufferViews.push(bufferView);
                         this.writeAttributeData(kind, vertexData, byteStride, binaryWriter);
                     }
@@ -993,7 +1009,7 @@ var BABYLON;
                         var indices = bufferMesh.getIndices();
                         if (indices) {
                             var byteLength = indices.length * 4;
-                            bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
+                            bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
                             this._bufferViews.push(bufferView);
                             indexBufferViewIndex = this._bufferViews.length - 1;
                             for (var k = 0, length_7 = indices.length; k < length_7; ++k) {
@@ -1052,9 +1068,9 @@ var BABYLON;
                                         if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                             minMax = { min: null, max: null };
                                             if (attributeKind == BABYLON.VertexBuffer.PositionKind) {
-                                                minMax = GLTF2._GLTFUtilities.CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
+                                                minMax = GLTF2._GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
                                             }
-                                            var accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
+                                            var accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
                                             this._accessors.push(accessor);
                                             this.setAttributeKind(meshPrimitive, attributeKind);
                                             if (meshPrimitive.attributes.TEXCOORD_0 != null || meshPrimitive.attributes.TEXCOORD_1 != null) {
@@ -1066,7 +1082,7 @@ var BABYLON;
                             }
                             if (indexBufferViewIndex) {
                                 // Create accessor
-                                var accessor = GLTF2._GLTFUtilities.CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
+                                var accessor = GLTF2._GLTFUtilities._CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
                                 this._accessors.push(accessor);
                                 meshPrimitive.indices = this._accessors.length - 1;
                             }
@@ -2120,7 +2136,7 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._gonvertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var promises = [];
                 var metallicRoughness = {
                     baseColor: babylonPBRMaterial.albedoColor,
@@ -2252,39 +2268,41 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughness = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var _this = this;
-                var samplers = this._exporter._samplers;
-                var textures = this._exporter._textures;
-                var specGloss = {
-                    diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
-                    specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
-                    glossiness: babylonPBRMaterial.microSurface || 1,
-                };
-                var samplerIndex = null;
-                var sampler = this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
-                if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
-                    samplers.push(sampler);
-                    samplerIndex = samplers.length - 1;
-                }
-                if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
-                    return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
-                }
-                return this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
-                    if (hasTextureCoords) {
-                        if (metallicRoughnessFactors.baseColorTextureBase64) {
-                            var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFBaseColorTexture) {
-                                glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                return Promise.resolve().then(function () {
+                    var samplers = _this._exporter._samplers;
+                    var textures = _this._exporter._textures;
+                    var specGloss = {
+                        diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
+                        specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
+                        glossiness: babylonPBRMaterial.microSurface || 1,
+                    };
+                    var samplerIndex = null;
+                    var sampler = _this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
+                    if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
+                        samplers.push(sampler);
+                        samplerIndex = samplers.length - 1;
+                    }
+                    if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
+                        return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
+                    }
+                    if ((babylonPBRMaterial.albedoTexture || babylonPBRMaterial.reflectivityTexture) && hasTextureCoords) {
+                        return _this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
+                            if (metallicRoughnessFactors.baseColorTextureBase64) {
+                                var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFBaseColorTexture) {
+                                    glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                                }
                             }
-                        }
-                        if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
-                            var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFMRColorTexture) {
-                                glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                            if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
+                                var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFMRColorTexture) {
+                                    glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                                }
                             }
-                        }
-                        return metallicRoughnessFactors;
+                            return metallicRoughnessFactors;
+                        });
                     }
                     else {
                         return _this._convertSpecularGlossinessToMetallicRoughness(specGloss);
@@ -2317,12 +2335,12 @@ var BABYLON;
                             babylonPBRMaterial.alpha
                         ];
                     }
-                    return this._gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
                 else {
-                    return this._convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
@@ -2748,26 +2766,26 @@ var BABYLON;
                     var nodeIndex = nodeMap[babylonTransformNode.uniqueId];
                     // Creates buffer view and accessor for key frames.
                     var byteLength = animationData.inputs.length * 4;
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
                     bufferViews.push(bufferView);
                     animationData.inputs.forEach(function (input) {
                         binaryWriter.setFloat32(input);
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
                     accessors.push(accessor);
                     keyframeAccessorIndex = accessors.length - 1;
                     // create bufferview and accessor for keyed values.
                     outputLength = animationData.outputs.length;
                     byteLength = dataAccessorType === "VEC3" /* VEC3 */ ? animationData.outputs.length * 12 : animationData.outputs.length * 16;
                     // check for in and out tangents
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
                     bufferViews.push(bufferView);
                     animationData.outputs.forEach(function (output) {
                         output.forEach(function (entry) {
                             binaryWriter.setFloat32(entry);
                         });
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
                     accessors.push(accessor);
                     dataAccessorIndex = accessors.length - 1;
                     // create sampler
@@ -2907,7 +2925,7 @@ var BABYLON;
                             BABYLON.Quaternion.RotationYawPitchRollToRef(cacheValue.y, cacheValue.x, cacheValue.z, quaternionCache);
                         }
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(quaternionCache);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(quaternionCache);
                             if (!babylonTransformNode.parent) {
                                 quaternionCache = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(quaternionCache);
                             }
@@ -2917,7 +2935,7 @@ var BABYLON;
                     else {
                         cacheValue = value;
                         if (convertToRightHandedSystem && (animationChannelTargetPath !== "scale" /* SCALE */)) {
-                            GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(cacheValue);
+                            GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(cacheValue);
                             if (!babylonTransformNode.parent) {
                                 cacheValue.x *= -1;
                                 cacheValue.z *= -1;
@@ -2972,7 +2990,7 @@ var BABYLON;
                         if (babylonTransformNode.rotationQuaternion) {
                             basePositionRotationOrScale = babylonTransformNode.rotationQuaternion.asArray();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     basePositionRotationOrScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(basePositionRotationOrScale)).asArray();
                                 }
@@ -2984,13 +3002,13 @@ var BABYLON;
                     }
                     else {
                         basePositionRotationOrScale = babylonTransformNode.rotation.asArray();
-                        GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                     basePositionRotationOrScale = babylonTransformNode.position.asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else { // scale
@@ -3018,7 +3036,7 @@ var BABYLON;
                         var array = BABYLON.Vector3.FromArray(value);
                         var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z);
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                             if (!babylonTransformNode.parent) {
                                 rotationQuaternion = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(rotationQuaternion);
                             }
@@ -3027,7 +3045,7 @@ var BABYLON;
                     }
                     else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(value);
+                            GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(value);
                             if (!babylonTransformNode.parent) {
                                 value[0] *= -1;
                                 value[2] *= -1;
@@ -3042,7 +3060,7 @@ var BABYLON;
                         if (animationChannelTargetPath === "rotation" /* ROTATION */) {
                             var posRotScale = useQuaternion ? newPositionRotationOrScale : BABYLON.Quaternion.RotationYawPitchRoll(newPositionRotationOrScale.y, newPositionRotationOrScale.x, newPositionRotationOrScale.z).normalize();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(posRotScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(posRotScale);
                                 if (!babylonTransformNode.parent) {
                                     posRotScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(posRotScale);
                                 }
@@ -3051,7 +3069,7 @@ var BABYLON;
                         }
                         else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     newPositionRotationOrScale.x *= -1;
                                     newPositionRotationOrScale.z *= -1;
@@ -3064,7 +3082,7 @@ var BABYLON;
                 else if (animationType === BABYLON.Animation.ANIMATIONTYPE_QUATERNION) {
                     value = keyFrame.value.normalize().asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(value);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(value);
                         if (!babylonTransformNode.parent) {
                             value = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(value)).asArray();
                         }
@@ -3152,7 +3170,7 @@ var BABYLON;
                                 tangent = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z).asArray();
                             }
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(tangent);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(tangent);
                                 if (!babylonTransformNode.parent) {
                                     tangent = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(tangent)).asArray();
                                 }
@@ -3167,7 +3185,7 @@ var BABYLON;
                             tangent = tangentValue.scale(frameDelta).asArray();
                             if (convertToRightHandedSystem) {
                                 if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
-                                    GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(tangent);
+                                    GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(tangent);
                                     if (!babylonTransformNode.parent) {
                                         tangent[0] *= -1; // x
                                         tangent[2] *= -1; // z
@@ -3224,7 +3242,7 @@ var BABYLON;
              * @param name name of the buffer view
              * @returns bufferView for glTF
              */
-            _GLTFUtilities.CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
+            _GLTFUtilities._CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
                 var bufferview = { buffer: bufferIndex, byteLength: byteLength };
                 if (byteOffset) {
                     bufferview.byteOffset = byteOffset;
@@ -3249,7 +3267,7 @@ var BABYLON;
              * @param max Maximum value of each component in this attribute
              * @returns accessor for glTF
              */
-            _GLTFUtilities.CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
+            _GLTFUtilities._CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
                 var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
                 if (min != null) {
                     accessor.min = min;
@@ -3269,7 +3287,7 @@ var BABYLON;
              * @param vertexCount Number of vertices to check for min and max values
              * @returns min number array and max number array
              */
-            _GLTFUtilities.CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
+            _GLTFUtilities._CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
                 var min = [Infinity, Infinity, Infinity];
                 var max = [-Infinity, -Infinity, -Infinity];
                 var positionStrideSize = 3;
@@ -3281,7 +3299,7 @@ var BABYLON;
                         indexOffset = positionStrideSize * i;
                         position = BABYLON.Vector3.FromArray(positions, indexOffset);
                         if (convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedPositionVector3FromRef(position);
+                            _GLTFUtilities._GetRightHandedPositionVector3FromRef(position);
                         }
                         vector = position.asArray();
                         for (var j = 0; j < positionStrideSize; ++j) {
@@ -3303,21 +3321,21 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedPositionVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
@@ -3325,28 +3343,28 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedNormalVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedVector4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedVector4FromRef = function (vector) {
                 vector.z *= -1;
                 vector.w *= -1;
             };
@@ -3354,7 +3372,7 @@ var BABYLON;
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedArray4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedArray4FromRef = function (vector) {
                 vector[2] *= -1;
                 vector[3] *= -1;
             };
@@ -3362,7 +3380,7 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionFromRef = function (quaternion) {
                 quaternion.x *= -1;
                 quaternion.y *= -1;
             };
@@ -3370,10 +3388,16 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionArrayFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionArrayFromRef = function (quaternion) {
                 quaternion[0] *= -1;
                 quaternion[1] *= -1;
             };
+            _GLTFUtilities._NormalizeTangentFromRef = function (tangent) {
+                var length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z + tangent.z);
+                tangent.x /= length;
+                tangent.y /= length;
+                tangent.z /= length;
+            };
             return _GLTFUtilities;
         }());
         GLTF2._GLTFUtilities = _GLTFUtilities;

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


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

@@ -575,7 +575,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         private _getGLTFTextureSampler(texture);
         private _getGLTFTextureWrapMode(wrapMode);
         private _getGLTFTextureWrapModesSampler(texture);
@@ -590,7 +590,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -819,7 +819,7 @@ declare module BABYLON.GLTF2 {
          * @param name name of the buffer view
          * @returns bufferView for glTF
          */
-        static CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
+        static _CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex The index of the bufferview referenced by this accessor
@@ -832,7 +832,7 @@ declare module BABYLON.GLTF2 {
          * @param max Maximum value of each component in this attribute
          * @returns accessor for glTF
          */
-        static CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
+        static _CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
         /**
          * Calculates the minimum and maximum values of an array of position floats
          * @param positions Positions array of a mesh
@@ -840,7 +840,7 @@ declare module BABYLON.GLTF2 {
          * @param vertexCount Number of vertices to check for min and max values
          * @returns min number array and max number array
          */
-        static CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
+        static _CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
             min: number[];
             max: number[];
         };
@@ -849,52 +849,53 @@ declare module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedPositionVector3(vector: Vector3): Vector3;
+        static _GetRightHandedPositionVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedPositionVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedPositionVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedPositionArray3FromRef(vector: number[]): void;
+        static _GetRightHandedPositionArray3FromRef(vector: number[]): void;
         /**
          * Converts a new right-handed Vector3
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedNormalVector3(vector: Vector3): Vector3;
+        static _GetRightHandedNormalVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedNormalVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedNormalVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedNormalArray3FromRef(vector: number[]): void;
+        static _GetRightHandedNormalArray3FromRef(vector: number[]): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedVector4FromRef(vector: Vector4): void;
+        static _GetRightHandedVector4FromRef(vector: Vector4): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedArray4FromRef(vector: number[]): void;
+        static _GetRightHandedArray4FromRef(vector: number[]): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
+        static _GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _NormalizeTangentFromRef(tangent: Vector4): void;
     }
 }

+ 36 - 477
dist/preview release/typedocValidationBaseline.json

@@ -1,195 +1,64 @@
 {
-  "errors": 4318,
+  "errors": 4233,
   "babylon.typedoc.json": {
-    "errors": 4318,
-    "Animatable": {
-      "Class": {
-        "Comments": {
-          "MissingText": true
-        }
-      },
-      "Constructor": {
-        "new Animatable": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "scene": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "target": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "fromFrame": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "toFrame": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "loopAnimation": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "speedRatio": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "onAnimationEnd": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "animations": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      },
+    "errors": 4233,
+    "AbstractScene": {
       "Property": {
-        "animationStarted": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "fromFrame": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "loopAnimation": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "onAnimationEnd": {
-          "Comments": {
-            "MissingText": true
+        "effectLayers": {
+          "Naming": {
+            "NotPascalCase": true
           }
         },
-        "target": {
-          "Comments": {
-            "MissingText": true
+        "layers": {
+          "Naming": {
+            "NotPascalCase": true
           }
         },
-        "toFrame": {
-          "Comments": {
-            "MissingText": true
+        "lensFlareSystems": {
+          "Naming": {
+            "NotPascalCase": true
           }
         }
       },
       "Method": {
-        "appendAnimations": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "target": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "animations": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "disableBlending": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "enableBlending": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "blendingSpeed": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getAnimationByTargetProperty": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "property": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
+        "addEffectLayer": {
+          "Naming": {
+            "NotPascalCase": true
           }
         },
-        "getAnimations": {
-          "Comments": {
-            "MissingText": true
+        "addLensFlareSystem": {
+          "Naming": {
+            "NotPascalCase": true
           }
         },
-        "getRuntimeAnimationByTargetProperty": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "property": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
+        "getGlowLayerByName": {
+          "Naming": {
+            "NotPascalCase": true
           }
         },
-        "goToFrame": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "frame": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
+        "getHighlightLayerByName": {
+          "Naming": {
+            "NotPascalCase": true
           }
         },
-        "pause": {
-          "Comments": {
-            "MissingText": true
+        "getLensFlareSystemByID": {
+          "Naming": {
+            "NotPascalCase": true
           }
         },
-        "reset": {
-          "Comments": {
-            "MissingText": true
+        "getLensFlareSystemByName": {
+          "Naming": {
+            "NotPascalCase": true
           }
         },
-        "restart": {
-          "Comments": {
-            "MissingText": true
+        "removeEffectLayer": {
+          "Naming": {
+            "NotPascalCase": true
           }
         },
-        "stop": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "animationName": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
+        "removeLensFlareSystem": {
+          "Naming": {
+            "NotPascalCase": true
           }
         }
       }
@@ -6434,9 +6303,6 @@
     "GPUParticleSystem": {
       "Method": {
         "addColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          },
           "Parameter": {
             "color1": {
               "Comments": {
@@ -6444,21 +6310,6 @@
               }
             }
           }
-        },
-        "addSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
         }
       }
     },
@@ -8600,23 +8451,6 @@
             }
           }
         },
-        "render": {
-          "Comments": {
-            "MissingReturn": true
-          },
-          "Parameter": {
-            "subMesh": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "enableAlphaMode": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
         "setMaterialByID": {
           "Comments": {
             "MissingReturn": true
@@ -10923,31 +10757,11 @@
             "MissingReturn": true
           }
         },
-        "addLifeTimeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "addSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
         "removeColorGradient": {
           "Comments": {
             "MissingReturn": true
           }
         },
-        "removeLifeTimeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
         "_GetEffectCreationOptions": {
           "Comments": {
             "MissingText": true
@@ -16819,243 +16633,6 @@
         }
       }
     },
-    "SphericalHarmonics": {
-      "Class": {
-        "Comments": {
-          "MissingText": true
-        }
-      },
-      "Property": {
-        "L00": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L10": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L11": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L1_1": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L20": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L21": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L22": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L2_1": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L2_2": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        }
-      },
-      "Method": {
-        "addLight": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "direction": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "color": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "deltaSolidAngle": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "convertIncidentRadianceToIrradiance": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "convertIrradianceToLambertianRadiance": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "scale": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "scale": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getsphericalHarmonicsFromPolynomial": {
-          "Naming": {
-            "NotPascalCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "polynomial": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      }
-    },
-    "SphericalPolynomial": {
-      "Class": {
-        "Comments": {
-          "MissingText": true
-        }
-      },
-      "Property": {
-        "x": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "xx": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "xy": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "y": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "yy": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "yz": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "z": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "zx": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "zz": {
-          "Comments": {
-            "MissingText": true
-          }
-        }
-      },
-      "Method": {
-        "addAmbient": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "color": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "scale": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "scale": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getSphericalPolynomialFromHarmonics": {
-          "Naming": {
-            "NotPascalCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "harmonics": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      }
-    },
     "Sprite": {
       "Class": {
         "Comments": {
@@ -23010,9 +22587,6 @@
     "IParticleSystem": {
       "Method": {
         "addColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          },
           "Parameter": {
             "color1": {
               "Comments": {
@@ -23020,21 +22594,6 @@
               }
             }
           }
-        },
-        "addSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
         }
       }
     },

+ 28 - 22
dist/preview release/viewer/babylon.viewer.d.ts

@@ -19,6 +19,7 @@ declare module BabylonViewer {
     
     
     
+    
     /**
         * BabylonJS Viewer
         *
@@ -947,6 +948,28 @@ declare module BabylonViewer {
 }
 
 declare module BabylonViewer {
+    
+    export interface IViewerTemplatePlugin {
+        readonly templateName: string;
+        readonly eventsToAttach?: Array<string>;
+        interactionPredicate(event: EventCallback): boolean;
+        onEvent?(event: EventCallback): void;
+        addHTMLTemplate?(template: Template): void;
+    }
+    export abstract class AbstractViewerNavbarButton implements IViewerTemplatePlugin {
+        readonly templateName: string;
+        readonly eventsToAttach: Array<string>;
+        protected _prepend: boolean;
+        protected abstract _buttonClass: string;
+        protected abstract _htmlTemplate: string;
+        interactionPredicate(event: EventCallback): boolean;
+        abstract onEvent(event: EventCallback): void;
+        addHTMLTemplate(template: Template): void;
+        protected _generateHTMLElement(template: Template): Element | DocumentFragment;
+    }
+}
+
+declare module BabylonViewer {
     /**
         * Will attach an init function the the DOMContentLoaded event.
         * The init function will be removed automatically after the event was triggered.
@@ -1189,6 +1212,7 @@ declare module BabylonViewer {
             updateParams(params: {
                     [key: string]: string | number | boolean | object;
             }, append?: boolean): void;
+            redraw(): void;
             /**
                 * Get the template'S configuration
                 */
@@ -1230,28 +1254,6 @@ declare module BabylonViewer {
 
 declare module BabylonViewer {
     
-    export interface IViewerTemplatePlugin {
-        readonly templateName: string;
-        readonly eventsToAttach?: Array<string>;
-        interactionPredicate(event: EventCallback): boolean;
-        onEvent?(event: EventCallback): void;
-        addHTMLTemplate?(template: Template): void;
-    }
-    export abstract class AbstractViewerNavbarButton implements IViewerTemplatePlugin {
-        readonly templateName: string;
-        readonly eventsToAttach: Array<string>;
-        protected _prepend: boolean;
-        protected abstract _buttonClass: string;
-        protected abstract _htmlTemplate: string;
-        interactionPredicate(event: EventCallback): boolean;
-        abstract onEvent(event: EventCallback): void;
-        addHTMLTemplate(template: Template): void;
-        protected _generateHTMLElement(template: Template): Element | DocumentFragment;
-    }
-}
-
-declare module BabylonViewer {
-    
     
     
     
@@ -1508,6 +1510,10 @@ declare module BabylonViewer {
                 */
             onViewerInitDoneObservable: BABYLON.Observable<any>;
             /**
+                * Will notify when the viewer init started (after configuration was loaded)
+                */
+            onViewerInitStartedObservable: BABYLON.Observable<any>;
+            /**
                 * Functions added to this observable will be executed on each frame rendered.
                 */
             onFrameRenderedObservable: BABYLON.Observable<any>;

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


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


+ 46 - 40
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -19,6 +19,7 @@ declare module 'babylonjs-viewer' {
     import { ViewerModel, ModelState } from 'babylonjs-viewer/model/viewerModel';
     import { AnimationPlayMode, AnimationState } from 'babylonjs-viewer/model/modelAnimation';
     import { ILoaderPlugin } from 'babylonjs-viewer/loader/plugins/loaderPlugin';
+    import { AbstractViewerNavbarButton } from 'babylonjs-viewer/templating/viewerTemplatePlugin';
     /**
         * BabylonJS Viewer
         *
@@ -34,7 +35,7 @@ declare module 'babylonjs-viewer' {
         */
     function disposeAll(): void;
     const Version: string;
-    export { BABYLON, Version, InitTags, DefaultViewer, AbstractViewer, viewerGlobals, telemetryManager, disableInit, viewerManager, mapperManager, disposeAll, ModelLoader, ViewerModel, AnimationPlayMode, AnimationState, ModelState, ILoaderPlugin };
+    export { BABYLON, Version, InitTags, DefaultViewer, AbstractViewer, viewerGlobals, telemetryManager, disableInit, viewerManager, mapperManager, disposeAll, ModelLoader, ViewerModel, AnimationPlayMode, AnimationState, ModelState, ILoaderPlugin, AbstractViewerNavbarButton };
     export * from 'babylonjs-viewer/configuration';
 }
 
@@ -515,11 +516,11 @@ declare module 'babylonjs-viewer/managers/telemetryManager' {
 }
 
 declare module 'babylonjs-viewer/loader/modelLoader' {
-    import { IModelConfiguration } from "babylonjs-viewer/configuration/interfaces/modelConfiguration";
-    import { ViewerModel } from "babylonjs-viewer/model/viewerModel";
+    import { ConfigurationContainer } from 'babylonjs-viewer/configuration/configurationContainer';
+    import { IModelConfiguration } from 'babylonjs-viewer/configuration/interfaces/modelConfiguration';
+    import { ObservablesManager } from 'babylonjs-viewer/managers/observablesManager';
+    import { ViewerModel } from 'babylonjs-viewer/model/viewerModel';
     import { ILoaderPlugin } from 'babylonjs-viewer/loader/plugins';
-    import { ObservablesManager } from "babylonjs-viewer/managers/observablesManager";
-    import { ConfigurationContainer } from "babylonjs-viewer/configuration/configurationContainer";
     /**
         * An instance of the class is in charge of loading the model correctly.
         * This class will continously be expended with tasks required from the specific loaders Babylon has.
@@ -718,7 +719,7 @@ declare module 'babylonjs-viewer/model/viewerModel' {
 }
 
 declare module 'babylonjs-viewer/model/modelAnimation' {
-    import { AnimationGroup, Vector3 } from "babylonjs";
+    import { AnimationGroup, Vector3 } from 'babylonjs';
     /**
         * Animation play mode enum - is the animation looping or playing once
         */
@@ -946,6 +947,28 @@ declare module 'babylonjs-viewer/loader/plugins/loaderPlugin' {
     }
 }
 
+declare module 'babylonjs-viewer/templating/viewerTemplatePlugin' {
+    import { EventCallback, Template } from "babylonjs-viewer/templating/templateManager";
+    export interface IViewerTemplatePlugin {
+        readonly templateName: string;
+        readonly eventsToAttach?: Array<string>;
+        interactionPredicate(event: EventCallback): boolean;
+        onEvent?(event: EventCallback): void;
+        addHTMLTemplate?(template: Template): void;
+    }
+    export abstract class AbstractViewerNavbarButton implements IViewerTemplatePlugin {
+        readonly templateName: string;
+        readonly eventsToAttach: Array<string>;
+        protected _prepend: boolean;
+        protected abstract _buttonClass: string;
+        protected abstract _htmlTemplate: string;
+        interactionPredicate(event: EventCallback): boolean;
+        abstract onEvent(event: EventCallback): void;
+        addHTMLTemplate(template: Template): void;
+        protected _generateHTMLElement(template: Template): Element | DocumentFragment;
+    }
+}
+
 declare module 'babylonjs-viewer/initializer' {
     /**
         * Will attach an init function the the DOMContentLoaded event.
@@ -967,7 +990,7 @@ declare module 'babylonjs-viewer/configuration' {
 
 declare module 'babylonjs-viewer/configuration/configuration' {
     import { EngineOptions } from 'babylonjs';
-    import { IVRConfiguration, IObserversConfiguration, IModelConfiguration, ISceneConfiguration, ISceneOptimizerConfiguration, ICameraConfiguration, ISkyboxConfiguration, IGroundConfiguration, ILightConfiguration, IDefaultRenderingPipelineConfiguration, ITemplateConfiguration } from 'babylonjs-viewer/configuration/interfaces';
+    import { ICameraConfiguration, IDefaultRenderingPipelineConfiguration, IGroundConfiguration, ILightConfiguration, IModelConfiguration, IObserversConfiguration, ISceneConfiguration, ISceneOptimizerConfiguration, ISkyboxConfiguration, ITemplateConfiguration, IVRConfiguration } from 'babylonjs-viewer/configuration/interfaces';
     export function getConfigurationKey(key: string, configObject: any): any;
     export interface ViewerConfiguration {
             version?: string;
@@ -1189,6 +1212,7 @@ declare module 'babylonjs-viewer/templating/templateManager' {
             updateParams(params: {
                     [key: string]: string | number | boolean | object;
             }, append?: boolean): void;
+            redraw(): void;
             /**
                 * Get the template'S configuration
                 */
@@ -1228,28 +1252,6 @@ declare module 'babylonjs-viewer/templating/templateManager' {
     }
 }
 
-declare module 'babylonjs-viewer/templating/viewerTemplatePlugin' {
-    import { EventCallback, Template } from "babylonjs-viewer/templating/templateManager";
-    export interface IViewerTemplatePlugin {
-        readonly templateName: string;
-        readonly eventsToAttach?: Array<string>;
-        interactionPredicate(event: EventCallback): boolean;
-        onEvent?(event: EventCallback): void;
-        addHTMLTemplate?(template: Template): void;
-    }
-    export abstract class AbstractViewerNavbarButton implements IViewerTemplatePlugin {
-        readonly templateName: string;
-        readonly eventsToAttach: Array<string>;
-        protected _prepend: boolean;
-        protected abstract _buttonClass: string;
-        protected abstract _htmlTemplate: string;
-        interactionPredicate(event: EventCallback): boolean;
-        abstract onEvent(event: EventCallback): void;
-        addHTMLTemplate(template: Template): void;
-        protected _generateHTMLElement(template: Template): Element | DocumentFragment;
-    }
-}
-
 declare module 'babylonjs-viewer/managers/sceneManager' {
     import { Scene, ArcRotateCamera, Engine, Light, SceneOptimizer, EnvironmentHelper, Color3, Observable, DefaultRenderingPipeline, Nullable, VRExperienceHelper } from 'babylonjs';
     import { ILightConfiguration, ISceneConfiguration, ISceneOptimizerConfiguration, ICameraConfiguration, ISkyboxConfiguration, ViewerConfiguration, IGroundConfiguration, IModelConfiguration, IVRConfiguration } from 'babylonjs-viewer/configuration';
@@ -1463,8 +1465,8 @@ declare module 'babylonjs-viewer/configuration/loader' {
 }
 
 declare module 'babylonjs-viewer/managers/observablesManager' {
-    import { Observable, Scene, Engine, SceneLoaderProgressEvent, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs";
-    import { ViewerModel } from "babylonjs-viewer/model/viewerModel";
+    import { Engine, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Observable, Scene, SceneLoaderProgressEvent } from 'babylonjs';
+    import { ViewerModel } from 'babylonjs-viewer/model/viewerModel';
     export class ObservablesManager {
             /**
                 * Will notify when the scene was initialized
@@ -1508,6 +1510,10 @@ declare module 'babylonjs-viewer/managers/observablesManager' {
                 */
             onViewerInitDoneObservable: Observable<any>;
             /**
+                * Will notify when the viewer init started (after configuration was loaded)
+                */
+            onViewerInitStartedObservable: Observable<any>;
+            /**
                 * Functions added to this observable will be executed on each frame rendered.
                 */
             onFrameRenderedObservable: Observable<any>;
@@ -1761,10 +1767,10 @@ declare module 'babylonjs-viewer/loader/plugins/telemetryLoaderPlugin' {
 }
 
 declare module 'babylonjs-viewer/loader/plugins/msftLodLoaderPlugin' {
-    import { ILoaderPlugin } from "babylonjs-viewer/loader/plugins/loaderPlugin";
-    import { ViewerModel } from "babylonjs-viewer/model/viewerModel";
-    import { ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs";
-    import { IGLTFLoaderExtension } from "babylonjs-loaders";
+    import { ISceneLoaderPlugin, ISceneLoaderPluginAsync } from 'babylonjs';
+    import { IGLTFLoaderExtension } from 'babylonjs-loaders';
+    import { ViewerModel } from 'babylonjs-viewer/model/viewerModel';
+    import { ILoaderPlugin } from 'babylonjs-viewer/loader/plugins/loaderPlugin';
     /**
       * A loder plugin to use MSFT_lod extension correctly (glTF)
       */
@@ -1775,9 +1781,9 @@ declare module 'babylonjs-viewer/loader/plugins/msftLodLoaderPlugin' {
 }
 
 declare module 'babylonjs-viewer/loader/plugins/applyMaterialConfig' {
-    import { ILoaderPlugin } from "babylonjs-viewer/loader/plugins/loaderPlugin";
-    import { ViewerModel } from "babylonjs-viewer/model/viewerModel";
-    import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, Material } from "babylonjs";
+    import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, Material } from 'babylonjs';
+    import { ViewerModel } from 'babylonjs-viewer/model/viewerModel';
+    import { ILoaderPlugin } from 'babylonjs-viewer/loader/plugins/loaderPlugin';
     /**
       * Force-apply material configuration right after a material was loaded.
       */
@@ -1788,8 +1794,8 @@ declare module 'babylonjs-viewer/loader/plugins/applyMaterialConfig' {
 }
 
 declare module 'babylonjs-viewer/loader/plugins/extendedMaterialLoaderPlugin' {
-    import { ILoaderPlugin } from "babylonjs-viewer/loader/plugins/loaderPlugin";
-    import { Material } from "babylonjs";
+    import { Material } from 'babylonjs';
+    import { ILoaderPlugin } from 'babylonjs-viewer/loader/plugins/loaderPlugin';
     /**
       * A (PBR) material will be extended using this function.
       * This function will hold extra default configuration for the viewer, if not implemented in Babylon itself.

+ 12 - 4
dist/preview release/what's new.md

@@ -7,7 +7,8 @@
 - New GUI 3D controls toolset. [Complete doc + demos](http://doc.babylonjs.com/how_to/gui3d) ([Deltakosh](https://github.com/deltakosh))
 - Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
 - New GUI control: the [Grid](http://doc.babylonjs.com/how_to/gui#grid) ([Deltakosh](https://github.com/deltakosh))
-- Gizmo and GizmoManager classes used to manipulate meshes in a scene. Gizmo types include: position, scale, rotation and bounding box ([TrevorDev](https://github.com/TrevorDev))
+- Gizmo and GizmoManager classes used to manipulate meshes in a scene. Gizmo types include: position, scale, rotation and bounding box. [Doc](http://doc.babylonjs.com/how_to/gizmo) ([TrevorDev](https://github.com/TrevorDev))
+- New behaviors: PointerDragBehavior, SixDofDragBehavior and MultiPointerScaleBehavior to enable smooth drag and drop/scaling with mouse or 6dof controller on a mesh. [Doc](http://doc.babylonjs.com/how_to/meshbehavior) ([TrevorDev](https://github.com/TrevorDev))
 - Particle system improvements ([Deltakosh](https://github.com/deltakosh))
   - Added a ParticleHelper class to create some pre-configured particle systems in a one-liner method style. [Doc](https://doc.babylonjs.com/How_To/ParticleHelper) ([Deltakosh](https://github.com/deltakosh)) / ([DevChris](https://github.com/yovanoc))
   - Improved CPU particles rendering performance (up to x2 on low end devices)
@@ -22,6 +23,8 @@
   - Added support for `minInitialRotation` and `maxInitialRotation`. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
   - Added support for size gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#size)
   - Added support for life time gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#lifetime)
+  - Added support for angular speed gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
+- Added SceneComponent to help decoupling Scene from its components ([sebavan](http://www.github.com/sebavan))
 
 ## Updates
 
@@ -40,7 +43,6 @@
 - Get a root mesh from an asset container, load a mesh from a file with a single string url ([TrevorDev](https://github.com/TrevorDev))
 - UtilityLayer class to render another scene as a layer on top of an existing scene ([TrevorDev](https://github.com/TrevorDev))
 - AnimationGroup has now onAnimationGroupEnd observable ([RaananW](https://github.com/RaananW))
-- New behaviors: PointerDragBehavior, SixDofDragBehavior and MultiPointerScaleBehavior to enable smooth drag and drop/scaling with mouse or 6dof controller on a mesh ([TrevorDev](https://github.com/TrevorDev))
 - New `serialize` and `Parse` functions to serialize and parse all procedural textures from the Procedural Textures Library ([julien-moreau](https://github.com/julien-moreau))
 - Added a new `mesh.ignoreNonUniformScaling` to turn off non uniform scaling compensation ([Deltakosh](https://github.com/deltakosh))
 - AssetsManager tasks will only run when their state is INIT. It is now possible to remove a task from the assets manager ([RaananW](https://github.com/RaananW))
@@ -57,9 +59,11 @@
 - `Sound` now accepts `MediaStream` as source to enable easier WebAudio and WebRTC integrations ([menduz](https://github.com/menduz))
 - Vector x, y and z constructor parameters are now optional and default to 0 ([TrevorDev](https://github.com/TrevorDev))
 - New vertical mode for sliders in 2D GUI. [Demo](https://www.babylonjs-playground.com/#U9AC0N#53) ([Saket Saurabh](https://github.com/ssaket))
-- Added and remove camera methods in the default pipeline ([TrevorDev](https://github.com/TrevorDev))
+- Added and removed camera methods in the default pipeline ([TrevorDev](https://github.com/TrevorDev))
 - Added internal texture `format` support for RenderTargetCubeTexture ([PeapBoy](https://github.com/NicolasBuecher))
 - Added canvas toBlob polyfill in tools ([sebavan](http://www.github.com/sebavan))
+- Added `RawCubeTexture` class with RGBD and mipmap support ([bghgary](http://www.github.com/bghgary))
+- Added effect layer per rendering group addressing [Issue 4463](https://github.com/BabylonJS/Babylon.js/issues/4463) ([sebavan](http://www.github.com/sebavan))
 
 ### glTF Loader
 
@@ -67,6 +71,7 @@
 - Added `onNodeLODsLoadedObservable` and `onMaterialLODsLoadedObservable` to MSFT_lod loader extension ([bghgary](http://www.github.com/bghgary))
 - Added glTF loader settings to the GLTF tab in the debug layer ([bghgary](http://www.github.com/bghgary))
 - Added debug logging and performance counters ([bghgary](http://www.github.com/bghgary))
+- Added support for EXT_lights_imageBased ([bghgary](http://www.github.com/bghgary))
 
 ### Viewer
 
@@ -81,6 +86,7 @@
 - An initial support for WebVR is implemented ([RaananW](https://github.com/RaananW))
 - It is now possible to choose the element that goes fullscreen in the default viewer ([RaananW](https://github.com/RaananW))
 - The default viewer has a plugin system with which new buttons can be added externally ([RaananW](https://github.com/RaananW))
+- The extended configuration is now the default when not providing the "extended" parameter ([RaananW](https://github.com/RaananW))
 
 ### Documentation
 
@@ -95,8 +101,9 @@
 - VR experience helper's controllers will not fire pointer events when laser's are disabled, instead the camera ray pointer event will be used ([TrevorDev](https://github.com/TrevorDev))
 - Node's setParent(node.parent) will no longer throw an exception when parent is undefined and will behave the same as setParent(null) ([TrevorDev](https://github.com/TrevorDev))
 - Mesh.MergeMeshes flips triangles on meshes with negative scaling ([SvenFrankson](http://svenfrankson.com))
-- Avoid firing button events multiple times when calling vrController.attachMesh() ([TrevorDev]
+- Avoid firing button events multiple times when calling vrController.attachMesh() ([TrevorDev](https://github.com/TrevorDev))
 - Parse geometry when load binary mesh ([SinhNQ](https://github.com/quocsinh))
+- Removing observers during observable notify should not skip over valid observers ([TrevorDev](https://github.com/TrevorDev))
 
 ### Core Engine
 
@@ -125,6 +132,7 @@
 - Removed element IDs from viewer's templates to allow muitiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
 - Viewer is not using Engine.LastCreatedScene anymore, to support multiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
 - Template location was ignored if html was defined ([RaananW](https://github.com/RaananW))
+- Drag and Drop only worked if a model was already loaded before ([RaananW](https://github.com/RaananW))
 
 ### Loaders
 

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

@@ -75,7 +75,7 @@ module BABYLON.GUI {
 
             this.onIsCheckedChangedObservable.notifyObservers(value);
 
-            if (this._isChecked) {
+            if (this._isChecked && this._host) {
                 // Update all controls from same group
                 this._host.executeOnAllControls((control) => {
                     if (control === this) {

+ 122 - 0
loaders/src/glTF/2.0/Extensions/EXT_lights_imageBased.ts

@@ -0,0 +1,122 @@
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GLTF2.Extensions {
+    const NAME = "EXT_lights_imageBased";
+
+    interface ILightReference {
+        light: number;
+    }
+
+    interface ILight extends IChildRootProperty {
+        intensity: number;
+        rotation: number[];
+        specularImages: number[][];
+        irradianceCoefficients: number[][];
+
+        _babylonTexture?: BaseTexture;
+        _loaded?: Promise<void>;
+    }
+
+    interface ILights {
+        lights: ILight[];
+    }
+
+    /**
+     * [Specification](TODO) (Experimental)
+     */
+    export class EXT_lights_imageBased extends GLTFLoaderExtension {
+        public readonly name = NAME;
+
+        protected _loadSceneAsync(context: string, scene: _ILoaderScene): Nullable<Promise<void>> { 
+            return this._loadExtensionAsync<ILightReference>(context, scene, (extensionContext, extension) => {
+                const promises = new Array<Promise<void>>();
+
+                promises.push(this._loader._loadSceneAsync(context, scene));
+
+                this._loader._parent._logOpen(`${extensionContext}`);
+
+                const light = GLTFLoader._GetProperty(`${extensionContext}/light`, this._lights, extension.light);
+                promises.push(this._loadLightAsync(`#/extensions/${this.name}/lights/${extension.light}`, light).then(texture => {
+                    this._loader._babylonScene.environmentTexture = texture;
+                }));
+
+                this._loader._parent._logClose();
+
+                return Promise.all(promises).then(() => {});
+            });
+        }
+
+        private _loadLightAsync(context: string, light: ILight): Promise<BaseTexture> {
+            if (!light._loaded) {
+                const promises = new Array<Promise<void>>();
+
+                this._loader._parent._logOpen(`${context}`);
+
+                const imageData = new Array<Array<ArrayBufferView>>(light.specularImages.length);
+                for (let mipmap = 0; mipmap < light.specularImages.length; mipmap++) {
+                    const faces = light.specularImages[mipmap];
+                    imageData[mipmap] = new Array<ArrayBufferView>(faces.length);
+                    for (let face = 0; face < faces.length; face++) {
+                        const specularImageContext = `${context}/specularImages/${mipmap}/${face}`;
+                        this._loader._parent._logOpen(`${specularImageContext}`);
+
+                        const index = faces[face];
+                        const image = GLTFLoader._GetProperty(specularImageContext, this._loader._gltf.images, index);
+                        promises.push(this._loader._loadImageAsync(`#/images/${index}`, image).then(data => {
+                            imageData[mipmap][face] = data;
+                        }));
+
+                        this._loader._parent._logClose();
+                    }
+                }
+
+                this._loader._parent._logClose();
+
+                light._loaded = Promise.all(promises).then(() => {
+                    const size = Math.pow(2, imageData.length - 1);
+                    const babylonTexture = new RawCubeTexture(this._loader._babylonScene, null, size);
+                    light._babylonTexture = babylonTexture;
+
+                    if (light.intensity != undefined) {
+                        babylonTexture.level = light.intensity;
+                    }
+
+                    if (light.rotation) {
+                        let rotation = Quaternion.FromArray(light.rotation);
+
+                        // Invert the rotation so that positive rotation is counter-clockwise.
+                        if (!this._loader._babylonScene.useRightHandedSystem) {
+                            rotation = Quaternion.Inverse(rotation);
+                        }
+
+                        Matrix.FromQuaternionToRef(rotation, babylonTexture.getReflectionTextureMatrix());
+                    }
+
+                    const sphericalHarmonics = SphericalHarmonics.FromArray(light.irradianceCoefficients);
+                    sphericalHarmonics.scale(light.intensity);
+
+                    sphericalHarmonics.convertIrradianceToLambertianRadiance();
+                    const sphericalPolynomial = SphericalPolynomial.FromHarmonics(sphericalHarmonics);
+
+                    return babylonTexture.updateRGBDAsync(imageData, sphericalPolynomial);
+                });
+            }
+
+            return light._loaded.then(() => {
+                return light._babylonTexture!;
+            });
+        }
+
+        private get _lights(): Array<ILight> {
+            const extensions = this._loader._gltf.extensions;
+            if (!extensions || !extensions[this.name]) {
+                throw new Error(`#/extensions: '${this.name}' not found`);
+            }
+
+            const extension = extensions[this.name] as ILights;
+            return extension.lights;
+        }
+    }
+
+    GLTFLoader._Register(NAME, loader => new EXT_lights_imageBased(loader));
+}

+ 5 - 7
loaders/src/glTF/2.0/Extensions/KHR_lights.ts

@@ -18,11 +18,10 @@ module BABYLON.GLTF2.Extensions {
         type: LightType;
         color?: number[];
         intensity?: number;
-    }
-
-    interface ISpotLight extends ILight {
-        innerConeAngle?: number;
-        outerConeAngle?: number;
+        spot?: {
+            innerConeAngle?: number;
+            outerConeAngle?: number;
+        };
     }
 
     interface ILights {
@@ -71,10 +70,9 @@ module BABYLON.GLTF2.Extensions {
                         break;
                     }
                     case LightType.SPOT: {
-                        const spotLight = light as ISpotLight;
                         // TODO: support inner and outer cone angles
                         //const innerConeAngle = spotLight.innerConeAngle || 0;
-                        const outerConeAngle = spotLight.outerConeAngle || Math.PI / 4;
+                        const outerConeAngle = light.spot && light.spot.outerConeAngle || Math.PI / 4;
                         babylonLight = new SpotLight(name, Vector3.Zero(), Vector3.Forward(), outerConeAngle, 2, this._loader._babylonScene);
                         break;
                     }

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

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

+ 1 - 1
loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts

@@ -35,7 +35,7 @@ module BABYLON.GLTF2.Extensions {
                 }
 
                 if (properties.baseColorTexture) {
-                    promises.push(this._loader._loadTextureAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
+                    promises.push(this._loader._loadTextureInfoAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
                         babylonMaterial.albedoTexture = texture;
                     }));
                 }

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

@@ -16,9 +16,9 @@ module BABYLON.GLTF2.Extensions {
     export class KHR_texture_transform extends GLTFLoaderExtension {
         public readonly name = NAME;
 
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>> {
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>> {
             return this._loadExtensionAsync<IKHRTextureTransform>(context, textureInfo, (extensionContext, extension) => {
-                return this._loader._loadTextureAsync(context, textureInfo, babylonTexture => {
+                return this._loader._loadTextureInfoAsync(context, textureInfo, babylonTexture => {
                     if (extension.offset) {
                         babylonTexture.uOffset = extension.offset[0];
                         babylonTexture.vOffset = extension.offset[1];

+ 51 - 43
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -10,7 +10,8 @@ module BABYLON.GLTF2 {
         _total?: number;
     }
 
-    class _ArrayItem {
+    /** @hidden */
+    export class _ArrayItem {
         public static Assign(values?: _IArrayItem[]): void {
             if (values) {
                 for (let index = 0; index < values.length; index++) {
@@ -320,9 +321,9 @@ module BABYLON.GLTF2 {
         }
 
         public _loadSceneAsync(context: string, scene: _ILoaderScene): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             const promises = new Array<Promise<void>>();
@@ -436,9 +437,9 @@ module BABYLON.GLTF2 {
         }
 
         public _loadNodeAsync(context: string, node: _ILoaderNode): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadNodeAsync(this, context, node);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadNodeAsync(this, context, node);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             if (node._babylonMesh) {
@@ -549,9 +550,9 @@ module BABYLON.GLTF2 {
         }
 
         private _loadVertexDataAsync(context: string, primitive: _ILoaderMeshPrimitive, babylonMesh: Mesh): Promise<Geometry> {
-            const promise = GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             const attributes = primitive.attributes;
@@ -1254,13 +1255,13 @@ module BABYLON.GLTF2 {
                 babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
 
                 if (properties.baseColorTexture) {
-                    promises.push(this._loadTextureAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
+                    promises.push(this._loadTextureInfoAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
                         babylonMaterial.albedoTexture = texture;
                     }));
                 }
 
                 if (properties.metallicRoughnessTexture) {
-                    promises.push(this._loadTextureAsync(`${context}/metallicRoughnessTexture`, properties.metallicRoughnessTexture, texture => {
+                    promises.push(this._loadTextureInfoAsync(`${context}/metallicRoughnessTexture`, properties.metallicRoughnessTexture, texture => {
                         babylonMaterial.metallicTexture = texture;
                     }));
 
@@ -1276,9 +1277,9 @@ module BABYLON.GLTF2 {
         }
 
         public _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             material._babylonData = material._babylonData || {};
@@ -1315,9 +1316,9 @@ module BABYLON.GLTF2 {
         }
 
         public _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             const promises = new Array<Promise<void>>();
@@ -1346,7 +1347,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.normalTexture) {
-                promises.push(this._loadTextureAsync(`${context}/normalTexture`, material.normalTexture, texture => {
+                promises.push(this._loadTextureInfoAsync(`${context}/normalTexture`, material.normalTexture, texture => {
                     babylonMaterial.bumpTexture = texture;
                 }));
 
@@ -1358,7 +1359,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.occlusionTexture) {
-                promises.push(this._loadTextureAsync(`${context}/occlusionTexture`, material.occlusionTexture, texture => {
+                promises.push(this._loadTextureInfoAsync(`${context}/occlusionTexture`, material.occlusionTexture, texture => {
                     babylonMaterial.ambientTexture = texture;
                 }));
 
@@ -1369,7 +1370,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.emissiveTexture) {
-                promises.push(this._loadTextureAsync(`${context}/emissiveTexture`, material.emissiveTexture, texture => {
+                promises.push(this._loadTextureInfoAsync(`${context}/emissiveTexture`, material.emissiveTexture, texture => {
                     babylonMaterial.emissiveTexture = texture;
                 }));
             }
@@ -1406,16 +1407,30 @@ module BABYLON.GLTF2 {
             }
         }
 
-        public _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadTextureAsync(this, context, textureInfo, assign);
-            if (promise) {
-                return promise;
+        public _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Promise<void> {
+            const extensionPromise = GLTFLoaderExtension._LoadTextureInfoAsync(this, context, textureInfo, assign);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             this._parent._logOpen(`${context}`);
 
             const texture = GLTFLoader._GetProperty(`${context}/index`, this._gltf.textures, textureInfo.index);
-            context = `#/textures/${textureInfo.index}`;
+            const promise = this._loadTextureAsync(`#/textures/${textureInfo.index}`, texture, babylonTexture => {
+                babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
+                assign(babylonTexture);
+            });
+
+            this._parent._logClose();
+
+            return promise;
+        }
+
+        public _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Promise<void> {
+            const extensionPromise = GLTFLoaderExtension._LoadTextureAsync(this, context, texture, assign);
+            if (extensionPromise) {
+                return extensionPromise;
+            }
 
             const promises = new Array<Promise<void>>();
 
@@ -1439,19 +1454,17 @@ module BABYLON.GLTF2 {
             babylonTexture.name = texture.name || `texture${texture._index}`;
             babylonTexture.wrapU = samplerData.wrapU;
             babylonTexture.wrapV = samplerData.wrapV;
-            babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
 
             const image = GLTFLoader._GetProperty(`${context}/source`, this._gltf.images, texture.source);
-            promises.push(this._loadImageAsync(`#/images/${image._index}`, image).then(blob => {
+            promises.push(this._loadImageAsync(`#/images/${image._index}`, image).then(data => {
                 const dataUrl = `data:${this._rootUrl}${image.uri || `image${image._index}`}`;
-                babylonTexture.updateURL(dataUrl, blob);
+                babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
             }));
 
             assign(babylonTexture);
             this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
 
             this._parent._logClose();
-            this._parent._logClose();
 
             return Promise.all(promises).then(() => {});
         }
@@ -1469,33 +1482,28 @@ module BABYLON.GLTF2 {
             return sampler._data;
         }
 
-        private _loadImageAsync(context: string, image: _ILoaderImage): Promise<Blob> {
-            if (!image._blob) {
+        public _loadImageAsync(context: string, image: _ILoaderImage): Promise<ArrayBufferView> {
+            if (!image._data) {
                 this._parent._logOpen(`${context} ${image.name || ""}`);
 
-                let promise: Promise<ArrayBufferView>;
                 if (image.uri) {
-                    promise = this._loadUriAsync(context, image.uri);
+                    image._data = this._loadUriAsync(context, image.uri);
                 }
                 else {
                     const bufferView = GLTFLoader._GetProperty(`${context}/bufferView`, this._gltf.bufferViews, image.bufferView);
-                    promise = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView);
+                    image._data = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView);
                 }
 
-                image._blob = promise.then(data => {
-                    return new Blob([data], { type: image.mimeType });
-                });
-
                 this._parent._logClose();
             }
 
-            return image._blob;
+            return image._data;
         }
 
         public _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView> {
-            const promise = GLTFLoaderExtension._LoadUriAsync(this, context, uri);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadUriAsync(this, context, uri);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             if (!GLTFLoader._ValidateUri(uri)) {

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

@@ -65,10 +65,16 @@ module BABYLON.GLTF2 {
         protected _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>> { return null; }
 
         /**
+         * Override this method to modify the default behavior for loading texture infos.
+         * @hidden
+         */
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>> { return null; }
+
+        /**
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          */
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>> { return null; }
+        protected _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>> { return null; }
 
         /**
          * Override this method to modify the default behavior for loading uris.
@@ -175,11 +181,19 @@ module BABYLON.GLTF2 {
         }
 
         /**
+         * Helper method called by the loader to allow extensions to override loading texture infos.
+         * @hidden
+         */
+        public static _LoadTextureInfoAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>> {
+            return loader._applyExtensions(extension => extension._loadTextureInfoAsync(context, textureInfo, assign));
+        }
+
+        /**
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          */
-        public static _LoadTextureAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>> {
-            return loader._applyExtensions(extension => extension._loadTextureAsync(context, textureInfo, assign));
+        public static _LoadTextureAsync(loader: GLTFLoader, context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>> {
+            return loader._applyExtensions(extension => extension._loadTextureAsync(context, texture, assign));
         }
 
         /**

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

@@ -54,7 +54,7 @@ module BABYLON.GLTF2 {
 
     /** @hidden */
     export interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
 
     /** @hidden */

BIN
sandbox/Assets/environment.dds


+ 11 - 5
sandbox/index.js

@@ -53,7 +53,7 @@ if (BABYLON.Engine.isSupported()) {
     var currentScene;
     var currentSkybox;
     var currentPluginName;
-    var skyboxPath = "Assets/environment.dds";
+    var skyboxPath = "https://assets.babylonjs.com/environments/environmentSpecular.env";
     var debugLayerEnabled = false;
     var debugLayerLastActiveTab = 0;
 
@@ -126,7 +126,7 @@ if (BABYLON.Engine.isSupported()) {
 
         // Attach camera to canvas inputs
         if (!currentScene.activeCamera || currentScene.lights.length === 0) {
-            currentScene.createDefaultCameraOrLight(true);
+            currentScene.createDefaultCamera(true);
 
             if (cameraPosition) {
                 currentScene.activeCamera.setPosition(cameraPosition);
@@ -160,10 +160,16 @@ if (BABYLON.Engine.isSupported()) {
 
         currentScene.activeCamera.attachControl(canvas);
 
-        // Environment
+        // Lighting
         if (currentPluginName === "gltf") {
-            var hdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(skyboxPath, currentScene);
-            currentSkybox = currentScene.createDefaultSkybox(hdrTexture, true, (currentScene.activeCamera.maxZ - currentScene.activeCamera.minZ) / 2, 0.3);
+            if (!currentScene.environmentTexture) {
+                currentScene.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(skyboxPath, currentScene);
+            }
+
+            currentSkybox = currentScene.createDefaultSkybox(currentScene.environmentTexture, true, (currentScene.activeCamera.maxZ - currentScene.activeCamera.minZ) / 2, 0.3, false);
+        }
+        else {
+            currentScene.createDefaultLight();
         }
 
         // In case of error during loading, meshes will be empty and clearColor is set to red

+ 16 - 16
serializers/src/glTF/2.0/babylon.glTFAnimation.ts

@@ -271,14 +271,14 @@ module BABYLON.GLTF2 {
 
                 // Creates buffer view and accessor for key frames.
                 let byteLength = animationData.inputs.length * 4;
-                bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, `${name}  keyframe data view`);
+                bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, `${name}  keyframe data view`);
                 bufferViews.push(bufferView);
 
                 animationData.inputs.forEach(function (input) {
                     binaryWriter.setFloat32(input);
                 });
 
-                accessor = _GLTFUtilities.CreateAccessor(bufferViews.length - 1, `${name}  keyframes`, AccessorType.SCALAR, AccessorComponentType.FLOAT, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
+                accessor = _GLTFUtilities._CreateAccessor(bufferViews.length - 1, `${name}  keyframes`, AccessorType.SCALAR, AccessorComponentType.FLOAT, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
                 accessors.push(accessor);
                 keyframeAccessorIndex = accessors.length - 1;
 
@@ -287,7 +287,7 @@ module BABYLON.GLTF2 {
                 byteLength = dataAccessorType === AccessorType.VEC3 ? animationData.outputs.length * 12 : animationData.outputs.length * 16;
 
                 // check for in and out tangents
-                bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, `${name}  data view`);
+                bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, `${name}  data view`);
                 bufferViews.push(bufferView);
 
                 animationData.outputs.forEach(function (output) {
@@ -296,7 +296,7 @@ module BABYLON.GLTF2 {
                     });
                 });
 
-                accessor = _GLTFUtilities.CreateAccessor(bufferViews.length - 1, `${name}  data`, dataAccessorType, AccessorComponentType.FLOAT, outputLength, null, null, null);
+                accessor = _GLTFUtilities._CreateAccessor(bufferViews.length - 1, `${name}  data`, dataAccessorType, AccessorComponentType.FLOAT, outputLength, null, null, null);
                 accessors.push(accessor);
                 dataAccessorIndex = accessors.length - 1;
 
@@ -447,7 +447,7 @@ module BABYLON.GLTF2 {
                         Quaternion.RotationYawPitchRollToRef(cacheValue.y, cacheValue.x, cacheValue.z, quaternionCache);
                     }
                     if (convertToRightHandedSystem) {
-                        _GLTFUtilities.GetRightHandedQuaternionFromRef(quaternionCache);
+                        _GLTFUtilities._GetRightHandedQuaternionFromRef(quaternionCache);
 
                         if (!babylonTransformNode.parent) {
                             quaternionCache = Quaternion.FromArray([0, 1, 0, 0]).multiply(quaternionCache);
@@ -458,7 +458,7 @@ module BABYLON.GLTF2 {
                 else {
                     cacheValue = value as Vector3;
                     if (convertToRightHandedSystem && (animationChannelTargetPath !== AnimationChannelTargetPath.SCALE)) {
-                        _GLTFUtilities.GetRightHandedPositionVector3FromRef(cacheValue);
+                        _GLTFUtilities._GetRightHandedPositionVector3FromRef(cacheValue);
                         if (!babylonTransformNode.parent) {
                             cacheValue.x *= -1;
                             cacheValue.z *= -1;
@@ -536,7 +536,7 @@ module BABYLON.GLTF2 {
                     if (babylonTransformNode.rotationQuaternion) {
                         basePositionRotationOrScale = babylonTransformNode.rotationQuaternion.asArray();
                         if (convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
+                            _GLTFUtilities._GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
                             if (!babylonTransformNode.parent) {
                                 basePositionRotationOrScale = Quaternion.FromArray([0, 1, 0, 0]).multiply(Quaternion.FromArray(basePositionRotationOrScale)).asArray();
                             }
@@ -548,13 +548,13 @@ module BABYLON.GLTF2 {
                 }
                 else {
                     basePositionRotationOrScale = babylonTransformNode.rotation.asArray();
-                    _GLTFUtilities.GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
+                    _GLTFUtilities._GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
                 }
             }
             else if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
                 basePositionRotationOrScale = babylonTransformNode.position.asArray();
                 if (convertToRightHandedSystem) {
-                    _GLTFUtilities.GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
+                    _GLTFUtilities._GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
                 }
             }
             else { // scale
@@ -583,7 +583,7 @@ module BABYLON.GLTF2 {
                     const array = Vector3.FromArray(value);
                     let rotationQuaternion = Quaternion.RotationYawPitchRoll(array.y, array.x, array.z);
                     if (convertToRightHandedSystem) {
-                        _GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                        _GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
 
                         if (!babylonTransformNode.parent) {
                             rotationQuaternion = Quaternion.FromArray([0, 1, 0, 0]).multiply(rotationQuaternion);
@@ -593,7 +593,7 @@ module BABYLON.GLTF2 {
                 }
                 else if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
                     if (convertToRightHandedSystem) {
-                        _GLTFUtilities.GetRightHandedNormalArray3FromRef(value);
+                        _GLTFUtilities._GetRightHandedNormalArray3FromRef(value);
                         if (!babylonTransformNode.parent) {
                             value[0] *= -1;
                             value[2] *= -1;
@@ -609,7 +609,7 @@ module BABYLON.GLTF2 {
                     if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION) {
                         let posRotScale = useQuaternion ? newPositionRotationOrScale as Quaternion : Quaternion.RotationYawPitchRoll(newPositionRotationOrScale.y, newPositionRotationOrScale.x, newPositionRotationOrScale.z).normalize();
                         if (convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedQuaternionFromRef(posRotScale);
+                            _GLTFUtilities._GetRightHandedQuaternionFromRef(posRotScale);
 
                             if (!babylonTransformNode.parent) {
                                 posRotScale = Quaternion.FromArray([0, 1, 0, 0]).multiply(posRotScale);
@@ -619,7 +619,7 @@ module BABYLON.GLTF2 {
                     }
                     else if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
                         if (convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedNormalVector3FromRef(newPositionRotationOrScale as Vector3);
+                            _GLTFUtilities._GetRightHandedNormalVector3FromRef(newPositionRotationOrScale as Vector3);
 
                             if (!babylonTransformNode.parent) {
                                 newPositionRotationOrScale.x *= -1;
@@ -634,7 +634,7 @@ module BABYLON.GLTF2 {
                 value = (keyFrame.value as Quaternion).normalize().asArray();
 
                 if (convertToRightHandedSystem) {
-                    _GLTFUtilities.GetRightHandedQuaternionArrayFromRef(value);
+                    _GLTFUtilities._GetRightHandedQuaternionArrayFromRef(value);
 
                     if (!babylonTransformNode.parent) {
                         value = Quaternion.FromArray([0, 1, 0, 0]).multiply(Quaternion.FromArray(value)).asArray();
@@ -730,7 +730,7 @@ module BABYLON.GLTF2 {
                         }
 
                         if (convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedQuaternionArrayFromRef(tangent);
+                            _GLTFUtilities._GetRightHandedQuaternionArrayFromRef(tangent);
                             if (!babylonTransformNode.parent) {
                                 tangent = Quaternion.FromArray([0, 1, 0, 0]).multiply(Quaternion.FromArray(tangent)).asArray();
                             }
@@ -745,7 +745,7 @@ module BABYLON.GLTF2 {
                         tangent = (tangentValue as Vector3).scale(frameDelta).asArray();
                         if (convertToRightHandedSystem) {
                             if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
-                                _GLTFUtilities.GetRightHandedPositionArray3FromRef(tangent);
+                                _GLTFUtilities._GetRightHandedPositionArray3FromRef(tangent);
                                 if (!babylonTransformNode.parent) {
                                     tangent[0] *= -1; // x
                                     tangent[2] *= -1; // z

+ 32 - 13
serializers/src/glTF/2.0/babylon.glTFExporter.ts

@@ -437,12 +437,28 @@ module BABYLON.GLTF2 {
             for (let vertex of vertices) {
                 if (this._convertToRightHandedSystem && !(vertexAttributeKind === VertexBuffer.ColorKind) && !(vertex instanceof Vector2)) {
                     if (vertex instanceof Vector3) {
-                        (vertexAttributeKind === VertexBuffer.PositionKind) ? _GLTFUtilities.GetRightHandedPositionVector3FromRef(vertex) : _GLTFUtilities.GetRightHandedNormalVector3FromRef(vertex);
+                        if (vertexAttributeKind === VertexBuffer.NormalKind) {
+                            _GLTFUtilities._GetRightHandedNormalVector3FromRef(vertex);
+                        }
+                        else if (vertexAttributeKind === VertexBuffer.PositionKind) {
+                            _GLTFUtilities._GetRightHandedPositionVector3FromRef(vertex);
+                        }
+                        else {
+                            Tools.Error('Unsupported vertex attribute kind!');
+                        }
                     }
                     else {
-                        _GLTFUtilities.GetRightHandedVector4FromRef(vertex);
+                        
+                        _GLTFUtilities._GetRightHandedVector4FromRef(vertex);   
                     }
                 }
+                if (vertexAttributeKind === VertexBuffer.NormalKind) {
+                    vertex.normalize();
+                }
+                else if (vertexAttributeKind === VertexBuffer.TangentKind && vertex instanceof Vector4) {
+                    _GLTFUtilities._NormalizeTangentFromRef(vertex);
+                }
+
                 for (let component of vertex.asArray()) {
                     binaryWriter.setFloat32(component, byteOffset);
                     byteOffset += 4;
@@ -469,7 +485,7 @@ module BABYLON.GLTF2 {
                         index = k * stride;
                         const vertexData = Vector3.FromArray(meshAttributeArray, index);
                         if (this._convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedPositionVector3FromRef(vertexData);
+                            _GLTFUtilities._GetRightHandedPositionVector3FromRef(vertexData);
                         }
                         vertexAttributes.push(vertexData.asArray());
                     }
@@ -480,8 +496,9 @@ module BABYLON.GLTF2 {
                         index = k * stride;
                         const vertexData = Vector3.FromArray(meshAttributeArray, index);
                         if (this._convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedNormalVector3FromRef(vertexData);
+                            _GLTFUtilities._GetRightHandedNormalVector3FromRef(vertexData);
                         }
+                        vertexData.normalize();
                         vertexAttributes.push(vertexData.asArray());
                     }
                     break;
@@ -491,8 +508,10 @@ module BABYLON.GLTF2 {
                         index = k * stride;
                         const vertexData = Vector4.FromArray(meshAttributeArray, index);
                         if (this._convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedVector4FromRef(vertexData);
+                            _GLTFUtilities._GetRightHandedVector4FromRef(vertexData);
                         }
+                        _GLTFUtilities._NormalizeTangentFromRef(vertexData);
+
                         vertexAttributes.push(vertexData.asArray());
                     }
                     break;
@@ -584,7 +603,7 @@ module BABYLON.GLTF2 {
                         if (image.uri) {
                             imageData = this._imageData[image.uri];
                             imageName = image.uri.split('.')[0] + " image";
-                            bufferView = _GLTFUtilities.CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
+                            bufferView = _GLTFUtilities._CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                             byteOffset += imageData.data.buffer.byteLength;
                             this._bufferViews.push(bufferView);
                             image.bufferView = this._bufferViews.length - 1;
@@ -762,7 +781,7 @@ module BABYLON.GLTF2 {
          */
         private setNodeTransformation(node: INode, babylonTransformNode: TransformNode): void {
             if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-                node.translation = this._convertToRightHandedSystem ? _GLTFUtilities.GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+                node.translation = this._convertToRightHandedSystem ? _GLTFUtilities._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
             }
 
             if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
@@ -775,7 +794,7 @@ module BABYLON.GLTF2 {
             }
             if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
                 if (this._convertToRightHandedSystem) {
-                    _GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                    _GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
 
                 }
                 node.rotation = rotationQuaternion.normalize().asArray();
@@ -808,7 +827,7 @@ module BABYLON.GLTF2 {
 
                 if (vertexData) {
                     const byteLength = vertexData.length * 4;
-                    const bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
+                    const bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                     this._bufferViews.push(bufferView);
 
                     this.writeAttributeData(
@@ -963,7 +982,7 @@ module BABYLON.GLTF2 {
                     const indices = bufferMesh.getIndices();
                     if (indices) {
                         const byteLength = indices.length * 4;
-                        bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
+                        bufferView = _GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
                         this._bufferViews.push(bufferView);
                         indexBufferViewIndex = this._bufferViews.length - 1;
 
@@ -1026,9 +1045,9 @@ module BABYLON.GLTF2 {
                                     if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                         minMax = { min: null, max: null };
                                         if (attributeKind == VertexBuffer.PositionKind) {
-                                            minMax = _GLTFUtilities.CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
+                                            minMax = _GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
                                         }
-                                        const accessor = _GLTFUtilities.CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, AccessorComponentType.FLOAT, vertexData.length / stride, 0, minMax.min, minMax.max);
+                                        const accessor = _GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, AccessorComponentType.FLOAT, vertexData.length / stride, 0, minMax.min, minMax.max);
                                         this._accessors.push(accessor);
                                         this.setAttributeKind(meshPrimitive, attributeKind);
                                         if (meshPrimitive.attributes.TEXCOORD_0 != null || meshPrimitive.attributes.TEXCOORD_1 != null) {
@@ -1040,7 +1059,7 @@ module BABYLON.GLTF2 {
                         }
                         if (indexBufferViewIndex) {
                             // Create accessor
-                            const accessor = _GLTFUtilities.CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, AccessorType.SCALAR, AccessorComponentType.UNSIGNED_INT, submesh.indexCount, submesh.indexStart * 4, null, null);
+                            const accessor = _GLTFUtilities._CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, AccessorType.SCALAR, AccessorComponentType.UNSIGNED_INT, submesh.indexCount, submesh.indexStart * 4, null, null);
                             this._accessors.push(accessor);
                             meshPrimitive.indices = this._accessors.length - 1;
                         }

+ 37 - 36
serializers/src/glTF/2.0/babylon.glTFMaterialExporter.ts

@@ -793,7 +793,7 @@ module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, hasTextureCoords: boolean): Promise<_IPBRMetallicRoughness> {
+        private _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, hasTextureCoords: boolean): Promise<_IPBRMetallicRoughness> {
             const promises = [];
             const metallicRoughness: _IPBRMetallicRoughness = {
                 baseColor: babylonPBRMaterial.albedoColor,
@@ -933,40 +933,41 @@ module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, hasTextureCoords: boolean): Promise<_IPBRMetallicRoughness> {
-            const samplers = this._exporter._samplers;
-            const textures = this._exporter._textures;
-            const specGloss: _IPBRSpecularGlossiness = {
-                diffuseColor: babylonPBRMaterial.albedoColor || Color3.White(),
-                specularColor: babylonPBRMaterial.reflectivityColor || Color3.White(),
-                glossiness: babylonPBRMaterial.microSurface || 1,
-            };
-            let samplerIndex: Nullable<number> = null;
-            const sampler = this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
-            if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
-                samplers.push(sampler);
-                samplerIndex = samplers.length - 1;
-            }
-            if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
-                return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
-            }
-
-            return this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(metallicRoughnessFactors => {
-                if (hasTextureCoords) {
-                    if (metallicRoughnessFactors.baseColorTextureBase64) {
-                        const glTFBaseColorTexture = this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
-                        if (glTFBaseColorTexture) {
-                            glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
-                        }
-                    }
-                    if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
-                        const glTFMRColorTexture = this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
-                        if (glTFMRColorTexture) {
-                            glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
-                        }
-                    }
+        private _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, hasTextureCoords: boolean): Promise<_IPBRMetallicRoughness> {
+            return Promise.resolve().then(() => {
+                const samplers = this._exporter._samplers;
+                const textures = this._exporter._textures;
+                const specGloss: _IPBRSpecularGlossiness = {
+                    diffuseColor: babylonPBRMaterial.albedoColor || Color3.White(),
+                    specularColor: babylonPBRMaterial.reflectivityColor || Color3.White(),
+                    glossiness: babylonPBRMaterial.microSurface || 1,
+                };
+                let samplerIndex: Nullable<number> = null;
+                const sampler = this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
+                if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
+                    samplers.push(sampler);
+                    samplerIndex = samplers.length - 1;
+                }
+                if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
+                    return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
+                }
+                if ((babylonPBRMaterial.albedoTexture || babylonPBRMaterial.reflectivityTexture) && hasTextureCoords) {
+                    return this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(metallicRoughnessFactors => {
+                            if (metallicRoughnessFactors.baseColorTextureBase64) {
+                                const glTFBaseColorTexture = this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFBaseColorTexture) {
+                                    glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                                }
+                            }
+                            if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
+                                const glTFMRColorTexture = this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFMRColorTexture) {
+                                    glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                                }
+                            }
 
-                    return metallicRoughnessFactors;
+                            return metallicRoughnessFactors;
+                    });
                 }
                 else {
                     return this._convertSpecularGlossinessToMetallicRoughness(specGloss);
@@ -1000,12 +1001,12 @@ module BABYLON.GLTF2 {
                         babylonPBRMaterial.alpha
                     ]
                 }
-                return this._gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(metallicRoughness => {
+                return this._convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(metallicRoughness => {
                     return this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                 });
             }
             else {
-                return this._convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(metallicRoughness => {
+                return this._convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(metallicRoughness => {
                     return this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                 });
             }

+ 21 - 14
serializers/src/glTF/2.0/babylon.glTFUtilities.ts

@@ -14,7 +14,7 @@ module BABYLON.GLTF2 {
          * @param name name of the buffer view
          * @returns bufferView for glTF
          */
-        public static CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView {
+        public static _CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView {
             let bufferview: IBufferView = { buffer: bufferIndex, byteLength: byteLength };
             if (byteOffset) {
                 bufferview.byteOffset = byteOffset;
@@ -41,7 +41,7 @@ module BABYLON.GLTF2 {
          * @param max Maximum value of each component in this attribute
          * @returns accessor for glTF 
          */
-        public static CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor {
+        public static _CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor {
             let accessor: IAccessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
 
             if (min != null) {
@@ -64,7 +64,7 @@ module BABYLON.GLTF2 {
          * @param vertexCount Number of vertices to check for min and max values
          * @returns min number array and max number array
          */
-        public static CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): { min: number[], max: number[] } {
+        public static _CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): { min: number[], max: number[] } {
             const min = [Infinity, Infinity, Infinity];
             const max = [-Infinity, -Infinity, -Infinity];
             const positionStrideSize = 3;
@@ -78,7 +78,7 @@ module BABYLON.GLTF2 {
 
                     position = Vector3.FromArray(positions, indexOffset);
                     if (convertToRightHandedSystem) {
-                        _GLTFUtilities.GetRightHandedPositionVector3FromRef(position);
+                        _GLTFUtilities._GetRightHandedPositionVector3FromRef(position);
                     }
                     vector = position.asArray();
 
@@ -102,7 +102,7 @@ module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        public static GetRightHandedPositionVector3(vector: Vector3): Vector3 {
+        public static _GetRightHandedPositionVector3(vector: Vector3): Vector3 {
             return new Vector3(vector.x, vector.y, -vector.z);
         }
 
@@ -110,7 +110,7 @@ module BABYLON.GLTF2 {
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        public static GetRightHandedPositionVector3FromRef(vector: Vector3) {
+        public static _GetRightHandedPositionVector3FromRef(vector: Vector3) {
             vector.z *= -1;
         }
 
@@ -118,7 +118,7 @@ module BABYLON.GLTF2 {
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        public static GetRightHandedPositionArray3FromRef(vector: number[]) {
+        public static _GetRightHandedPositionArray3FromRef(vector: number[]) {
             vector[2] *= -1;
         }
 
@@ -127,7 +127,7 @@ module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        public static GetRightHandedNormalVector3(vector: Vector3): Vector3 {
+        public static _GetRightHandedNormalVector3(vector: Vector3): Vector3 {
             return new Vector3(vector.x, vector.y, -vector.z);
         }
 
@@ -135,7 +135,7 @@ module BABYLON.GLTF2 {
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        public static GetRightHandedNormalVector3FromRef(vector: Vector3) {
+        public static _GetRightHandedNormalVector3FromRef(vector: Vector3) {
             vector.z *= -1;
         }
 
@@ -143,7 +143,7 @@ module BABYLON.GLTF2 {
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        public static GetRightHandedNormalArray3FromRef(vector: number[]) {
+        public static _GetRightHandedNormalArray3FromRef(vector: number[]) {
             vector[2] *= -1;
         }
 
@@ -151,7 +151,7 @@ module BABYLON.GLTF2 {
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        public static GetRightHandedVector4FromRef(vector: Vector4) {
+        public static _GetRightHandedVector4FromRef(vector: Vector4) {
             vector.z *= -1;
             vector.w *= -1;
         }
@@ -160,7 +160,7 @@ module BABYLON.GLTF2 {
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        public static GetRightHandedArray4FromRef(vector: number[]) {
+        public static _GetRightHandedArray4FromRef(vector: number[]) {
             vector[2] *= -1;
             vector[3] *= -1;
         }
@@ -169,7 +169,7 @@ module BABYLON.GLTF2 {
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        public static GetRightHandedQuaternionFromRef(quaternion: Quaternion) {
+        public static _GetRightHandedQuaternionFromRef(quaternion: Quaternion) {
              quaternion.x *= -1;
              quaternion.y *= -1;
         }
@@ -178,9 +178,16 @@ module BABYLON.GLTF2 {
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        public static GetRightHandedQuaternionArrayFromRef(quaternion: number[]) {
+        public static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]) {
              quaternion[0] *= -1;
              quaternion[1] *= -1;
         }
+
+        public static _NormalizeTangentFromRef(tangent: Vector4) {
+            const length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z + tangent.z);
+            tangent.x /= length;
+            tangent.y /= length;
+            tangent.z /= length;
+        }
     }
 }

+ 3 - 3
src/Actions/babylon.actionManager.ts

@@ -257,7 +257,7 @@
         constructor(scene: Scene) {
             this._scene = scene || Engine.LastCreatedScene;
 
-            scene._actionManagers.push(this);
+            scene.actionManagers.push(this);
         }
 
         // Methods
@@ -266,7 +266,7 @@
          * Releases all associated resources
          */
         public dispose(): void {
-            var index = this._scene._actionManagers.indexOf(this);
+            var index = this._scene.actionManagers.indexOf(this);
 
             for (var i = 0; i < this.actions.length; i++) {
                 var action = this.actions[i];
@@ -277,7 +277,7 @@
             }
 
             if (index > -1) {
-                this._scene._actionManagers.splice(index, 1);
+                this._scene.actionManagers.splice(index, 1);
             }
         }
 

+ 94 - 9
src/Animations/babylon.animatable.ts

@@ -1,4 +1,7 @@
 module BABYLON {
+    /**
+     * Class used to store an actual running animation
+     */
     export class Animatable {
         private _localDelayOffset: Nullable<number> = null;
         private _pausedDelay: Nullable<number> = null;
@@ -9,6 +12,15 @@
         private _weight = -1.0;
         private _syncRoot: Animatable;
 
+        /**
+         * Gets or sets a boolean indicating if the animatable must be disposed and removed at the end of the animation.
+         * This will only apply for non looping animation (default is true)
+         */
+        public disposeOnEnd = true;
+
+        /**
+         * Gets a boolean indicating if the animation has started
+         */
         public animationStarted = false;
 
         /**
@@ -69,7 +81,30 @@
         }
 
 
-        constructor(scene: Scene, public target: any, public fromFrame: number = 0, public toFrame: number = 100, public loopAnimation: boolean = false, speedRatio: number = 1.0, public onAnimationEnd?: Nullable<() => void>, animations?: any) {
+        /**
+         * Creates a new Animatable
+         * @param scene defines the hosting scene
+         * @param target defines the target object
+         * @param fromFrame defines the starting frame number (default is 0)
+         * @param toFrame defines the ending frame number (default is 100)
+         * @param loopAnimation defines if the animation must loop (default is false)
+         * @param speedRatio defines the factor to apply to animation speed (default is 1)
+         * @param onAnimationEnd defines a callback to call when animation ends if it is not looping
+         * @param animations defines a group of animation to add to the new Animatable
+         */
+        constructor(scene: Scene, 
+            /** defines the target object */
+            public target: any, 
+            /** defines the starting frame number (default is 0) */
+            public fromFrame: number = 0, 
+            /** defines the ending frame number (default is 100) */
+            public toFrame: number = 100, 
+            /** defines if the animation must loop (default is false)  */
+            public loopAnimation: boolean = false, 
+            speedRatio: number = 1.0, 
+            /** defines a callback to call when animation ends if it is not looping */
+            public onAnimationEnd?: Nullable<() => void>, 
+            animations?: Animation[]) {
             this._scene = scene;
             if (animations) {
                 this.appendAnimations(target, animations);
@@ -101,10 +136,19 @@
             return this;
         }
 
+        /**
+         * Gets the list of runtime animations
+         * @returns an array of RuntimeAnimation
+         */
         public getAnimations(): RuntimeAnimation[] {
             return this._runtimeAnimations;
         }
 
+        /**
+         * Adds more animations to the current animatable
+         * @param target defines the target of the animations
+         * @param animations defines the new animations to add
+         */
         public appendAnimations(target: any, animations: Animation[]): void {
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
@@ -113,6 +157,11 @@
             }
         }
 
+        /**
+         * Gets the source animation for a specific property
+         * @param property defines the propertyu to look for
+         * @returns null or the source animation for the given property
+         */
         public getAnimationByTargetProperty(property: string): Nullable<Animation> {
             var runtimeAnimations = this._runtimeAnimations;
 
@@ -125,6 +174,11 @@
             return null;
         }
 
+        /**
+         * Gets the runtime animation for a specific property
+         * @param property defines the propertyu to look for
+         * @returns null or the runtime animation for the given property
+         */
         public getRuntimeAnimationByTargetProperty(property: string): Nullable<RuntimeAnimation> {
             var runtimeAnimations = this._runtimeAnimations;
 
@@ -137,6 +191,9 @@
             return null;
         }
 
+        /**
+         * Resets the animatable to its original state
+         */
         public reset(): void {
             var runtimeAnimations = this._runtimeAnimations;
 
@@ -148,6 +205,11 @@
             this._pausedDelay = null;
         }
 
+        /**
+         * Allows the animatable to blend with current running animations
+         * @see http://doc.babylonjs.com/babylon101/animations#animation-blending
+         * @param blendingSpeed defines the blending speed to use
+         */
         public enableBlending(blendingSpeed: number): void {
             var runtimeAnimations = this._runtimeAnimations;
 
@@ -157,6 +219,10 @@
             }
         }
 
+        /**
+         * Disable animation blending
+         * @see http://doc.babylonjs.com/babylon101/animations#animation-blending
+         */
         public disableBlending(): void {
             var runtimeAnimations = this._runtimeAnimations;
 
@@ -165,6 +231,10 @@
             }
         }
 
+        /**
+         * Jump directly to a given frame
+         * @param frame defines the frame to jump to
+         */
         public goToFrame(frame: number): void {
             var runtimeAnimations = this._runtimeAnimations;
 
@@ -184,6 +254,9 @@
             }
         }
 
+        /**
+         * Pause the animation
+         */
         public pause(): void {
             if (this._paused) {
                 return;
@@ -191,6 +264,9 @@
             this._paused = true;
         }
 
+        /**
+         * Restart the animation
+         */
         public restart(): void {
             this._paused = false;
         }
@@ -203,6 +279,10 @@
             this.onAnimationEndObservable.notifyObservers(this);
         }
 
+        /**
+         * Stop and delete the current animation
+         * @param animationName defines a string used to only stop some of the runtime animations instead of all
+         */
         public stop(animationName?: string): void {
             if (animationName) {
                 var idx = this._scene._activeAnimatables.indexOf(this);
@@ -291,18 +371,23 @@
             this.animationStarted = running;
 
             if (!running) {
-                // Remove from active animatables
-                index = this._scene._activeAnimatables.indexOf(this);
-                this._scene._activeAnimatables.splice(index, 1);
+                if (this.disposeOnEnd) {
+                    // Remove from active animatables
+                    index = this._scene._activeAnimatables.indexOf(this);
+                    this._scene._activeAnimatables.splice(index, 1);
 
-                // Dispose all runtime animations
-                for (index = 0; index < runtimeAnimations.length; index++) {
-                    runtimeAnimations[index].dispose();
+                    // Dispose all runtime animations
+                    for (index = 0; index < runtimeAnimations.length; index++) {
+                        runtimeAnimations[index].dispose();
+                    }
                 }
 
                 this._raiseOnAnimationEnd();
-                this.onAnimationEnd = null
-                this.onAnimationEndObservable.clear();
+
+                if (this.disposeOnEnd) {
+                    this.onAnimationEnd = null
+                    this.onAnimationEndObservable.clear();
+                }
             }
 
             return running;

+ 1 - 1
src/Culling/babylon.boundingSphere.ts

@@ -59,7 +59,7 @@
 
             var distance = Math.sqrt((x * x) + (y * y) + (z * z));
 
-            if (Math.abs(this.radiusWorld - distance) < Epsilon)
+            if (this.radiusWorld < distance)
                 return false;
 
             return true;

+ 17 - 12
src/Engine/babylon.engine.ts

@@ -781,6 +781,9 @@
          */
         public postProcesses = new Array<PostProcess>();
 
+        /** Gets or sets a boolean indicating if the engine should validate programs after compilation */
+        public validateShaderPrograms = false;
+
         // Observables
 
         /**
@@ -1037,7 +1040,7 @@
         private _uintIndicesCurrentlySet = false;
         private _currentBoundBuffer = new Array<Nullable<WebGLBuffer>>();
         /** @hidden */
-        protected _currentFramebuffer: Nullable<WebGLFramebuffer>;
+        protected _currentFramebuffer: Nullable<WebGLFramebuffer> = null;
         private _currentBufferPointers = new Array<BufferPointer>();
         private _currentInstanceLocations = new Array<number>();
         private _currentInstanceBuffers = new Array<WebGLBuffer>();
@@ -3543,13 +3546,15 @@
                 }
             }
 
-            context.validateProgram(shaderProgram);
-            var validated = context.getProgramParameter(shaderProgram, context.VALIDATE_STATUS);
+            if (this.validateShaderPrograms) {
+                context.validateProgram(shaderProgram);
+                var validated = context.getProgramParameter(shaderProgram, context.VALIDATE_STATUS);
 
-            if(!validated) {
-                var error = context.getProgramInfoLog(shaderProgram);
-                if (error) {
-                    throw new Error(error);
+                if(!validated) {
+                    var error = context.getProgramInfoLog(shaderProgram);
+                    if (error) {
+                        throw new Error(error);
+                    }
                 }
             }
 
@@ -5654,8 +5659,8 @@
                         texture.width = info.width;
                         texture.height = info.width;
 
-                        EnvironmentTextureTools.UploadPolynomials(texture, data, info!);
-                        EnvironmentTextureTools.UploadLevelsAsync(texture, data, info!).then(() => {
+                        EnvironmentTextureTools.UploadEnvSpherical(texture, info);
+                        EnvironmentTextureTools.UploadEnvLevelsAsync(texture, data, info).then(() => {
                             texture.isReady = true;
                             if (onLoad) {
                                 onLoad();
@@ -5874,15 +5879,15 @@
          * @param data defines the array of data to use to create each face
          * @param size defines the size of the textures
          * @param format defines the format of the data
-         * @param type defines the type fo the data (like BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT)
+         * @param type defines the type of the data (like BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT)
          * @param generateMipMaps  defines if the engine should generate the mip levels
          * @param invertY defines if data must be stored with Y axis inverted
          * @param samplingMode defines the required sampling mode (like BABYLON.Texture.NEAREST_SAMPLINGMODE)
          * @param compression defines the compression used (null by default)
          * @returns the cube texture as an InternalTexture
          */
-        public createRawCubeTexture(data: Nullable<ArrayBufferView[]>, size: number, format: number, type: number, 
-                                    generateMipMaps: boolean, invertY: boolean, samplingMode: number, 
+        public createRawCubeTexture(data: Nullable<ArrayBufferView[]>, size: number, format: number, type: number,
+                                    generateMipMaps: boolean, invertY: boolean, samplingMode: number,
                                     compression: Nullable<string> = null): InternalTexture {
             var gl = this._gl;
             var texture = new InternalTexture(this, InternalTexture.DATASOURCE_CUBERAW);

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

@@ -442,5 +442,11 @@
 
         public releaseEffects() {
         }
+
+        public displayLoadingUI(): void {
+        }
+
+        public hideLoadingUI(): void {
+        }
     }
 }

+ 27 - 4
src/Layer/babylon.effectLayer.ts

@@ -24,6 +24,11 @@
          * The camera attached to the layer.
          */
         camera: Nullable<Camera>;
+
+        /**
+         * The rendering group to draw the layer in.
+         */
+        renderingGroupId: number;
     }
 
     /**
@@ -80,6 +85,14 @@
         }
 
         /**
+         * Gets the rendering group id the layer should render in.
+         */
+        @serialize()
+        public get renderingGroupId(): number {
+            return this._effectLayerOptions.renderingGroupId;
+        }
+
+        /**
          * An event triggered when the effect layer has been disposed.
          */
         public onDisposeObservable = new Observable<EffectLayer>();
@@ -116,7 +129,13 @@
             this.name = name;
 
             this._scene = scene || Engine.LastCreatedScene;
-            this._engine = scene.getEngine();
+            let component = this._scene._getComponent(SceneComponentConstants.NAME_EFFECTLAYER) as EffectLayerSceneComponent;
+            if (!component) {
+                component = new EffectLayerSceneComponent(this._scene);
+                this._scene._addComponent(component);
+            }
+
+            this._engine = this._scene.getEngine();
             this._maxSize = this._engine.getCaps().maxTextureSize;
             this._scene.effectLayers.push(this);
 
@@ -191,6 +210,7 @@
                 mainTextureRatio: 0.5,
                 alphaBlendingMode: Engine.ALPHA_COMBINE,
                 camera: null,
+                renderingGroupId: -1,
                 ...options,
             };
 
@@ -483,7 +503,10 @@
          * @returns true if the mesh will be used
          */
         public hasMesh(mesh: AbstractMesh): boolean {
-            return true;
+            if (this.renderingGroupId === -1 || mesh.renderingGroupId === this.renderingGroupId) {
+                return true;
+            }
+            return false;
         }
 
         /**
@@ -595,7 +618,7 @@
                 mesh._processRendering(subMesh, this._effectLayerMapGenerationEffect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
                     (isInstance, world) => this._effectLayerMapGenerationEffect.setMatrix("world", world));
             } else {
-                // Need to reset refresh rate of the shadowMap
+                // Need to reset refresh rate of the main map
                 this._mainTexture.resetRefreshCounter();
             }
         }
@@ -686,7 +709,7 @@
          */
         public static Parse(parsedEffectLayer: any, scene: Scene, rootUrl: string): EffectLayer {
             var effectLayerType = Tools.Instantiate(parsedEffectLayer.customType);
-            
+
             return effectLayerType.Parse(parsedEffectLayer, scene, rootUrl);
         }
     }

+ 237 - 0
src/Layer/babylon.effectLayerSceneComponent.ts

@@ -0,0 +1,237 @@
+module BABYLON {
+    // Adds the parser to the scene parsers.
+    AbstractScene.AddParser(SceneComponentConstants.NAME_EFFECTLAYER, (parsedData: any, scene: Scene, container: AssetContainer, rootUrl: string) => {
+        if (parsedData.effectLayers) {
+            for (let index = 0; index < parsedData.effectLayers.length; index++) {
+                var effectLayer = EffectLayer.Parse(parsedData.effectLayers[index], scene, rootUrl);
+                container.effectLayers.push(effectLayer);
+            }
+        }
+    });
+
+    export interface AbstractScene {
+        /**
+         * The list of effect layers (highlights/glow) added to the scene
+         * @see http://doc.babylonjs.com/how_to/highlight_layer
+         * @see http://doc.babylonjs.com/how_to/glow_layer
+         */
+        effectLayers: Array<EffectLayer>;
+
+        /**
+         * Removes the given effect layer from this scene.
+         * @param toRemove defines the effect layer to remove
+         * @returns the index of the removed effect layer
+         */
+        removeEffectLayer(toRemove: EffectLayer): number;
+
+        /**
+         * Adds the given effect layer to this scene
+         * @param newEffectLayer defines the effect layer to add
+         */     
+        addEffectLayer(newEffectLayer: EffectLayer): void;
+    }
+
+    AbstractScene.prototype.removeEffectLayer = function(toRemove: EffectLayer): number {
+        var index = this.effectLayers.indexOf(toRemove);
+        if (index !== -1) {
+            this.effectLayers.splice(index, 1);
+        }
+
+        return index;
+    }
+
+    AbstractScene.prototype.addEffectLayer = function(newEffectLayer: EffectLayer): void {
+        this.effectLayers.push(newEffectLayer);
+    }
+
+    /**
+     * Defines the layer scene component responsible to manage any effect layers
+     * in a given scene.
+     */
+    export class EffectLayerSceneComponent implements ISceneComponent {
+        /**
+         * The component name helpfull to identify the component in the list of scene components.
+         */
+        public readonly name = SceneComponentConstants.NAME_EFFECTLAYER;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene
+
+        private _engine: Engine;
+        private _effectLayers: Array<EffectLayer>;
+
+        private _renderEffects = false;
+        private _needStencil = false;
+        private _previousStencilState = false;
+
+        /**
+         * Creates a new instance of the component for the given scene
+         * @param scene Defines the scene to register the component in
+         */
+        constructor(scene: Scene) {
+            this.scene = scene;
+            this._engine = scene.getEngine();
+            this._effectLayers = scene.effectLayers = new Array<EffectLayer>();
+        }
+
+        /**
+         * Registers the component in a given scene
+         */
+        public register(): void {
+            this.scene._isReadyForMeshStage.registerStep(SceneComponentConstants.STEP_ISREADYFORMESH_EFFECTLAYER, this, this._isReadyForMesh);
+
+            this.scene._cameraDrawRenderTargetStage.registerStep(SceneComponentConstants.STEP_CAMERADRAWRENDERTARGET_EFFECTLAYER, this, this._renderMainTexture);
+
+            this.scene._beforeCameraDrawStage.registerStep(SceneComponentConstants.STEP_BEFORECAMERADRAW_EFFECTLAYER, this, this._setStencil);
+
+            this.scene._afterRenderingGroupDrawStage.registerStep(SceneComponentConstants.STEP_AFTERRENDERINGGROUPDRAW_EFFECTLAYER_DRAW, this, this._drawRenderingGroup);
+
+            this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_EFFECTLAYER, this, this._setStencilBack);
+            this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_EFFECTLAYER_DRAW, this, this._drawCamera);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            for (let effectLayer of this._effectLayers) {
+                effectLayer._rebuild();
+            }
+        }
+
+        /**
+         * Serializes the component data to the specified json object
+         * @param serializationObject The object to serialize to
+         */
+        public serialize(serializationObject: any): void {
+            // Effect layers
+            serializationObject.effectLayers = [];
+
+            for (let effectLayer of this._effectLayers) {
+                if (effectLayer.serialize) {
+                    serializationObject.effectLayers.push(effectLayer.serialize());
+                }
+            }
+        }
+
+        /**
+         * Adds all the element from the container to the scene
+         * @param container the container holding the elements
+         */
+        public addFromContainer(container: AbstractScene): void {
+            if (!container.effectLayers) {
+                return;
+            }
+            container.effectLayers.forEach((o) => {
+                this.scene.addEffectLayer(o);
+            });
+        }
+
+        /**
+         * Removes all the elements in the container from the scene
+         * @param container contains the elements to remove 
+         */
+        public removeFromContainer(container: AbstractScene): void {
+            if (!container.effectLayers) {
+                return;
+            }
+            container.effectLayers.forEach((o) => {
+                this.scene.removeEffectLayer(o);
+            });
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            while (this._effectLayers.length) {
+                this._effectLayers[0].dispose();
+            }
+        }
+
+        private _isReadyForMesh(mesh: AbstractMesh, hardwareInstancedRendering: boolean): boolean {
+            for (let layer of this._effectLayers) {
+                if (!layer.hasMesh(mesh)) {
+                    continue;
+                }
+
+                for (var subMesh of mesh.subMeshes) {
+                    if (!layer.isReady(subMesh, hardwareInstancedRendering)) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
+        private _renderMainTexture(camera: Camera): void {
+            this._renderEffects = false;
+            this._needStencil = false;
+
+            if (this._effectLayers && this._effectLayers.length > 0) {
+                this._previousStencilState = this._engine.getStencilBuffer();
+                for (let effectLayer of this._effectLayers) {
+                    if (effectLayer.shouldRender() &&
+                        (!effectLayer.camera ||
+                            (effectLayer.camera.cameraRigMode === Camera.RIG_MODE_NONE && camera === effectLayer.camera) ||
+                            (effectLayer.camera.cameraRigMode !== Camera.RIG_MODE_NONE && effectLayer.camera._rigCameras.indexOf(camera) > -1))) {
+
+                        this._renderEffects = true;
+                        this._needStencil = this._needStencil || effectLayer.needStencil();
+
+                        let renderTarget = (<RenderTargetTexture>(<any>effectLayer)._mainTexture);
+                        if (renderTarget._shouldRender()) {
+                            this.scene.incrementRenderId();
+                            renderTarget.render(false, false);
+                        }
+                    }
+                }
+
+                this.scene.incrementRenderId();
+            }
+        }
+
+        private _setStencil(camera: Camera) {
+            // Activate effect Layer stencil
+            if (this._needStencil) {
+                this._engine.setStencilBuffer(true);
+            }
+        }
+
+        private _setStencilBack(camera: Camera) {
+            // Restore effect Layer stencil
+            if (this._needStencil) {
+                this._engine.setStencilBuffer(this._previousStencilState);
+            }
+        }
+
+        private _draw(renderingGroupId: number): void {
+            if (this._renderEffects) {
+                this._engine.setDepthBuffer(false);
+                for (let i = 0; i < this._effectLayers.length; i++) {
+                    const effectLayer = this._effectLayers[i];
+                    if (effectLayer.renderingGroupId === renderingGroupId) {
+                        if (effectLayer.shouldRender()) {
+                            effectLayer.render();
+                        }
+                    }
+                }
+                this._engine.setDepthBuffer(true);
+            }
+        }
+
+        private _drawCamera(camera: Camera): void {
+            if (this._renderEffects) {
+                this._draw(-1);
+            }
+        }
+        private _drawRenderingGroup(index: number): void {
+            if (!this.scene._isInIntermediateRendering() && this._renderEffects) {
+                this._draw(index);
+            }
+        }
+    }
+} 

+ 32 - 1
src/Layer/babylon.glowLayer.ts

@@ -1,4 +1,24 @@
 module BABYLON {
+
+    export interface AbstractScene {
+        /**
+         * Return a the first highlight layer of the scene with a given name.
+         * @param name The name of the highlight layer to look for.
+         * @return The highlight layer if found otherwise null.
+         */
+        getGlowLayerByName(name: string): Nullable<GlowLayer>;
+    }
+
+    AbstractScene.prototype.getGlowLayerByName = function(name: string): Nullable<GlowLayer> {
+        for (var index = 0; index < this.effectLayers.length; index++) {
+            if (this.effectLayers[index].name === name && this.effectLayers[index].getEffectName() === GlowLayer.EffectName) {
+                return (<any>this.effectLayers[index]) as GlowLayer;
+            }
+        }
+
+        return null;
+    }
+
     /**
      * Glow layer options. This helps customizing the behaviour
      * of the glow layer.
@@ -29,6 +49,11 @@
          * Enable MSAA by chosing the number of samples.
          */
         mainTextureSamples?: number;
+
+        /**
+         * The rendering group to draw the layer in.
+         */
+        renderingGroupId: number;
     }
 
     /**
@@ -130,6 +155,7 @@
                 mainTextureFixedSize: undefined,
                 camera: null,
                 mainTextureSamples: 1,
+                renderingGroupId: -1,
                 ...options,
             };
 
@@ -138,7 +164,8 @@
                 alphaBlendingMode: Engine.ALPHA_ADD,
                 camera: this._options.camera,
                 mainTextureFixedSize: this._options.mainTextureFixedSize,
-                mainTextureRatio: this._options.mainTextureRatio
+                mainTextureRatio: this._options.mainTextureRatio,
+                renderingGroupId: this._options.renderingGroupId
             });
         }
 
@@ -423,6 +450,10 @@
          * @returns true if the mesh will be highlighted by the current glow layer
          */
         public hasMesh(mesh: AbstractMesh): boolean {
+            if (!super.hasMesh(mesh)) {
+                return false;
+            }
+
             // Included Mesh
             if (this._includedOnlyMeshes.length) {
                 return this._includedOnlyMeshes.indexOf(mesh.uniqueId) !== -1;

+ 39 - 1
src/Layer/babylon.highlightLayer.ts

@@ -1,4 +1,23 @@
 module BABYLON {
+    export interface AbstractScene {
+        /**
+         * Return a the first highlight layer of the scene with a given name.
+         * @param name The name of the highlight layer to look for.
+         * @return The highlight layer if found otherwise null.
+         */
+        getHighlightLayerByName(name: string): Nullable<HighlightLayer>;
+    }
+
+    AbstractScene.prototype.getHighlightLayerByName = function(name: string): Nullable<HighlightLayer> {
+        for (var index = 0; index < this.effectLayers.length; index++) {
+            if (this.effectLayers[index].name === name && this.effectLayers[index].getEffectName() === HighlightLayer.EffectName) {
+                return (<any>this.effectLayers[index]) as HighlightLayer;
+            }
+        }
+
+        return null;
+    }
+
     /**
      * Special Glow Blur post process only blurring the alpha channel
      * It enforces keeping the most luminous color in the color channel.
@@ -61,6 +80,11 @@
          * Should we display highlight as a solid stroke?
          */
         isStroke?: boolean;
+
+        /**
+         * The rendering group to draw the layer in.
+         */
+        renderingGroupId: number;
     }
 
     /**
@@ -225,6 +249,7 @@
                 blurVerticalSize: 1.0,
                 alphaBlendingMode: Engine.ALPHA_COMBINE,
                 camera: null,
+                renderingGroupId: -1,
                 ...options,
             };
 
@@ -233,7 +258,8 @@
                 alphaBlendingMode: this._options.alphaBlendingMode,
                 camera: this._options.camera,
                 mainTextureFixedSize: this._options.mainTextureFixedSize,
-                mainTextureRatio: this._options.mainTextureRatio
+                mainTextureRatio: this._options.mainTextureRatio,
+                renderingGroupId: this._options.renderingGroupId
             });
 
             // Do not render as long as no meshes have been added
@@ -460,6 +486,10 @@
                 return false;
             };
 
+            if (!super.hasMesh(mesh)) {
+                return false;
+            }
+
             return true;
         }
 
@@ -553,6 +583,10 @@
                 return false;
             }
 
+            if (!super.hasMesh(mesh)) {
+                return false;
+            }
+
             return this._meshes[mesh.uniqueId] !== undefined && this._meshes[mesh.uniqueId] !== null;
         }
 
@@ -587,6 +621,10 @@
                     observerDefault: mesh.onAfterRenderObservable.add(this._defaultStencilReference),
                     glowEmissiveOnly: glowEmissiveOnly
                 };
+
+                mesh.onDisposeObservable.add(() => {
+                    this._disposeMesh(mesh);
+                });
             }
 
             this._shouldRender = true;

+ 5 - 0
src/Layer/babylon.layer.ts

@@ -63,6 +63,11 @@
             this.color = color === undefined ? new Color4(1, 1, 1, 1) : color;
             
             this._scene = <Scene>(scene || Engine.LastCreatedScene);
+            let layerComponent = this._scene._getComponent(SceneComponentConstants.NAME_LAYER) as LayerSceneComponent;
+            if (!layerComponent) {
+                layerComponent = new LayerSceneComponent(this._scene);
+                this._scene._addComponent(layerComponent);
+            }
             this._scene.layers.push(this);
 
             var engine = this._scene.getEngine();

+ 126 - 0
src/Layer/babylon.layerSceneComponent.ts

@@ -0,0 +1,126 @@
+module BABYLON {
+    export interface AbstractScene {
+        /**
+         * The list of layers (background and foreground) of the scene
+         */
+        layers: Array<Layer>;
+    }
+
+    /**
+     * Defines the layer scene component responsible to manage any layers
+     * in a given scene.
+     */
+    export class LayerSceneComponent implements ISceneComponent {
+        /**
+         * The component name helpfull to identify the component in the list of scene components.
+         */
+        public readonly name = SceneComponentConstants.NAME_LAYER;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        private _engine: Engine;
+        private _layers: Array<Layer>;
+
+        /**
+         * Creates a new instance of the component for the given scene
+         * @param scene Defines the scene to register the component in
+         */
+        constructor(scene: Scene) {
+            this.scene = scene;
+            this._engine = scene.getEngine();
+            this._layers = scene.layers = new Array<Layer>();
+        }
+
+        /**
+         * Registers the component in a given scene
+         */
+        public register(): void {
+            this.scene._beforeCameraDrawStage.registerStep(SceneComponentConstants.STEP_BEFORECAMERADRAW_LAYER, this, this._drawBackground);
+            this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_LAYER, this, this._drawForeground);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            for (let layer of this._layers) {
+                layer._rebuild();
+            }
+        }
+
+        /**
+         * Serializes the component data to the specified json object
+         * @param serializationObject The object to serialize to
+         */
+        public serialize(serializationObject: any): void {
+            // TODO. Implement layer serialization
+            // serializationObject.layers = [];
+
+            // for (let layer of this._layers) {
+            //     if (layer.serialize) {
+            //         serializationObject.layers.push(layer.serialize());
+            //     }
+            // }
+        }
+
+        /**
+         * Adds all the element from the container to the scene
+         * @param container the container holding the elements
+         */
+        public addFromContainer(container: AbstractScene): void {
+            if (!container.layers) {
+                return;
+            }
+            // container.layers.forEach((o) => {
+            //     this.scene.addLayer(o);
+            // });
+        }
+
+        /**
+         * Removes all the elements in the container from the scene
+         * @param container contains the elements to remove 
+         */
+        public removeFromContainer(container: AbstractScene): void {
+            if (!container.layers) {
+                return;
+            }
+            // container.layers.forEach((o) => {
+            //     this.scene.removeLayer(o);
+            // });
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            while (this._layers.length) {
+                this._layers[0].dispose();
+            }
+        }
+
+        private _draw(camera: Camera, isBackground: boolean): void {
+            if (this._layers.length) {
+                this._engine.setDepthBuffer(false);
+                const cameraLayerMask = camera.layerMask;
+                for (let layer of this._layers) {
+                    if (layer.isBackground === isBackground && ((layer.layerMask & cameraLayerMask) !== 0)) {
+                        layer.render();
+                    }
+                }
+                this._engine.setDepthBuffer(true);
+            }
+        }
+
+        private _drawBackground(camera: Camera): void {
+            this._draw(camera, true);
+        }
+
+        private _drawForeground(camera: Camera): void {
+            this._draw(camera, false);
+        }
+    }
+} 

+ 7 - 1
src/LensFlare/babylon.lensFlareSystem.ts

@@ -19,6 +19,12 @@
         constructor(public name: string, emitter: any, scene: Scene) {
 
             this._scene = scene || Engine.LastCreatedScene;
+            let component = this._scene._getComponent(SceneComponentConstants.NAME_LENSFLARESYSTEM) as LensFlareSystemSceneComponent;
+            if (!component) {
+                component = new LensFlareSystemSceneComponent(this._scene);
+                scene._addComponent(component);
+            }
+
             this._emitter = emitter;
             this.id = name;
             scene.lensFlareSystems.push(this);
@@ -305,4 +311,4 @@
             return serializationObject;
         }
     }
-} 
+}

+ 182 - 0
src/LensFlare/babylon.lensFlareSystemSceneComponent.ts

@@ -0,0 +1,182 @@
+module BABYLON {
+    // Adds the parser to the scene parsers.
+    AbstractScene.AddParser(SceneComponentConstants.NAME_LENSFLARESYSTEM, (parsedData: any, scene: Scene, container: AssetContainer, rootUrl: string) => {
+        // Lens flares
+        if (parsedData.lensFlareSystems !== undefined && parsedData.lensFlareSystems !== null) {
+            for (let index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
+                var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
+                var lf = LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
+                container.lensFlareSystems.push(lf);
+            }
+        }
+    });
+
+    export interface AbstractScene {
+        /**
+         * The list of lens flare system added to the scene
+         * @see http://doc.babylonjs.com/how_to/how_to_use_lens_flares
+         */         
+        lensFlareSystems: Array<LensFlareSystem>;
+        /**
+         * Removes the given lens flare system from this scene.
+         * @param toRemove The lens flare system to remove
+         * @returns The index of the removed lens flare system
+         */          
+        removeLensFlareSystem(toRemove: LensFlareSystem): number;
+
+        /**
+         * Adds the given lens flare system to this scene
+         * @param newLensFlareSystem The lens flare system to add
+         */          
+        addLensFlareSystem(newLensFlareSystem: LensFlareSystem): void;
+
+        /**
+         * Gets a lens flare system using its name
+         * @param name defines the name to look for
+         * @returns the lens flare system or null if not found
+         */
+        getLensFlareSystemByName(name: string): Nullable<LensFlareSystem>;
+
+        /**
+         * Gets a lens flare system using its id
+         * @param id defines the id to look for
+         * @returns the lens flare system or null if not found
+         */
+        getLensFlareSystemByID(id: string): Nullable<LensFlareSystem>;
+    }
+
+    AbstractScene.prototype.getLensFlareSystemByName = function(name: string): Nullable<LensFlareSystem> {
+        for (var index = 0; index < this.lensFlareSystems.length; index++) {
+            if (this.lensFlareSystems[index].name === name) {
+                return this.lensFlareSystems[index];
+            }
+        }
+
+        return null;
+    }
+
+    AbstractScene.prototype.getLensFlareSystemByID = function(id: string): Nullable<LensFlareSystem> {
+        for (var index = 0; index < this.lensFlareSystems.length; index++) {
+            if (this.lensFlareSystems[index].id === id) {
+                return this.lensFlareSystems[index];
+            }
+        }
+
+        return null;
+    }
+
+    AbstractScene.prototype.removeLensFlareSystem = function(toRemove: LensFlareSystem): number {
+        var index = this.lensFlareSystems.indexOf(toRemove);
+        if (index !== -1) {
+            this.lensFlareSystems.splice(index, 1);
+        }
+        return index;
+    }
+
+    AbstractScene.prototype.addLensFlareSystem = function(newLensFlareSystem: LensFlareSystem): void {
+        this.lensFlareSystems.push(newLensFlareSystem);
+    }
+
+    /**
+     * Defines the layer scene component responsible to manage any layers
+     * in a given scene.
+     */
+    export class LensFlareSystemSceneComponent implements ISceneComponent {
+        /**
+         * The component name helpfull to identify the component in the list of scene components.
+         */
+        public readonly name = SceneComponentConstants.NAME_LENSFLARESYSTEM;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        private _lensFlareSystems: Array<LensFlareSystem>;
+
+        /**
+         * Creates a new instance of the component for the given scene
+         * @param scene Defines the scene to register the component in
+         */
+        constructor(scene: Scene) {
+            this.scene = scene;
+
+            this._lensFlareSystems = scene.lensFlareSystems = new Array<LensFlareSystem>();
+        }
+
+        /**
+         * Registers the component in a given scene
+         */
+        public register(): void {
+            this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_LENSFLARESYSTEM, this, this._draw);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            // Nothing to do for lens flare
+        }
+
+        /**
+         * Adds all the element from the container to the scene
+         * @param container the container holding the elements
+         */
+        public addFromContainer(container: AbstractScene): void {
+            if (!container.lensFlareSystems) {
+                return;
+            }
+            container.lensFlareSystems.forEach((o) => {
+                this.scene.addLensFlareSystem(o);
+            });
+        }
+
+        /**
+         * Removes all the elements in the container from the scene
+         * @param container contains the elements to remove 
+         */
+        public removeFromContainer(container: AbstractScene): void {
+            if (!container.lensFlareSystems) {
+                return;
+            }
+            container.lensFlareSystems.forEach((o) => {
+                this.scene.removeLensFlareSystem(o);
+            });
+        }
+
+        /**
+         * Serializes the component data to the specified json object
+         * @param serializationObject The object to serialize to
+         */
+        public serialize(serializationObject: any): void {
+            // Lens flares
+            serializationObject.lensFlareSystems = [];
+            for (let lensFlareSystem of this._lensFlareSystems) {
+                serializationObject.lensFlareSystems.push(lensFlareSystem.serialize());
+            }
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            while (this._lensFlareSystems.length) {
+                this._lensFlareSystems[0].dispose();
+            }
+        }
+
+        private _draw(camera: Camera): void {
+            // Lens flares
+            if (this.scene.lensFlaresEnabled) {
+                Tools.StartPerformanceCounter("Lens flares", this._lensFlareSystems.length > 0);
+                for (let lensFlareSystem of this._lensFlareSystems) {
+                    if ((camera.layerMask & lensFlareSystem.layerMask) !== 0) {
+                        lensFlareSystem.render();
+                    }
+                }
+                Tools.EndPerformanceCounter("Lens flares", this._lensFlareSystems.length > 0);
+            }
+        }
+    }
+} 

+ 3 - 19
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -1,5 +1,4 @@
 module BABYLON {
-
     var parseMaterialById = (id: string, parsedData: any, scene: Scene, rootUrl: string) => {
         for (var index = 0, cache = parsedData.materials.length; index < cache; index++) {
             var parsedMaterial = parsedData.materials[index];
@@ -309,21 +308,12 @@
                 }
             }
 
-            // Lens flares
-            if (parsedData.lensFlareSystems !== undefined && parsedData.lensFlareSystems !== null) {
-                for (index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
-                    var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
-                    var lf = LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
-                    container.lensFlareSystems.push(lf);
-                }
-            }
-
             // Shadows
             if (parsedData.shadowGenerators !== undefined && parsedData.shadowGenerators !== null) {
                 for (index = 0, cache = parsedData.shadowGenerators.length; index < cache; index++) {
                     var parsedShadowGenerator = parsedData.shadowGenerators[index];
-                    var sg = ShadowGenerator.Parse(parsedShadowGenerator, scene);
-                    container.shadowGenerators.push(sg);
+                    ShadowGenerator.Parse(parsedShadowGenerator, scene);
+                    // SG would be available on their associated lights
                 }
             }
 
@@ -357,13 +347,7 @@
                 }
             }
 
-            // Effect layers
-            if (parsedData.effectLayers) {
-                for (index = 0; index < parsedData.effectLayers.length; index++) {
-                    var effectLayer = EffectLayer.Parse(parsedData.effectLayers[index], scene, rootUrl);
-                    container.effectLayers.push(effectLayer);
-                }
-            }
+            AbstractScene.Parse(parsedData, scene, container, rootUrl);
 
             // Actions (scene)
             if (parsedData.actions !== undefined && parsedData.actions !== null) {

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

@@ -1,7 +1,6 @@
 module BABYLON {
     export class CubeTexture extends BaseTexture {
         public url: string;
-        public coordinatesMode = Texture.CUBIC_MODE;
 
         /**
          * Gets or sets the center of the bounding box associated with the cube texture
@@ -110,6 +109,7 @@
             this.isCube = true;
             this._textureMatrix = Matrix.Identity();
             this._createPolynomials = createPolynomials;
+            this.coordinatesMode = Texture.CUBIC_MODE;
 
             if (!rootUrl && !files) {
                 return;

+ 16 - 3
src/Materials/Textures/babylon.internalTexture.ts

@@ -53,6 +53,10 @@ module BABYLON {
          * Texture content is a depth texture
          */
         public static DATASOURCE_DEPTHTEXTURE = 11;
+        /**
+         * Texture data comes from a raw cube data encoded with RGBD
+         */
+        public static DATASOURCE_CUBERAW_RGBD = 12;
 
         /**
          * Defines if the texture is ready
@@ -146,6 +150,8 @@ module BABYLON {
         /** @hidden */
         public _bufferViewArray: Nullable<ArrayBufferView[]>;
         /** @hidden */
+        public _bufferViewArrayArray: Nullable<ArrayBufferView[][]>;
+        /** @hidden */
         public _size: number;
         /** @hidden */
         public _extension: string;
@@ -186,7 +192,7 @@ module BABYLON {
         /** @hidden */
         public _comparisonFunction: number = 0;
         /** @hidden */
-        public _sphericalPolynomial: Nullable<SphericalPolynomial>;
+        public _sphericalPolynomial: Nullable<SphericalPolynomial> = null;
         /** @hidden */
         public _lodGenerationScale: number = 0;
         /** @hidden */
@@ -350,12 +356,19 @@ module BABYLON {
                     return;
 
                 case InternalTexture.DATASOURCE_CUBERAW:
-                    proxy = this._engine.createRawCubeTexture(this._bufferViewArray, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
+                    proxy = this._engine.createRawCubeTexture(this._bufferViewArray!, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
                     proxy._swapAndDie(this);
-
                     this.isReady = true;
                     return;
 
+                case InternalTexture.DATASOURCE_CUBERAW_RGBD:
+                    proxy = this._engine.createRawCubeTexture(null, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
+                    RawCubeTexture._UpdateRGBDAsync(proxy, this._bufferViewArrayArray!, this._sphericalPolynomial, this._lodGenerationScale, this._lodGenerationOffset).then(() => {
+                        this.isReady = true;
+                    });
+                    proxy._swapAndDie(this);
+                    return;
+
                 case InternalTexture.DATASOURCE_CUBEPREFILTERED:
                     proxy = this._engine.createPrefilteredCubeTexture(this.url, null, this._lodGenerationScale, this._lodGenerationOffset, (proxy) => {
                         if (proxy) {

+ 85 - 0
src/Materials/Textures/babylon.rawCubeTexture.ts

@@ -0,0 +1,85 @@
+module BABYLON {
+    /**
+     * Raw cube texture where the raw buffers are passed in
+     */
+    export class RawCubeTexture extends CubeTexture {
+        /**
+         * Creates a cube texture where the raw buffers are passed in.
+         * @param scene defines the scene the texture is attached to
+         * @param data defines the array of data to use to create each face
+         * @param size defines the size of the textures
+         * @param format defines the format of the data
+         * @param type defines the type of the data (like BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT)
+         * @param generateMipMaps  defines if the engine should generate the mip levels
+         * @param invertY defines if data must be stored with Y axis inverted
+         * @param samplingMode defines the required sampling mode (like BABYLON.Texture.NEAREST_SAMPLINGMODE)
+         * @param compression defines the compression used (null by default)
+         */
+        constructor(scene: Scene, data: Nullable<ArrayBufferView[]>, size: number,
+                    format: number = Engine.TEXTUREFORMAT_RGBA, type: number = Engine.TEXTURETYPE_UNSIGNED_INT,
+                    generateMipMaps: boolean = false, invertY: boolean = false, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE,
+                    compression: Nullable<string> = null) {
+            super("", scene);
+
+            this._texture = scene.getEngine().createRawCubeTexture(data, size, format, type, generateMipMaps, invertY, samplingMode, compression);
+        }
+
+        /**
+         * Updates the raw cube texture.
+         * @param data defines the data to store
+         * @param format defines the data format
+         * @param type defines the type fo the data (BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT by default)
+         * @param invertY defines if data must be stored with Y axis inverted
+         * @param compression defines the compression used (null by default)
+         * @param level defines which level of the texture to update
+         */
+        public update(data: ArrayBufferView[], format: number, type: number, invertY: boolean, compression: Nullable<string> = null, level = 0): void {
+            this._texture!.getEngine().updateRawCubeTexture(this._texture!, data, format, type, invertY, compression);
+        }
+
+        /**
+         * Updates a raw cube texture with RGBD encoded data.
+         * @param data defines the array of data [mipmap][face] to use to create each face
+         * @param sphericalPolynomial defines the spherical polynomial for irradiance
+         * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
+         * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
+         * @returns a promsie that resolves when the operation is complete
+         */
+        public updateRGBDAsync(data: ArrayBufferView[][], sphericalPolynomial: Nullable<SphericalPolynomial> = null, lodScale: number = 0.8, lodOffset: number = 0): Promise<void> {
+            return RawCubeTexture._UpdateRGBDAsync(this._texture!, data, sphericalPolynomial, lodScale, lodOffset);
+        }
+
+        /**
+         * Clones the raw cube texture.
+         * @return a new cube texture
+         */
+        public clone(): CubeTexture {
+            return SerializationHelper.Clone(() => {
+                const scene = this.getScene()!;
+                const internalTexture = this._texture!;
+
+                const texture = new RawCubeTexture(scene, internalTexture._bufferViewArray!, internalTexture.width, internalTexture.format, internalTexture.type,
+                    internalTexture.generateMipMaps, internalTexture.invertY, internalTexture.samplingMode, internalTexture._compression);
+
+                if (internalTexture.dataSource === InternalTexture.DATASOURCE_CUBERAW_RGBD) {
+                    texture.updateRGBDAsync(internalTexture._bufferViewArrayArray!, internalTexture._sphericalPolynomial, internalTexture._lodGenerationScale, internalTexture._lodGenerationOffset);
+                }
+
+                return texture;
+            }, this);
+        }
+
+        /** @hidden */
+        public static _UpdateRGBDAsync(internalTexture: InternalTexture, data: ArrayBufferView[][], sphericalPolynomial: Nullable<SphericalPolynomial>, lodScale: number, lodOffset: number): Promise<void> {
+            internalTexture._dataSource = InternalTexture.DATASOURCE_CUBERAW_RGBD;
+            internalTexture._bufferViewArrayArray = data;
+            internalTexture._lodGenerationScale = lodScale;
+            internalTexture._lodGenerationOffset = lodOffset;
+            internalTexture._sphericalPolynomial = sphericalPolynomial;
+
+            return EnvironmentTextureTools.UploadLevelsAsync(internalTexture, data).then(() => {
+                internalTexture.isReady = true;
+            });
+        }
+    }
+}

+ 228 - 79
src/Math/babylon.sphericalPolynomial.ts

@@ -1,15 +1,57 @@
 module BABYLON {
+    /**
+     * Class representing spherical polynomial coefficients to the 3rd degree
+     */
     export class SphericalPolynomial {
+        /**
+         * The x coefficients of the spherical polynomial
+         */
         public x: Vector3 = Vector3.Zero();
+
+        /**
+         * The y coefficients of the spherical polynomial
+         */
         public y: Vector3 = Vector3.Zero();
+
+        /**
+         * The z coefficients of the spherical polynomial
+         */
         public z: Vector3 = Vector3.Zero();
+
+        /**
+         * The xx coefficients of the spherical polynomial
+         */
         public xx: Vector3 = Vector3.Zero();
+
+        /**
+         * The yy coefficients of the spherical polynomial
+         */
         public yy: Vector3 = Vector3.Zero();
+
+        /**
+         * The zz coefficients of the spherical polynomial
+         */
         public zz: Vector3 = Vector3.Zero();
+
+        /**
+         * The xy coefficients of the spherical polynomial
+         */
         public xy: Vector3 = Vector3.Zero();
+
+        /**
+         * The yz coefficients of the spherical polynomial
+         */
         public yz: Vector3 = Vector3.Zero();
+
+        /**
+         * The zx coefficients of the spherical polynomial
+         */
         public zx: Vector3 = Vector3.Zero();
 
+        /**
+         * Adds an ambient color to the spherical polynomial
+         * @param color the color to add
+         */
         public addAmbient(color: Color3): void {
             var colorVector = new Vector3(color.r, color.g, color.b);
             this.xx = this.xx.add(colorVector);
@@ -17,26 +59,10 @@ module BABYLON {
             this.zz = this.zz.add(colorVector);
         }
 
-        public static getSphericalPolynomialFromHarmonics(harmonics: SphericalHarmonics): SphericalPolynomial {
-            var result = new SphericalPolynomial();
-
-            result.x = harmonics.L11.scale(1.02333);
-            result.y = harmonics.L1_1.scale(1.02333);
-            result.z = harmonics.L10.scale(1.02333);
-
-            result.xx = harmonics.L00.scale(0.886277).subtract(harmonics.L20.scale(0.247708)).add(harmonics.L22.scale(0.429043));
-            result.yy = harmonics.L00.scale(0.886277).subtract(harmonics.L20.scale(0.247708)).subtract(harmonics.L22.scale(0.429043));
-            result.zz = harmonics.L00.scale(0.886277).add(harmonics.L20.scale(0.495417));
-
-            result.yz = harmonics.L2_1.scale(0.858086);
-            result.zx = harmonics.L21.scale(0.858086);
-            result.xy = harmonics.L2_2.scale(0.858086);
-
-            result.scale(1.0 / Math.PI);
-
-            return result;
-        }
-
+        /**
+         * Scales the spherical polynomial by the given amount
+         * @param scale the amount to scale
+         */
         public scale(scale: number)
         {
             this.x = this.x.scale(scale);
@@ -49,105 +75,228 @@ module BABYLON {
             this.zx = this.zx.scale(scale);
             this.xy = this.xy.scale(scale);
         }
+
+        /**
+         * Gets the spherical polynomial from harmonics
+         * @param harmonics the spherical harmonics
+         * @returns the spherical polynomial
+         */
+        public static FromHarmonics(harmonics: SphericalHarmonics): SphericalPolynomial {
+            var result = new SphericalPolynomial();
+
+            result.x = harmonics.l11.scale(1.02333);
+            result.y = harmonics.l1_1.scale(1.02333);
+            result.z = harmonics.l10.scale(1.02333);
+
+            result.xx = harmonics.l00.scale(0.886277).subtract(harmonics.l20.scale(0.247708)).add(harmonics.lL22.scale(0.429043));
+            result.yy = harmonics.l00.scale(0.886277).subtract(harmonics.l20.scale(0.247708)).subtract(harmonics.lL22.scale(0.429043));
+            result.zz = harmonics.l00.scale(0.886277).add(harmonics.l20.scale(0.495417));
+
+            result.yz = harmonics.l2_1.scale(0.858086);
+            result.zx = harmonics.l21.scale(0.858086);
+            result.xy = harmonics.l2_2.scale(0.858086);
+
+            result.scale(1.0 / Math.PI);
+
+            return result;
+        }
+
+        /**
+         * Constructs a spherical polynomial from an array.
+         * @param data defines the 9x3 coefficients (x, y, z, xx, yy, zz, yz, zx, xy)
+         * @returns the spherical polynomial
+         */
+        public static FromArray(data: ArrayLike<ArrayLike<number>>): SphericalPolynomial {
+            const sp = new SphericalPolynomial();
+            Vector3.FromArrayToRef(data[0], 0, sp.x);
+            Vector3.FromArrayToRef(data[1], 0, sp.y);
+            Vector3.FromArrayToRef(data[2], 0, sp.z);
+            Vector3.FromArrayToRef(data[3], 0, sp.xx);
+            Vector3.FromArrayToRef(data[4], 0, sp.yy);
+            Vector3.FromArrayToRef(data[5], 0, sp.zz);
+            Vector3.FromArrayToRef(data[6], 0, sp.yz);
+            Vector3.FromArrayToRef(data[7], 0, sp.zx);
+            Vector3.FromArrayToRef(data[8], 0, sp.xy);
+            return sp;
+        }
     }
 
+    /**
+     * Class representing spherical harmonics coefficients to the 3rd degree
+     */
     export class SphericalHarmonics {
-        public L00: Vector3 = Vector3.Zero();
-        public L1_1: Vector3 = Vector3.Zero();
-        public L10: Vector3 = Vector3.Zero();
-        public L11: Vector3 = Vector3.Zero();
-        public L2_2: Vector3 = Vector3.Zero();
-        public L2_1: Vector3 = Vector3.Zero();
-        public L20: Vector3 = Vector3.Zero();
-        public L21: Vector3 = Vector3.Zero();
-        public L22: Vector3 = Vector3.Zero();
+        /**
+         * The l0,0 coefficients of the spherical harmonics
+         */
+        public l00: Vector3 = Vector3.Zero();
+
+        /**
+         * The l1,-1 coefficients of the spherical harmonics
+         */
+        public l1_1: Vector3 = Vector3.Zero();
+
+        /**
+         * The l1,0 coefficients of the spherical harmonics
+         */
+        public l10: Vector3 = Vector3.Zero();
 
+        /**
+         * The l1,1 coefficients of the spherical harmonics
+         */
+        public l11: Vector3 = Vector3.Zero();
+
+        /**
+         * The l2,-2 coefficients of the spherical harmonics
+         */
+        public l2_2: Vector3 = Vector3.Zero();
+
+        /**
+         * The l2,-1 coefficients of the spherical harmonics
+         */
+        public l2_1: Vector3 = Vector3.Zero();
+
+        /**
+         * The l2,0 coefficients of the spherical harmonics
+         */
+        public l20: Vector3 = Vector3.Zero();
+
+        /**
+         * The l2,1 coefficients of the spherical harmonics
+         */
+        public l21: Vector3 = Vector3.Zero();
+
+        /**
+         * The l2,2 coefficients of the spherical harmonics
+         */
+        public lL22: Vector3 = Vector3.Zero();
+
+        /**
+         * Adds a light to the spherical harmonics
+         * @param direction the direction of the light
+         * @param color the color of the light
+         * @param deltaSolidAngle the delta solid angle of the light
+         */
         public addLight(direction: Vector3, color: Color3, deltaSolidAngle: number): void {
             var colorVector = new Vector3(color.r, color.g, color.b);
             var c = colorVector.scale(deltaSolidAngle);
 
-            this.L00 = this.L00.add(c.scale(0.282095));
+            this.l00 = this.l00.add(c.scale(0.282095));
 
-            this.L1_1 = this.L1_1.add(c.scale(0.488603 * direction.y));
-            this.L10 = this.L10.add(c.scale(0.488603 * direction.z));
-            this.L11 = this.L11.add(c.scale(0.488603 * direction.x));
+            this.l1_1 = this.l1_1.add(c.scale(0.488603 * direction.y));
+            this.l10 = this.l10.add(c.scale(0.488603 * direction.z));
+            this.l11 = this.l11.add(c.scale(0.488603 * direction.x));
 
-            this.L2_2 = this.L2_2.add(c.scale(1.092548 * direction.x * direction.y));
-            this.L2_1 = this.L2_1.add(c.scale(1.092548 * direction.y * direction.z));
-            this.L21 = this.L21.add(c.scale(1.092548 * direction.x * direction.z));
+            this.l2_2 = this.l2_2.add(c.scale(1.092548 * direction.x * direction.y));
+            this.l2_1 = this.l2_1.add(c.scale(1.092548 * direction.y * direction.z));
+            this.l21 = this.l21.add(c.scale(1.092548 * direction.x * direction.z));
 
-            this.L20 = this.L20.add(c.scale(0.315392 * (3.0 * direction.z * direction.z - 1.0)));
-            this.L22 = this.L22.add(c.scale(0.546274 * (direction.x * direction.x - direction.y * direction.y)));
+            this.l20 = this.l20.add(c.scale(0.315392 * (3.0 * direction.z * direction.z - 1.0)));
+            this.lL22 = this.lL22.add(c.scale(0.546274 * (direction.x * direction.x - direction.y * direction.y)));
         }
 
+        /**
+         * Scales the spherical harmonics by the given amount
+         * @param scale the amount to scale
+         */
         public scale(scale: number): void {
-            this.L00 = this.L00.scale(scale);
-            this.L1_1 = this.L1_1.scale(scale);
-            this.L10 = this.L10.scale(scale);
-            this.L11 = this.L11.scale(scale);
-            this.L2_2 = this.L2_2.scale(scale);
-            this.L2_1 = this.L2_1.scale(scale);
-            this.L20 = this.L20.scale(scale);
-            this.L21 = this.L21.scale(scale);
-            this.L22 = this.L22.scale(scale);
+            this.l00 = this.l00.scale(scale);
+            this.l1_1 = this.l1_1.scale(scale);
+            this.l10 = this.l10.scale(scale);
+            this.l11 = this.l11.scale(scale);
+            this.l2_2 = this.l2_2.scale(scale);
+            this.l2_1 = this.l2_1.scale(scale);
+            this.l20 = this.l20.scale(scale);
+            this.l21 = this.l21.scale(scale);
+            this.lL22 = this.lL22.scale(scale);
         }
 
+        /**
+         * Convert from incident radiance (Li) to irradiance (E) by applying convolution with the cosine-weighted hemisphere.
+         *
+         * ```
+         * E_lm = A_l * L_lm
+         * ```
+         *
+         * In spherical harmonics this convolution amounts to scaling factors for each frequency band.
+         * This corresponds to equation 5 in "An Efficient Representation for Irradiance Environment Maps", where
+         * the scaling factors are given in equation 9.
+         */
         public convertIncidentRadianceToIrradiance(): void
         {
-            // Convert from incident radiance (Li) to irradiance (E) by applying convolution with the cosine-weighted hemisphere.
-            //
-            //      E_lm = A_l * L_lm
-            // 
-            // In spherical harmonics this convolution amounts to scaling factors for each frequency band.
-            // This corresponds to equation 5 in "An Efficient Representation for Irradiance Environment Maps", where
-            // the scaling factors are given in equation 9.
-
             // Constant (Band 0)
-            this.L00 = this.L00.scale(3.141593);
+            this.l00 = this.l00.scale(3.141593);
 
             // Linear (Band 1)
-            this.L1_1 = this.L1_1.scale(2.094395);
-            this.L10 = this.L10.scale(2.094395);
-            this.L11 = this.L11.scale(2.094395);
+            this.l1_1 = this.l1_1.scale(2.094395);
+            this.l10 = this.l10.scale(2.094395);
+            this.l11 = this.l11.scale(2.094395);
 
             // Quadratic (Band 2)
-            this.L2_2 = this.L2_2.scale(0.785398);
-            this.L2_1 = this.L2_1.scale(0.785398);
-            this.L20 = this.L20.scale(0.785398);
-            this.L21 = this.L21.scale(0.785398);
-            this.L22 = this.L22.scale(0.785398);
+            this.l2_2 = this.l2_2.scale(0.785398);
+            this.l2_1 = this.l2_1.scale(0.785398);
+            this.l20 = this.l20.scale(0.785398);
+            this.l21 = this.l21.scale(0.785398);
+            this.lL22 = this.lL22.scale(0.785398);
         }
 
+        /**
+         * Convert from irradiance to outgoing radiance for Lambertian BDRF, suitable for efficient shader evaluation.
+         *
+         * ```
+         * L = (1/pi) * E * rho
+         * ```
+         *
+         * This is done by an additional scale by 1/pi, so is a fairly trivial operation but important conceptually.
+         */
         public convertIrradianceToLambertianRadiance(): void
         {
-            // Convert from irradiance to outgoing radiance for Lambertian BDRF, suitable for efficient shader evaluation.
-            //      L = (1/pi) * E * rho
-            // 
-            // This is done by an additional scale by 1/pi, so is a fairly trivial operation but important conceptually.
-
             this.scale(1.0 / Math.PI);
 
             // The resultant SH now represents outgoing radiance, so includes the Lambert 1/pi normalisation factor but without albedo (rho) applied
             // (The pixel shader must apply albedo after texture fetches, etc).
         }
 
-        public static getsphericalHarmonicsFromPolynomial(polynomial: SphericalPolynomial): SphericalHarmonics
+        /**
+         * Gets the spherical harmonics from polynomial
+         * @param polynomial the spherical polynomial
+         * @returns the spherical harmonics
+         */
+        public static FromPolynomial(polynomial: SphericalPolynomial): SphericalHarmonics
         {
             var result = new SphericalHarmonics();
 
-            result.L00 = polynomial.xx.scale(0.376127).add(polynomial.yy.scale(0.376127)).add(polynomial.zz.scale(0.376126));
-            result.L1_1 = polynomial.y.scale(0.977204);
-            result.L10 = polynomial.z.scale(0.977204);
-            result.L11 = polynomial.x.scale(0.977204);
-            result.L2_2 = polynomial.xy.scale(1.16538);
-            result.L2_1 = polynomial.yz.scale(1.16538);
-            result.L20 = polynomial.zz.scale(1.34567).subtract(polynomial.xx.scale(0.672834)).subtract(polynomial.yy.scale(0.672834));
-            result.L21 = polynomial.zx.scale(1.16538);
-            result.L22 = polynomial.xx.scale(1.16538).subtract(polynomial.yy.scale(1.16538));
+            result.l00 = polynomial.xx.scale(0.376127).add(polynomial.yy.scale(0.376127)).add(polynomial.zz.scale(0.376126));
+            result.l1_1 = polynomial.y.scale(0.977204);
+            result.l10 = polynomial.z.scale(0.977204);
+            result.l11 = polynomial.x.scale(0.977204);
+            result.l2_2 = polynomial.xy.scale(1.16538);
+            result.l2_1 = polynomial.yz.scale(1.16538);
+            result.l20 = polynomial.zz.scale(1.34567).subtract(polynomial.xx.scale(0.672834)).subtract(polynomial.yy.scale(0.672834));
+            result.l21 = polynomial.zx.scale(1.16538);
+            result.lL22 = polynomial.xx.scale(1.16538).subtract(polynomial.yy.scale(1.16538));
 
             result.scale(Math.PI);
 
             return result;
         }
+
+        /**
+         * Constructs a spherical harmonics from an array.
+         * @param data defines the 9x3 coefficients (l00, l1-1, l10, l11, l2-2, l2-1, l20, l21, l22)
+         * @returns the spherical harmonics
+         */
+        public static FromArray(data: ArrayLike<ArrayLike<number>>): SphericalHarmonics {
+            const sh = new SphericalHarmonics();
+            Vector3.FromArrayToRef(data[0], 0, sh.l00);
+            Vector3.FromArrayToRef(data[1], 0, sh.l1_1);
+            Vector3.FromArrayToRef(data[2], 0, sh.l10);
+            Vector3.FromArrayToRef(data[3], 0, sh.l11);
+            Vector3.FromArrayToRef(data[4], 0, sh.l2_2);
+            Vector3.FromArrayToRef(data[5], 0, sh.l2_1);
+            Vector3.FromArrayToRef(data[6], 0, sh.l20);
+            Vector3.FromArrayToRef(data[7], 0, sh.l21);
+            Vector3.FromArrayToRef(data[8], 0, sh.lL22);
+            return sh;
+        }
     }
 }

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

@@ -1285,9 +1285,10 @@
         }
 
         /**
-         * Triggers the draw call for the mesh.
-         * Usually, you don't need to call this method by your own because the mesh rendering is handled by the scene rendering manager.   
-         * Returns the Mesh.   
+         * Triggers the draw call for the mesh. Usually, you don't need to call this method by your own because the mesh rendering is handled by the scene rendering manager
+         * @param subMesh defines the subMesh to render
+         * @param enableAlphaMode defines if alpha mode can be changed
+         * @returns the current mesh
          */
         public render(subMesh: SubMesh, enableAlphaMode: boolean): Mesh {
 
@@ -1766,14 +1767,6 @@
                 this.instances[0].dispose();
             }
 
-            // Effect layers.
-            let effectLayers = this.getScene().effectLayers;
-            for (let i = 0; i < effectLayers.length; i++) {
-                let effectLayer = effectLayers[i];
-                if (effectLayer) {
-                    effectLayer._disposeMesh(this);
-                }
-            }
             super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
 

+ 44 - 0
src/Particles/babylon.IParticleSystem.ts

@@ -255,11 +255,13 @@ module BABYLON {
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color defines the color to affect to the specified gradient
          * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
+         * @returns the current particle system
          */
         addColorGradient(gradient: number, color1: Color4, color2?: Color4): IParticleSystem;   
         /**
          * Remove a specific color gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         removeColorGradient(gradient: number): IParticleSystem;
         /**
@@ -267,11 +269,13 @@ module BABYLON {
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the size factor to affect to the specified gradient
          * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
          */
         addSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
         /**
          * Remove a specific size gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         removeSizeGradient(gradient: number): IParticleSystem;
         /**
@@ -286,5 +290,45 @@ module BABYLON {
          * @returns the list of size gradients
          */
         getSizeGradients(): Nullable<Array<FactorGradient>>;
+        /**
+         * Gets the current list of angular speed gradients.
+         * You must use addAngularSpeedGradient and removeAngularSpeedGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        getAngularSpeedGradients(): Nullable<Array<FactorGradient>>;   
+        /**
+         * Adds a new angular speed gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        addAngularSpeedGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
+        /**
+         * Remove a specific angular speed gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        removeAngularSpeedGradient(gradient: number): IParticleSystem;   
+        /**
+         * Gets the current list of velocity gradients.
+         * You must use addVelocityGradient and removeVelocityGradient to udpate this list
+         * @returns the list of velocity gradients
+         */
+        getVelocityGradients(): Nullable<Array<FactorGradient>>;
+        /**
+         * Adds a new velocity gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        addVelocityGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
+        /**
+         * Remove a specific velocity gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        removeVelocityGradient(gradient: number): IParticleSystem;
     }  
 }

+ 223 - 71
src/Particles/babylon.gpuParticleSystem.ts

@@ -63,7 +63,9 @@
         private _updateEffectOptions: EffectCreationOptions;
 
         private _randomTextureSize: number;
-        private _actualFrame = 0;        
+        private _actualFrame = 0;      
+        
+        private readonly _rawTextureWidth = 256;
 
         /**
          * List of animations used by the particle system.
@@ -441,13 +443,55 @@
          */
         public getSizeGradients(): Nullable<Array<FactorGradient>> {
             return this._sizeGradients;
-        }               
+        }      
+        
+        /**
+         * Gets the current list of angular speed gradients.
+         * You must use addAngularSpeedGradient and removeAngularSpeedGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        public getAngularSpeedGradients(): Nullable<Array<FactorGradient>> {
+            return this._angularSpeedGradients;
+        } 
+
+        /**
+         * Gets the current list of velocity gradients.
+         * You must use addVelocityGradient and removeVelocityGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        public getVelocityGradients(): Nullable<Array<FactorGradient>> {
+            return this._velocityGradients;
+        }         
+
+        private _removeGradient(gradient: number, gradients: Nullable<IValueGradient[]>, texture: RawTexture): GPUParticleSystem {
+            if (!gradients) {
+                return this;
+            }
+
+            let index = 0;
+            for (var valueGradient of gradients) {
+                if (valueGradient.gradient === gradient) {
+                    gradients.splice(index, 1);
+                    break;
+                }
+                index++;
+            }
+
+            if (texture) {
+                texture.dispose();
+            }            
+
+            this._releaseBuffers();
+
+            return this;
+        }    
         
         /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color defines the color to affect to the specified gradient
          * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
+         * @returns the current particle system
          */
         public addColorGradient(gradient: number, color1: Color4, color2?: Color4): GPUParticleSystem {
             if (!this._colorGradients) {
@@ -482,50 +526,31 @@
         /**
          * Remove a specific color gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeColorGradient(gradient: number): GPUParticleSystem {
-            if (!this._colorGradients) {
-                return this;
-            }
-
-            let index = 0;
-            for (var colorGradient of this._colorGradients) {
-                if (colorGradient.gradient === gradient) {
-                    this._colorGradients.splice(index, 1);
-                    break;
-                }
-                index++;
-            }
-
-            if (this._colorGradientsTexture) {
-                this._colorGradientsTexture.dispose();
-                (<any>this._colorGradientsTexture) = null;
-            }            
-
-            this._releaseBuffers();
+            this._removeGradient(gradient, this._colorGradients, this._colorGradientsTexture);
+            (<any>this._colorGradientsTexture) = null;
 
             return this;
         }    
+
+        private _angularSpeedGradients: Nullable<Array<FactorGradient>> = null;
+        private _angularSpeedGradientsTexture: RawTexture;    
         
         private _sizeGradients: Nullable<Array<FactorGradient>> = null;
-        private _sizeGradientsTexture: RawTexture;        
+        private _sizeGradientsTexture: RawTexture;     
         
-        /**
-         * Adds a new size gradient
-         * @param gradient defines the gradient to use (between 0 and 1)
-         * @param factor defines the size factor to affect to the specified gradient
-         */
-        public addSizeGradient(gradient: number, factor: number): GPUParticleSystem {
-            if (!this._sizeGradients) {
-                this._sizeGradients = [];
-            }
+        private _velocityGradients: Nullable<Array<FactorGradient>> = null;
+        private _velocityGradientsTexture: RawTexture;    
 
-            let sizeGradient = new FactorGradient();
-            sizeGradient.gradient = gradient;
-            sizeGradient.factor1 = factor;
-            this._sizeGradients.push(sizeGradient);
+        private _addFactorGradient(factorGradients: FactorGradient[], gradient: number, factor: number) {
+            let valueGradient = new FactorGradient();
+            valueGradient.gradient = gradient;
+            valueGradient.factor1 = factor;
+            factorGradients.push(valueGradient);
 
-            this._sizeGradients.sort((a, b) => {
+            factorGradients.sort((a, b) => {
                 if (a.gradient < b.gradient) {
                     return -1;
                 } else if (a.gradient > b.gradient) {
@@ -535,6 +560,22 @@
                 return 0;
             });
 
+            this._releaseBuffers();               
+        }
+        
+        /**
+         * Adds a new size gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient
+         * @returns the current particle system
+         */
+        public addSizeGradient(gradient: number, factor: number): GPUParticleSystem {
+            if (!this._sizeGradients) {
+                this._sizeGradients = [];
+            }
+
+            this._addFactorGradient(this._sizeGradients, gradient, factor);
+
             if (this._sizeGradientsTexture) {
                 this._sizeGradientsTexture.dispose();
                 (<any>this._sizeGradientsTexture) = null;
@@ -548,30 +589,84 @@
         /**
          * Remove a specific size gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeSizeGradient(gradient: number): GPUParticleSystem {
-            if (!this._sizeGradients) {
-                return this;
+            this._removeGradient(gradient, this._sizeGradients, this._sizeGradientsTexture);
+            (<any>this._sizeGradientsTexture) = null;
+
+            return this;            
+        }   
+        
+        /**
+         * Adds a new angular speed gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient    
+         * @returns the current particle system     
+         */
+        public addAngularSpeedGradient(gradient: number, factor: number): GPUParticleSystem {
+            if (!this._angularSpeedGradients) {
+                this._angularSpeedGradients = [];
             }
 
-            let index = 0;
-            for (var sizeGradient of this._sizeGradients) {
-                if (sizeGradient.gradient === gradient) {
-                    this._sizeGradients.splice(index, 1);
-                    break;
-                }
-                index++;
+            this._addFactorGradient(this._angularSpeedGradients, gradient, factor);
+
+            if (this._angularSpeedGradientsTexture) {
+                this._angularSpeedGradientsTexture.dispose();
+                (<any>this._angularSpeedGradientsTexture) = null;
             }
 
-            if (this._sizeGradientsTexture) {
-                this._sizeGradientsTexture.dispose();
-                (<any>this._sizeGradientsTexture) = null;
+            this._releaseBuffers();   
+
+            return this;
+        }
+
+        /**
+         * Remove a specific angular speed gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeAngularSpeedGradient(gradient: number): GPUParticleSystem {
+            this._removeGradient(gradient, this._angularSpeedGradients, this._angularSpeedGradientsTexture);
+            (<any>this._angularSpeedGradientsTexture) = null;
+
+            return this;           
+        }           
+        
+        /**
+         * Adds a new velocity gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient    
+         * @returns the current particle system     
+         */
+        public addVelocityGradient(gradient: number, factor: number): GPUParticleSystem {
+            if (!this._velocityGradients) {
+                this._velocityGradients = [];
             }
 
-            this._releaseBuffers();               
+            this._addFactorGradient(this._velocityGradients, gradient, factor);
+
+            if (this._velocityGradientsTexture) {
+                this._velocityGradientsTexture.dispose();
+                (<any>this._velocityGradientsTexture) = null;
+            }
+
+            this._releaseBuffers();   
 
             return this;
-        }            
+        }
+
+        /**
+         * Remove a specific velocity gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeVelocityGradient(gradient: number): GPUParticleSystem {
+            this._removeGradient(gradient, this._velocityGradients, this._velocityGradientsTexture);
+            (<any>this._velocityGradientsTexture) = null;
+
+            return this;           
+        }          
 
         /**
          * Instantiates a GPU particle system.
@@ -618,7 +713,7 @@
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
                                 "angleRange", "radiusRange", "cellInfos"],
                 uniformBuffersNames: [],
-                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler"],
+                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler"],
                 defines: "",
                 fallbacks: null,  
                 onCompiled: null,
@@ -679,8 +774,13 @@
                 offset += 3;
             }
 
-            updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2);
-            offset += 2;
+            if (this._angularSpeedGradientsTexture) {
+                updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 1);
+                offset += 1;
+            } else {
+                updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2);
+                offset += 2;
+            }
 
             if (this._isAnimationSheetEnabled) {
                 updateVertexBuffers["cellIndex"] = source.createVertexBuffer("cellIndex", offset, 1);
@@ -713,8 +813,13 @@
                 renderVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3, this._attributesStrideSize, true);
                 offset += 3;
             }
-            renderVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2, this._attributesStrideSize, true);
-            offset += 2;
+
+            renderVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 1, this._attributesStrideSize, true);
+            if (this._angularSpeedGradientsTexture) {
+                offset++;
+            } else {
+                offset += 2;
+            }
 
             if (this._isAnimationSheetEnabled) {
                 renderVertexBuffers["cellIndex"] = source.createVertexBuffer("cellIndex", offset, 1, this._attributesStrideSize, true);
@@ -746,6 +851,10 @@
                 this._attributesStrideSize -= 4;
             }
 
+            if (this._angularSpeedGradientsTexture) {
+                this._attributesStrideSize -= 1;
+            }            
+
             if (this._isAnimationSheetEnabled) {
                 this._attributesStrideSize += 1;
             }            
@@ -793,7 +902,10 @@
 
                 // angle
                 data.push(0.0);  
-                data.push(0.0); 
+
+                if (!this._angularSpeedGradientsTexture) {
+                    data.push(0.0); 
+                }
 
                 if (this._isAnimationSheetEnabled) {
                     data.push(0.0); 
@@ -842,6 +954,14 @@
             if (this._sizeGradientsTexture) {
                 defines += "\n#define SIZEGRADIENTS";
             }     
+
+            if (this._angularSpeedGradientsTexture) {
+                defines += "\n#define ANGULARSPEEDGRADIENTS";
+            }               
+            
+            if (this._velocityGradientsTexture) {
+                defines += "\n#define VELOCITYGRADIENTS";
+            }                    
             
             if (this.isAnimationSheetEnabled) {
                 defines += "\n#define ANIMATESHEET";
@@ -926,36 +1046,48 @@
             }             
         }    
 
-        private _createSizeGradientTexture() {
-            if (!this._sizeGradients || !this._sizeGradients.length || this._sizeGradientsTexture) {
+        private _createFactorGradientTexture(factorGradients: Nullable<IValueGradient[]>, textureName: string) {
+            let texture:RawTexture = (<any>this)[textureName];
+
+            if (!factorGradients || !factorGradients.length || texture) {
                 return;
             }
 
-            let textureWidth = 256;
-            let data = new Float32Array(textureWidth);
+            let data = new Float32Array(this._rawTextureWidth);
 
-            for (var x = 0; x < textureWidth; x++) {
-                var ratio = x / textureWidth;
+            for (var x = 0; x < this._rawTextureWidth; x++) {
+                var ratio = x / this._rawTextureWidth;
 
-                Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
+                Tools.GetCurrentGradient(ratio, factorGradients, (currentGradient, nextGradient, scale) => {
                     data[x] = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
                 });
             }
 
-            this._sizeGradientsTexture = RawTexture.CreateRTexture(data, textureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
-        }        
+            (<any>this)[textureName] = RawTexture.CreateRTexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+        }            
+
+        private _createSizeGradientTexture() {
+            this._createFactorGradientTexture(this._sizeGradients, "_sizeGradientsTexture");
+        }     
+        
+        private _createAngularSpeedGradientTexture() {
+            this._createFactorGradientTexture(this._angularSpeedGradients, "_angularSpeedGradientsTexture");
+        }     
+
+        private _createVelocityGradientTexture() {
+            this._createFactorGradientTexture(this._velocityGradients, "_velocityGradientsTexture");
+        }          
             
         private _createColorGradientTexture() {
             if (!this._colorGradients || !this._colorGradients.length || this._colorGradientsTexture) {
                 return;
             }
 
-            let textureWidth = 256;
-            let data = new Uint8Array(textureWidth * 4);
+            let data = new Uint8Array(this._rawTextureWidth * 4);
             let tmpColor = Tmp.Color4[0];
 
-            for (var x = 0; x < textureWidth; x++) {
-                var ratio = x / textureWidth;
+            for (var x = 0; x < this._rawTextureWidth; x++) {
+                var ratio = x / this._rawTextureWidth;
 
                 Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
 
@@ -968,7 +1100,7 @@
 
             }
 
-            this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, textureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+            this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
         }
 
         /**
@@ -983,6 +1115,8 @@
 
             this._createColorGradientTexture();
             this._createSizeGradientTexture();
+            this._createAngularSpeedGradientTexture();
+            this._createVelocityGradientTexture();
 
             this._recreateUpdateEffect();
             this._recreateRenderEffect();
@@ -1046,6 +1180,14 @@
                 this._updateEffect.setTexture("sizeGradientSampler", this._sizeGradientsTexture);      
             }
 
+            if (this._angularSpeedGradientsTexture) {      
+                this._updateEffect.setTexture("angularSpeedGradientSampler", this._angularSpeedGradientsTexture);      
+            }
+
+            if (this._velocityGradientsTexture) {      
+                this._updateEffect.setTexture("velocityGradientSampler", this._velocityGradientsTexture);      
+            }
+
             if (this.particleEmitterType) {
                 this.particleEmitterType.applyToShader(this._updateEffect);
             }
@@ -1205,7 +1347,17 @@
             if (this._sizeGradientsTexture) {
                 this._sizeGradientsTexture.dispose();
                 (<any>this._sizeGradientsTexture) = null;
-            }            
+            }    
+            
+            if (this._angularSpeedGradientsTexture) {
+                this._angularSpeedGradientsTexture.dispose();
+                (<any>this._angularSpeedGradientsTexture) = null;
+            }             
+
+            if (this._velocityGradientsTexture) {
+                this._velocityGradientsTexture.dispose();
+                (<any>this._velocityGradientsTexture) = null;
+            }                
          
             if (this._randomTexture) {
                 this._randomTexture.dispose();

+ 25 - 1
src/Particles/babylon.particle.ts

@@ -79,7 +79,21 @@
         /** @hidden */
         public _currentSize1 = 0;
         /** @hidden */
-        public _currentSize2 = 0;        
+        public _currentSize2 = 0;      
+        
+        /** @hidden */
+        public _currentAngularSpeedGradient: Nullable<FactorGradient>;
+        /** @hidden */
+        public _currentAngularSpeed1 = 0;
+        /** @hidden */
+        public _currentAngularSpeed2 = 0;          
+
+        /** @hidden */
+        public _currentVelocityGradient: Nullable<FactorGradient>;
+        /** @hidden */
+        public _currentVelocity1 = 0;
+        /** @hidden */
+        public _currentVelocity2 = 0;             
 
         /**
          * Creates a new instance Particle
@@ -147,6 +161,16 @@
                 other._currentSize1 = this._currentSize1;
                 other._currentSize2 = this._currentSize2;
             }
+            if (this._currentAngularSpeedGradient) {
+                other._currentAngularSpeedGradient = this._currentAngularSpeedGradient;
+                other._currentAngularSpeed1 = this._currentAngularSpeed1;
+                other._currentAngularSpeed2 = this._currentAngularSpeed2;
+            }        
+            if (this._currentVelocityGradient) {
+                other._currentVelocityGradient = this._currentVelocityGradient;
+                other._currentVelocity1 = this._currentVelocity1;
+                other._currentVelocity2 = this._currentVelocity2;
+            }                  
             if (this.particleSystem.isAnimationSheetEnabled) {
                 other._initialStartSpriteCellID = this._initialStartSpriteCellID;
                 other._initialEndSpriteCellID = this._initialEndSpriteCellID;

+ 12 - 5
src/Particles/babylon.particleHelper.ts

@@ -15,21 +15,28 @@ module BABYLON {
          * @param gpu If the system will use gpu
          * @returns the ParticleSystemSet created
          */
-        public static CreateAsync(type: string, scene: Nullable<Scene> = Engine.LastCreatedScene, gpu: boolean = false): Promise<ParticleSystemSet> {
+        public static CreateAsync(type: string, scene: Nullable<Scene>, gpu: boolean = false): Promise<ParticleSystemSet> {
             
-            return new Promise((resolve, reject) => {
-                if (!scene) {
-                    scene = Engine.LastCreatedScene;;
-                }
+            if (!scene) {
+                scene = Engine.LastCreatedScene;;
+            }
 
+            let token = {};
+
+            scene!._addPendingData(token);
+
+            return new Promise((resolve, reject) => {
                 if (gpu && !GPUParticleSystem.IsSupported) {
+                    scene!._removePendingData(token);
                     return reject("Particle system with GPU is not supported.");
                 }
 
                 Tools.LoadFile(`${ParticleHelper.BaseAssetsUrl}/systems/${type}.json`, (data, response) => {
+                    scene!._removePendingData(token);
                     const newData = JSON.parse(data.toString());
                     return resolve(ParticleSystemSet.Parse(newData, scene!, gpu));
                 }, undefined, undefined, undefined, (req, exception) => {
+                    scene!._removePendingData(token);
                     return reject(`An error occured while the creation of your particle system. Check if your type '${type}' exists.`);
                 });
 

+ 178 - 5
src/Particles/babylon.particleSystem.ts

@@ -187,6 +187,8 @@
         private _colorGradients: Nullable<Array<ColorGradient>> = null;
         private _sizeGradients: Nullable<Array<FactorGradient>> = null;
         private _lifeTimeGradients: Nullable<Array<FactorGradient>> = null;
+        private _angularSpeedGradients: Nullable<Array<FactorGradient>> = null;
+        private _velocityGradients: Nullable<Array<FactorGradient>> = null;
 
         /**
          * Gets the current list of color gradients.
@@ -213,7 +215,25 @@
          */
         public getLifeTimeGradients(): Nullable<Array<FactorGradient>> {
             return this._lifeTimeGradients;
-        }          
+        }   
+        
+        /**
+         * Gets the current list of angular speed gradients.
+         * You must use addAngularSpeedGradient and removeAngularSpeedGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        public getAngularSpeedGradients(): Nullable<Array<FactorGradient>> {
+            return this._angularSpeedGradients;
+        } 
+
+        /**
+         * Gets the current list of velocity gradients.
+         * You must use addVelocityGradient and removeVelocityGradient to udpate this list
+         * @returns the list of velocity gradients
+         */
+        public getVelocityGradients(): Nullable<Array<FactorGradient>> {
+            return this._velocityGradients;
+        }         
 
         /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
@@ -523,15 +543,37 @@
                                 particle.color.a = 0;
                             }
                         }
+
+                        if (this._angularSpeedGradients && this._angularSpeedGradients.length > 0) {                  
+                            Tools.GetCurrentGradient(ratio, this._angularSpeedGradients, (currentGradient, nextGradient, scale) => {
+                                if (currentGradient !== particle._currentAngularSpeedGradient) {
+                                    particle._currentAngularSpeed1 = particle._currentAngularSpeed2;
+                                    particle._currentAngularSpeed2 = (<FactorGradient>nextGradient).getFactor();    
+                                    particle._currentAngularSpeedGradient = (<FactorGradient>currentGradient);
+                                }                                
+                                particle.angularSpeed = Scalar.Lerp(particle._currentAngularSpeed1, particle._currentAngularSpeed2, scale);
+                            });
+                        }                        
                         particle.angle += particle.angularSpeed * this._scaledUpdateSpeed;
 
-                        particle.direction.scaleToRef(this._scaledUpdateSpeed, this._scaledDirection);
+                        let directionScale = this._scaledUpdateSpeed;
+                        if (this._velocityGradients && this._velocityGradients.length > 0) {                  
+                            Tools.GetCurrentGradient(ratio, this._velocityGradients, (currentGradient, nextGradient, scale) => {
+                                if (currentGradient !== particle._currentVelocityGradient) {
+                                    particle._currentVelocity1 = particle._currentVelocity2;
+                                    particle._currentVelocity2 = (<FactorGradient>nextGradient).getFactor();    
+                                    particle._currentVelocityGradient = (<FactorGradient>currentGradient);
+                                }                                
+                                directionScale *= Scalar.Lerp(particle._currentVelocity1, particle._currentVelocity2, scale);
+                            });
+                        }                          
+                        particle.direction.scaleToRef(directionScale, this._scaledDirection);
                         particle.position.addInPlace(this._scaledDirection);
 
                         this.gravity.scaleToRef(this._scaledUpdateSpeed, this._scaledGravity);
                         particle.direction.addInPlace(this._scaledGravity);
 
-                        // Gradient
+                        // Size
                         if (this._sizeGradients && this._sizeGradients.length > 0) {                  
                             Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
                                 if (currentGradient !== particle._currentSizeGradient) {
@@ -589,6 +631,7 @@
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the life time factor to affect to the specified gradient         
          * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
          */
         public addLifeTimeGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
             if (!this._lifeTimeGradients) {
@@ -603,6 +646,7 @@
         /**
          * Remove a specific life time gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeLifeTimeGradient(gradient: number): ParticleSystem {
             this._removeFactorGradient(this._lifeTimeGradients, gradient);
@@ -615,6 +659,7 @@
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the size factor to affect to the specified gradient         
          * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
          */
         public addSizeGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
             if (!this._sizeGradients) {
@@ -629,6 +674,7 @@
         /**
          * Remove a specific size gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeSizeGradient(gradient: number): ParticleSystem {
             this._removeFactorGradient(this._sizeGradients, gradient);
@@ -637,6 +683,62 @@
         }        
 
         /**
+         * Adds a new angular speed gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addAngularSpeedGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
+            if (!this._angularSpeedGradients) {
+                this._angularSpeedGradients = [];
+            }
+
+            this._addFactorGradient(this._angularSpeedGradients, gradient, factor, factor2);
+
+            return this;
+        }
+
+        /**
+         * Remove a specific angular speed gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeAngularSpeedGradient(gradient: number): ParticleSystem {
+            this._removeFactorGradient(this._angularSpeedGradients, gradient);
+
+            return this;
+        }          
+        
+        /**
+         * Adds a new velocity gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addVelocityGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
+            if (!this._velocityGradients) {
+                this._velocityGradients = [];
+            }
+
+            this._addFactorGradient(this._velocityGradients, gradient, factor, factor2);
+
+            return this;
+        }
+
+        /**
+         * Remove a specific velocity gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeVelocityGradient(gradient: number): ParticleSystem {
+            this._removeFactorGradient(this._velocityGradients, gradient);
+
+            return this;
+        }         
+
+        /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color defines the color to affect to the specified gradient
@@ -1043,9 +1145,33 @@
                 particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
 
                 // Angle
-                particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
+                if (!this._angularSpeedGradients || this._angularSpeedGradients.length === 0) {
+                    particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
+                } else {
+                    particle._currentAngularSpeedGradient = this._angularSpeedGradients[0];
+                    particle.angularSpeed =  particle._currentAngularSpeedGradient.getFactor();
+                    particle._currentAngularSpeed1 = particle.angularSpeed;
+
+                    if (this._angularSpeedGradients.length > 1) {
+                        particle._currentAngularSpeed2 = this._angularSpeedGradients[1].getFactor();
+                    } else {
+                        particle._currentAngularSpeed2 = particle._currentAngularSpeed1;
+                    }
+                }
                 particle.angle = Scalar.RandomRange(this.minInitialRotation, this.maxInitialRotation);
 
+                // Velocity
+                if (this._velocityGradients && this._velocityGradients.length > 0) {
+                    particle._currentVelocityGradient = this._velocityGradients[0];
+                    particle._currentVelocity1 = particle._currentVelocityGradient.getFactor();
+
+                    if (this._velocityGradients.length > 1) {
+                        particle._currentVelocity2 = this._velocityGradients[1].getFactor();
+                    } else {
+                        particle._currentVelocity2 = particle._currentVelocity1;
+                    }
+                }                
+
                 // Color
                 if (!this._colorGradients || this._colorGradients.length === 0) {
                     var step = Scalar.RandomRange(0, 1.0);
@@ -1580,8 +1706,43 @@
 
                     serializationObject.sizeGradients.push(serializedGradient);
                 }
-            }            
+            }       
+                        
+            let angularSpeedGradients = particleSystem.getAngularSpeedGradients();
+            if (angularSpeedGradients) {
+                serializationObject.angularSpeedGradients = [];
+                for (var angularSpeedGradient of angularSpeedGradients) {
 
+                    var serializedGradient: any = {
+                        gradient: angularSpeedGradient.gradient,
+                        factor1: angularSpeedGradient.factor1
+                    };
+
+                    if (angularSpeedGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = angularSpeedGradient.factor2;
+                    }
+
+                    serializationObject.angularSpeedGradients.push(serializedGradient);
+                }
+            }  
+
+            let velocityGradients = particleSystem.getVelocityGradients();
+            if (velocityGradients) {
+                serializationObject.velocityGradients = [];
+                for (var velocityGradient of velocityGradients) {
+
+                    var serializedGradient: any = {
+                        gradient: velocityGradient.gradient,
+                        factor1: velocityGradient.factor1
+                    };
+
+                    if (velocityGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = velocityGradient.factor2;
+                    }
+
+                    serializationObject.velocityGradients.push(serializedGradient);
+                }
+            }              
         }
 
         /** @hidden */
@@ -1671,6 +1832,18 @@
                     particleSystem.addSizeGradient(sizeGradient.gradient, sizeGradient.factor1 !== undefined ?  sizeGradient.factor1 : sizeGradient.factor, sizeGradient.factor2);
                 }
             }       
+
+            if (parsedParticleSystem.angularSpeedGradients) {
+                for (var angularSpeedGradient of parsedParticleSystem.angularSpeedGradients) {
+                    particleSystem.addAngularSpeedGradient(angularSpeedGradient.gradient, angularSpeedGradient.factor1 !== undefined ?  angularSpeedGradient.factor1 : angularSpeedGradient.factor, angularSpeedGradient.factor2);
+                }
+            }       
+            
+            if (parsedParticleSystem.velocityGradients) {
+                for (var velocityGradient of parsedParticleSystem.velocityGradients) {
+                    particleSystem.addVelocityGradient(velocityGradient.gradient, velocityGradient.factor1 !== undefined ?  velocityGradient.factor1 : velocityGradient.factor, velocityGradient.factor2);
+                }
+            }              
             
             // Emitter
             let emitterType: IParticleEmitterType;

+ 2 - 0
src/Particles/babylon.particleSystemSet.ts

@@ -116,6 +116,8 @@ module BABYLON {
             var result = new ParticleSystemSet();
             var rootUrl = ParticleHelper.BaseAssetsUrl + "/textures/";
 
+            scene = scene || Engine.LastCreatedScene;
+
             for (var system of data.systems) {
                 result.systems.push(gpu ? GPUParticleSystem.Parse(system, scene, rootUrl) : ParticleSystem.Parse(system, scene, rootUrl));
             }

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

@@ -110,8 +110,15 @@
                     observable.notifyObservers(info, renderingGroupMask);
                 }
 
-                if (renderingGroup)
+                if (renderingGroup) {
+                    for (let step of this._scene._beforeRenderingGroupDrawStage) {
+                        step.action(index);
+                    }
                     renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
+                    for (let step of this._scene._afterRenderingGroupDrawStage) {
+                        step.action(index);
+                    }
+                }
 
                 // Fire POSTTRANSPARENT stage
                 if (observable && info) {

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

@@ -13,7 +13,7 @@ in vec3 size;
 #ifndef BILLBOARD
 in vec3 initialDirection;
 #endif
-in vec2 angle;
+in float angle;
 #ifdef ANIMATESHEET
 in float cellIndex;
 #endif
@@ -85,8 +85,8 @@ void main() {
 	rotatedCorner.w = 0.;
 
 	#ifdef BILLBOARDY	
-		rotatedCorner.x = cornerPos.x * cos(angle.x) - cornerPos.y * sin(angle.x);
-		rotatedCorner.z = cornerPos.x * sin(angle.x) + cornerPos.y * cos(angle.x);
+		rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
+		rotatedCorner.z = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
 		rotatedCorner.y = 0.;
 
 		vec3 yaxis = position - eyePosition;
@@ -96,8 +96,8 @@ void main() {
 		vec4 viewPosition = (view * vec4(worldPos, 1.0)); 
 	#else
 		// Rotate
-		rotatedCorner.x = cornerPos.x * cos(angle.x) - cornerPos.y * sin(angle.x);
-		rotatedCorner.y = cornerPos.x * sin(angle.x) + cornerPos.y * cos(angle.x);
+		rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
+		rotatedCorner.y = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
 		rotatedCorner.z = 0.;
 
 		// Expand position
@@ -107,9 +107,9 @@ void main() {
 #else
   // Rotate
 	vec3 rotatedCorner;
-	rotatedCorner.x = cornerPos.x * cos(angle.x) - cornerPos.y * sin(angle.x);
+	rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
 	rotatedCorner.y = 0.;
-	rotatedCorner.z = cornerPos.x * sin(angle.x) + cornerPos.y * cos(angle.x);
+	rotatedCorner.z = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
 
 	vec3 yaxis = normalize(initialDirection);
 	vec3 worldPos = rotate(yaxis, rotatedCorner);

+ 39 - 4
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -57,7 +57,11 @@ in vec3 direction;
 #ifndef BILLBOARD
 in vec3 initialDirection;
 #endif
+#ifdef ANGULARSPEEDGRADIENTS
+in float angle;
+#else
 in vec2 angle;
+#endif
 #ifdef ANIMATESHEET
 in float cellIndex;
 #endif
@@ -75,7 +79,11 @@ out vec3 outDirection;
 #ifndef BILLBOARD
 out vec3 outInitialDirection;
 #endif
+#ifdef ANGULARSPEEDGRADIENTS
+out float outAngle;
+#else
 out vec2 outAngle;
+#endif
 #ifdef ANIMATESHEET
 out float outCellIndex;
 #endif
@@ -84,6 +92,15 @@ out float outCellIndex;
 uniform sampler2D sizeGradientSampler;
 #endif 
 
+#ifdef ANGULARSPEEDGRADIENTS
+uniform sampler2D angularSpeedGradientSampler;
+#endif 
+
+#ifdef VELOCITYGRADIENTS
+uniform sampler2D velocityGradientSampler;
+#endif
+
+
 #ifdef ANIMATESHEET
 uniform vec3 cellInfos;
 #endif
@@ -146,8 +163,12 @@ void main() {
 #endif
 
     // Angular speed
+#ifndef ANGULARSPEEDGRADIENTS    
     outAngle.y = angleRange.x + (angleRange.y - angleRange.x) * randoms.a;
     outAngle.x = angleRange.z + (angleRange.w - angleRange.z) * randoms.r;
+#else
+    outAngle = angleRange.z + (angleRange.w - angleRange.z) * randoms.r;
+#endif        
 
     // Position / Direction (based on emitter type)
 #ifdef BOXEMITTER
@@ -211,7 +232,7 @@ void main() {
     float power = emitPower.x + (emitPower.y - emitPower.x) * randoms.a;
 
     outPosition = (emitterWM * vec4(position, 1.)).xyz;
-    vec3 initial = (emitterWM * vec4(direction, 0.)).xyz;
+    vec3 initial = (emitterWM * vec4(normalize(direction), 0.)).xyz;
     outDirection = initial * power;
 #ifndef BILLBOARD        
     outInitialDirection = initial;
@@ -221,7 +242,14 @@ void main() {
 #endif
 
   } else {   
-    outPosition = position + direction * timeDelta;
+    float directionScale = timeDelta;
+    float ageGradient = age / life;
+
+#ifdef VELOCITYGRADIENTS
+    directionScale *= texture(velocityGradientSampler, vec2(ageGradient, 0)).r;
+#endif
+
+    outPosition = position + direction * directionScale;
     outAge = age + timeDelta;
     outLife = life;
     outSeed = seed;
@@ -230,7 +258,7 @@ void main() {
 #endif
 
 #ifdef SIZEGRADIENTS
-	outSize.x = texture(sizeGradientSampler, vec2(age / life, 0)).r;
+	outSize.x = texture(sizeGradientSampler, vec2(ageGradient, 0)).r;
     outSize.yz = size.yz;
 #else
     outSize = size;
@@ -239,8 +267,15 @@ void main() {
 #ifndef BILLBOARD    
     outInitialDirection = initialDirection;
 #endif
-    outDirection = direction + gravity * timeDelta;
+    outDirection = direction;// + gravity * timeDelta;
+
+#ifdef ANGULARSPEEDGRADIENTS
+    float angularSpeed = texture(angularSpeedGradientSampler, vec2(ageGradient, 0)).r;
+    outAngle = angle + angularSpeed * timeDelta;
+#else
     outAngle = vec2(angle.x + angle.y * timeDelta, angle.y);
+#endif
+
 #ifdef ANIMATESHEET      
     float dist = cellInfos.y - cellInfos.x;
     float ratio = clamp(mod(outAge * cellInfos.z, life) / life, 0., 1.0);

+ 1 - 1
src/Tools/HDR/babylon.cubemapToSphericalPolynomial.ts

@@ -172,7 +172,7 @@ module BABYLON {
             sphericalHarmonics.convertIncidentRadianceToIrradiance();
             sphericalHarmonics.convertIrradianceToLambertianRadiance();
 
-            return SphericalPolynomial.getSphericalPolynomialFromHarmonics(sphericalHarmonics);
+            return SphericalPolynomial.FromHarmonics(sphericalHarmonics);
         }
     }
 } 

+ 63 - 195
src/Tools/babylon.environmentTextureTools.ts

@@ -58,20 +58,6 @@ module BABYLON {
      * Defines the required storage to save the environment irradiance information.
      */
     interface EnvironmentTextureIrradianceInfoV1 {
-        polynomials: boolean;
-
-        l00: Array<number>;
-
-        l1_1: Array<number>;
-        l10: Array<number>;
-        l11: Array<number>;
-
-        l2_2: Array<number>;
-        l2_1: Array<number>;
-        l20: Array<number>;
-        l21: Array<number>;
-        l22: Array<number>;
-
         x: Array<number>;
         y: Array<number>;
         z: Array<number>;
@@ -112,7 +98,7 @@ module BABYLON {
                     return null;
                 }
             }
-            
+
             // Read json manifest - collect characters up to null terminator
             let manifestString = '';
             let charCode = 0x00;
@@ -164,7 +150,7 @@ module BABYLON {
 
             let cubeWidth = internalTexture.width;
             let hostingScene = new Scene(engine);
-            let specularTextures: { [key: number]: ArrayBuffer } = { };
+            let specularTextures: { [key: number]: ArrayBuffer } = {};
             let promises: Promise<void>[] = [];
 
             // Read and collect all mipmaps data from the cube.
@@ -186,7 +172,7 @@ module BABYLON {
                             rgbdPostProcess.onApply = (effect) => {
                                 effect._bindTexture("textureSampler", tempTexture);
                             }
-            
+
                             // As the process needs to happen on the main canvas, keep track of the current size
                             let currentW = engine.getRenderWidth();
                             let currentH = engine.getRenderHeight();
@@ -246,7 +232,7 @@ module BABYLON {
                 let infoString = JSON.stringify(info);
                 let infoBuffer = new ArrayBuffer(infoString.length + 1);
                 let infoView = new Uint8Array(infoBuffer); // Limited to ascii subset matching unicode.
-                for (let i= 0, strLen = infoString.length; i < strLen; i++) {
+                for (let i = 0, strLen = infoString.length; i < strLen; i++) {
                     infoView[i] = infoString.charCodeAt(i);
                 }
                 // Ends up with a null terminator for easier parsing
@@ -287,15 +273,13 @@ module BABYLON {
          * @param texture defines the texture containing the polynomials
          * @return the JSON representation of the spherical info
          */
-        private static _CreateEnvTextureIrradiance(texture: CubeTexture) : Nullable<EnvironmentTextureIrradianceInfoV1> {
+        private static _CreateEnvTextureIrradiance(texture: CubeTexture): Nullable<EnvironmentTextureIrradianceInfoV1> {
             let polynmials = texture.sphericalPolynomial;
             if (polynmials == null) {
                 return null;
             }
 
             return {
-                polynomials: true,
-
                 x: [polynmials.x.x, polynmials.x.y, polynmials.x.z],
                 y: [polynmials.y.x, polynmials.y.y, polynmials.y.z],
                 z: [polynmials.z.x, polynmials.z.y, polynmials.z.z],
@@ -311,13 +295,13 @@ module BABYLON {
         }
 
         /**
-         * Uploads the texture info contained in the env file to te GPU.
+         * Uploads the texture info contained in the env file to the GPU.
          * @param texture defines the internal texture to upload to
          * @param arrayBuffer defines the buffer cotaining the data to load
          * @param info defines the texture info retrieved through the GetEnvInfo method
          * @returns a promise
          */
-        public static UploadLevelsAsync(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): Promise<void> {
+        public static UploadEnvLevelsAsync(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): Promise<void> {
             if (info.version !== 1) {
                 Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
             }
@@ -335,13 +319,34 @@ module BABYLON {
                 Tools.Warn('Unsupported specular mipmaps number "' + specularInfo.mipmaps.length + '"');
             }
 
+            const imageData = new Array<Array<ArrayBufferView>>(mipmapsCount);
+            for (let i = 0; i < mipmapsCount; i++) {
+                imageData[i] = new Array<ArrayBufferView>(6);
+                for (let face = 0; face < 6; face++) {
+                    const imageInfo = specularInfo.mipmaps[i * 6 + face];
+                    imageData[i][face] = new Uint8Array(arrayBuffer, specularInfo.specularDataPosition! + imageInfo.position, imageInfo.length);
+                }
+            }
+
+            return EnvironmentTextureTools.UploadLevelsAsync(texture, imageData);
+        }
+
+        /**
+         * Uploads the levels of image data to the GPU.
+         * @param texture defines the internal texture to upload to
+         * @param imageData defines the array buffer views of image data [mipmap][face]
+         * @returns a promise
+         */
+        public static UploadLevelsAsync(texture: InternalTexture, imageData: ArrayBufferView[][]): Promise<void> {
+            const mipmapsCount = imageData.length;
+
             // Gets everything ready.
             let engine = texture.getEngine();
             let expandTexture = false;
             let generateNonLODTextures = false;
             let rgbdPostProcess: Nullable<PostProcess> = null;
             let cubeRtt: Nullable<InternalTexture> = null;
-            let lodTextures: Nullable<{ [lod: number]: BaseTexture}> = null;
+            let lodTextures: Nullable<{ [lod: number]: BaseTexture }> = null;
             let caps = engine.getCaps();
 
             texture.format = Engine.TEXTUREFORMAT_RGBA;
@@ -352,7 +357,7 @@ module BABYLON {
             if (!caps.textureLOD) {
                 expandTexture = false;
                 generateNonLODTextures = true;
-                lodTextures = { };
+                lodTextures = {};
             }
             // in webgl 1 there are no ways to either render or copy lod level information for float textures.
             else if (engine.webGLVersion < 2) {
@@ -373,7 +378,7 @@ module BABYLON {
             if (expandTexture) {
                 // Simply run through the decode PP
                 rgbdPostProcess = new PostProcess("rgbdDecode", "rgbdDecode", null, null, 1, null, Texture.TRILINEAR_SAMPLINGMODE, engine, false, undefined, texture.type, undefined, null, false);
-                
+
                 texture._isRGBD = false;
                 texture.invertY = false;
                 cubeRtt = engine.createRenderTargetCubeTexture(texture.width, {
@@ -394,24 +399,24 @@ module BABYLON {
                     let mipSlices = 3;
                     let scale = texture._lodGenerationScale;
                     let offset = texture._lodGenerationOffset;
-    
+
                     for (let i = 0; i < mipSlices; i++) {
                         //compute LOD from even spacing in smoothness (matching shader calculation)
                         let smoothness = i / (mipSlices - 1);
                         let roughness = 1 - smoothness;
-    
+
                         let minLODIndex = offset; // roughness = 0
-                        let maxLODIndex = Scalar.Log2(info.width) * scale + offset; // roughness = 1
-    
+                        let maxLODIndex = (mipmapsCount - 1) * scale + offset; // roughness = 1 (mipmaps start from 0)
+
                         let lodIndex = minLODIndex + (maxLODIndex - minLODIndex) * roughness;
                         let mipmapIndex = Math.round(Math.min(Math.max(lodIndex, 0), maxLODIndex));
-    
+
                         let glTextureFromLod = new InternalTexture(engine, InternalTexture.DATASOURCE_TEMP);
                         glTextureFromLod.isCube = true;
                         glTextureFromLod.invertY = true;
                         glTextureFromLod.generateMipMaps = false;
                         engine.updateTextureSamplingMode(Texture.LINEAR_LINEAR, glTextureFromLod);
-    
+
                         // Wrap in a base texture for easy binding.
                         let lodTexture = new BaseTexture(null);
                         lodTexture.isCube = true;
@@ -420,14 +425,14 @@ module BABYLON {
 
                         switch (i) {
                             case 0:
-                            texture._lodTextureLow = lodTexture;
-                            break;
+                                texture._lodTextureLow = lodTexture;
+                                break;
                             case 1:
-                            texture._lodTextureMid = lodTexture;
-                            break;
+                                texture._lodTextureMid = lodTexture;
+                                break;
                             case 2:
-                            texture._lodTextureHigh = lodTexture;
-                            break;
+                                texture._lodTextureHigh = lodTexture;
+                                break;
                         }
                     }
                 }
@@ -438,25 +443,22 @@ module BABYLON {
             for (let i = 0; i < mipmapsCount; i++) {
                 // All faces
                 for (let face = 0; face < 6; face++) {
-                    // Retrieves the face data
-                    let imageData = specularInfo.mipmaps[i * 6 + face];
-                    let bytes = new Uint8Array(arrayBuffer, specularInfo.specularDataPosition! + imageData.position, imageData.length);
-
-                    // Constructs an image element from bytes
+                    // Constructs an image element from image data
+                    let bytes = imageData[i][face];
                     let blob = new Blob([bytes], { type: 'image/png' });
                     let url = URL.createObjectURL(blob);
                     let image = new Image();
                     image.src = url;
 
                     // Enqueue promise to upload to the texture.
-                    let promise = new Promise<void>((resolve, reject) => {;
+                    let promise = new Promise<void>((resolve, reject) => {
                         image.onload = () => {
                             if (expandTexture) {
                                 let tempTexture = engine.createTexture(null, true, true, null, Texture.NEAREST_SAMPLINGMODE, null,
-                                (message) => {
-                                    reject(message);
-                                },
-                                image);
+                                    (message) => {
+                                        reject(message);
+                                    },
+                                    image);
 
                                 rgbdPostProcess!.getEffect().executeWhenCompiled(() => {
                                     // Uncompress the data to a RTT
@@ -464,7 +466,7 @@ module BABYLON {
                                         effect._bindTexture("textureSampler", tempTexture);
                                         effect.setFloat2("scale", 1, 1);
                                     }
-                                    
+
                                     engine.scenes[0].postProcessManager.directRender([rgbdPostProcess!], cubeRtt, true, face, i);
 
                                     // Cleanup
@@ -524,10 +526,9 @@ module BABYLON {
         /**
          * Uploads spherical polynomials information to the texture.
          * @param texture defines the texture we are trying to upload the information to
-         * @param arrayBuffer defines the array buffer holding the data
          * @param info defines the environment texture info retrieved through the GetEnvInfo method
          */
-        public static UploadPolynomials(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): void {
+        public static UploadEnvSpherical(texture: InternalTexture, info: EnvironmentTextureInfo): void {
             if (info.version !== 1) {
                 Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
             }
@@ -536,151 +537,18 @@ module BABYLON {
             if (!irradianceInfo) {
                 return;
             }
-            
-            //harmonics now represent radiance
-            texture._sphericalPolynomial = new SphericalPolynomial();
-
-            if (irradianceInfo.polynomials) {
-                EnvironmentTextureTools._UploadSP(irradianceInfo, texture._sphericalPolynomial);
-            }
-            else {
-                // convert From SH to SP.
-                EnvironmentTextureTools._ConvertSHIrradianceToLambertianRadiance(irradianceInfo);
-                EnvironmentTextureTools._ConvertSHToSP(irradianceInfo, texture._sphericalPolynomial);
-            }
-        }
 
-        /**
-         * Upload spherical polynomial coefficients to the texture
-         * @param polynmials Spherical polynmial coefficients (9)
-         * @param outPolynomialCoefficents Polynomial coefficients (9) object to store result
-         */
-        private static _UploadSP(polynmials: EnvironmentTextureIrradianceInfoV1, outPolynomialCoefficents: SphericalPolynomial) {
-            outPolynomialCoefficents.x.x = polynmials.x[0];
-            outPolynomialCoefficents.x.y = polynmials.x[1];
-            outPolynomialCoefficents.x.z = polynmials.x[2];
-
-            outPolynomialCoefficents.y.x = polynmials.y[0];
-            outPolynomialCoefficents.y.y = polynmials.y[1];
-            outPolynomialCoefficents.y.z = polynmials.y[2];
-
-            outPolynomialCoefficents.z.x = polynmials.z[0];
-            outPolynomialCoefficents.z.y = polynmials.z[1];
-            outPolynomialCoefficents.z.z = polynmials.z[2];
-
-            //xx
-            outPolynomialCoefficents.xx.x = polynmials.xx[0];
-            outPolynomialCoefficents.xx.y = polynmials.xx[1];
-            outPolynomialCoefficents.xx.z = polynmials.xx[2];
-
-            outPolynomialCoefficents.yy.x = polynmials.yy[0];
-            outPolynomialCoefficents.yy.y = polynmials.yy[1];
-            outPolynomialCoefficents.yy.z = polynmials.yy[2];
-
-            outPolynomialCoefficents.zz.x = polynmials.zz[0];
-            outPolynomialCoefficents.zz.y = polynmials.zz[1];
-            outPolynomialCoefficents.zz.z = polynmials.zz[2];
-
-            //yz
-            outPolynomialCoefficents.yz.x = polynmials.yz[0];
-            outPolynomialCoefficents.yz.y = polynmials.yz[1];
-            outPolynomialCoefficents.yz.z = polynmials.yz[2];
-
-            outPolynomialCoefficents.zx.x = polynmials.zx[0];
-            outPolynomialCoefficents.zx.y = polynmials.zx[1];
-            outPolynomialCoefficents.zx.z = polynmials.zx[2];
-
-            outPolynomialCoefficents.xy.x = polynmials.xy[0];
-            outPolynomialCoefficents.xy.y = polynmials.xy[1];
-            outPolynomialCoefficents.xy.z = polynmials.xy[2];
-        }
-
-        /**
-         * Convert from irradiance to outgoing radiance for Lambertian BDRF, suitable for efficient shader evaluation.
-         *	  L = (1/pi) * E * rho
-         * 
-         * This is done by an additional scale by 1/pi, so is a fairly trivial operation but important conceptually.
-         * @param harmonics Spherical harmonic coefficients (9)
-         */
-        private static _ConvertSHIrradianceToLambertianRadiance(harmonics: any): void {
-            let scaleFactor = 1 / Math.PI;
-            // The resultant SH now represents outgoing radiance, so includes the Lambert 1/pi normalisation factor but without albedo (rho) applied
-            // (The pixel shader must apply albedo after texture fetches, etc).
-            harmonics.l00[0] *= scaleFactor;
-            harmonics.l00[1] *= scaleFactor;
-            harmonics.l00[2] *= scaleFactor;
-            harmonics.l1_1[0] *= scaleFactor;
-            harmonics.l1_1[1] *= scaleFactor;
-            harmonics.l1_1[2] *= scaleFactor;
-            harmonics.l10[0] *= scaleFactor;
-            harmonics.l10[1] *= scaleFactor;
-            harmonics.l10[2] *= scaleFactor;
-            harmonics.l11[0] *= scaleFactor;
-            harmonics.l11[1] *= scaleFactor;
-            harmonics.l11[2] *= scaleFactor;
-            harmonics.l2_2[0] *= scaleFactor;
-            harmonics.l2_2[1] *= scaleFactor;
-            harmonics.l2_2[2] *= scaleFactor;
-            harmonics.l2_1[0] *= scaleFactor;
-            harmonics.l2_1[1] *= scaleFactor;
-            harmonics.l2_1[2] *= scaleFactor;
-            harmonics.l20[0] *= scaleFactor;
-            harmonics.l20[1] *= scaleFactor;
-            harmonics.l20[2] *= scaleFactor;
-            harmonics.l21[0] *= scaleFactor;
-            harmonics.l21[1] *= scaleFactor;
-            harmonics.l21[2] *= scaleFactor;
-            harmonics.l22[0] *= scaleFactor;
-            harmonics.l22[1] *= scaleFactor;
-            harmonics.l22[2] *= scaleFactor;
-        }
-
-        /**
-         * Convert spherical harmonics to spherical polynomial coefficients
-         * @param harmonics Spherical harmonic coefficients (9)
-         * @param outPolynomialCoefficents Polynomial coefficients (9) object to store result
-         */
-        private static _ConvertSHToSP(harmonics: any, outPolynomialCoefficents: SphericalPolynomial) {
-            let rPi = 1 / Math.PI;
-
-            //x
-            outPolynomialCoefficents.x.x = 1.02333 * harmonics.l11[0] * rPi;
-            outPolynomialCoefficents.x.y = 1.02333 * harmonics.l11[1] * rPi;
-            outPolynomialCoefficents.x.z = 1.02333 * harmonics.l11[2] * rPi;
-
-            outPolynomialCoefficents.y.x = 1.02333 * harmonics.l1_1[0] * rPi;
-            outPolynomialCoefficents.y.y = 1.02333 * harmonics.l1_1[1] * rPi;
-            outPolynomialCoefficents.y.z = 1.02333 * harmonics.l1_1[2] * rPi;
-
-            outPolynomialCoefficents.z.x = 1.02333 * harmonics.l10[0] * rPi;
-            outPolynomialCoefficents.z.y = 1.02333 * harmonics.l10[1] * rPi;
-            outPolynomialCoefficents.z.z = 1.02333 * harmonics.l10[2] * rPi;
-
-            //xx
-            outPolynomialCoefficents.xx.x = (0.886277 * harmonics.l00[0] - 0.247708 * harmonics.l20[0] + 0.429043 * harmonics.l22[0]) * rPi;
-            outPolynomialCoefficents.xx.y = (0.886277 * harmonics.l00[1] - 0.247708 * harmonics.l20[1] + 0.429043 * harmonics.l22[1]) * rPi;
-            outPolynomialCoefficents.xx.z = (0.886277 * harmonics.l00[2] - 0.247708 * harmonics.l20[2] + 0.429043 * harmonics.l22[2]) * rPi;
-
-            outPolynomialCoefficents.yy.x = (0.886277 * harmonics.l00[0] - 0.247708 * harmonics.l20[0] - 0.429043 * harmonics.l22[0]) * rPi;
-            outPolynomialCoefficents.yy.y = (0.886277 * harmonics.l00[1] - 0.247708 * harmonics.l20[1] - 0.429043 * harmonics.l22[1]) * rPi;
-            outPolynomialCoefficents.yy.z = (0.886277 * harmonics.l00[2] - 0.247708 * harmonics.l20[2] - 0.429043 * harmonics.l22[2]) * rPi;
-
-            outPolynomialCoefficents.zz.x = (0.886277 * harmonics.l00[0] + 0.495417 * harmonics.l20[0]) * rPi;
-            outPolynomialCoefficents.zz.y = (0.886277 * harmonics.l00[1] + 0.495417 * harmonics.l20[1]) * rPi;
-            outPolynomialCoefficents.zz.z = (0.886277 * harmonics.l00[2] + 0.495417 * harmonics.l20[2]) * rPi;
-
-            //yz
-            outPolynomialCoefficents.yz.x = 0.858086 * harmonics.l2_1[0] * rPi;
-            outPolynomialCoefficents.yz.y = 0.858086 * harmonics.l2_1[1] * rPi;
-            outPolynomialCoefficents.yz.z = 0.858086 * harmonics.l2_1[2] * rPi;
-
-            outPolynomialCoefficents.zx.x = 0.858086 * harmonics.l21[0] * rPi;
-            outPolynomialCoefficents.zx.y = 0.858086 * harmonics.l21[1] * rPi;
-            outPolynomialCoefficents.zx.z = 0.858086 * harmonics.l21[2] * rPi;
-
-            outPolynomialCoefficents.xy.x = 0.858086 * harmonics.l2_2[0] * rPi;
-            outPolynomialCoefficents.xy.y = 0.858086 * harmonics.l2_2[1] * rPi;
-            outPolynomialCoefficents.xy.z = 0.858086 * harmonics.l2_2[2] * rPi;
+            const sp = new SphericalPolynomial();
+            Vector3.FromArrayToRef(irradianceInfo.x, 0, sp.x);
+            Vector3.FromArrayToRef(irradianceInfo.y, 0, sp.y);
+            Vector3.FromArrayToRef(irradianceInfo.z, 0, sp.z);
+            Vector3.FromArrayToRef(irradianceInfo.xx, 0, sp.xx);
+            Vector3.FromArrayToRef(irradianceInfo.yy, 0, sp.yy);
+            Vector3.FromArrayToRef(irradianceInfo.zz, 0, sp.zz);
+            Vector3.FromArrayToRef(irradianceInfo.yz, 0, sp.yz);
+            Vector3.FromArrayToRef(irradianceInfo.zx, 0, sp.zx);
+            Vector3.FromArrayToRef(irradianceInfo.xy, 0, sp.xy);
+            texture._sphericalPolynomial = sp;
         }
     }
 }

+ 20 - 4
src/Tools/babylon.observable.ts

@@ -217,8 +217,7 @@
             var index = this._observers.indexOf(observer);
 
             if (index !== -1) {
-
-                this._observers.splice(index, 1);
+                this._deferUnregister(observer);
                 return true;
             }
 
@@ -236,7 +235,7 @@
 
             for (var index = 0; index < this._observers.length; index++) {
                 if (this._observers[index].callback === callback && (!scope || scope === this._observers[index].scope)) {
-                    this._observers.splice(index, 1);
+                    this._deferUnregister(this._observers[index]);
                     return true;
                 }
             }
@@ -248,10 +247,27 @@
             observer.unregisterOnNextCall = false;
             observer._willBeUnregistered = true;
             Tools.SetImmediate(() => {
-                this.remove(observer);
+                this._remove(observer);
             })
         }
 
+        // This should only be called when not iterating over _observers to avoid callback skipping.
+        // Removes an observer from the _observer Array.
+        private _remove(observer: Nullable<Observer<T>>): boolean {
+            if (!observer) {
+                return false;
+            }
+
+            var index = this._observers.indexOf(observer);
+
+            if (index !== -1) {
+                this._observers.splice(index, 1);
+                return true;
+            }
+
+            return false;
+        }
+
         /**
          * Notify all Observers by calling their respective callback with the given data
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute

+ 0 - 0
src/Tools/babylon.sceneSerializer.ts


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