Browse Source

Merge branch 'master' into GUILine

SvenFrankson 7 năm trước cách đây
mục cha
commit
0928b644fe
57 tập tin đã thay đổi với 25233 bổ sung22601 xóa
  1. 7340 6961
      Playground/babylon.d.txt
  2. 3 3
      Tools/Publisher/index.js
  3. 33 33
      Viewer/dist/viewer.js
  4. 33 33
      Viewer/dist/viewer.min.js
  5. 1 8
      Viewer/package.json
  6. 167 133
      Viewer/src/configuration/configuration.ts
  7. 4 2
      Viewer/src/templateManager.ts
  8. 0 39
      Viewer/src/util/promiseObservable.ts
  9. 75 183
      Viewer/src/viewer/defaultViewer.ts
  10. 515 171
      Viewer/src/viewer/viewer.ts
  11. 21 9
      Viewer/tsconfig.json
  12. 13 9
      Viewer/webpack.config.js
  13. 11450 11079
      dist/preview release/babylon.d.ts
  14. 31 31
      dist/preview release/babylon.js
  15. 426 100
      dist/preview release/babylon.max.js
  16. 31 31
      dist/preview release/babylon.worker.js
  17. 1607 1236
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  18. 22 22
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  19. 426 100
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  20. 426 100
      dist/preview release/customConfigurations/minimalGLTFViewer/es6.js
  21. 426 100
      dist/preview release/es6.js
  22. 1 1
      dist/preview release/gui/package.json
  23. 1 1
      dist/preview release/inspector/package.json
  24. 1 1
      dist/preview release/loaders/package.json
  25. 1 1
      dist/preview release/materialsLibrary/package.json
  26. 1 1
      dist/preview release/postProcessesLibrary/package.json
  27. 1 1
      dist/preview release/proceduralTexturesLibrary/package.json
  28. 57 49
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  29. 381 299
      dist/preview release/serializers/babylon.glTF2Serializer.js
  30. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  31. 381 299
      dist/preview release/serializers/babylonjs.serializers.js
  32. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  33. 57 49
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  34. 1 1
      dist/preview release/serializers/package.json
  35. 2 1394
      dist/preview release/typedocValidationBaseline.json
  36. 33 33
      dist/preview release/viewer/babylon.viewer.js
  37. 1 1
      dist/preview release/viewer/package.json
  38. 0 1
      dist/preview release/what's new.md
  39. 1 1
      package.json
  40. 111 2
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  41. 67 0
      src/Materials/PBR/babylon.pbrMaterial.ts
  42. 9 0
      src/Materials/PBR/babylon.pbrMetallicRoughnessMaterial.ts
  43. 9 0
      src/Materials/PBR/babylon.pbrSpecularGlossinessMaterial.ts
  44. 1 1
      src/Materials/Textures/babylon.mirrorTexture.ts
  45. 396 4
      src/Materials/babylon.effect.ts
  46. 506 6
      src/Materials/babylon.material.ts
  47. 1 1
      src/Mesh/babylon.mesh.ts
  48. 28 25
      src/Mesh/babylon.transformNode.ts
  49. 2 2
      src/Physics/Plugins/babylon.cannonJSPlugin.ts
  50. 12 9
      src/PostProcess/babylon.postProcess.ts
  51. 93 19
      src/Tools/babylon.observable.ts
  52. 10 11
      src/babylon.assetContainer.ts
  53. 11 3
      src/babylon.node.ts
  54. BIN
      tests/validation/ReferenceImages/LightProjectionTexture.png
  55. BIN
      tests/validation/ReferenceImages/gltfMaterialSpecularGlossiness.png
  56. BIN
      tests/validation/ReferenceImages/gltfPrimitiveAttribute.png
  57. 5 0
      tests/validation/config.json

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 7340 - 6961
Playground/babylon.d.txt


+ 3 - 3
Tools/Publisher/index.js

@@ -55,6 +55,7 @@ function updateEngineVersion(newVersion) {
 
 function runGulp() {
     // run gulp typescript-all
+    console.log("Running gulp compilation");
     let exec = shelljs.exec("gulp typescript-all --gulpfile ../Gulp/gulpfile.js");
     if (exec.code) {
         console.log("error during compilation, aborting");
@@ -62,7 +63,7 @@ function runGulp() {
     }
 }
 
-function processPackages() {
+function processPackages(version) {
     packages.forEach((package) => {
         if (package.name === "core") {
             processCore(package, version);
@@ -82,7 +83,6 @@ function processPackages() {
 //check if logged in
 console.log("Using npm user:");
 let loginCheck = shelljs.exec('npm whoami');
-console.log("Not that I can check, but - did you run gulp typescript-all?");
 if (loginCheck.code === 0) {
     prompt.start();
 
@@ -90,7 +90,7 @@ if (loginCheck.code === 0) {
         let version = result.version;
         updateEngineVersion(version);
         runGulp();
-        processPackages();
+        processPackages(version);
 
         console.log("done, please tag git with " + version);
     });

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 33 - 33
Viewer/dist/viewer.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 33 - 33
Viewer/dist/viewer.min.js


+ 1 - 8
Viewer/package.json

@@ -32,12 +32,5 @@
         "uglifyjs-webpack-plugin": "^1.1.6",
         "webpack": "^3.10.0",
         "webpack-dev-server": "^2.11.0"
-    },
-    "dependencies": {
-        "babylonjs": "^3.2.0-alpha4",
-        "babylonjs-loaders": "^3.2.0-alpha4",
-        "deepmerge": "^2.0.1",
-        "es6-promise": "^4.2.2",
-        "handlebars": "^4.0.11"
     }
-}
+}

+ 167 - 133
Viewer/src/configuration/configuration.ts

@@ -15,143 +15,20 @@ export interface ViewerConfiguration {
     };
 
     // names of functions in the window context.
-    observers?: {
-        onEngineInit?: string;
-        onSceneInit?: string;
-        onModelLoaded?: string;
-    }
+    observers?: IObserversConfiguration;
 
     canvasElement?: string; // if there is a need to override the standard implementation - ID of HTMLCanvasElement
 
-    model?: {
-        url?: string;
-        loader?: string; // obj, gltf?
-        position?: { x: number, y: number, z: number };
-        rotation?: { x: number, y: number, z: number, w: number };
-        scaling?: { x: number, y: number, z: number };
-        parentObjectIndex?: number; // the index of the parent object of the model in the loaded meshes array.
-
-        title: string;
-        subtitle?: string;
-        thumbnail?: string; // URL or data-url
-
-        [propName: string]: any; // further configuration, like title and creator
-    } | string;
-
-    scene?: {
-        debug?: boolean;
-        autoRotate?: boolean;
-        rotationSpeed?: number;
-        defaultCamera?: boolean;
-        defaultLight?: boolean;
-        clearColor?: { r: number, g: number, b: number, a: number };
-        imageProcessingConfiguration?: IImageProcessingConfiguration;
-        environmentTexture?: string;
-    },
-    optimizer?: {
-        targetFrameRate?: number;
-        trackerDuration?: number;
-        autoGeneratePriorities?: boolean;
-        improvementMode?: boolean;
-        degradation?: string; // low, moderate, high
-        types?: {
-            texture?: SceneOptimizerParameters;
-            hardwareScaling?: SceneOptimizerParameters;
-            shadow?: SceneOptimizerParameters;
-            postProcess?: SceneOptimizerParameters;
-            lensFlare?: SceneOptimizerParameters;
-            particles?: SceneOptimizerParameters;
-            renderTarget?: SceneOptimizerParameters;
-            mergeMeshes?: SceneOptimizerParameters;
-        }
-    },
-    // at the moment, support only a single camera.
-    camera?: {
-        position?: { x: number, y: number, z: number };
-        rotation?: { x: number, y: number, z: number, w: number };
-        fov?: number;
-        fovMode?: number;
-        minZ?: number;
-        maxZ?: number;
-        inertia?: number;
-        behaviors?: {
-            [name: string]: number | {
-                type: number;
-                [propName: string]: any;
-            };
-        };
-
-        [propName: string]: any;
-    },
-    skybox?: {
-        cubeTexture?: {
-            noMipMap?: boolean;
-            gammaSpace?: boolean;
-            url?: string | Array<string>;
-        };
-        color?: { r: number, g: number, b: number };
-        pbr?: boolean; // deprecated
-        scale?: number;
-        blur?: number; // deprecated
-        material?: {
-            imageProcessingConfiguration?: IImageProcessingConfiguration;
-        };
-        infiniteDIstance?: boolean;
-
-    };
+    model?: IModelConfiguration | string;
 
-    ground?: boolean | {
-        size?: number;
-        receiveShadows?: boolean;
-        shadowLevel?: number;
-        shadowOnly?: boolean; // deprecated
-        mirror?: boolean | {
-            sizeRatio?: number;
-            blurKernel?: number;
-            amount?: number;
-            fresnelWeight?: number;
-            fallOffDistance?: number;
-            textureType?: number;
-        };
-        texture?: string;
-        color?: { r: number, g: number, b: number };
-        opacity?: number;
-        material?: { // deprecated!
-            [propName: string]: any;
-        };
-
-    };
-    lights?: {
-        [name: string]: {
-            type: number;
-            name?: string;
-            disabled?: boolean;
-            position?: { x: number, y: number, z: number };
-            target?: { x: number, y: number, z: number };
-            direction?: { x: number, y: number, z: number };
-            diffuse?: { r: number, g: number, b: number };
-            specular?: { r: number, g: number, b: number };
-            intensity?: number;
-            radius?: number;
-            shadownEnabled?: boolean; // only on specific lights!
-            shadowConfig?: {
-                useBlurExponentialShadowMap?: boolean;
-                useKernelBlur?: boolean;
-                blurKernel?: number;
-                blurScale?: number;
-                [propName: string]: any;
-            }
-            [propName: string]: any;
+    scene?: ISceneConfiguration;
+    optimizer?: ISceneOptimizerConfiguration | boolean;
+    // at the moment, support only a single camera.
+    camera?: ICameraConfiguration,
+    skybox?: boolean | ISkyboxConfiguration;
 
-            // no behaviors for light at the moment, but allowing configuration for future reference.
-            behaviors?: {
-                [name: string]: number | {
-                    type: number;
-                    [propName: string]: any;
-                };
-            };
-        }
-    },
+    ground?: boolean | IGroundConfiguration;
+    lights?: { [name: string]: boolean | ILightConfiguration },
     // engine configuration. optional!
     engine?: {
         antialiasing?: boolean;
@@ -173,9 +50,166 @@ export interface ViewerConfiguration {
             [key: string]: string;
         }
     }
+
+    // features that are being tested.
+    // those features' syntax will change and move out! 
+    // Don't use in production (or be ready to make the changes :) )
+    lab?: {
+        flashlight?: boolean | {
+            exponent?: number;
+            angle?: number;
+            intensity?: number;
+            diffuse?: { r: number, g: number, b: number };
+            specular?: { r: number, g: number, b: number };
+        }
+        hideLoadingDelay?: number;
+    }
+}
+
+export interface IModelConfiguration {
+    url: string;
+    loader?: string; // obj, gltf?
+    position?: { x: number, y: number, z: number };
+    rotation?: { x: number, y: number, z: number, w?: number };
+    scaling?: { x: number, y: number, z: number };
+    parentObjectIndex?: number; // the index of the parent object of the model in the loaded meshes array.
+
+    castShadow?: boolean;
+
+    title: string;
+    subtitle?: string;
+    thumbnail?: string; // URL or data-url
+
+    // [propName: string]: any; // further configuration, like title and creator
+}
+
+export interface ISkyboxConfiguration {
+    cubeTexture?: {
+        noMipMap?: boolean;
+        gammaSpace?: boolean;
+        url?: string | Array<string>;
+    };
+    color?: { r: number, g: number, b: number };
+    pbr?: boolean; // deprecated
+    scale?: number;
+    blur?: number; // deprecated
+    material?: {
+        imageProcessingConfiguration?: IImageProcessingConfiguration;
+    };
+    infiniteDIstance?: boolean;
+
+}
+
+export interface IGroundConfiguration {
+    size?: number;
+    receiveShadows?: boolean;
+    shadowLevel?: number;
+    shadowOnly?: boolean; // deprecated
+    mirror?: boolean | {
+        sizeRatio?: number;
+        blurKernel?: number;
+        amount?: number;
+        fresnelWeight?: number;
+        fallOffDistance?: number;
+        textureType?: number;
+    };
+    texture?: string;
+    color?: { r: number, g: number, b: number };
+    opacity?: number;
+    material?: { // deprecated!
+        [propName: string]: any;
+    };
+}
+
+export interface ISceneConfiguration {
+    debug?: boolean;
+    autoRotate?: boolean; // deprecated
+    rotationSpeed?: number; // deprecated
+    defaultCamera?: boolean; // deprecated
+    defaultLight?: boolean; // deprecated
+    clearColor?: { r: number, g: number, b: number, a: number };
+    imageProcessingConfiguration?: IImageProcessingConfiguration;
+    environmentTexture?: string;
+}
+
+export interface ISceneOptimizerConfiguration {
+    targetFrameRate?: number;
+    trackerDuration?: number;
+    autoGeneratePriorities?: boolean;
+    improvementMode?: boolean;
+    degradation?: string; // low, moderate, high
+    types?: {
+        texture?: ISceneOptimizerParameters;
+        hardwareScaling?: ISceneOptimizerParameters;
+        shadow?: ISceneOptimizerParameters;
+        postProcess?: ISceneOptimizerParameters;
+        lensFlare?: ISceneOptimizerParameters;
+        particles?: ISceneOptimizerParameters;
+        renderTarget?: ISceneOptimizerParameters;
+        mergeMeshes?: ISceneOptimizerParameters;
+    }
+}
+
+export interface IObserversConfiguration {
+    onEngineInit?: string;
+    onSceneInit?: string;
+    onModelLoaded?: string;
+}
+
+export interface ICameraConfiguration {
+    position?: { x: number, y: number, z: number };
+    rotation?: { x: number, y: number, z: number, w: number };
+    fov?: number;
+    fovMode?: number;
+    minZ?: number;
+    maxZ?: number;
+    inertia?: number;
+    behaviors?: {
+        [name: string]: number | {
+            type: number;
+            [propName: string]: any;
+        };
+    };
+
+    [propName: string]: any;
+}
+
+export interface ILightConfiguration {
+    type: number;
+    name?: string;
+    disabled?: boolean;
+    position?: { x: number, y: number, z: number };
+    target?: { x: number, y: number, z: number };
+    direction?: { x: number, y: number, z: number };
+    diffuse?: { r: number, g: number, b: number };
+    specular?: { r: number, g: number, b: number };
+    intensity?: number;
+    intensityMode?: number;
+    radius?: number;
+    shadownEnabled?: boolean; // only on specific lights!
+    shadowConfig?: {
+        useBlurExponentialShadowMap?: boolean;
+        useKernelBlur?: boolean;
+        blurKernel?: number;
+        blurScale?: number;
+        minZ?: number;
+        maxZ?: number;
+        frustumSize?: number;
+        angleScale?: number;
+        [propName: string]: any;
+    }
+    [propName: string]: any;
+
+    // no behaviors for light at the moment, but allowing configuration for future reference.
+    behaviors?: {
+        [name: string]: number | {
+            type: number;
+            [propName: string]: any;
+        };
+    };
 }
 
-export interface SceneOptimizerParameters {
+export interface ISceneOptimizerParameters {
     priority?: number;
     maximumSize?: number;
     step?: number;

+ 4 - 2
Viewer/src/templateManager.ts

@@ -110,7 +110,10 @@ export class TemplateManager {
      * @memberof TemplateManager
      */
     private buildHTMLTree(templates: { [key: string]: ITemplateConfiguration }): Promise<object> {
-        let promises = Object.keys(templates).map(name => {
+        let promises: Array<Promise<Template | boolean>> = Object.keys(templates).map(name => {
+            // if the template was overridden
+            if (!templates[name]) return Promise.resolve(false);
+            // else - we have a template, let's do our job!
             let template = new Template(name, templates[name]);
             // make sure the global onEventTriggered is called as well
             template.onEventTriggered.add(eventData => this.onEventTriggered.notifyObservers(eventData));
@@ -171,7 +174,6 @@ export class TemplateManager {
 
 
 import * as Handlebars from '../assets/handlebars.min.js';
-import { PromiseObservable } from './util/promiseObservable';
 import { EventManager } from './eventManager';
 // register a new helper. modified https://stackoverflow.com/questions/9838925/is-there-any-method-to-iterate-a-map-with-handlebars-js
 Handlebars.registerHelper('eachInMap', function (map, block) {

+ 0 - 39
Viewer/src/util/promiseObservable.ts

@@ -1,39 +0,0 @@
-import { Observable } from 'babylonjs';
-
-export class PromiseObservable<T> extends Observable<T> {
-
-    public notifyWithPromise(eventData: T, mask: number = -1, target?: any, currentTarget?: any): Promise<any> {
-
-        let p = Promise.resolve();
-
-        if (!this._observers.length) {
-            return p;
-        }
-
-        let state = this['_eventState'];
-        state.mask = mask;
-        state.target = target;
-        state.currentTarget = currentTarget;
-        state.skipNextObservers = false;
-
-        this._observers.forEach(obs => {
-            if (state.skipNextObservers) {
-                return;
-            }
-            if (obs.mask & mask) {
-                if (obs.scope) {
-                    // TODO - I can add the variable from the last function here. Requires changing callback sig
-                    p = p.then(() => {
-                        return obs.callback.apply(obs.scope, [eventData, state]);
-                    });
-                } else {
-                    p = p.then(() => {
-                        return obs.callback(eventData, state);
-                    });
-                }
-            }
-        });
-
-        return p;
-    }
-}

+ 75 - 183
Viewer/src/viewer/defaultViewer.ts

@@ -1,15 +1,13 @@
 
 
-import { ViewerConfiguration } from './../configuration/configuration';
+import { ViewerConfiguration, IModelConfiguration, ILightConfiguration } from './../configuration/configuration';
 import { Template, EventCallback } from './../templateManager';
 import { AbstractViewer } from './viewer';
-import { MirrorTexture, Plane, ShadowGenerator, Texture, BackgroundMaterial, Observable, ShadowLight, CubeTexture, BouncingBehavior, FramingBehavior, Behavior, Light, Engine, Scene, AutoRotationBehavior, AbstractMesh, Quaternion, StandardMaterial, ArcRotateCamera, ImageProcessingConfiguration, Color3, Vector3, SceneLoader, Mesh, HemisphericLight } from 'babylonjs';
+import { SpotLight, MirrorTexture, Plane, ShadowGenerator, Texture, BackgroundMaterial, Observable, ShadowLight, CubeTexture, BouncingBehavior, FramingBehavior, Behavior, Light, Engine, Scene, AutoRotationBehavior, AbstractMesh, Quaternion, StandardMaterial, ArcRotateCamera, ImageProcessingConfiguration, Color3, Vector3, SceneLoader, Mesh, HemisphericLight } from 'babylonjs';
 import { CameraBehavior } from '../interfaces';
 
 export class DefaultViewer extends AbstractViewer {
 
-    public camera: ArcRotateCamera;
-
     constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = { extends: 'default' }) {
         super(containerElement, initialConfiguration);
         this.onModelLoadedObservable.add(this.onModelLoaded);
@@ -105,66 +103,57 @@ export class DefaultViewer extends AbstractViewer {
         this.containerElement.style.display = 'flex';
     }
 
-    public loadModel(model: any = this.configuration.model): Promise<Scene> {
-        this.showLoadingScreen();
-        return super.loadModel(model, true).catch((error) => {
-            console.log(error);
-            this.hideLoadingScreen();
-            this.showOverlayScreen('error');
-            return this.scene;
-        });
-    }
-
-    private onModelLoaded = (meshes: Array<AbstractMesh>) => {
-
-        // here we could set the navbar's model information:
-        this.setModelMetaData();
-
-        // with a short timeout, making sure everything is there already.
-        setTimeout(() => {
-            this.hideLoadingScreen();
-        }, 500);
-
-
-        // recreate the camera
-        this.scene.createDefaultCameraOrLight(true, true, true);
-        this.camera = <ArcRotateCamera>this.scene.activeCamera;
-
-        meshes[0].rotation.y += Math.PI;
-
-        this.setupCamera(meshes);
-        this.setupLights(meshes);
-
-        return; //this.initEnvironment(meshes);
-    }
+    protected configureModel(modelConfiguration: Partial<IModelConfiguration>) {
+        super.configureModel(modelConfiguration);
 
-    private setModelMetaData() {
         let navbar = this.templateManager.getTemplate('navBar');
         if (!navbar) return;
 
         let metadataContainer = navbar.parent.querySelector('#model-metadata');
-
-        //title
-        if (metadataContainer && typeof this.configuration.model === 'object') {
-            if (this.configuration.model.title) {
+        if (metadataContainer) {
+            if (modelConfiguration.title !== undefined) {
                 let element = metadataContainer.querySelector('span.model-title');
                 if (element) {
-                    element.innerHTML = this.configuration.model.title;
+                    element.innerHTML = modelConfiguration.title;
                 }
             }
 
-            if (this.configuration.model.subtitle) {
+            if (modelConfiguration.subtitle !== undefined) {
                 let element = metadataContainer.querySelector('span.model-subtitle');
                 if (element) {
-                    element.innerHTML = this.configuration.model.subtitle;
+                    element.innerHTML = modelConfiguration.subtitle;
                 }
             }
 
-            if (this.configuration.model.thumbnail) {
-                (<HTMLDivElement>metadataContainer.querySelector('.thumbnail')).style.backgroundImage = `url('${this.configuration.model.thumbnail}')`;
+            if (modelConfiguration.thumbnail !== undefined) {
+                (<HTMLDivElement>metadataContainer.querySelector('.thumbnail')).style.backgroundImage = `url('${modelConfiguration.thumbnail}')`;
             }
         }
+    }
 
+    public loadModel(model: any = this.configuration.model): Promise<Scene> {
+        this.showLoadingScreen();
+        return super.loadModel(model, true).catch((error) => {
+            console.log(error);
+            this.hideLoadingScreen();
+            this.showOverlayScreen('error');
+            return this.scene;
+        });
+    }
+
+    private onModelLoaded = (meshes: Array<AbstractMesh>) => {
+        // with a short timeout, making sure everything is there already.
+        let hideLoadingDelay = 500;
+        if (this.configuration.lab && this.configuration.lab.hideLoadingDelay !== undefined) {
+            hideLoadingDelay = this.configuration.lab.hideLoadingDelay;
+        }
+        setTimeout(() => {
+            this.hideLoadingScreen();
+        }, hideLoadingDelay);
+
+        meshes[0].rotation.y += Math.PI;
+
+        return; //this.initEnvironment(meshes);
     }
 
     /*protected initEnvironment(focusMeshes: Array<AbstractMesh> = []): Promise<Scene> {
@@ -356,150 +345,53 @@ export class DefaultViewer extends AbstractViewer {
         }));
     }
 
-    private setupLights(focusMeshes: Array<AbstractMesh> = []) {
-
-        let sceneConfig = this.configuration.scene || { defaultLight: true };
-
-        if (!sceneConfig.defaultLight && (this.configuration.lights && Object.keys(this.configuration.lights).length)) {
-            // remove old lights
-            this.scene.lights.forEach(l => {
-                l.dispose();
-            });
-
-            Object.keys(this.configuration.lights).forEach((name, idx) => {
-                let lightConfig = this.configuration.lights && this.configuration.lights[name] || { name: name, type: 0 };
-                lightConfig.name = name;
-                let constructor = Light.GetConstructorFromName(lightConfig.type, lightConfig.name, this.scene);
-                if (!constructor) return;
-                let light = constructor();
-
-                //enabled
-                if (light.isEnabled() !== !lightConfig.disabled) {
-                    light.setEnabled(!lightConfig.disabled);
+    protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean } = {}, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
+        super.configureLights(lightsConfiguration, focusMeshes);
+        console.log("flashlight", this.configuration.lab);
+        // labs feature - flashlight
+        if (this.configuration.lab && this.configuration.lab.flashlight) {
+            let pointerPosition = BABYLON.Vector3.Zero();
+            let lightTarget;
+            let angle = 0.5;
+            let exponent = Math.PI / 2;
+            if (typeof this.configuration.lab.flashlight === "object") {
+                exponent = this.configuration.lab.flashlight.exponent || exponent;
+                angle = this.configuration.lab.flashlight.angle || angle;
+            }
+            var flashlight = new SpotLight("flashlight", Vector3.Zero(),
+                Vector3.Zero(), exponent, angle, this.scene);
+            if (typeof this.configuration.lab.flashlight === "object") {
+                flashlight.intensity = this.configuration.lab.flashlight.intensity || flashlight.intensity;
+                if (this.configuration.lab.flashlight.diffuse) {
+                    flashlight.diffuse.r = this.configuration.lab.flashlight.diffuse.r;
+                    flashlight.diffuse.g = this.configuration.lab.flashlight.diffuse.g;
+                    flashlight.diffuse.b = this.configuration.lab.flashlight.diffuse.b;
+                }
+                if (this.configuration.lab.flashlight.specular) {
+                    flashlight.specular.r = this.configuration.lab.flashlight.specular.r;
+                    flashlight.specular.g = this.configuration.lab.flashlight.specular.g;
+                    flashlight.specular.b = this.configuration.lab.flashlight.specular.b;
                 }
 
-                this.extendClassWithConfig(light, lightConfig);
-
-                //position. Some lights don't support shadows
-                if (light instanceof ShadowLight) {
-                    if (lightConfig.shadowEnabled && this.maxShadows) {
-                        var shadowGenerator = new ShadowGenerator(512, light);
-                        this.extendClassWithConfig(shadowGenerator, lightConfig.shadowConfig || {});
-                        // add the focues meshes to the shadow list
-                        let shadownMap = shadowGenerator.getShadowMap();
-                        if (!shadownMap) return;
-                        let renderList = shadownMap.renderList;
-                        for (var index = 0; index < focusMeshes.length; index++) {
-                            renderList && renderList.push(focusMeshes[index]);
-                        }
-                    }
+            }
+            this.scene.constantlyUpdateMeshUnderPointer = true;
+            this.scene.onPointerObservable.add((eventData, eventState) => {
+                if (eventData.type === 4 && eventData.pickInfo) {
+                    lightTarget = (eventData.pickInfo.pickedPoint);
+                } else {
+                    lightTarget = undefined;
                 }
             });
-        } else {
-            if (!this.configuration.lights && this.configuration.ground) {
-                let light = this.scene.lights[0];
-                if (light instanceof ShadowLight) {
-                    if (this.maxShadows) {
-                        var shadowGenerator = new ShadowGenerator(512, light);
-                        // add the focues meshes to the shadow list
-                        let shadownMap = shadowGenerator.getShadowMap();
-                        if (!shadownMap) return;
-                        let renderList = shadownMap.renderList;
-                        for (var index = 0; index < focusMeshes.length; index++) {
-                            renderList && renderList.push(focusMeshes[index]);
-                        }
+            let updateFlashlightFunction = () => {
+                if (this.camera && flashlight) {
+                    flashlight.position.copyFrom(this.camera.position);
+                    if (lightTarget) {
+                        lightTarget.subtractToRef(flashlight.position, flashlight.direction);
                     }
                 }
             }
-        }
-    }
-
-    private setupCamera(focusMeshes: Array<AbstractMesh> = []) {
-
-        let cameraConfig = this.configuration.camera || {};
-        let sceneConfig = this.configuration.scene || { autoRotate: false, defaultCamera: true };
-
-        if (!this.configuration.camera && sceneConfig.defaultCamera) {
-            if (sceneConfig.autoRotate) {
-                this.camera.useAutoRotationBehavior = true;
-            }
-            return;
-        }
-
-        if (cameraConfig.position) {
-            this.camera.position.copyFromFloats(cameraConfig.position.x || 0, cameraConfig.position.y || 0, cameraConfig.position.z || 0);
-        }
-
-        if (cameraConfig.rotation) {
-            this.camera.rotationQuaternion = new Quaternion(cameraConfig.rotation.x || 0, cameraConfig.rotation.y || 0, cameraConfig.rotation.z || 0, cameraConfig.rotation.w || 0)
-        }
-
-        this.camera.minZ = cameraConfig.minZ || this.camera.minZ;
-        this.camera.maxZ = cameraConfig.maxZ || this.camera.maxZ;
-
-        if (cameraConfig.behaviors) {
-            for (let name in cameraConfig.behaviors) {
-                this.setCameraBehavior(cameraConfig.behaviors[name], focusMeshes);
-            }
-        };
-
-        if (sceneConfig.autoRotate) {
-            this.camera.useAutoRotationBehavior = true;
-        }
-
-        const sceneExtends = this.scene.getWorldExtends();
-        const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
-        const sceneDiagonalLenght = sceneDiagonal.length();
-        this.camera.upperRadiusLimit = sceneDiagonalLenght * 3;
-    }
-
-    private setCameraBehavior(behaviorConfig: number | {
-        type: number;
-        [propName: string]: any;
-    }, payload: any) {
-
-        let behavior: Behavior<ArcRotateCamera> | null;
-        let type = (typeof behaviorConfig !== "object") ? behaviorConfig : behaviorConfig.type;
-
-        let config: { [propName: string]: any } = (typeof behaviorConfig === "object") ? behaviorConfig : {};
-
-        // constructing behavior
-        switch (type) {
-            case CameraBehavior.AUTOROTATION:
-                behavior = new AutoRotationBehavior();
-                break;
-            case CameraBehavior.BOUNCING:
-                behavior = new BouncingBehavior();
-                break;
-            case CameraBehavior.FRAMING:
-                behavior = new FramingBehavior();
-                break;
-            default:
-                behavior = null;
-                break;
-        }
-
-        if (behavior) {
-            if (typeof behaviorConfig === "object") {
-                this.extendClassWithConfig(behavior, behaviorConfig);
-            }
-            this.camera.addBehavior(behavior);
-        }
-
-        // post attach configuration. Some functionalities require the attached camera.
-        switch (type) {
-            case CameraBehavior.AUTOROTATION:
-                break;
-            case CameraBehavior.BOUNCING:
-                break;
-            case CameraBehavior.FRAMING:
-                if (config.zoomOnBoundingInfo) {
-                    //payload is an array of meshes
-                    let meshes = <Array<AbstractMesh>>payload;
-                    let bounding = meshes[0].getHierarchyBoundingVectors();
-                    (<FramingBehavior>behavior).zoomOnBoundingInfo(bounding.min, bounding.max);
-                }
-                break;
+            this.scene.registerBeforeRender(updateFlashlightFunction);
+            this.registeredOnBeforerenderFunctions.push(updateFlashlightFunction);
         }
     }
 }

+ 515 - 171
Viewer/src/viewer/viewer.ts

@@ -1,9 +1,11 @@
 import { viewerManager } from './viewerManager';
 import { TemplateManager } from './../templateManager';
 import configurationLoader from './../configuration/loader';
-import { CubeTexture, Color3, IEnvironmentHelperOptions, EnvironmentHelper, Effect, SceneOptimizer, SceneOptimizerOptions, Observable, Engine, Scene, ArcRotateCamera, Vector3, SceneLoader, AbstractMesh, Mesh, HemisphericLight, Database, SceneLoaderProgressEvent, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from 'babylonjs';
-import { ViewerConfiguration } from '../configuration/configuration';
-import { PromiseObservable } from '../util/promiseObservable';
+import { CubeTexture, Color3, IEnvironmentHelperOptions, EnvironmentHelper, Effect, SceneOptimizer, SceneOptimizerOptions, Observable, Engine, Scene, ArcRotateCamera, Vector3, SceneLoader, AbstractMesh, Mesh, HemisphericLight, Database, SceneLoaderProgressEvent, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Quaternion, Light, ShadowLight, ShadowGenerator, Tags, AutoRotationBehavior, BouncingBehavior, FramingBehavior, Behavior } from 'babylonjs';
+import { ViewerConfiguration, ISceneConfiguration, ISceneOptimizerConfiguration, IObserversConfiguration, IModelConfiguration, ISkyboxConfiguration, IGroundConfiguration, ILightConfiguration, ICameraConfiguration } from '../configuration/configuration';
+
+import * as deepmerge from '../../assets/deepmerge.min.js';
+import { CameraBehavior } from 'src/interfaces';
 
 export abstract class AbstractViewer {
 
@@ -11,6 +13,7 @@ export abstract class AbstractViewer {
 
     public engine: Engine;
     public scene: Scene;
+    public camera: ArcRotateCamera;
     public sceneOptimizer: SceneOptimizer;
     public baseId: string;
 
@@ -32,14 +35,18 @@ export abstract class AbstractViewer {
 
 
     // observables
-    public onSceneInitObservable: PromiseObservable<Scene>;
-    public onEngineInitObservable: PromiseObservable<Engine>;
-    public onModelLoadedObservable: PromiseObservable<AbstractMesh[]>;
-    public onModelLoadProgressObservable: PromiseObservable<SceneLoaderProgressEvent>;
-    public onInitDoneObservable: PromiseObservable<AbstractViewer>;
+    public onSceneInitObservable: Observable<Scene>;
+    public onEngineInitObservable: Observable<Engine>;
+    public onModelLoadedObservable: Observable<AbstractMesh[]>;
+    public onModelLoadProgressObservable: Observable<SceneLoaderProgressEvent>;
+    public onModelLoadErrorObservable: Observable<{ message: string; exception: any }>;
+    public onLoaderInitObservable: Observable<ISceneLoaderPlugin | ISceneLoaderPluginAsync>;
+    public onInitDoneObservable: Observable<AbstractViewer>;
 
     protected canvas: HTMLCanvasElement;
 
+    protected registeredOnBeforerenderFunctions: Array<() => void>;
+
     constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = {}) {
         // if exists, use the container id. otherwise, generate a random string.
         if (containerElement.id) {
@@ -48,11 +55,15 @@ export abstract class AbstractViewer {
             this.baseId = containerElement.id = 'bjs' + Math.random().toString(32).substr(2, 8);
         }
 
-        this.onSceneInitObservable = new PromiseObservable();
-        this.onEngineInitObservable = new PromiseObservable();
-        this.onModelLoadedObservable = new PromiseObservable();
-        this.onModelLoadProgressObservable = new PromiseObservable();
-        this.onInitDoneObservable = new PromiseObservable();
+        this.onSceneInitObservable = new Observable();
+        this.onEngineInitObservable = new Observable();
+        this.onModelLoadedObservable = new Observable();
+        this.onModelLoadProgressObservable = new Observable();
+        this.onModelLoadErrorObservable = new Observable();
+        this.onInitDoneObservable = new Observable();
+        this.onLoaderInitObservable = new Observable();
+
+        this.registeredOnBeforerenderFunctions = [];
 
         // add this viewer to the viewer manager
         viewerManager.addViewer(this);
@@ -65,19 +76,10 @@ export abstract class AbstractViewer {
         // extend the configuration
         configurationLoader.loadConfiguration(initialConfiguration).then((configuration) => {
             this.configuration = configuration;
-
-            // adding preconfigured functions
             if (this.configuration.observers) {
-                if (this.configuration.observers.onEngineInit) {
-                    this.onEngineInitObservable.add(window[this.configuration.observers.onEngineInit]);
-                }
-                if (this.configuration.observers.onSceneInit) {
-                    this.onSceneInitObservable.add(window[this.configuration.observers.onSceneInit]);
-                }
-                if (this.configuration.observers.onModelLoaded) {
-                    this.onModelLoadedObservable.add(window[this.configuration.observers.onModelLoaded]);
-                }
+                this.configureObservers(this.configuration.observers);
             }
+            //this.updateConfiguration(configuration);
 
             // initialize the templates
             let templateConfiguration = this.configuration.templates || {};
@@ -118,7 +120,415 @@ export abstract class AbstractViewer {
     }
 
     protected render = (): void => {
-        this.scene && this.scene.render();
+        this.scene && this.scene.activeCamera && this.scene.render();
+    }
+
+    /**
+     * Update the current viewer configuration with new values.
+     * Only provided information will be updated, old configuration values will be kept.
+     * If this.configuration was manually changed, you can trigger this function with no parameters, 
+     * and the entire configuration will be updated. 
+     * @param newConfiguration 
+     */
+    public updateConfiguration(newConfiguration: Partial<ViewerConfiguration> = this.configuration) {
+        // update scene configuration
+        if (newConfiguration.scene) {
+            this.configureScene(newConfiguration.scene);
+        }
+        // optimizer
+        if (newConfiguration.optimizer) {
+            this.configureOptimizer(newConfiguration.optimizer);
+        }
+
+        // observers in configuration
+        if (newConfiguration.observers) {
+            this.configureObservers(newConfiguration.observers);
+        }
+
+        // configure model
+        if (newConfiguration.model && typeof newConfiguration.model === 'object') {
+            this.configureModel(newConfiguration.model);
+        }
+
+        // lights
+        if (newConfiguration.lights) {
+            this.configureLights(newConfiguration.lights);
+        }
+
+        // environment
+        if (newConfiguration.skybox !== undefined || newConfiguration.ground !== undefined) {
+            this.configureEnvironment(newConfiguration.skybox, newConfiguration.ground);
+        }
+
+        // update this.configuration with the new data
+        this.configuration = deepmerge(this.configuration || {}, newConfiguration);
+    }
+
+    protected configureEnvironment(skyboxConifguration?: ISkyboxConfiguration | boolean, groundConfiguration?: IGroundConfiguration | boolean) {
+        if (!skyboxConifguration && !groundConfiguration) {
+            if (this.environmentHelper) {
+                this.environmentHelper.dispose();
+            };
+            return Promise.resolve(this.scene);
+        }
+
+        const options: Partial<IEnvironmentHelperOptions> = {
+            createGround: !!groundConfiguration,
+            createSkybox: !!skyboxConifguration,
+            setupImageProcessing: false // will be done at the scene level!
+        };
+
+        if (groundConfiguration) {
+            let groundConfig = (typeof groundConfiguration === 'boolean') ? {} : groundConfiguration;
+
+            let groundSize = groundConfig.size || (typeof skyboxConifguration === 'object' && skyboxConifguration.scale);
+            if (groundSize) {
+                options.groundSize = groundSize;
+            }
+
+            options.enableGroundShadow = groundConfig === true || groundConfig.receiveShadows;
+            if (groundConfig.shadowLevel) {
+                options.groundShadowLevel = groundConfig.shadowLevel;
+            }
+            options.enableGroundMirror = !!groundConfig.mirror;
+            if (groundConfig.texture) {
+                options.groundTexture = groundConfig.texture;
+            }
+            if (groundConfig.color) {
+                options.groundColor = new Color3(groundConfig.color.r, groundConfig.color.g, groundConfig.color.b)
+            }
+
+            if (groundConfig.mirror) {
+                options.enableGroundMirror = true;
+                // to prevent undefines
+                if (typeof groundConfig.mirror === "object") {
+                    if (groundConfig.mirror.amount)
+                        options.groundMirrorAmount = groundConfig.mirror.amount;
+                    if (groundConfig.mirror.sizeRatio)
+                        options.groundMirrorSizeRatio = groundConfig.mirror.sizeRatio;
+                    if (groundConfig.mirror.blurKernel)
+                        options.groundMirrorBlurKernel = groundConfig.mirror.blurKernel;
+                    if (groundConfig.mirror.fresnelWeight)
+                        options.groundMirrorFresnelWeight = groundConfig.mirror.fresnelWeight;
+                    if (groundConfig.mirror.fallOffDistance)
+                        options.groundMirrorFallOffDistance = groundConfig.mirror.fallOffDistance;
+                    if (this.defaultHighpTextureType !== undefined)
+                        options.groundMirrorTextureType = this.defaultHighpTextureType;
+                }
+            }
+
+        }
+
+        let postInitSkyboxMaterial = false;
+        if (skyboxConifguration) {
+            let conf = skyboxConifguration === true ? {} : skyboxConifguration;
+            if (conf.material && conf.material.imageProcessingConfiguration) {
+                options.setupImageProcessing = false; // will be configured later manually.
+            }
+            let skyboxSize = conf.scale;
+            if (skyboxSize) {
+                options.skyboxSize = skyboxSize;
+            }
+            options.sizeAuto = !options.skyboxSize;
+            if (conf.color) {
+                options.skyboxColor = new Color3(conf.color.r, conf.color.g, conf.color.b)
+            }
+            if (conf.cubeTexture && conf.cubeTexture.url) {
+                if (typeof conf.cubeTexture.url === "string") {
+                    options.skyboxTexture = conf.cubeTexture.url;
+                } else {
+                    // init later!
+                    postInitSkyboxMaterial = true;
+                }
+            }
+
+            if (conf.material && conf.material.imageProcessingConfiguration) {
+                postInitSkyboxMaterial = true;
+            }
+        }
+
+        if (!this.environmentHelper) {
+            this.environmentHelper = this.scene.createDefaultEnvironment(options)!;
+        } else {
+            // there might be a new scene! we need to dispose.
+
+            // get the scene used by the envHelper
+            let scene: Scene = this.environmentHelper.rootMesh.getScene();
+            // is it a different scene? Oh no!
+            if (scene !== this.scene) {
+                this.environmentHelper.dispose();
+                this.environmentHelper = this.scene.createDefaultEnvironment(options)!;
+            } else {
+                this.environmentHelper.updateOptions(options)!;
+            }
+        }
+
+        if (postInitSkyboxMaterial) {
+            let skyboxMaterial = this.environmentHelper.skyboxMaterial;
+            if (skyboxMaterial) {
+                if (typeof skyboxConifguration === 'object' && skyboxConifguration.material && skyboxConifguration.material.imageProcessingConfiguration) {
+                    this.extendClassWithConfig(skyboxMaterial.imageProcessingConfiguration, skyboxConifguration.material.imageProcessingConfiguration);
+                }
+            }
+        }
+    }
+
+    protected configureScene(sceneConfig: ISceneConfiguration, optimizerConfig?: ISceneOptimizerConfiguration) {
+        // sanity check!
+        if (!this.scene) {
+            return;
+        }
+        if (sceneConfig.debug) {
+            this.scene.debugLayer.show();
+        } else {
+            if (this.scene.debugLayer.isVisible()) {
+                this.scene.debugLayer.hide();
+            }
+        }
+
+        if (sceneConfig.clearColor) {
+            let cc = sceneConfig.clearColor;
+            let oldcc = this.scene.clearColor;
+            if (cc.r !== undefined) {
+                oldcc.r = cc.r;
+            }
+            if (cc.g !== undefined) {
+                oldcc.g = cc.g
+            }
+            if (cc.b !== undefined) {
+                oldcc.b = cc.b
+            }
+            if (cc.a !== undefined) {
+                oldcc.a = cc.a
+            }
+        }
+
+        // image processing configuration - optional.
+        if (sceneConfig.imageProcessingConfiguration) {
+            this.extendClassWithConfig(this.scene.imageProcessingConfiguration, sceneConfig.imageProcessingConfiguration);
+        }
+        if (sceneConfig.environmentTexture) {
+            if (this.scene.environmentTexture) {
+                this.scene.environmentTexture.dispose();
+            }
+            const environmentTexture = CubeTexture.CreateFromPrefilteredData(sceneConfig.environmentTexture, this.scene);
+            this.scene.environmentTexture = environmentTexture;
+        }
+
+        if (sceneConfig.autoRotate) {
+            this.camera.useAutoRotationBehavior = true;
+        }
+    }
+
+    protected configureOptimizer(optimizerConfig: ISceneOptimizerConfiguration | boolean) {
+        if (typeof optimizerConfig === 'boolean') {
+            if (this.sceneOptimizer) {
+                this.sceneOptimizer.stop();
+                this.sceneOptimizer.dispose();
+                delete this.sceneOptimizer;
+            }
+            if (optimizerConfig) {
+                this.sceneOptimizer = new SceneOptimizer(this.scene);
+                this.sceneOptimizer.start();
+            }
+        } else {
+            let optimizerOptions: SceneOptimizerOptions = new SceneOptimizerOptions(optimizerConfig.targetFrameRate, optimizerConfig.trackerDuration);
+            // check for degradation
+            if (optimizerConfig.degradation) {
+                switch (optimizerConfig.degradation) {
+                    case "low":
+                        optimizerOptions = SceneOptimizerOptions.LowDegradationAllowed(optimizerConfig.targetFrameRate);
+                        break;
+                    case "moderate":
+                        optimizerOptions = SceneOptimizerOptions.ModerateDegradationAllowed(optimizerConfig.targetFrameRate);
+                        break;
+                    case "hight":
+                        optimizerOptions = SceneOptimizerOptions.HighDegradationAllowed(optimizerConfig.targetFrameRate);
+                        break;
+                }
+            }
+            if (this.sceneOptimizer) {
+                this.sceneOptimizer.stop();
+                this.sceneOptimizer.dispose()
+            }
+            this.sceneOptimizer = new SceneOptimizer(this.scene, optimizerOptions, optimizerConfig.autoGeneratePriorities, optimizerConfig.improvementMode);
+            this.sceneOptimizer.start();
+        }
+    }
+
+    protected configureObservers(observersConfiguration: IObserversConfiguration) {
+        if (observersConfiguration.onEngineInit) {
+            this.onEngineInitObservable.add(window[observersConfiguration.onEngineInit]);
+        } else {
+            if (observersConfiguration.onEngineInit === '' && this.configuration.observers && this.configuration.observers!.onEngineInit) {
+                this.onEngineInitObservable.removeCallback(window[this.configuration.observers!.onEngineInit!]);
+            }
+        }
+        if (observersConfiguration.onSceneInit) {
+            this.onSceneInitObservable.add(window[observersConfiguration.onSceneInit]);
+        } else {
+            if (observersConfiguration.onSceneInit === '' && this.configuration.observers && this.configuration.observers!.onSceneInit) {
+                this.onSceneInitObservable.removeCallback(window[this.configuration.observers!.onSceneInit!]);
+            }
+        }
+        if (observersConfiguration.onModelLoaded) {
+            this.onModelLoadedObservable.add(window[observersConfiguration.onModelLoaded]);
+        } else {
+            if (observersConfiguration.onModelLoaded === '' && this.configuration.observers && this.configuration.observers!.onModelLoaded) {
+                this.onModelLoadedObservable.removeCallback(window[this.configuration.observers!.onModelLoaded!]);
+            }
+        }
+    }
+
+    protected configureCamera(cameraConfig: ICameraConfiguration, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
+        if (!this.scene.activeCamera) {
+            this.scene.createDefaultCamera(true, true, true);
+            this.camera = <ArcRotateCamera>this.scene.activeCamera!;
+        }
+        if (cameraConfig.position) {
+            this.camera.position.copyFromFloats(cameraConfig.position.x || 0, cameraConfig.position.y || 0, cameraConfig.position.z || 0);
+        }
+
+        if (cameraConfig.rotation) {
+            this.camera.rotationQuaternion = new Quaternion(cameraConfig.rotation.x || 0, cameraConfig.rotation.y || 0, cameraConfig.rotation.z || 0, cameraConfig.rotation.w || 0)
+        }
+
+        this.camera.minZ = cameraConfig.minZ || this.camera.minZ;
+        this.camera.maxZ = cameraConfig.maxZ || this.camera.maxZ;
+
+        if (cameraConfig.behaviors) {
+            for (let name in cameraConfig.behaviors) {
+                this.setCameraBehavior(cameraConfig.behaviors[name], focusMeshes);
+            }
+        };
+
+        const sceneExtends = this.scene.getWorldExtends();
+        const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
+        const sceneDiagonalLenght = sceneDiagonal.length();
+        this.camera.upperRadiusLimit = sceneDiagonalLenght * 3;
+    }
+
+    protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean } = {}, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
+        // sanity check!
+        if (!Object.keys(lightsConfiguration).length) return;
+
+        let lightsAvailable: Array<string> = this.scene.lights.map(light => light.name);
+
+        Object.keys(lightsConfiguration).forEach((name, idx) => {
+            let lightConfig: ILightConfiguration = { type: 0 };
+            if (typeof lightsConfiguration[name] === 'object') {
+                lightConfig = <ILightConfiguration>lightsConfiguration[name];
+            }
+
+            lightConfig.name = name;
+
+            let light;
+            // light is not already available
+            if (lightsAvailable.indexOf(name) === -1) {
+                let constructor = Light.GetConstructorFromName(lightConfig.type, lightConfig.name, this.scene);
+                if (!constructor) return;
+                light = constructor();
+            } else {
+                // available? get it from the scene
+                light = this.scene.getLightByName(name);
+                lightsAvailable = lightsAvailable.filter(ln => ln !== name);
+            }
+
+            // if config set the light to false, dispose it.
+            if (lightsConfiguration[name] === false) {
+                light.dispose();
+                return;
+            }
+
+            //enabled
+            if (light.isEnabled() !== !lightConfig.disabled) {
+                light.setEnabled(!lightConfig.disabled);
+            }
+
+            this.extendClassWithConfig(light, lightConfig);
+
+            //position. Some lights don't support shadows
+            if (light instanceof ShadowLight) {
+                let shadowGenerator = light.getShadowGenerator();
+                if (lightConfig.shadowEnabled && this.maxShadows) {
+                    if (!shadowGenerator) {
+                        shadowGenerator = new ShadowGenerator(512, light);
+                    }
+                    this.extendClassWithConfig(shadowGenerator, lightConfig.shadowConfig || {});
+                    // add the focues meshes to the shadow list
+                    let shadownMap = shadowGenerator.getShadowMap();
+                    if (!shadownMap) return;
+                    let renderList = shadownMap.renderList;
+                    for (var index = 0; index < focusMeshes.length; index++) {
+                        if (Tags.MatchesQuery(focusMeshes[index], 'castShadow')) {
+                            // renderList && renderList.push(focusMeshes[index]);
+                        }
+                    }
+                } else if (shadowGenerator) {
+                    shadowGenerator.dispose();
+                }
+            }
+        });
+
+        // remove the unneeded lights
+        /*lightsAvailable.forEach(name => {
+            let light = this.scene.getLightByName(name);
+            if (light) {
+                light.dispose();
+            }
+        });*/
+    }
+
+    protected configureModel(modelConfiguration: Partial<IModelConfiguration>, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
+        let meshesWithNoParent: Array<AbstractMesh> = focusMeshes.filter(m => !m.parent);
+        let updateMeshesWithNoParent = (variable: string, value: any, param?: string) => {
+            meshesWithNoParent.forEach(mesh => {
+                if (param) {
+                    mesh[variable][param] = value;
+                } else {
+                    mesh[variable] = value;
+                }
+            });
+        }
+        let updateXYZ = (variable: string, configValues: { x: number, y: number, z: number, w?: number }) => {
+            if (configValues.x !== undefined) {
+                updateMeshesWithNoParent(variable, configValues.x, 'x');
+            }
+            if (configValues.y !== undefined) {
+                updateMeshesWithNoParent(variable, configValues.y, 'y');
+            }
+            if (configValues.z !== undefined) {
+                updateMeshesWithNoParent(variable, configValues.z, 'z');
+            }
+            if (configValues.w !== undefined) {
+                updateMeshesWithNoParent(variable, configValues.w, 'w');
+            }
+        }
+        // position?
+        if (modelConfiguration.position) {
+            updateXYZ('position', modelConfiguration.position);
+        }
+        if (modelConfiguration.rotation) {
+            if (modelConfiguration.rotation.w) {
+                meshesWithNoParent.forEach(mesh => {
+                    if (!mesh.rotationQuaternion) {
+                        mesh.rotationQuaternion = new Quaternion();
+                    }
+                })
+                updateXYZ('rotationQuaternion', modelConfiguration.rotation);
+            } else {
+                updateXYZ('rotation', modelConfiguration.rotation);
+            }
+        }
+        if (modelConfiguration.scaling) {
+            updateXYZ('scaling', modelConfiguration.scaling);
+        }
+
+        if (modelConfiguration.castShadow) {
+            focusMeshes.forEach(mesh => {
+                Tags.AddTagsTo(mesh, 'castShadow');
+            });
+        }
     }
 
     public dispose() {
@@ -160,7 +570,7 @@ export abstract class AbstractViewer {
         return this.onTemplatesLoaded().then(() => {
             let autoLoadModel = !!this.configuration.model;
             return this.initEngine().then((engine) => {
-                return this.onEngineInitObservable.notifyWithPromise(engine);
+                return this.onEngineInitObservable.notifyObserversWithPromise(engine);
             }).then(() => {
                 if (autoLoadModel) {
                     return this.loadModel();
@@ -168,9 +578,9 @@ export abstract class AbstractViewer {
                     return this.scene || this.initScene();
                 }
             }).then((scene) => {
-                return this.onSceneInitObservable.notifyWithPromise(scene);
+                return this.onSceneInitObservable.notifyObserversWithPromise(scene);
             }).then(() => {
-                return this.onInitDoneObservable.notifyWithPromise(this);
+                return this.onInitDoneObservable.notifyObserversWithPromise(this);
             }).then(() => {
                 return this;
             });
@@ -228,48 +638,17 @@ export abstract class AbstractViewer {
         // create a new scene
         this.scene = new Scene(this.engine);
         // make sure there is a default camera and light.
-        this.scene.createDefaultCameraOrLight(true, true, true);
+        this.scene.createDefaultLight(true);
+
         if (this.configuration.scene) {
-            if (this.configuration.scene.debug) {
-                this.scene.debugLayer.show();
-            }
+            this.configureScene(this.configuration.scene);
 
             // Scene optimizer
             if (this.configuration.optimizer) {
-
-                let optimizerConfig = this.configuration.optimizer;
-                let optimizerOptions: SceneOptimizerOptions = new SceneOptimizerOptions(optimizerConfig.targetFrameRate, optimizerConfig.trackerDuration);
-                // check for degradation
-                if (optimizerConfig.degradation) {
-                    switch (optimizerConfig.degradation) {
-                        case "low":
-                            optimizerOptions = SceneOptimizerOptions.LowDegradationAllowed(optimizerConfig.targetFrameRate);
-                            break;
-                        case "moderate":
-                            optimizerOptions = SceneOptimizerOptions.ModerateDegradationAllowed(optimizerConfig.targetFrameRate);
-                            break;
-                        case "hight":
-                            optimizerOptions = SceneOptimizerOptions.HighDegradationAllowed(optimizerConfig.targetFrameRate);
-                            break;
-                    }
-                }
-
-                this.sceneOptimizer = new SceneOptimizer(this.scene, optimizerOptions, optimizerConfig.autoGeneratePriorities, optimizerConfig.improvementMode);
-                this.sceneOptimizer.start();
-            }
-
-            // image processing configuration - optional.
-            if (this.configuration.scene.imageProcessingConfiguration) {
-                this.extendClassWithConfig(this.scene.imageProcessingConfiguration, this.configuration.scene.imageProcessingConfiguration);
-            }
-            if (this.configuration.scene.environmentTexture) {
-                const environmentTexture = CubeTexture.CreateFromPrefilteredData(this.configuration.scene.environmentTexture, this.scene);
-                this.scene.environmentTexture = environmentTexture;
+                this.configureOptimizer(this.configuration.optimizer);
             }
         }
 
-
-
         return Promise.resolve(this.scene);
     }
 
@@ -282,23 +661,39 @@ export abstract class AbstractViewer {
         let plugin = (typeof model === 'string') ? undefined : model.loader;
 
         return Promise.resolve(this.scene).then((scene) => {
-            if (!scene || clearScene) return this.initScene();
-            else return this.scene!;
+            if (!scene) return this.initScene();
+
+            if (clearScene) {
+                scene.meshes.forEach(mesh => {
+                    mesh.dispose();
+                });
+            }
+            return scene!;
         }).then(() => {
             return new Promise<Array<AbstractMesh>>((resolve, reject) => {
                 this.lastUsedLoader = SceneLoader.ImportMesh(undefined, base, filename, this.scene, (meshes) => {
                     resolve(meshes);
                 }, (progressEvent) => {
-                    this.onModelLoadProgressObservable.notifyWithPromise(progressEvent);
+                    this.onModelLoadProgressObservable.notifyObserversWithPromise(progressEvent);
                 }, (e, m, exception) => {
                     // console.log(m, exception);
-                    reject(m);
+                    this.onModelLoadErrorObservable.notifyObserversWithPromise({ message: m, exception: exception }).then(() => {
+                        reject(exception);
+                    });
                 }, plugin)!;
+                this.onLoaderInitObservable.notifyObserversWithPromise(this.lastUsedLoader);
             });
         }).then((meshes: Array<AbstractMesh>) => {
-            return this.onModelLoadedObservable.notifyWithPromise(meshes)
+            return this.onModelLoadedObservable.notifyObserversWithPromise(meshes)
                 .then(() => {
-                    this.initEnvironment();
+                    // update the models' configuration
+                    this.configureModel(model, meshes);
+                    this.configureLights(this.configuration.lights);
+
+                    if (this.configuration.camera) {
+                        this.configureCamera(this.configuration.camera, meshes);
+                    }
+                    return this.initEnvironment(meshes);
                 }).then(() => {
                     return this.scene;
                 });
@@ -306,108 +701,7 @@ export abstract class AbstractViewer {
     }
 
     protected initEnvironment(focusMeshes: Array<AbstractMesh> = []): Promise<Scene> {
-        if (!this.configuration.skybox && !this.configuration.ground) {
-            if (this.environmentHelper) {
-                this.environmentHelper.dispose();
-            };
-            return Promise.resolve(this.scene);
-        }
-
-        const options: Partial<IEnvironmentHelperOptions> = {
-            createGround: !!this.configuration.ground,
-            createSkybox: !!this.configuration.skybox,
-            setupImageProcessing: false // will be done at the scene level!
-        };
-
-        if (this.configuration.ground) {
-            let groundConfig = (typeof this.configuration.ground === 'boolean') ? {} : this.configuration.ground;
-
-            let groundSize = groundConfig.size || (this.configuration.skybox && this.configuration.skybox.scale);
-            if (groundSize) {
-                options.groundSize = groundSize;
-            }
-
-            options.enableGroundShadow = this.configuration.ground === true || groundConfig.receiveShadows;
-            if (groundConfig.shadowLevel) {
-                options.groundShadowLevel = groundConfig.shadowLevel;
-            }
-            options.enableGroundMirror = !!groundConfig.mirror;
-            if (groundConfig.texture) {
-                options.groundTexture = groundConfig.texture;
-            }
-            if (groundConfig.color) {
-                options.groundColor = new Color3(groundConfig.color.r, groundConfig.color.g, groundConfig.color.b)
-            }
-
-            if (groundConfig.mirror) {
-                options.enableGroundMirror = true;
-                // to prevent undefines
-                if (typeof groundConfig.mirror === "object") {
-                    if (groundConfig.mirror.amount)
-                        options.groundMirrorAmount = groundConfig.mirror.amount;
-                    if (groundConfig.mirror.sizeRatio)
-                        options.groundMirrorSizeRatio = groundConfig.mirror.sizeRatio;
-                    if (groundConfig.mirror.blurKernel)
-                        options.groundMirrorBlurKernel = groundConfig.mirror.blurKernel;
-                    if (groundConfig.mirror.fresnelWeight)
-                        options.groundMirrorFresnelWeight = groundConfig.mirror.fresnelWeight;
-                    if (groundConfig.mirror.fallOffDistance)
-                        options.groundMirrorFallOffDistance = groundConfig.mirror.fallOffDistance;
-                    if (this.defaultHighpTextureType !== undefined)
-                        options.groundMirrorTextureType = this.defaultHighpTextureType;
-                }
-            }
-
-        }
-
-        let postInitSkyboxMaterial = false;
-        if (this.configuration.skybox) {
-            let conf = this.configuration.skybox;
-            if (conf.material && conf.material.imageProcessingConfiguration) {
-                options.setupImageProcessing = false; // will be configured later manually.
-            }
-            let skyboxSize = this.configuration.skybox.scale;
-            if (skyboxSize) {
-                options.skyboxSize = skyboxSize;
-            }
-            options.sizeAuto = !options.skyboxSize;
-            if (conf.color) {
-                options.skyboxColor = new Color3(conf.color.r, conf.color.g, conf.color.b)
-            }
-            if (conf.cubeTexture && conf.cubeTexture.url) {
-                if (typeof conf.cubeTexture.url === "string") {
-                    options.skyboxTexture = conf.cubeTexture.url;
-                } else {
-                    // init later!
-                    postInitSkyboxMaterial = true;
-                }
-            }
-
-            if (conf.material && conf.material.imageProcessingConfiguration) {
-                postInitSkyboxMaterial = true;
-            }
-        }
-
-        if (!this.environmentHelper) {
-            this.environmentHelper = this.scene.createDefaultEnvironment(options)!;
-        }
-        else {
-            // there might be a new scene! we need to dispose.
-            // Need to decide if a scene should stay or be disposed.
-            this.environmentHelper.dispose();
-            //this.environmentHelper.updateOptions(options);
-            this.environmentHelper = this.scene.createDefaultEnvironment(options)!;
-        }
-        console.log(options);
-
-        if (postInitSkyboxMaterial) {
-            let skyboxMaterial = this.environmentHelper.skyboxMaterial;
-            if (skyboxMaterial) {
-                if (this.configuration.skybox && this.configuration.skybox.material && this.configuration.skybox.material.imageProcessingConfiguration) {
-                    this.extendClassWithConfig(skyboxMaterial.imageProcessingConfiguration, this.configuration.skybox.material.imageProcessingConfiguration);
-                }
-            }
-        }
+        this.configureEnvironment(this.configuration.skybox, this.configuration.ground);
 
         return Promise.resolve(this.scene);
     }
@@ -486,4 +780,54 @@ export abstract class AbstractViewer {
             }
         });
     }
+
+    private setCameraBehavior(behaviorConfig: number | {
+        type: number;
+        [propName: string]: any;
+    }, payload: any) {
+
+        let behavior: Behavior<ArcRotateCamera> | null;
+        let type = (typeof behaviorConfig !== "object") ? behaviorConfig : behaviorConfig.type;
+
+        let config: { [propName: string]: any } = (typeof behaviorConfig === "object") ? behaviorConfig : {};
+
+        // constructing behavior
+        switch (type) {
+            case CameraBehavior.AUTOROTATION:
+                behavior = new AutoRotationBehavior();
+                break;
+            case CameraBehavior.BOUNCING:
+                behavior = new BouncingBehavior();
+                break;
+            case CameraBehavior.FRAMING:
+                behavior = new FramingBehavior();
+                break;
+            default:
+                behavior = null;
+                break;
+        }
+
+        if (behavior) {
+            if (typeof behaviorConfig === "object") {
+                this.extendClassWithConfig(behavior, behaviorConfig);
+            }
+            this.camera.addBehavior(behavior);
+        }
+
+        // post attach configuration. Some functionalities require the attached camera.
+        switch (type) {
+            case CameraBehavior.AUTOROTATION:
+                break;
+            case CameraBehavior.BOUNCING:
+                break;
+            case CameraBehavior.FRAMING:
+                if (config.zoomOnBoundingInfo) {
+                    //payload is an array of meshes
+                    let meshes = <Array<AbstractMesh>>payload;
+                    let bounding = meshes[0].getHierarchyBoundingVectors();
+                    (<FramingBehavior>behavior).zoomOnBoundingInfo(bounding.min, bounding.max);
+                }
+                break;
+        }
+    }
 }

+ 21 - 9
Viewer/tsconfig.json

@@ -2,25 +2,37 @@
     "compilerOptions": {
         "target": "es5",
         "module": "commonjs",
-        "noResolve": true,
+        "noResolve": false,
         "noImplicitAny": false, //mainly due to usage of external libs without typings.
         "strictNullChecks": true,
         "removeComments": true,
         "preserveConstEnums": true,
-        "sourceMap": true,
+        "sourceMap": false,
         "experimentalDecorators": true,
         "isolatedModules": false,
+        "declaration": false,
         "lib": [
             "dom",
             "es2015.promise",
             "es5"
         ],
-        //"declaration": true,
-        "outDir": "./temp/",
         "types": [
-            "node",
-            "babylonjs",
-            "babylonjs-loaders"
-        ]
-    }
+            "node"
+        ],
+        "baseUrl": ".",
+        "paths": {
+            "babylonjs": [
+                "../dist/preview release/babylon.max.js",
+                "../dist/preview release/babylon.d.ts"
+            ],
+            "babylonjs-loaders": [
+                "../dist/preview release/loaders/babylonjs.loaders.js",
+                "../dist/preview release/loaders/babylonjs.loaders.module.d.ts"
+            ]
+        }
+    },
+    "exclude": [
+        "node_modules",
+        "dist"
+    ]
 }

+ 13 - 9
Viewer/webpack.config.js

@@ -1,11 +1,9 @@
-const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
 const path = require('path');
 const webpack = require('webpack');
 
 module.exports = {
     entry: {
-        'viewer': './src/index.ts',
-        'viewer.min': './src/index.ts',
+        'viewer': './src/index.ts'
     },
     output: {
         path: path.resolve(__dirname, 'dist'),
@@ -16,17 +14,23 @@ module.exports = {
         devtoolModuleFilenameTemplate: '[absolute-resource-path]'
     },
     resolve: {
-        extensions: ['.ts', '.tsx', '.js']
+        extensions: ['.ts', '.js'],
+        alias: {
+            "babylonjs": __dirname + '/../dist/preview release/babylon.max.js',
+            "babylonjs-materials": __dirname + '/../dist/preview release/materialsLibrary/babylonjs.materials.js',
+            "babylonjs-loaders": __dirname + '/../dist/preview release/loaders/babylonjs.loaders.js',
+            "deepmerge": __dirname + '/assets/deepmerge.min.js'
+        }
+    },
+    externals: {
+        // until physics will be integrated in the viewer, ignore cannon
+        cannon: 'CANNON'
     },
     devtool: 'source-map',
     plugins: [
         new webpack.WatchIgnorePlugin([
             /\.d\.ts$/
-        ]),
-        new UglifyJSPlugin({
-            parallel: true,
-            test: /\.min\.js$/i,
-        })
+        ])
     ],
     module: {
         loaders: [{

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 11450 - 11079
dist/preview release/babylon.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 31 - 31
dist/preview release/babylon.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 426 - 100
dist/preview release/babylon.max.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 31 - 31
dist/preview release/babylon.worker.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1607 - 1236
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 22 - 22
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 426 - 100
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 426 - 100
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 426 - 100
dist/preview release/es6.js


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

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

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

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

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "3.2.0-alpha4",
+    "version": "3.2.0-alpha6",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

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

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

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

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

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

+ 57 - 49
dist/preview release/serializers/babylon.glTF2Serializer.d.ts

@@ -107,12 +107,14 @@ declare module BABYLON.GLTF2 {
         constructor(babylonScene: Scene, options?: IExporterOptions);
         /**
          * Creates a buffer view based on teh supplied arguments
-         * @param {number} bufferIndex - index value of the specified buffer
-         * @param {number} byteOffset - byte offset value
-         * @param {number} byteLength - byte length of the bufferView
+         * @param bufferIndex - index value of the specified buffer
+         * @param byteOffset - byte offset value
+         * @param byteLength - byte length of the bufferView
+         * @param byteStride - byte distance between conequential elements.
+         * @param name - name of the buffer view
          * @returns - bufferView for glTF
          */
-        private createBufferView(bufferIndex, byteOffset, byteLength, name?);
+        private createBufferView(bufferIndex, byteOffset, byteLength, byteStride?, name?);
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex
@@ -124,43 +126,41 @@ declare module BABYLON.GLTF2 {
          * @param max
          * @returns - accessor for glTF
          */
-        private createAccessor(bufferviewIndex, name, type, componentType, count, min?, max?);
+        private createAccessor(bufferviewIndex, name, type, componentType, count, byteOffset?, min?, max?);
         /**
          * Calculates the minimum and maximum values of an array of floats, based on stride
-         * @param buff
-         * @param vertexStart
-         * @param vertexCount
-         * @param arrayOffset
-         * @param stride
-         * @returns - min number array and max number array
+         * @param buff - Data to check for min and max values.
+         * @param vertexStart - Start offset to calculate min and max values.
+         * @param vertexCount - Number of vertices to check for min and max values.
+         * @param stride - Offset between consecutive attributes.
+         * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+         * @returns - min number array and max number array.
          */
-        private calculateMinMax(buff, vertexStart, vertexCount, arrayOffset, stride);
+        private calculateMinMax(buff, vertexStart, vertexCount, stride, useRightHandedSystem);
         /**
-         * Write mesh attribute data to buffer.
+         * Writes mesh attribute data to a data buffer.
          * Returns the bytelength of the data.
-         * @param vertexBufferType
-         * @param submesh
-         * @param meshAttributeArray
-         * @param strideSize
-         * @param byteOffset
-         * @param dataBuffer
-         * @param useRightHandedSystem
-         * @returns - byte length
+         * @param vertexBufferKind - Indicates what kind of vertex data is being passed in.
+         * @param meshAttributeArray - Array containing the attribute data.
+         * @param strideSize - Represents the offset between consecutive attributes
+         * @param byteOffset - The offset to start counting bytes from.
+         * @param dataBuffer - The buffer to write the binary data to.
+         * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+         * @returns - Byte length of the attribute data.
          */
-        private writeAttributeData(vertexBufferType, submesh, meshAttributeArray, strideSize, byteOffset, dataBuffer, useRightHandedSystem);
+        private writeAttributeData(vertexBufferKind, meshAttributeArray, strideSize, vertexBufferOffset, byteOffset, dataBuffer, useRightHandedSystem);
         /**
          * Generates glTF json data
-         * @param glb
-         * @param glTFPrefix
-         * @param prettyPrint
+         * @param shouldUseGlb - Indicates whether the json should be written for a glb file.
+         * @param glTFPrefix - Text to use when prefixing a glTF file.
+         * @param prettyPrint - Indicates whether the json file should be pretty printed (true) or not (false).
          * @returns - json data as string
          */
-        private generateJSON(glb, glTFPrefix?, prettyPrint?);
+        private generateJSON(shouldUseGlb, glTFPrefix?, prettyPrint?);
         /**
          * Generates data for .gltf and .bin files based on the glTF prefix string
-         * @param glTFPrefix
-         * @returns - object with glTF json tex filename
-         * and binary file name as keys and their data as values
+         * @param glTFPrefix - Text to use when prefixing a glTF file.
+         * @returns - GLTFData with glTF file data.
          */
         _generateGLTF(glTFPrefix: string): _GLTFData;
         /**
@@ -169,7 +169,7 @@ declare module BABYLON.GLTF2 {
          */
         private generateBinary();
         /**
-         * Pads the number to a power of 4
+         * Pads the number to a multiple of 4
          * @param num - number to pad
          * @returns - padded number
          */
@@ -177,45 +177,53 @@ declare module BABYLON.GLTF2 {
         /**
          * Generates a glb file from the json and binary data.
          * Returns an object with the glb file name as the key and data as the value.
-         * @param jsonText
-         * @param binaryBuffer
          * @param glTFPrefix
          * @returns - object with glb filename as key and data as value
          */
         _generateGLB(glTFPrefix: string): _GLTFData;
         /**
          * Sets the TRS for each node
-         * @param node
-         * @param babylonMesh
-         * @param useRightHandedSystem
+         * @param node - glTF Node for storing the transformation data.
+         * @param babylonMesh - Babylon mesh used as the source for the transformation data.
+         * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
          */
         private setNodeTransformation(node, babylonMesh, useRightHandedSystem);
         /**
          *
-         * @param babylonTexture
-         * @return - glTF texture, or null if the texture format is not supported
+         * @param babylonTexture - Babylon texture to extract.
+         * @param mimeType - Mime Type of the babylonTexture.
+         * @return - glTF texture, or null if the texture format is not supported.
          */
         private exportTexture(babylonTexture, mimeType?);
         /**
+         * Creates a bufferview based on the vertices type for the Babylon mesh
+         * @param kind - Indicates the type of vertices data.
+         * @param babylonMesh - The Babylon mesh to get the vertices data from.
+         * @param byteOffset - The offset from the buffer to start indexing from.
+         * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+         * @param dataBuffer - The buffer to write the bufferview data to.
+         * @returns bytelength of the bufferview data.
+         */
+        private createBufferViewKind(kind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+        /**
          * Sets data for the primitive attributes of each submesh
-         * @param mesh
-         * @param babylonMesh
-         * @param byteOffset
-         * @param useRightHandedSystem
-         * @param dataBuffer
-         * @returns - bytelength of the primitive attributes plus the passed in byteOffset
+         * @param mesh - glTF Mesh object to store the primitive attribute information.
+         * @param babylonMesh - Babylon mesh to get the primitive attribute data from.
+         * @param byteOffset - The offset in bytes of the buffer data.
+         * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+         * @param dataBuffer - Buffer to write the attribute data to.
+         * @returns - bytelength of the primitive attributes plus the passed in byteOffset.
          */
-        private setPrimitiveAttributes(mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer?);
+        private setPrimitiveAttributes(mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
         /**
          * Creates a glTF scene based on the array of meshes.
          * Returns the the total byte offset.
-         * @param gltf
-         * @param byteOffset
-         * @param buffer
-         * @param dataBuffer
+         * @param babylonScene - Babylon scene to get the mesh data from.
+         * @param byteOffset - Offset to start from in bytes.
+         * @param dataBuffer - Buffer to write geometry data to.
          * @returns bytelength + byteoffset
          */
-        private createScene(babylonScene, byteOffset, dataBuffer?);
+        private createScene(babylonScene, byteOffset, dataBuffer);
     }
 }
 

+ 381 - 299
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -78,22 +78,25 @@ var BABYLON;
                 this.nodes = new Array();
                 this.images = new Array();
                 this.materials = new Array();
+                this.textures = new Array();
                 this.imageData = {};
                 if (options !== undefined) {
                     this.options = options;
                 }
                 var totalByteLength = 0;
-                totalByteLength = this.createScene(this.babylonScene, totalByteLength);
+                totalByteLength = this.createScene(this.babylonScene, totalByteLength, null);
                 this.totalByteLength = totalByteLength;
             }
             /**
              * Creates a buffer view based on teh supplied arguments
-             * @param {number} bufferIndex - index value of the specified buffer
-             * @param {number} byteOffset - byte offset value
-             * @param {number} byteLength - byte length of the bufferView
+             * @param bufferIndex - index value of the specified buffer
+             * @param byteOffset - byte offset value
+             * @param byteLength - byte length of the bufferView
+             * @param byteStride - byte distance between conequential elements.
+             * @param name - name of the buffer view
              * @returns - bufferView for glTF
              */
-            _Exporter.prototype.createBufferView = function (bufferIndex, byteOffset, byteLength, name) {
+            _Exporter.prototype.createBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
                 var bufferview = { buffer: bufferIndex, byteLength: byteLength };
                 if (byteOffset > 0) {
                     bufferview.byteOffset = byteOffset;
@@ -101,6 +104,9 @@ var BABYLON;
                 if (name) {
                     bufferview.name = name;
                 }
+                if (byteStride) {
+                    bufferview.byteStride = byteStride;
+                }
                 return bufferview;
             };
             /**
@@ -114,7 +120,7 @@ var BABYLON;
              * @param max
              * @returns - accessor for glTF
              */
-            _Exporter.prototype.createAccessor = function (bufferviewIndex, name, type, componentType, count, min, max) {
+            _Exporter.prototype.createAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
                 var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
                 if (min) {
                     accessor.min = min;
@@ -122,30 +128,38 @@ var BABYLON;
                 if (max) {
                     accessor.max = max;
                 }
+                if (byteOffset) {
+                    accessor.byteOffset = byteOffset;
+                }
                 return accessor;
             };
             /**
              * Calculates the minimum and maximum values of an array of floats, based on stride
-             * @param buff
-             * @param vertexStart
-             * @param vertexCount
-             * @param arrayOffset
-             * @param stride
-             * @returns - min number array and max number array
+             * @param buff - Data to check for min and max values.
+             * @param vertexStart - Start offset to calculate min and max values.
+             * @param vertexCount - Number of vertices to check for min and max values.
+             * @param stride - Offset between consecutive attributes.
+             * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+             * @returns - min number array and max number array.
              */
-            _Exporter.prototype.calculateMinMax = function (buff, vertexStart, vertexCount, arrayOffset, stride) {
+            _Exporter.prototype.calculateMinMax = function (buff, vertexStart, vertexCount, stride, useRightHandedSystem) {
                 var min = [Infinity, Infinity, Infinity];
                 var max = [-Infinity, -Infinity, -Infinity];
                 var end = vertexStart + vertexCount;
                 if (vertexCount > 0) {
                     for (var i = vertexStart; i < end; ++i) {
                         var index = stride * i;
+                        var scale = 1;
                         for (var j = 0; j < stride; ++j) {
-                            if (buff[index] < min[j]) {
-                                min[j] = buff[index];
+                            if (j === (stride - 1) && !useRightHandedSystem) {
+                                scale = -1;
                             }
-                            if (buff[index] > max[j]) {
-                                max[j] = buff[index];
+                            var num = scale * buff[index];
+                            if (num < min[j]) {
+                                min[j] = num;
+                            }
+                            if (num > max[j]) {
+                                max[j] = num;
                             }
                             ++index;
                         }
@@ -154,24 +168,24 @@ var BABYLON;
                 return { min: min, max: max };
             };
             /**
-             * Write mesh attribute data to buffer.
+             * Writes mesh attribute data to a data buffer.
              * Returns the bytelength of the data.
-             * @param vertexBufferType
-             * @param submesh
-             * @param meshAttributeArray
-             * @param strideSize
-             * @param byteOffset
-             * @param dataBuffer
-             * @param useRightHandedSystem
-             * @returns - byte length
+             * @param vertexBufferKind - Indicates what kind of vertex data is being passed in.
+             * @param meshAttributeArray - Array containing the attribute data.
+             * @param strideSize - Represents the offset between consecutive attributes
+             * @param byteOffset - The offset to start counting bytes from.
+             * @param dataBuffer - The buffer to write the binary data to.
+             * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+             * @returns - Byte length of the attribute data.
              */
-            _Exporter.prototype.writeAttributeData = function (vertexBufferType, submesh, meshAttributeArray, strideSize, byteOffset, dataBuffer, useRightHandedSystem) {
+            _Exporter.prototype.writeAttributeData = function (vertexBufferKind, meshAttributeArray, strideSize, vertexBufferOffset, byteOffset, dataBuffer, useRightHandedSystem) {
                 var byteOff = byteOffset;
-                var end = submesh.verticesStart + submesh.verticesCount;
+                var start = 0;
+                var end = meshAttributeArray.length / strideSize;
                 var byteLength = 0;
-                switch (vertexBufferType) {
+                switch (vertexBufferKind) {
                     case BABYLON.VertexBuffer.PositionKind: {
-                        for (var k = submesh.verticesStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
@@ -185,11 +199,11 @@ var BABYLON;
                             }
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 12;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     case BABYLON.VertexBuffer.NormalKind: {
-                        for (var k = submesh.verticesStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
@@ -203,11 +217,11 @@ var BABYLON;
                             }
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 12;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     case BABYLON.VertexBuffer.TangentKind: {
-                        for (var k = submesh.indexStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
@@ -223,11 +237,11 @@ var BABYLON;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 16;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     case BABYLON.VertexBuffer.ColorKind: {
-                        for (var k = submesh.verticesStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
@@ -238,45 +252,45 @@ var BABYLON;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 16;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     case BABYLON.VertexBuffer.UVKind: {
-                        for (var k = submesh.verticesStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 8;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     case BABYLON.VertexBuffer.UV2Kind: {
-                        for (var k = submesh.verticesStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 8;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     default: {
-                        throw new Error("Unsupported vertex buffer type: " + vertexBufferType);
+                        throw new Error("Unsupported vertex buffer type: " + vertexBufferKind);
                     }
                 }
                 return byteLength;
             };
             /**
              * Generates glTF json data
-             * @param glb
-             * @param glTFPrefix
-             * @param prettyPrint
+             * @param shouldUseGlb - Indicates whether the json should be written for a glb file.
+             * @param glTFPrefix - Text to use when prefixing a glTF file.
+             * @param prettyPrint - Indicates whether the json file should be pretty printed (true) or not (false).
              * @returns - json data as string
              */
-            _Exporter.prototype.generateJSON = function (glb, glTFPrefix, prettyPrint) {
+            _Exporter.prototype.generateJSON = function (shouldUseGlb, glTFPrefix, prettyPrint) {
                 var buffer = { byteLength: this.totalByteLength };
                 var glTF = {
                     asset: this.asset
@@ -307,7 +321,7 @@ var BABYLON;
                     glTF.textures = this.textures;
                 }
                 if (this.images && this.images.length !== 0) {
-                    if (!glb) {
+                    if (!shouldUseGlb) {
                         glTF.images = this.images;
                     }
                     else {
@@ -320,7 +334,7 @@ var BABYLON;
                             if (image.uri !== undefined) {
                                 var imageData = this.imageData[image.uri];
                                 var imageName = image.uri.split('.')[0] + " image";
-                                var bufferView = this.createBufferView(0, byteOffset, imageData.data.length, imageName);
+                                var bufferView = this.createBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                                 byteOffset += imageData.data.buffer.byteLength;
                                 this.bufferViews.push(bufferView);
                                 image.bufferView = this.bufferViews.length - 1;
@@ -333,7 +347,7 @@ var BABYLON;
                         buffer.byteLength = byteOffset;
                     }
                 }
-                if (!glb) {
+                if (!shouldUseGlb) {
                     buffer.uri = glTFPrefix + ".bin";
                 }
                 var jsonText = prettyPrint ? JSON.stringify(glTF, null, 2) : JSON.stringify(glTF);
@@ -341,9 +355,8 @@ var BABYLON;
             };
             /**
              * Generates data for .gltf and .bin files based on the glTF prefix string
-             * @param glTFPrefix
-             * @returns - object with glTF json tex filename
-             * and binary file name as keys and their data as values
+             * @param glTFPrefix - Text to use when prefixing a glTF file.
+             * @returns - GLTFData with glTF file data.
              */
             _Exporter.prototype._generateGLTF = function (glTFPrefix) {
                 var jsonText = this.generateJSON(false, glTFPrefix, true);
@@ -373,7 +386,7 @@ var BABYLON;
                 return binaryBuffer;
             };
             /**
-             * Pads the number to a power of 4
+             * Pads the number to a multiple of 4
              * @param num - number to pad
              * @returns - padded number
              */
@@ -385,8 +398,6 @@ var BABYLON;
             /**
              * Generates a glb file from the json and binary data.
              * Returns an object with the glb file name as the key and data as the value.
-             * @param jsonText
-             * @param binaryBuffer
              * @param glTFPrefix
              * @returns - object with glb filename as key and data as value
              */
@@ -403,7 +414,8 @@ var BABYLON;
                 }
                 var jsonPadding = this._getPadding(jsonLength);
                 var binPadding = this._getPadding(binaryBuffer.byteLength);
-                var byteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding + imageByteLength;
+                var imagePadding = this._getPadding(imageByteLength);
+                var byteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding + imageByteLength + imagePadding;
                 //header
                 var headerBuffer = new ArrayBuffer(headerLength);
                 var headerBufferView = new DataView(headerBuffer);
@@ -428,7 +440,7 @@ var BABYLON;
                 //binary chunk
                 var binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
                 var binaryChunkBufferView = new DataView(binaryChunkBuffer);
-                binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength + imageByteLength, true);
+                binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength + imageByteLength + imagePadding, true);
                 binaryChunkBufferView.setUint32(4, 0x004E4942, true);
                 // binary padding
                 var binPaddingBuffer = new ArrayBuffer(binPadding);
@@ -436,12 +448,18 @@ var BABYLON;
                 for (var i = 0; i < binPadding; ++i) {
                     binPaddingView[i] = 0;
                 }
+                var imagePaddingBuffer = new ArrayBuffer(imagePadding);
+                var imagePaddingView = new Uint8Array(imagePaddingBuffer);
+                for (var i = 0; i < imagePadding; ++i) {
+                    imagePaddingView[i] = 0;
+                }
                 var glbData = [headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer];
                 // binary data
                 for (var key in this.imageData) {
                     glbData.push(this.imageData[key].data.buffer);
                 }
                 glbData.push(binPaddingBuffer);
+                glbData.push(imagePaddingBuffer);
                 var glbFile = new Blob(glbData, { type: 'application/octet-stream' });
                 var container = new BABYLON._GLTFData();
                 container.glTFFiles[glbFileName] = glbFile;
@@ -449,9 +467,9 @@ var BABYLON;
             };
             /**
              * Sets the TRS for each node
-             * @param node
-             * @param babylonMesh
-             * @param useRightHandedSystem
+             * @param node - glTF Node for storing the transformation data.
+             * @param babylonMesh - Babylon mesh used as the source for the transformation data.
+             * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
              */
             _Exporter.prototype.setNodeTransformation = function (node, babylonMesh, useRightHandedSystem) {
                 if (!(babylonMesh.position.x === 0 && babylonMesh.position.y === 0 && babylonMesh.position.z === 0)) {
@@ -485,8 +503,9 @@ var BABYLON;
             };
             /**
              *
-             * @param babylonTexture
-             * @return - glTF texture, or null if the texture format is not supported
+             * @param babylonTexture - Babylon texture to extract.
+             * @param mimeType - Mime Type of the babylonTexture.
+             * @return - glTF texture, or null if the texture format is not supported.
              */
             _Exporter.prototype.exportTexture = function (babylonTexture, mimeType) {
                 if (mimeType === void 0) { mimeType = "image/jpeg" /* JPEG */; }
@@ -561,289 +580,352 @@ var BABYLON;
                 return textureInfo;
             };
             /**
-             * Sets data for the primitive attributes of each submesh
-             * @param mesh
-             * @param babylonMesh
-             * @param byteOffset
-             * @param useRightHandedSystem
-             * @param dataBuffer
-             * @returns - bytelength of the primitive attributes plus the passed in byteOffset
+             * Creates a bufferview based on the vertices type for the Babylon mesh
+             * @param kind - Indicates the type of vertices data.
+             * @param babylonMesh - The Babylon mesh to get the vertices data from.
+             * @param byteOffset - The offset from the buffer to start indexing from.
+             * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+             * @param dataBuffer - The buffer to write the bufferview data to.
+             * @returns bytelength of the bufferview data.
              */
-            _Exporter.prototype.setPrimitiveAttributes = function (mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer) {
-                // go through all mesh primitives (submeshes)
-                for (var j = 0; j < babylonMesh.subMeshes.length; ++j) {
-                    var bufferMesh = null;
-                    var submesh = babylonMesh.subMeshes[j];
-                    var meshPrimitive = { attributes: {} };
-                    if (babylonMesh instanceof BABYLON.Mesh) {
-                        bufferMesh = babylonMesh;
-                    }
-                    else if (babylonMesh instanceof BABYLON.InstancedMesh) {
-                        bufferMesh = babylonMesh.sourceMesh;
-                    }
-                    // Loop through each attribute of the submesh (mesh primitive)
-                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
-                        var positionVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
-                        var positionVertexBufferOffset = positionVertexBuffer.getOffset();
-                        var positions = positionVertexBuffer.getData();
-                        var positionStrideSize = positionVertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.PositionKind, submesh, positions, positionStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
-                        }
-                        else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 12;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Positions");
+            _Exporter.prototype.createBufferViewKind = function (kind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer) {
+                var bufferMesh = null;
+                var byteLength = 0;
+                if (babylonMesh instanceof BABYLON.Mesh) {
+                    bufferMesh = babylonMesh;
+                }
+                else if (babylonMesh instanceof BABYLON.InstancedMesh) {
+                    bufferMesh = babylonMesh.sourceMesh;
+                }
+                if (bufferMesh !== null) {
+                    var vertexBuffer = null;
+                    var vertexBufferOffset = null;
+                    var vertexData = null;
+                    var vertexStrideSize = null;
+                    if (bufferMesh.getVerticesDataKinds().indexOf(kind) > -1) {
+                        vertexBuffer = bufferMesh.getVertexBuffer(kind);
+                        vertexBufferOffset = vertexBuffer.getOffset();
+                        vertexData = vertexBuffer.getData();
+                        vertexStrideSize = vertexBuffer.getStrideSize();
+                        if (dataBuffer && vertexData) {
+                            byteLength = this.writeAttributeData(kind, vertexData, vertexStrideSize, vertexBufferOffset, byteOffset, dataBuffer, useRightHandedSystem);
                             byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var result = this.calculateMinMax(positions, submesh.verticesStart, submesh.verticesCount, positionVertexBufferOffset, positionStrideSize);
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Position", "VEC3" /* VEC3 */, 5126 /* FLOAT */, submesh.verticesCount, result.min, result.max);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.POSITION = this.accessors.length - 1;
-                        }
-                    }
-                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
-                        var normalVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.NormalKind);
-                        var normals = normalVertexBuffer.getData();
-                        var normalStrideSize = normalVertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.NormalKind, submesh, normals, normalStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
                         }
                         else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 12;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Normals");
-                            byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Normal", "VEC3" /* VEC3 */, 5126 /* FLOAT */, submesh.verticesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
+                            var bufferViewName = null;
+                            switch (kind) {
+                                case BABYLON.VertexBuffer.PositionKind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "Position - " + bufferMesh.name;
+                                    break;
+                                }
+                                case BABYLON.VertexBuffer.NormalKind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "Normal - " + bufferMesh.name;
+                                    break;
+                                }
+                                case BABYLON.VertexBuffer.TangentKind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "Tangent - " + bufferMesh.name;
+                                    break;
+                                }
+                                case BABYLON.VertexBuffer.ColorKind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "Color - " + bufferMesh.name;
+                                    break;
+                                }
+                                case BABYLON.VertexBuffer.UVKind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "TexCoord 0 - " + bufferMesh.name;
+                                    break;
+                                }
+                                case BABYLON.VertexBuffer.UV2Kind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "TexCoord 1 - " + bufferMesh.name;
+                                    break;
+                                }
+                                default: {
+                                    console.warn("Unsupported VertexBuffer kind: " + kind);
+                                }
+                            }
+                            if (bufferViewName !== null) {
+                                var bufferView = this.createBufferView(0, byteOffset, byteLength, vertexStrideSize * 4, bufferViewName);
+                                byteOffset += byteLength;
+                                this.bufferViews.push(bufferView);
+                            }
                         }
                     }
-                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
-                        var tangentVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.TangentKind);
-                        var tangents = tangentVertexBuffer.getData();
-                        var tangentStrideSize = tangentVertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.TangentKind, submesh, tangents, tangentStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
-                        }
-                        else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 16;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Tangents");
-                            byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Tangent", "VEC4" /* VEC4 */, 5126 /* FLOAT */, submesh.verticesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
-                        }
+                }
+                return byteLength;
+            };
+            /**
+             * Sets data for the primitive attributes of each submesh
+             * @param mesh - glTF Mesh object to store the primitive attribute information.
+             * @param babylonMesh - Babylon mesh to get the primitive attribute data from.
+             * @param byteOffset - The offset in bytes of the buffer data.
+             * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+             * @param dataBuffer - Buffer to write the attribute data to.
+             * @returns - bytelength of the primitive attributes plus the passed in byteOffset.
+             */
+            _Exporter.prototype.setPrimitiveAttributes = function (mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer) {
+                var bufferMesh = null;
+                if (babylonMesh instanceof BABYLON.Mesh) {
+                    bufferMesh = babylonMesh;
+                }
+                else if (babylonMesh instanceof BABYLON.InstancedMesh) {
+                    bufferMesh = babylonMesh.sourceMesh;
+                }
+                var positionBufferViewIndex = null;
+                var normalBufferViewIndex = null;
+                var colorBufferViewIndex = null;
+                var tangentBufferViewIndex = null;
+                var texCoord0BufferViewIndex = null;
+                var texCoord1BufferViewIndex = null;
+                var indexBufferViewIndex = null;
+                if (bufferMesh !== null) {
+                    // For each BabylonMesh, create bufferviews for each 'kind'
+                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.PositionKind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        positionBufferViewIndex = this.bufferViews.length - 1;
+                    }
+                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.NormalKind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        normalBufferViewIndex = this.bufferViews.length - 1;
                     }
                     if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
-                        var colorVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.ColorKind);
-                        var colors = colorVertexBuffer.getData();
-                        var colorStrideSize = colorVertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.ColorKind, submesh, colors, colorStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
-                        }
-                        else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 16;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Colors");
-                            byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Color", "VEC4" /* VEC4 */, 5126 /* FLOAT */, submesh.verticesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
-                        }
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.ColorKind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        colorBufferViewIndex = this.bufferViews.length - 1;
+                    }
+                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.TangentKind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        colorBufferViewIndex = this.bufferViews.length - 1;
                     }
                     if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                        var texCoord0VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UVKind);
-                        var texCoords0 = texCoord0VertexBuffer.getData();
-                        var texCoord0StrideSize = texCoord0VertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.UVKind, submesh, texCoords0, texCoord0StrideSize, byteOffset, dataBuffer, useRightHandedSystem);
-                        }
-                        else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 8;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Texture Coords0");
-                            byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Texture Coords", "VEC2" /* VEC2 */, 5126 /* FLOAT */, submesh.verticesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
-                        }
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.UVKind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        texCoord0BufferViewIndex = this.bufferViews.length - 1;
                     }
                     if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
-                        var texCoord1VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UV2Kind);
-                        var texCoords1 = texCoord1VertexBuffer.getData();
-                        var texCoord1StrideSize = texCoord1VertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.UV2Kind, submesh, texCoords1, texCoord1StrideSize, byteOffset, dataBuffer, useRightHandedSystem);
-                        }
-                        else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 8;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Texture Coords 1");
-                            byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Texture Coords", "VEC2" /* VEC2 */, 5126 /* FLOAT */, submesh.verticesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
-                        }
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.UV2Kind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        texCoord1BufferViewIndex = this.bufferViews.length - 1;
                     }
                     if (bufferMesh.getTotalIndices() > 0) {
+                        var indices = bufferMesh.getIndices();
                         if (dataBuffer) {
-                            var indices = bufferMesh.getIndices();
-                            var start = submesh.indexStart;
-                            var end = submesh.indexCount + start;
+                            var end = indices.length;
                             var byteOff = byteOffset;
-                            for (var k = start; k < end; k = k + 3) {
+                            for (var k = 0; k < end; ++k) {
                                 dataBuffer.setUint32(byteOff, indices[k], true);
                                 byteOff += 4;
-                                dataBuffer.setUint32(byteOff, indices[k + 1], true);
-                                byteOff += 4;
-                                dataBuffer.setUint32(byteOff, indices[k + 2], true);
-                                byteOff += 4;
                             }
-                            var byteLength = submesh.indexCount * 4;
-                            byteOffset += byteLength;
+                            byteOffset = byteOff;
                         }
                         else {
-                            // Create bufferview
-                            var indicesCount = submesh.indexCount;
-                            var byteLength = indicesCount * 4;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Indices");
+                            var byteLength = indices.length * 4;
+                            var bufferView = this.createBufferView(0, byteOffset, byteLength, undefined, "Indices - " + bufferMesh.name);
                             byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Indices", "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, indicesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.indices = this.accessors.length - 1;
+                            this.bufferViews.push(bufferView);
+                            indexBufferViewIndex = this.bufferViews.length - 1;
                         }
                     }
-                    if (bufferMesh.material) {
-                        if (bufferMesh.material instanceof BABYLON.StandardMaterial) {
-                            var babylonStandardMaterial = bufferMesh.material;
-                            var glTFMaterial = { name: babylonStandardMaterial.name };
-                            if (!babylonStandardMaterial.backFaceCulling) {
-                                glTFMaterial.doubleSided = true;
-                            }
-                            if (babylonStandardMaterial.bumpTexture) {
-                                var glTFTexture = this.exportTexture(babylonStandardMaterial.bumpTexture);
-                                if (glTFTexture) {
-                                    glTFMaterial.normalTexture = glTFTexture;
-                                }
-                            }
-                            if (babylonStandardMaterial.emissiveTexture) {
-                                var glTFEmissiveTexture = this.exportTexture(babylonStandardMaterial.emissiveTexture);
-                                if (glTFEmissiveTexture) {
-                                    glTFMaterial.emissiveTexture = glTFEmissiveTexture;
-                                }
-                                glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
+                }
+                // go through all mesh primitives (submeshes)
+                for (var j = 0; j < babylonMesh.subMeshes.length; ++j) {
+                    var submesh = babylonMesh.subMeshes[j];
+                    var meshPrimitive = { attributes: {} };
+                    if (bufferMesh !== null) {
+                        // Create a bufferview storing all the positions
+                        if (!dataBuffer) {
+                            // Loop through each attribute of the submesh (mesh primitive)
+                            if (positionBufferViewIndex !== null) {
+                                var positionVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
+                                var positions = positionVertexBuffer.getData();
+                                var positionStrideSize = positionVertexBuffer.getStrideSize();
+                                // Create accessor
+                                var result = this.calculateMinMax(positions, 0, positions.length / positionStrideSize, positionStrideSize, useRightHandedSystem);
+                                var accessor = this.createAccessor(positionBufferViewIndex, "Position", "VEC3" /* VEC3 */, 5126 /* FLOAT */, positions.length / positionStrideSize, 0, result.min, result.max);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.POSITION = this.accessors.length - 1;
                             }
-                            if (babylonStandardMaterial.ambientTexture) {
-                                var glTFOcclusionTexture = this.exportTexture(babylonStandardMaterial.ambientTexture);
-                                if (glTFOcclusionTexture) {
-                                    glTFMaterial.occlusionTexture = glTFOcclusionTexture;
-                                }
+                            if (normalBufferViewIndex !== null) {
+                                var normalVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.NormalKind);
+                                var normals = normalVertexBuffer.getData();
+                                var normalStrideSize = normalVertexBuffer.getStrideSize();
+                                // Create accessor
+                                var accessor = this.createAccessor(normalBufferViewIndex, "Normal", "VEC3" /* VEC3 */, 5126 /* FLOAT */, normals.length / normalStrideSize);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
                             }
-                            // Spec Gloss
-                            var glTFPbrMetallicRoughness = GLTF2._GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
-                            glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
-                            // TODO: Handle Textures
-                            this.materials.push(glTFMaterial);
-                            meshPrimitive.material = this.materials.length - 1;
-                        }
-                        else if (bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial) {
-                            if (!this.textures) {
-                                this.textures = new Array();
+                            if (tangentBufferViewIndex !== null) {
+                                var tangentVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.TangentKind);
+                                var tangents = tangentVertexBuffer.getData();
+                                var tangentStrideSize = tangentVertexBuffer.getStrideSize();
+                                // Create accessor
+                                var accessor = this.createAccessor(tangentBufferViewIndex, "Tangent", "VEC4" /* VEC4 */, 5126 /* FLOAT */, tangents.length / tangentStrideSize);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
                             }
-                            var babylonPBRMaterial = bufferMesh.material;
-                            var glTFPbrMetallicRoughness = {};
-                            if (babylonPBRMaterial.baseColor) {
-                                glTFPbrMetallicRoughness.baseColorFactor = [
-                                    babylonPBRMaterial.baseColor.r,
-                                    babylonPBRMaterial.baseColor.g,
-                                    babylonPBRMaterial.baseColor.b,
-                                    babylonPBRMaterial.alpha
-                                ];
+                            if (colorBufferViewIndex !== null) {
+                                var colorVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.ColorKind);
+                                var colors = colorVertexBuffer.getData();
+                                var colorStrideSize = colorVertexBuffer.getStrideSize();
+                                // Create accessor
+                                var accessor = this.createAccessor(colorBufferViewIndex, "Color", "VEC4" /* VEC4 */, 5126 /* FLOAT */, colors.length / colorStrideSize);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
                             }
-                            if (babylonPBRMaterial.baseTexture !== undefined) {
-                                var glTFTexture = this.exportTexture(babylonPBRMaterial.baseTexture);
-                                if (glTFTexture !== null) {
-                                    glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                                }
-                                glTFPbrMetallicRoughness.baseColorTexture;
+                            if (texCoord0BufferViewIndex !== null) {
+                                // Create accessor
+                                var texCoord0VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UVKind);
+                                var texCoord0s = texCoord0VertexBuffer.getData();
+                                var texCoord0StrideSize = texCoord0VertexBuffer.getStrideSize();
+                                var accessor = this.createAccessor(texCoord0BufferViewIndex, "Texture Coords 0", "VEC2" /* VEC2 */, 5126 /* FLOAT */, texCoord0s.length / texCoord0StrideSize);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
                             }
-                            if (babylonPBRMaterial.metallic !== undefined) {
-                                glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
+                            if (texCoord1BufferViewIndex !== null) {
+                                // Create accessor
+                                var texCoord1VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UV2Kind);
+                                var texCoord1s = texCoord1VertexBuffer.getData();
+                                var texCoord1StrideSize = texCoord1VertexBuffer.getStrideSize();
+                                var accessor = this.createAccessor(texCoord1BufferViewIndex, "Texture Coords 1", "VEC2" /* VEC2 */, 5126 /* FLOAT */, texCoord1s.length / texCoord1StrideSize);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
                             }
-                            if (babylonPBRMaterial.roughness !== undefined) {
-                                glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
+                            if (indexBufferViewIndex) {
+                                // Create accessor
+                                var accessor = this.createAccessor(indexBufferViewIndex, "Indices", "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4);
+                                this.accessors.push(accessor);
+                                meshPrimitive.indices = this.accessors.length - 1;
                             }
-                            var glTFMaterial = {
-                                name: babylonPBRMaterial.name
-                            };
-                            if (babylonPBRMaterial.doubleSided) {
-                                glTFMaterial.doubleSided = babylonPBRMaterial.doubleSided;
-                            }
-                            if (babylonPBRMaterial.normalTexture) {
-                                var glTFTexture = this.exportTexture(babylonPBRMaterial.normalTexture);
-                                if (glTFTexture) {
-                                    glTFMaterial.normalTexture = glTFTexture;
+                        }
+                        if (bufferMesh.material) {
+                            if (bufferMesh.material instanceof BABYLON.StandardMaterial) {
+                                console.warn("Standard Material is currently not fully supported/implemented in glTF serializer");
+                                var babylonStandardMaterial = bufferMesh.material;
+                                var glTFPbrMetallicRoughness = GLTF2._GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
+                                var glTFMaterial = { name: babylonStandardMaterial.name };
+                                if (!babylonStandardMaterial.backFaceCulling) {
+                                    glTFMaterial.doubleSided = true;
                                 }
-                            }
-                            if (babylonPBRMaterial.occlusionTexture) {
-                                var glTFTexture = this.exportTexture(babylonPBRMaterial.occlusionTexture);
-                                if (glTFTexture) {
-                                    glTFMaterial.occlusionTexture = glTFTexture;
-                                    if (babylonPBRMaterial.occlusionStrength !== undefined) {
-                                        glTFMaterial.occlusionTexture.strength = babylonPBRMaterial.occlusionStrength;
+                                if (babylonStandardMaterial.diffuseTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                                    var glTFTexture = this.exportTexture(babylonStandardMaterial.diffuseTexture);
+                                    if (glTFTexture !== null) {
+                                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                                     }
                                 }
-                            }
-                            if (babylonPBRMaterial.emissiveTexture) {
-                                var glTFTexture = this.exportTexture(babylonPBRMaterial.emissiveTexture);
-                                if (glTFTexture !== null) {
-                                    glTFMaterial.emissiveTexture = glTFTexture;
+                                if (babylonStandardMaterial.bumpTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                                    var glTFTexture = this.exportTexture(babylonStandardMaterial.bumpTexture);
+                                    if (glTFTexture) {
+                                        glTFMaterial.normalTexture = glTFTexture;
+                                    }
                                 }
+                                if (babylonStandardMaterial.emissiveTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                                    var glTFEmissiveTexture = this.exportTexture(babylonStandardMaterial.emissiveTexture);
+                                    if (glTFEmissiveTexture) {
+                                        glTFMaterial.emissiveTexture = glTFEmissiveTexture;
+                                    }
+                                    glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
+                                }
+                                if (babylonStandardMaterial.ambientTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                                    var glTFOcclusionTexture = this.exportTexture(babylonStandardMaterial.ambientTexture);
+                                    if (glTFOcclusionTexture) {
+                                        glTFMaterial.occlusionTexture = glTFOcclusionTexture;
+                                    }
+                                }
+                                if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
+                                    if (babylonStandardMaterial.alphaMode === BABYLON.Engine.ALPHA_COMBINE) {
+                                        glTFMaterial.alphaMode = "BLEND" /* BLEND */;
+                                    }
+                                    else {
+                                        console.warn("glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
+                                    }
+                                }
+                                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
+                                this.materials.push(glTFMaterial);
+                                meshPrimitive.material = this.materials.length - 1;
                             }
-                            if (!babylonPBRMaterial.emissiveColor.equals(new BABYLON.Color3(0.0, 0.0, 0.0))) {
-                                glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
-                            }
-                            if (babylonPBRMaterial.transparencyMode) {
-                                var alphaMode = GLTF2._GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
-                                if (alphaMode !== "OPAQUE" /* OPAQUE */) {
-                                    glTFMaterial.alphaMode = alphaMode;
-                                    if (alphaMode === "BLEND" /* BLEND */) {
-                                        glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
+                            else if (bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial) {
+                                var babylonPBRMaterial = bufferMesh.material;
+                                var glTFPbrMetallicRoughness = {};
+                                if (babylonPBRMaterial.baseColor) {
+                                    glTFPbrMetallicRoughness.baseColorFactor = [
+                                        babylonPBRMaterial.baseColor.r,
+                                        babylonPBRMaterial.baseColor.g,
+                                        babylonPBRMaterial.baseColor.b,
+                                        babylonPBRMaterial.alpha
+                                    ];
+                                }
+                                if (babylonPBRMaterial.baseTexture !== undefined) {
+                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.baseTexture);
+                                    if (glTFTexture !== null) {
+                                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                                     }
+                                    glTFPbrMetallicRoughness.baseColorTexture;
                                 }
+                                if (babylonPBRMaterial.metallic !== undefined) {
+                                    glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
+                                }
+                                if (babylonPBRMaterial.roughness !== undefined) {
+                                    glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
+                                }
+                                var glTFMaterial = {
+                                    name: babylonPBRMaterial.name
+                                };
+                                if (babylonPBRMaterial.doubleSided) {
+                                    glTFMaterial.doubleSided = babylonPBRMaterial.doubleSided;
+                                }
+                                if (babylonPBRMaterial.normalTexture) {
+                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.normalTexture);
+                                    if (glTFTexture) {
+                                        glTFMaterial.normalTexture = glTFTexture;
+                                    }
+                                }
+                                if (babylonPBRMaterial.occlusionTexture) {
+                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.occlusionTexture);
+                                    if (glTFTexture) {
+                                        glTFMaterial.occlusionTexture = glTFTexture;
+                                        if (babylonPBRMaterial.occlusionStrength !== undefined) {
+                                            glTFMaterial.occlusionTexture.strength = babylonPBRMaterial.occlusionStrength;
+                                        }
+                                    }
+                                }
+                                if (babylonPBRMaterial.emissiveTexture) {
+                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.emissiveTexture);
+                                    if (glTFTexture !== null) {
+                                        glTFMaterial.emissiveTexture = glTFTexture;
+                                    }
+                                }
+                                if (!babylonPBRMaterial.emissiveColor.equals(new BABYLON.Color3(0.0, 0.0, 0.0))) {
+                                    glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                                }
+                                if (babylonPBRMaterial.transparencyMode) {
+                                    var alphaMode = GLTF2._GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
+                                    if (alphaMode !== "OPAQUE" /* OPAQUE */) {
+                                        glTFMaterial.alphaMode = alphaMode;
+                                        if (alphaMode === "BLEND" /* BLEND */) {
+                                            glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
+                                        }
+                                    }
+                                }
+                                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
+                                this.materials.push(glTFMaterial);
+                                meshPrimitive.material = this.materials.length - 1;
+                            }
+                            else {
+                                console.warn("Material type is not yet implemented in glTF serializer: " + bufferMesh.material.name);
                             }
-                            glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
-                            // TODO: Handle Textures
-                            this.materials.push(glTFMaterial);
-                            meshPrimitive.material = this.materials.length - 1;
                         }
+                        mesh.primitives.push(meshPrimitive);
                     }
-                    mesh.primitives.push(meshPrimitive);
                 }
                 return byteOffset;
             };
             /**
              * Creates a glTF scene based on the array of meshes.
              * Returns the the total byte offset.
-             * @param gltf
-             * @param byteOffset
-             * @param buffer
-             * @param dataBuffer
+             * @param babylonScene - Babylon scene to get the mesh data from.
+             * @param byteOffset - Offset to start from in bytes.
+             * @param dataBuffer - Buffer to write geometry data to.
              * @returns bytelength + byteoffset
              */
             _Exporter.prototype.createScene = function (babylonScene, byteOffset, dataBuffer) {
@@ -996,7 +1078,7 @@ var BABYLON;
                 var diffuse = babylonSpecularGlossiness.diffuse;
                 var opacity = babylonSpecularGlossiness.opacity;
                 var specular = babylonSpecularGlossiness.specular;
-                var glossiness = babylonSpecularGlossiness.glossiness;
+                var glossiness = BABYLON.Scalar.Clamp(babylonSpecularGlossiness.glossiness);
                 var oneMinusSpecularStrength = 1 - Math.max(specular.r, Math.max(specular.g, specular.b));
                 var diffusePerceivedBrightness = _GLTFMaterial.PerceivedBrightness(diffuse);
                 var specularPerceivedBrightness = _GLTFMaterial.PerceivedBrightness(specular);

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 381 - 299
dist/preview release/serializers/babylonjs.serializers.js

@@ -222,22 +222,25 @@ var BABYLON;
                 this.nodes = new Array();
                 this.images = new Array();
                 this.materials = new Array();
+                this.textures = new Array();
                 this.imageData = {};
                 if (options !== undefined) {
                     this.options = options;
                 }
                 var totalByteLength = 0;
-                totalByteLength = this.createScene(this.babylonScene, totalByteLength);
+                totalByteLength = this.createScene(this.babylonScene, totalByteLength, null);
                 this.totalByteLength = totalByteLength;
             }
             /**
              * Creates a buffer view based on teh supplied arguments
-             * @param {number} bufferIndex - index value of the specified buffer
-             * @param {number} byteOffset - byte offset value
-             * @param {number} byteLength - byte length of the bufferView
+             * @param bufferIndex - index value of the specified buffer
+             * @param byteOffset - byte offset value
+             * @param byteLength - byte length of the bufferView
+             * @param byteStride - byte distance between conequential elements.
+             * @param name - name of the buffer view
              * @returns - bufferView for glTF
              */
-            _Exporter.prototype.createBufferView = function (bufferIndex, byteOffset, byteLength, name) {
+            _Exporter.prototype.createBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
                 var bufferview = { buffer: bufferIndex, byteLength: byteLength };
                 if (byteOffset > 0) {
                     bufferview.byteOffset = byteOffset;
@@ -245,6 +248,9 @@ var BABYLON;
                 if (name) {
                     bufferview.name = name;
                 }
+                if (byteStride) {
+                    bufferview.byteStride = byteStride;
+                }
                 return bufferview;
             };
             /**
@@ -258,7 +264,7 @@ var BABYLON;
              * @param max
              * @returns - accessor for glTF
              */
-            _Exporter.prototype.createAccessor = function (bufferviewIndex, name, type, componentType, count, min, max) {
+            _Exporter.prototype.createAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
                 var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
                 if (min) {
                     accessor.min = min;
@@ -266,30 +272,38 @@ var BABYLON;
                 if (max) {
                     accessor.max = max;
                 }
+                if (byteOffset) {
+                    accessor.byteOffset = byteOffset;
+                }
                 return accessor;
             };
             /**
              * Calculates the minimum and maximum values of an array of floats, based on stride
-             * @param buff
-             * @param vertexStart
-             * @param vertexCount
-             * @param arrayOffset
-             * @param stride
-             * @returns - min number array and max number array
+             * @param buff - Data to check for min and max values.
+             * @param vertexStart - Start offset to calculate min and max values.
+             * @param vertexCount - Number of vertices to check for min and max values.
+             * @param stride - Offset between consecutive attributes.
+             * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+             * @returns - min number array and max number array.
              */
-            _Exporter.prototype.calculateMinMax = function (buff, vertexStart, vertexCount, arrayOffset, stride) {
+            _Exporter.prototype.calculateMinMax = function (buff, vertexStart, vertexCount, stride, useRightHandedSystem) {
                 var min = [Infinity, Infinity, Infinity];
                 var max = [-Infinity, -Infinity, -Infinity];
                 var end = vertexStart + vertexCount;
                 if (vertexCount > 0) {
                     for (var i = vertexStart; i < end; ++i) {
                         var index = stride * i;
+                        var scale = 1;
                         for (var j = 0; j < stride; ++j) {
-                            if (buff[index] < min[j]) {
-                                min[j] = buff[index];
+                            if (j === (stride - 1) && !useRightHandedSystem) {
+                                scale = -1;
                             }
-                            if (buff[index] > max[j]) {
-                                max[j] = buff[index];
+                            var num = scale * buff[index];
+                            if (num < min[j]) {
+                                min[j] = num;
+                            }
+                            if (num > max[j]) {
+                                max[j] = num;
                             }
                             ++index;
                         }
@@ -298,24 +312,24 @@ var BABYLON;
                 return { min: min, max: max };
             };
             /**
-             * Write mesh attribute data to buffer.
+             * Writes mesh attribute data to a data buffer.
              * Returns the bytelength of the data.
-             * @param vertexBufferType
-             * @param submesh
-             * @param meshAttributeArray
-             * @param strideSize
-             * @param byteOffset
-             * @param dataBuffer
-             * @param useRightHandedSystem
-             * @returns - byte length
+             * @param vertexBufferKind - Indicates what kind of vertex data is being passed in.
+             * @param meshAttributeArray - Array containing the attribute data.
+             * @param strideSize - Represents the offset between consecutive attributes
+             * @param byteOffset - The offset to start counting bytes from.
+             * @param dataBuffer - The buffer to write the binary data to.
+             * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+             * @returns - Byte length of the attribute data.
              */
-            _Exporter.prototype.writeAttributeData = function (vertexBufferType, submesh, meshAttributeArray, strideSize, byteOffset, dataBuffer, useRightHandedSystem) {
+            _Exporter.prototype.writeAttributeData = function (vertexBufferKind, meshAttributeArray, strideSize, vertexBufferOffset, byteOffset, dataBuffer, useRightHandedSystem) {
                 var byteOff = byteOffset;
-                var end = submesh.verticesStart + submesh.verticesCount;
+                var start = 0;
+                var end = meshAttributeArray.length / strideSize;
                 var byteLength = 0;
-                switch (vertexBufferType) {
+                switch (vertexBufferKind) {
                     case BABYLON.VertexBuffer.PositionKind: {
-                        for (var k = submesh.verticesStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
@@ -329,11 +343,11 @@ var BABYLON;
                             }
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 12;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     case BABYLON.VertexBuffer.NormalKind: {
-                        for (var k = submesh.verticesStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
@@ -347,11 +361,11 @@ var BABYLON;
                             }
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 12;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     case BABYLON.VertexBuffer.TangentKind: {
-                        for (var k = submesh.indexStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
@@ -367,11 +381,11 @@ var BABYLON;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 16;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     case BABYLON.VertexBuffer.ColorKind: {
-                        for (var k = submesh.verticesStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
@@ -382,45 +396,45 @@ var BABYLON;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 16;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     case BABYLON.VertexBuffer.UVKind: {
-                        for (var k = submesh.verticesStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 8;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     case BABYLON.VertexBuffer.UV2Kind: {
-                        for (var k = submesh.verticesStart; k < end; ++k) {
+                        for (var k = start; k < end; ++k) {
                             var index = k * strideSize;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
                             byteOff += 4;
                             dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
                             byteOff += 4;
                         }
-                        byteLength = submesh.verticesCount * 8;
+                        byteLength = meshAttributeArray.length * 4;
                         break;
                     }
                     default: {
-                        throw new Error("Unsupported vertex buffer type: " + vertexBufferType);
+                        throw new Error("Unsupported vertex buffer type: " + vertexBufferKind);
                     }
                 }
                 return byteLength;
             };
             /**
              * Generates glTF json data
-             * @param glb
-             * @param glTFPrefix
-             * @param prettyPrint
+             * @param shouldUseGlb - Indicates whether the json should be written for a glb file.
+             * @param glTFPrefix - Text to use when prefixing a glTF file.
+             * @param prettyPrint - Indicates whether the json file should be pretty printed (true) or not (false).
              * @returns - json data as string
              */
-            _Exporter.prototype.generateJSON = function (glb, glTFPrefix, prettyPrint) {
+            _Exporter.prototype.generateJSON = function (shouldUseGlb, glTFPrefix, prettyPrint) {
                 var buffer = { byteLength: this.totalByteLength };
                 var glTF = {
                     asset: this.asset
@@ -451,7 +465,7 @@ var BABYLON;
                     glTF.textures = this.textures;
                 }
                 if (this.images && this.images.length !== 0) {
-                    if (!glb) {
+                    if (!shouldUseGlb) {
                         glTF.images = this.images;
                     }
                     else {
@@ -464,7 +478,7 @@ var BABYLON;
                             if (image.uri !== undefined) {
                                 var imageData = this.imageData[image.uri];
                                 var imageName = image.uri.split('.')[0] + " image";
-                                var bufferView = this.createBufferView(0, byteOffset, imageData.data.length, imageName);
+                                var bufferView = this.createBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                                 byteOffset += imageData.data.buffer.byteLength;
                                 this.bufferViews.push(bufferView);
                                 image.bufferView = this.bufferViews.length - 1;
@@ -477,7 +491,7 @@ var BABYLON;
                         buffer.byteLength = byteOffset;
                     }
                 }
-                if (!glb) {
+                if (!shouldUseGlb) {
                     buffer.uri = glTFPrefix + ".bin";
                 }
                 var jsonText = prettyPrint ? JSON.stringify(glTF, null, 2) : JSON.stringify(glTF);
@@ -485,9 +499,8 @@ var BABYLON;
             };
             /**
              * Generates data for .gltf and .bin files based on the glTF prefix string
-             * @param glTFPrefix
-             * @returns - object with glTF json tex filename
-             * and binary file name as keys and their data as values
+             * @param glTFPrefix - Text to use when prefixing a glTF file.
+             * @returns - GLTFData with glTF file data.
              */
             _Exporter.prototype._generateGLTF = function (glTFPrefix) {
                 var jsonText = this.generateJSON(false, glTFPrefix, true);
@@ -517,7 +530,7 @@ var BABYLON;
                 return binaryBuffer;
             };
             /**
-             * Pads the number to a power of 4
+             * Pads the number to a multiple of 4
              * @param num - number to pad
              * @returns - padded number
              */
@@ -529,8 +542,6 @@ var BABYLON;
             /**
              * Generates a glb file from the json and binary data.
              * Returns an object with the glb file name as the key and data as the value.
-             * @param jsonText
-             * @param binaryBuffer
              * @param glTFPrefix
              * @returns - object with glb filename as key and data as value
              */
@@ -547,7 +558,8 @@ var BABYLON;
                 }
                 var jsonPadding = this._getPadding(jsonLength);
                 var binPadding = this._getPadding(binaryBuffer.byteLength);
-                var byteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding + imageByteLength;
+                var imagePadding = this._getPadding(imageByteLength);
+                var byteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding + imageByteLength + imagePadding;
                 //header
                 var headerBuffer = new ArrayBuffer(headerLength);
                 var headerBufferView = new DataView(headerBuffer);
@@ -572,7 +584,7 @@ var BABYLON;
                 //binary chunk
                 var binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
                 var binaryChunkBufferView = new DataView(binaryChunkBuffer);
-                binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength + imageByteLength, true);
+                binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength + imageByteLength + imagePadding, true);
                 binaryChunkBufferView.setUint32(4, 0x004E4942, true);
                 // binary padding
                 var binPaddingBuffer = new ArrayBuffer(binPadding);
@@ -580,12 +592,18 @@ var BABYLON;
                 for (var i = 0; i < binPadding; ++i) {
                     binPaddingView[i] = 0;
                 }
+                var imagePaddingBuffer = new ArrayBuffer(imagePadding);
+                var imagePaddingView = new Uint8Array(imagePaddingBuffer);
+                for (var i = 0; i < imagePadding; ++i) {
+                    imagePaddingView[i] = 0;
+                }
                 var glbData = [headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer];
                 // binary data
                 for (var key in this.imageData) {
                     glbData.push(this.imageData[key].data.buffer);
                 }
                 glbData.push(binPaddingBuffer);
+                glbData.push(imagePaddingBuffer);
                 var glbFile = new Blob(glbData, { type: 'application/octet-stream' });
                 var container = new BABYLON._GLTFData();
                 container.glTFFiles[glbFileName] = glbFile;
@@ -593,9 +611,9 @@ var BABYLON;
             };
             /**
              * Sets the TRS for each node
-             * @param node
-             * @param babylonMesh
-             * @param useRightHandedSystem
+             * @param node - glTF Node for storing the transformation data.
+             * @param babylonMesh - Babylon mesh used as the source for the transformation data.
+             * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
              */
             _Exporter.prototype.setNodeTransformation = function (node, babylonMesh, useRightHandedSystem) {
                 if (!(babylonMesh.position.x === 0 && babylonMesh.position.y === 0 && babylonMesh.position.z === 0)) {
@@ -629,8 +647,9 @@ var BABYLON;
             };
             /**
              *
-             * @param babylonTexture
-             * @return - glTF texture, or null if the texture format is not supported
+             * @param babylonTexture - Babylon texture to extract.
+             * @param mimeType - Mime Type of the babylonTexture.
+             * @return - glTF texture, or null if the texture format is not supported.
              */
             _Exporter.prototype.exportTexture = function (babylonTexture, mimeType) {
                 if (mimeType === void 0) { mimeType = "image/jpeg" /* JPEG */; }
@@ -705,289 +724,352 @@ var BABYLON;
                 return textureInfo;
             };
             /**
-             * Sets data for the primitive attributes of each submesh
-             * @param mesh
-             * @param babylonMesh
-             * @param byteOffset
-             * @param useRightHandedSystem
-             * @param dataBuffer
-             * @returns - bytelength of the primitive attributes plus the passed in byteOffset
+             * Creates a bufferview based on the vertices type for the Babylon mesh
+             * @param kind - Indicates the type of vertices data.
+             * @param babylonMesh - The Babylon mesh to get the vertices data from.
+             * @param byteOffset - The offset from the buffer to start indexing from.
+             * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+             * @param dataBuffer - The buffer to write the bufferview data to.
+             * @returns bytelength of the bufferview data.
              */
-            _Exporter.prototype.setPrimitiveAttributes = function (mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer) {
-                // go through all mesh primitives (submeshes)
-                for (var j = 0; j < babylonMesh.subMeshes.length; ++j) {
-                    var bufferMesh = null;
-                    var submesh = babylonMesh.subMeshes[j];
-                    var meshPrimitive = { attributes: {} };
-                    if (babylonMesh instanceof BABYLON.Mesh) {
-                        bufferMesh = babylonMesh;
-                    }
-                    else if (babylonMesh instanceof BABYLON.InstancedMesh) {
-                        bufferMesh = babylonMesh.sourceMesh;
-                    }
-                    // Loop through each attribute of the submesh (mesh primitive)
-                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
-                        var positionVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
-                        var positionVertexBufferOffset = positionVertexBuffer.getOffset();
-                        var positions = positionVertexBuffer.getData();
-                        var positionStrideSize = positionVertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.PositionKind, submesh, positions, positionStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
-                        }
-                        else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 12;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Positions");
+            _Exporter.prototype.createBufferViewKind = function (kind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer) {
+                var bufferMesh = null;
+                var byteLength = 0;
+                if (babylonMesh instanceof BABYLON.Mesh) {
+                    bufferMesh = babylonMesh;
+                }
+                else if (babylonMesh instanceof BABYLON.InstancedMesh) {
+                    bufferMesh = babylonMesh.sourceMesh;
+                }
+                if (bufferMesh !== null) {
+                    var vertexBuffer = null;
+                    var vertexBufferOffset = null;
+                    var vertexData = null;
+                    var vertexStrideSize = null;
+                    if (bufferMesh.getVerticesDataKinds().indexOf(kind) > -1) {
+                        vertexBuffer = bufferMesh.getVertexBuffer(kind);
+                        vertexBufferOffset = vertexBuffer.getOffset();
+                        vertexData = vertexBuffer.getData();
+                        vertexStrideSize = vertexBuffer.getStrideSize();
+                        if (dataBuffer && vertexData) {
+                            byteLength = this.writeAttributeData(kind, vertexData, vertexStrideSize, vertexBufferOffset, byteOffset, dataBuffer, useRightHandedSystem);
                             byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var result = this.calculateMinMax(positions, submesh.verticesStart, submesh.verticesCount, positionVertexBufferOffset, positionStrideSize);
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Position", "VEC3" /* VEC3 */, 5126 /* FLOAT */, submesh.verticesCount, result.min, result.max);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.POSITION = this.accessors.length - 1;
-                        }
-                    }
-                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
-                        var normalVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.NormalKind);
-                        var normals = normalVertexBuffer.getData();
-                        var normalStrideSize = normalVertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.NormalKind, submesh, normals, normalStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
                         }
                         else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 12;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Normals");
-                            byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Normal", "VEC3" /* VEC3 */, 5126 /* FLOAT */, submesh.verticesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
+                            var bufferViewName = null;
+                            switch (kind) {
+                                case BABYLON.VertexBuffer.PositionKind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "Position - " + bufferMesh.name;
+                                    break;
+                                }
+                                case BABYLON.VertexBuffer.NormalKind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "Normal - " + bufferMesh.name;
+                                    break;
+                                }
+                                case BABYLON.VertexBuffer.TangentKind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "Tangent - " + bufferMesh.name;
+                                    break;
+                                }
+                                case BABYLON.VertexBuffer.ColorKind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "Color - " + bufferMesh.name;
+                                    break;
+                                }
+                                case BABYLON.VertexBuffer.UVKind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "TexCoord 0 - " + bufferMesh.name;
+                                    break;
+                                }
+                                case BABYLON.VertexBuffer.UV2Kind: {
+                                    byteLength = vertexData.length * 4;
+                                    bufferViewName = "TexCoord 1 - " + bufferMesh.name;
+                                    break;
+                                }
+                                default: {
+                                    console.warn("Unsupported VertexBuffer kind: " + kind);
+                                }
+                            }
+                            if (bufferViewName !== null) {
+                                var bufferView = this.createBufferView(0, byteOffset, byteLength, vertexStrideSize * 4, bufferViewName);
+                                byteOffset += byteLength;
+                                this.bufferViews.push(bufferView);
+                            }
                         }
                     }
-                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
-                        var tangentVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.TangentKind);
-                        var tangents = tangentVertexBuffer.getData();
-                        var tangentStrideSize = tangentVertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.TangentKind, submesh, tangents, tangentStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
-                        }
-                        else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 16;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Tangents");
-                            byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Tangent", "VEC4" /* VEC4 */, 5126 /* FLOAT */, submesh.verticesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
-                        }
+                }
+                return byteLength;
+            };
+            /**
+             * Sets data for the primitive attributes of each submesh
+             * @param mesh - glTF Mesh object to store the primitive attribute information.
+             * @param babylonMesh - Babylon mesh to get the primitive attribute data from.
+             * @param byteOffset - The offset in bytes of the buffer data.
+             * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+             * @param dataBuffer - Buffer to write the attribute data to.
+             * @returns - bytelength of the primitive attributes plus the passed in byteOffset.
+             */
+            _Exporter.prototype.setPrimitiveAttributes = function (mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer) {
+                var bufferMesh = null;
+                if (babylonMesh instanceof BABYLON.Mesh) {
+                    bufferMesh = babylonMesh;
+                }
+                else if (babylonMesh instanceof BABYLON.InstancedMesh) {
+                    bufferMesh = babylonMesh.sourceMesh;
+                }
+                var positionBufferViewIndex = null;
+                var normalBufferViewIndex = null;
+                var colorBufferViewIndex = null;
+                var tangentBufferViewIndex = null;
+                var texCoord0BufferViewIndex = null;
+                var texCoord1BufferViewIndex = null;
+                var indexBufferViewIndex = null;
+                if (bufferMesh !== null) {
+                    // For each BabylonMesh, create bufferviews for each 'kind'
+                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.PositionKind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        positionBufferViewIndex = this.bufferViews.length - 1;
+                    }
+                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.NormalKind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        normalBufferViewIndex = this.bufferViews.length - 1;
                     }
                     if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
-                        var colorVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.ColorKind);
-                        var colors = colorVertexBuffer.getData();
-                        var colorStrideSize = colorVertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.ColorKind, submesh, colors, colorStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
-                        }
-                        else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 16;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Colors");
-                            byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Color", "VEC4" /* VEC4 */, 5126 /* FLOAT */, submesh.verticesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
-                        }
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.ColorKind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        colorBufferViewIndex = this.bufferViews.length - 1;
+                    }
+                    if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.TangentKind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        colorBufferViewIndex = this.bufferViews.length - 1;
                     }
                     if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                        var texCoord0VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UVKind);
-                        var texCoords0 = texCoord0VertexBuffer.getData();
-                        var texCoord0StrideSize = texCoord0VertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.UVKind, submesh, texCoords0, texCoord0StrideSize, byteOffset, dataBuffer, useRightHandedSystem);
-                        }
-                        else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 8;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Texture Coords0");
-                            byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Texture Coords", "VEC2" /* VEC2 */, 5126 /* FLOAT */, submesh.verticesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
-                        }
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.UVKind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        texCoord0BufferViewIndex = this.bufferViews.length - 1;
                     }
                     if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
-                        var texCoord1VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UV2Kind);
-                        var texCoords1 = texCoord1VertexBuffer.getData();
-                        var texCoord1StrideSize = texCoord1VertexBuffer.getStrideSize();
-                        if (dataBuffer) {
-                            byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.UV2Kind, submesh, texCoords1, texCoord1StrideSize, byteOffset, dataBuffer, useRightHandedSystem);
-                        }
-                        else {
-                            // Create bufferview
-                            var byteLength = submesh.verticesCount * 8;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Texture Coords 1");
-                            byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Texture Coords", "VEC2" /* VEC2 */, 5126 /* FLOAT */, submesh.verticesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
-                        }
+                        byteOffset += this.createBufferViewKind(BABYLON.VertexBuffer.UV2Kind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                        texCoord1BufferViewIndex = this.bufferViews.length - 1;
                     }
                     if (bufferMesh.getTotalIndices() > 0) {
+                        var indices = bufferMesh.getIndices();
                         if (dataBuffer) {
-                            var indices = bufferMesh.getIndices();
-                            var start = submesh.indexStart;
-                            var end = submesh.indexCount + start;
+                            var end = indices.length;
                             var byteOff = byteOffset;
-                            for (var k = start; k < end; k = k + 3) {
+                            for (var k = 0; k < end; ++k) {
                                 dataBuffer.setUint32(byteOff, indices[k], true);
                                 byteOff += 4;
-                                dataBuffer.setUint32(byteOff, indices[k + 1], true);
-                                byteOff += 4;
-                                dataBuffer.setUint32(byteOff, indices[k + 2], true);
-                                byteOff += 4;
                             }
-                            var byteLength = submesh.indexCount * 4;
-                            byteOffset += byteLength;
+                            byteOffset = byteOff;
                         }
                         else {
-                            // Create bufferview
-                            var indicesCount = submesh.indexCount;
-                            var byteLength = indicesCount * 4;
-                            var bufferview = this.createBufferView(0, byteOffset, byteLength, "Indices");
+                            var byteLength = indices.length * 4;
+                            var bufferView = this.createBufferView(0, byteOffset, byteLength, undefined, "Indices - " + bufferMesh.name);
                             byteOffset += byteLength;
-                            this.bufferViews.push(bufferview);
-                            // Create accessor
-                            var accessor = this.createAccessor(this.bufferViews.length - 1, "Indices", "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, indicesCount);
-                            this.accessors.push(accessor);
-                            meshPrimitive.indices = this.accessors.length - 1;
+                            this.bufferViews.push(bufferView);
+                            indexBufferViewIndex = this.bufferViews.length - 1;
                         }
                     }
-                    if (bufferMesh.material) {
-                        if (bufferMesh.material instanceof BABYLON.StandardMaterial) {
-                            var babylonStandardMaterial = bufferMesh.material;
-                            var glTFMaterial = { name: babylonStandardMaterial.name };
-                            if (!babylonStandardMaterial.backFaceCulling) {
-                                glTFMaterial.doubleSided = true;
-                            }
-                            if (babylonStandardMaterial.bumpTexture) {
-                                var glTFTexture = this.exportTexture(babylonStandardMaterial.bumpTexture);
-                                if (glTFTexture) {
-                                    glTFMaterial.normalTexture = glTFTexture;
-                                }
-                            }
-                            if (babylonStandardMaterial.emissiveTexture) {
-                                var glTFEmissiveTexture = this.exportTexture(babylonStandardMaterial.emissiveTexture);
-                                if (glTFEmissiveTexture) {
-                                    glTFMaterial.emissiveTexture = glTFEmissiveTexture;
-                                }
-                                glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
+                }
+                // go through all mesh primitives (submeshes)
+                for (var j = 0; j < babylonMesh.subMeshes.length; ++j) {
+                    var submesh = babylonMesh.subMeshes[j];
+                    var meshPrimitive = { attributes: {} };
+                    if (bufferMesh !== null) {
+                        // Create a bufferview storing all the positions
+                        if (!dataBuffer) {
+                            // Loop through each attribute of the submesh (mesh primitive)
+                            if (positionBufferViewIndex !== null) {
+                                var positionVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
+                                var positions = positionVertexBuffer.getData();
+                                var positionStrideSize = positionVertexBuffer.getStrideSize();
+                                // Create accessor
+                                var result = this.calculateMinMax(positions, 0, positions.length / positionStrideSize, positionStrideSize, useRightHandedSystem);
+                                var accessor = this.createAccessor(positionBufferViewIndex, "Position", "VEC3" /* VEC3 */, 5126 /* FLOAT */, positions.length / positionStrideSize, 0, result.min, result.max);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.POSITION = this.accessors.length - 1;
                             }
-                            if (babylonStandardMaterial.ambientTexture) {
-                                var glTFOcclusionTexture = this.exportTexture(babylonStandardMaterial.ambientTexture);
-                                if (glTFOcclusionTexture) {
-                                    glTFMaterial.occlusionTexture = glTFOcclusionTexture;
-                                }
+                            if (normalBufferViewIndex !== null) {
+                                var normalVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.NormalKind);
+                                var normals = normalVertexBuffer.getData();
+                                var normalStrideSize = normalVertexBuffer.getStrideSize();
+                                // Create accessor
+                                var accessor = this.createAccessor(normalBufferViewIndex, "Normal", "VEC3" /* VEC3 */, 5126 /* FLOAT */, normals.length / normalStrideSize);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
                             }
-                            // Spec Gloss
-                            var glTFPbrMetallicRoughness = GLTF2._GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
-                            glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
-                            // TODO: Handle Textures
-                            this.materials.push(glTFMaterial);
-                            meshPrimitive.material = this.materials.length - 1;
-                        }
-                        else if (bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial) {
-                            if (!this.textures) {
-                                this.textures = new Array();
+                            if (tangentBufferViewIndex !== null) {
+                                var tangentVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.TangentKind);
+                                var tangents = tangentVertexBuffer.getData();
+                                var tangentStrideSize = tangentVertexBuffer.getStrideSize();
+                                // Create accessor
+                                var accessor = this.createAccessor(tangentBufferViewIndex, "Tangent", "VEC4" /* VEC4 */, 5126 /* FLOAT */, tangents.length / tangentStrideSize);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
                             }
-                            var babylonPBRMaterial = bufferMesh.material;
-                            var glTFPbrMetallicRoughness = {};
-                            if (babylonPBRMaterial.baseColor) {
-                                glTFPbrMetallicRoughness.baseColorFactor = [
-                                    babylonPBRMaterial.baseColor.r,
-                                    babylonPBRMaterial.baseColor.g,
-                                    babylonPBRMaterial.baseColor.b,
-                                    babylonPBRMaterial.alpha
-                                ];
+                            if (colorBufferViewIndex !== null) {
+                                var colorVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.ColorKind);
+                                var colors = colorVertexBuffer.getData();
+                                var colorStrideSize = colorVertexBuffer.getStrideSize();
+                                // Create accessor
+                                var accessor = this.createAccessor(colorBufferViewIndex, "Color", "VEC4" /* VEC4 */, 5126 /* FLOAT */, colors.length / colorStrideSize);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
                             }
-                            if (babylonPBRMaterial.baseTexture !== undefined) {
-                                var glTFTexture = this.exportTexture(babylonPBRMaterial.baseTexture);
-                                if (glTFTexture !== null) {
-                                    glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                                }
-                                glTFPbrMetallicRoughness.baseColorTexture;
+                            if (texCoord0BufferViewIndex !== null) {
+                                // Create accessor
+                                var texCoord0VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UVKind);
+                                var texCoord0s = texCoord0VertexBuffer.getData();
+                                var texCoord0StrideSize = texCoord0VertexBuffer.getStrideSize();
+                                var accessor = this.createAccessor(texCoord0BufferViewIndex, "Texture Coords 0", "VEC2" /* VEC2 */, 5126 /* FLOAT */, texCoord0s.length / texCoord0StrideSize);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
                             }
-                            if (babylonPBRMaterial.metallic !== undefined) {
-                                glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
+                            if (texCoord1BufferViewIndex !== null) {
+                                // Create accessor
+                                var texCoord1VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UV2Kind);
+                                var texCoord1s = texCoord1VertexBuffer.getData();
+                                var texCoord1StrideSize = texCoord1VertexBuffer.getStrideSize();
+                                var accessor = this.createAccessor(texCoord1BufferViewIndex, "Texture Coords 1", "VEC2" /* VEC2 */, 5126 /* FLOAT */, texCoord1s.length / texCoord1StrideSize);
+                                this.accessors.push(accessor);
+                                meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
                             }
-                            if (babylonPBRMaterial.roughness !== undefined) {
-                                glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
+                            if (indexBufferViewIndex) {
+                                // Create accessor
+                                var accessor = this.createAccessor(indexBufferViewIndex, "Indices", "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4);
+                                this.accessors.push(accessor);
+                                meshPrimitive.indices = this.accessors.length - 1;
                             }
-                            var glTFMaterial = {
-                                name: babylonPBRMaterial.name
-                            };
-                            if (babylonPBRMaterial.doubleSided) {
-                                glTFMaterial.doubleSided = babylonPBRMaterial.doubleSided;
-                            }
-                            if (babylonPBRMaterial.normalTexture) {
-                                var glTFTexture = this.exportTexture(babylonPBRMaterial.normalTexture);
-                                if (glTFTexture) {
-                                    glTFMaterial.normalTexture = glTFTexture;
+                        }
+                        if (bufferMesh.material) {
+                            if (bufferMesh.material instanceof BABYLON.StandardMaterial) {
+                                console.warn("Standard Material is currently not fully supported/implemented in glTF serializer");
+                                var babylonStandardMaterial = bufferMesh.material;
+                                var glTFPbrMetallicRoughness = GLTF2._GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
+                                var glTFMaterial = { name: babylonStandardMaterial.name };
+                                if (!babylonStandardMaterial.backFaceCulling) {
+                                    glTFMaterial.doubleSided = true;
                                 }
-                            }
-                            if (babylonPBRMaterial.occlusionTexture) {
-                                var glTFTexture = this.exportTexture(babylonPBRMaterial.occlusionTexture);
-                                if (glTFTexture) {
-                                    glTFMaterial.occlusionTexture = glTFTexture;
-                                    if (babylonPBRMaterial.occlusionStrength !== undefined) {
-                                        glTFMaterial.occlusionTexture.strength = babylonPBRMaterial.occlusionStrength;
+                                if (babylonStandardMaterial.diffuseTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                                    var glTFTexture = this.exportTexture(babylonStandardMaterial.diffuseTexture);
+                                    if (glTFTexture !== null) {
+                                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                                     }
                                 }
-                            }
-                            if (babylonPBRMaterial.emissiveTexture) {
-                                var glTFTexture = this.exportTexture(babylonPBRMaterial.emissiveTexture);
-                                if (glTFTexture !== null) {
-                                    glTFMaterial.emissiveTexture = glTFTexture;
+                                if (babylonStandardMaterial.bumpTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                                    var glTFTexture = this.exportTexture(babylonStandardMaterial.bumpTexture);
+                                    if (glTFTexture) {
+                                        glTFMaterial.normalTexture = glTFTexture;
+                                    }
                                 }
+                                if (babylonStandardMaterial.emissiveTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                                    var glTFEmissiveTexture = this.exportTexture(babylonStandardMaterial.emissiveTexture);
+                                    if (glTFEmissiveTexture) {
+                                        glTFMaterial.emissiveTexture = glTFEmissiveTexture;
+                                    }
+                                    glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
+                                }
+                                if (babylonStandardMaterial.ambientTexture && bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                                    var glTFOcclusionTexture = this.exportTexture(babylonStandardMaterial.ambientTexture);
+                                    if (glTFOcclusionTexture) {
+                                        glTFMaterial.occlusionTexture = glTFOcclusionTexture;
+                                    }
+                                }
+                                if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
+                                    if (babylonStandardMaterial.alphaMode === BABYLON.Engine.ALPHA_COMBINE) {
+                                        glTFMaterial.alphaMode = "BLEND" /* BLEND */;
+                                    }
+                                    else {
+                                        console.warn("glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
+                                    }
+                                }
+                                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
+                                this.materials.push(glTFMaterial);
+                                meshPrimitive.material = this.materials.length - 1;
                             }
-                            if (!babylonPBRMaterial.emissiveColor.equals(new BABYLON.Color3(0.0, 0.0, 0.0))) {
-                                glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
-                            }
-                            if (babylonPBRMaterial.transparencyMode) {
-                                var alphaMode = GLTF2._GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
-                                if (alphaMode !== "OPAQUE" /* OPAQUE */) {
-                                    glTFMaterial.alphaMode = alphaMode;
-                                    if (alphaMode === "BLEND" /* BLEND */) {
-                                        glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
+                            else if (bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial) {
+                                var babylonPBRMaterial = bufferMesh.material;
+                                var glTFPbrMetallicRoughness = {};
+                                if (babylonPBRMaterial.baseColor) {
+                                    glTFPbrMetallicRoughness.baseColorFactor = [
+                                        babylonPBRMaterial.baseColor.r,
+                                        babylonPBRMaterial.baseColor.g,
+                                        babylonPBRMaterial.baseColor.b,
+                                        babylonPBRMaterial.alpha
+                                    ];
+                                }
+                                if (babylonPBRMaterial.baseTexture !== undefined) {
+                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.baseTexture);
+                                    if (glTFTexture !== null) {
+                                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                                     }
+                                    glTFPbrMetallicRoughness.baseColorTexture;
                                 }
+                                if (babylonPBRMaterial.metallic !== undefined) {
+                                    glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
+                                }
+                                if (babylonPBRMaterial.roughness !== undefined) {
+                                    glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
+                                }
+                                var glTFMaterial = {
+                                    name: babylonPBRMaterial.name
+                                };
+                                if (babylonPBRMaterial.doubleSided) {
+                                    glTFMaterial.doubleSided = babylonPBRMaterial.doubleSided;
+                                }
+                                if (babylonPBRMaterial.normalTexture) {
+                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.normalTexture);
+                                    if (glTFTexture) {
+                                        glTFMaterial.normalTexture = glTFTexture;
+                                    }
+                                }
+                                if (babylonPBRMaterial.occlusionTexture) {
+                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.occlusionTexture);
+                                    if (glTFTexture) {
+                                        glTFMaterial.occlusionTexture = glTFTexture;
+                                        if (babylonPBRMaterial.occlusionStrength !== undefined) {
+                                            glTFMaterial.occlusionTexture.strength = babylonPBRMaterial.occlusionStrength;
+                                        }
+                                    }
+                                }
+                                if (babylonPBRMaterial.emissiveTexture) {
+                                    var glTFTexture = this.exportTexture(babylonPBRMaterial.emissiveTexture);
+                                    if (glTFTexture !== null) {
+                                        glTFMaterial.emissiveTexture = glTFTexture;
+                                    }
+                                }
+                                if (!babylonPBRMaterial.emissiveColor.equals(new BABYLON.Color3(0.0, 0.0, 0.0))) {
+                                    glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                                }
+                                if (babylonPBRMaterial.transparencyMode) {
+                                    var alphaMode = GLTF2._GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
+                                    if (alphaMode !== "OPAQUE" /* OPAQUE */) {
+                                        glTFMaterial.alphaMode = alphaMode;
+                                        if (alphaMode === "BLEND" /* BLEND */) {
+                                            glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
+                                        }
+                                    }
+                                }
+                                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
+                                this.materials.push(glTFMaterial);
+                                meshPrimitive.material = this.materials.length - 1;
+                            }
+                            else {
+                                console.warn("Material type is not yet implemented in glTF serializer: " + bufferMesh.material.name);
                             }
-                            glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
-                            // TODO: Handle Textures
-                            this.materials.push(glTFMaterial);
-                            meshPrimitive.material = this.materials.length - 1;
                         }
+                        mesh.primitives.push(meshPrimitive);
                     }
-                    mesh.primitives.push(meshPrimitive);
                 }
                 return byteOffset;
             };
             /**
              * Creates a glTF scene based on the array of meshes.
              * Returns the the total byte offset.
-             * @param gltf
-             * @param byteOffset
-             * @param buffer
-             * @param dataBuffer
+             * @param babylonScene - Babylon scene to get the mesh data from.
+             * @param byteOffset - Offset to start from in bytes.
+             * @param dataBuffer - Buffer to write geometry data to.
              * @returns bytelength + byteoffset
              */
             _Exporter.prototype.createScene = function (babylonScene, byteOffset, dataBuffer) {
@@ -1140,7 +1222,7 @@ var BABYLON;
                 var diffuse = babylonSpecularGlossiness.diffuse;
                 var opacity = babylonSpecularGlossiness.opacity;
                 var specular = babylonSpecularGlossiness.specular;
-                var glossiness = babylonSpecularGlossiness.glossiness;
+                var glossiness = BABYLON.Scalar.Clamp(babylonSpecularGlossiness.glossiness);
                 var oneMinusSpecularStrength = 1 - Math.max(specular.r, Math.max(specular.g, specular.b));
                 var diffusePerceivedBrightness = _GLTFMaterial.PerceivedBrightness(diffuse);
                 var specularPerceivedBrightness = _GLTFMaterial.PerceivedBrightness(specular);

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


+ 57 - 49
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -121,12 +121,14 @@ declare module BABYLON.GLTF2 {
         constructor(babylonScene: Scene, options?: IExporterOptions);
         /**
          * Creates a buffer view based on teh supplied arguments
-         * @param {number} bufferIndex - index value of the specified buffer
-         * @param {number} byteOffset - byte offset value
-         * @param {number} byteLength - byte length of the bufferView
+         * @param bufferIndex - index value of the specified buffer
+         * @param byteOffset - byte offset value
+         * @param byteLength - byte length of the bufferView
+         * @param byteStride - byte distance between conequential elements.
+         * @param name - name of the buffer view
          * @returns - bufferView for glTF
          */
-        private createBufferView(bufferIndex, byteOffset, byteLength, name?);
+        private createBufferView(bufferIndex, byteOffset, byteLength, byteStride?, name?);
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex
@@ -138,43 +140,41 @@ declare module BABYLON.GLTF2 {
          * @param max
          * @returns - accessor for glTF
          */
-        private createAccessor(bufferviewIndex, name, type, componentType, count, min?, max?);
+        private createAccessor(bufferviewIndex, name, type, componentType, count, byteOffset?, min?, max?);
         /**
          * Calculates the minimum and maximum values of an array of floats, based on stride
-         * @param buff
-         * @param vertexStart
-         * @param vertexCount
-         * @param arrayOffset
-         * @param stride
-         * @returns - min number array and max number array
+         * @param buff - Data to check for min and max values.
+         * @param vertexStart - Start offset to calculate min and max values.
+         * @param vertexCount - Number of vertices to check for min and max values.
+         * @param stride - Offset between consecutive attributes.
+         * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+         * @returns - min number array and max number array.
          */
-        private calculateMinMax(buff, vertexStart, vertexCount, arrayOffset, stride);
+        private calculateMinMax(buff, vertexStart, vertexCount, stride, useRightHandedSystem);
         /**
-         * Write mesh attribute data to buffer.
+         * Writes mesh attribute data to a data buffer.
          * Returns the bytelength of the data.
-         * @param vertexBufferType
-         * @param submesh
-         * @param meshAttributeArray
-         * @param strideSize
-         * @param byteOffset
-         * @param dataBuffer
-         * @param useRightHandedSystem
-         * @returns - byte length
+         * @param vertexBufferKind - Indicates what kind of vertex data is being passed in.
+         * @param meshAttributeArray - Array containing the attribute data.
+         * @param strideSize - Represents the offset between consecutive attributes
+         * @param byteOffset - The offset to start counting bytes from.
+         * @param dataBuffer - The buffer to write the binary data to.
+         * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+         * @returns - Byte length of the attribute data.
          */
-        private writeAttributeData(vertexBufferType, submesh, meshAttributeArray, strideSize, byteOffset, dataBuffer, useRightHandedSystem);
+        private writeAttributeData(vertexBufferKind, meshAttributeArray, strideSize, vertexBufferOffset, byteOffset, dataBuffer, useRightHandedSystem);
         /**
          * Generates glTF json data
-         * @param glb
-         * @param glTFPrefix
-         * @param prettyPrint
+         * @param shouldUseGlb - Indicates whether the json should be written for a glb file.
+         * @param glTFPrefix - Text to use when prefixing a glTF file.
+         * @param prettyPrint - Indicates whether the json file should be pretty printed (true) or not (false).
          * @returns - json data as string
          */
-        private generateJSON(glb, glTFPrefix?, prettyPrint?);
+        private generateJSON(shouldUseGlb, glTFPrefix?, prettyPrint?);
         /**
          * Generates data for .gltf and .bin files based on the glTF prefix string
-         * @param glTFPrefix
-         * @returns - object with glTF json tex filename
-         * and binary file name as keys and their data as values
+         * @param glTFPrefix - Text to use when prefixing a glTF file.
+         * @returns - GLTFData with glTF file data.
          */
         _generateGLTF(glTFPrefix: string): _GLTFData;
         /**
@@ -183,7 +183,7 @@ declare module BABYLON.GLTF2 {
          */
         private generateBinary();
         /**
-         * Pads the number to a power of 4
+         * Pads the number to a multiple of 4
          * @param num - number to pad
          * @returns - padded number
          */
@@ -191,45 +191,53 @@ declare module BABYLON.GLTF2 {
         /**
          * Generates a glb file from the json and binary data.
          * Returns an object with the glb file name as the key and data as the value.
-         * @param jsonText
-         * @param binaryBuffer
          * @param glTFPrefix
          * @returns - object with glb filename as key and data as value
          */
         _generateGLB(glTFPrefix: string): _GLTFData;
         /**
          * Sets the TRS for each node
-         * @param node
-         * @param babylonMesh
-         * @param useRightHandedSystem
+         * @param node - glTF Node for storing the transformation data.
+         * @param babylonMesh - Babylon mesh used as the source for the transformation data.
+         * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
          */
         private setNodeTransformation(node, babylonMesh, useRightHandedSystem);
         /**
          *
-         * @param babylonTexture
-         * @return - glTF texture, or null if the texture format is not supported
+         * @param babylonTexture - Babylon texture to extract.
+         * @param mimeType - Mime Type of the babylonTexture.
+         * @return - glTF texture, or null if the texture format is not supported.
          */
         private exportTexture(babylonTexture, mimeType?);
         /**
+         * Creates a bufferview based on the vertices type for the Babylon mesh
+         * @param kind - Indicates the type of vertices data.
+         * @param babylonMesh - The Babylon mesh to get the vertices data from.
+         * @param byteOffset - The offset from the buffer to start indexing from.
+         * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+         * @param dataBuffer - The buffer to write the bufferview data to.
+         * @returns bytelength of the bufferview data.
+         */
+        private createBufferViewKind(kind, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+        /**
          * Sets data for the primitive attributes of each submesh
-         * @param mesh
-         * @param babylonMesh
-         * @param byteOffset
-         * @param useRightHandedSystem
-         * @param dataBuffer
-         * @returns - bytelength of the primitive attributes plus the passed in byteOffset
+         * @param mesh - glTF Mesh object to store the primitive attribute information.
+         * @param babylonMesh - Babylon mesh to get the primitive attribute data from.
+         * @param byteOffset - The offset in bytes of the buffer data.
+         * @param useRightHandedSystem - Indicates whether the data should be modified for a right or left handed coordinate system.
+         * @param dataBuffer - Buffer to write the attribute data to.
+         * @returns - bytelength of the primitive attributes plus the passed in byteOffset.
          */
-        private setPrimitiveAttributes(mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer?);
+        private setPrimitiveAttributes(mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
         /**
          * Creates a glTF scene based on the array of meshes.
          * Returns the the total byte offset.
-         * @param gltf
-         * @param byteOffset
-         * @param buffer
-         * @param dataBuffer
+         * @param babylonScene - Babylon scene to get the mesh data from.
+         * @param byteOffset - Offset to start from in bytes.
+         * @param dataBuffer - Buffer to write geometry data to.
          * @returns bytelength + byteoffset
          */
-        private createScene(babylonScene, byteOffset, dataBuffer?);
+        private createScene(babylonScene, byteOffset, dataBuffer);
     }
 }
 

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "3.2.0-alpha4",
+    "version": "3.2.0-alpha6",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 2 - 1394
dist/preview release/typedocValidationBaseline.json


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 33 - 33
dist/preview release/viewer/babylon.viewer.js


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

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

+ 0 - 1
dist/preview release/what's new.md

@@ -49,7 +49,6 @@
 - (Viewer) It is now possible to update parts of the configuration without rcreating the objects. ([RaananW](https://github.com/RaananW))
 - GUI.Line can have its world position set from one end or the other ([SvenFrankson](https://github.com/SvenFrankson))
 
-
 ## Bug fixes
 - `setPivotMatrix` ws not setting pivot correctly. This is now fixed. We also introduced a new `setPreTransformMatrix` to reproduce the sometimes needed behavior of the previous `setPivotMatrix` function ([deltakosh](https://github.com/deltakosh))
 - Texture extension detection in `Engine.CreateTexture` ([sebavan](https://github.com/sebavan))

+ 1 - 1
package.json

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

+ 111 - 2
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -1,4 +1,7 @@
 module BABYLON {
+    /**
+     * Manages the defines for the PBR Material.
+     */
     class PBRMaterialDefines extends MaterialDefines implements IImageProcessingConfigurationDefines {
         public PBR = true;
 
@@ -125,11 +128,17 @@
 
         public FORCENORMALFORWARD = false;
 
+        /**
+         * Initializes the PBR Material defines.
+         */
         constructor() {
             super();
             this.rebuild();
         }
 
+        /**
+         * Resets the PBR Material defines.
+         */
         public reset(): void {
             super.reset();
             this.ALPHATESTVALUE = 0.5;
@@ -170,6 +179,9 @@
          */
         protected _specularIntensity: number = 1.0;
 
+        /**
+         * This stores the direct, emissive, environment, and specular light intensities into a Vector4.
+         */
         private _lightingInfos: Vector4 = new Vector4(this._directIntensity, this._emissiveIntensity, this._environmentIntensity, this._specularIntensity);
 
         /**
@@ -192,12 +204,24 @@
          */
         protected _ambientTextureStrength: number = 1.0;
 
+        /**
+         * Stores the alpha values in a texture.
+         */
         protected _opacityTexture: BaseTexture;
 
+        /**
+         * Stores the reflection values in a texture.
+         */
         protected _reflectionTexture: BaseTexture;
 
+        /**
+         * Stores the refraction values in a texture.
+         */
         protected _refractionTexture: BaseTexture;
 
+        /**
+         * Stores the emissive values in a texture.
+         */
         protected _emissiveTexture: BaseTexture;
 
         /**
@@ -228,10 +252,19 @@
          */
         protected _microSurfaceTexture: BaseTexture;
 
+        /**
+         * Stores surface normal data used to displace a mesh in a texture.
+         */
         protected _bumpTexture: BaseTexture;
 
+        /**
+         * Stores the pre-calculated light information of a mesh in a texture.
+         */
         protected _lightmapTexture: BaseTexture;
 
+        /**
+         * The color of a material in ambient lighting.
+         */
         protected _ambientColor = new Color3(0, 0, 0);
 
         /**
@@ -244,8 +277,14 @@
          */
         protected _reflectivityColor = new Color3(1, 1, 1);
 
+        /**
+         * The color applied when light is reflected from a material.
+         */
         protected _reflectionColor = new Color3(1, 1, 1);
 
+        /**
+         * The color applied when light is emitted from a material.
+         */
         protected _emissiveColor = new Color3(0, 0, 0);
 
         /**
@@ -269,6 +308,9 @@
          */
         protected _linkRefractionWithTransparency = false;
 
+        /**
+         * Specifies that the material will use the light map as a show map.
+         */
         protected _useLightmapAsShadowmap = false;
 
         /**
@@ -471,8 +513,19 @@
             });
         }
 
+        /**
+         * Stores the available render targets.
+         */
         private _renderTargets = new SmartArray<RenderTargetTexture>(16);
+
+        /**
+         * Sets the global ambient color for the material used in lighting calculations.
+         */
         private _globalAmbientColor = new Color3(0, 0, 0);
+
+        /**
+         * Enables the use of logarithmic depth buffers, which is good for wide depth buffers.
+         */
         private _useLogarithmicDepth: boolean;
 
         /**
@@ -504,15 +557,24 @@
             this._environmentBRDFTexture = TextureTools.GetEnvironmentBRDFTexture(scene);
         }
 
+        /**
+         * Gets the name of the material class.
+         */
         public getClassName(): string {
             return "PBRBaseMaterial";
         }
 
+        /**
+         * Enabled the use of logarithmic depth buffers, which is good for wide depth buffers.
+         */
         @serialize()
         public get useLogarithmicDepth(): boolean {
             return this._useLogarithmicDepth;
         }
 
+        /**
+         * Enabled the use of logarithmic depth buffers, which is good for wide depth buffers.
+         */
         public set useLogarithmicDepth(value: boolean) {
             this._useLogarithmicDepth = value && this.getScene().getEngine().getCaps().fragmentDepthSupported;
         }
@@ -561,7 +623,8 @@
         }
 
         /**
-         * Specifies whether or not this material should be rendered in alpha blend mode for the given mesh.
+         * Specifies if the mesh will require alpha blending.
+         * @param mesh - BJS mesh.
          */
         public needAlphaBlendingForMesh(mesh: AbstractMesh): boolean {
             if (this._disableAlphaBlending) {
@@ -593,12 +656,25 @@
             return this._albedoTexture != null && this._albedoTexture.hasAlpha && this._useAlphaFromAlbedoTexture && this._transparencyMode !== PBRMaterial.PBRMATERIAL_OPAQUE;
         }
 
+        /**
+         * Gets the texture used for the alpha test.
+         */
         public getAlphaTestTexture(): BaseTexture {
             return this._albedoTexture;
         }
 
+        /**
+         * Stores the reflectivity values based on metallic roughness workflow.
+         */
         private static _scaledReflectivity = new Color3();
 
+        /**
+         * Specifies that the submesh is ready to be used.
+         * @param mesh - BJS mesh.
+         * @param subMesh - A submesh of the BJS mesh.  Used to check if it is ready. 
+         * @param useInstances - Specifies that instances should be used.
+         * @returns - boolean indicating that the submesh is ready or not.
+         */
         public isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean {
             if (subMesh.effect && this.isFrozen) {
                 if (this._wasPreviouslyReady) {
@@ -1121,6 +1197,9 @@
             return true;
         }
 
+        /**
+         * Initializes the uniform buffer layout for the shader.
+         */
         public buildUniformLayout(): void {
             // Order is important !
             this._uniformBuffer.addUniform("vAlbedoInfos", 2);
@@ -1158,7 +1237,9 @@
             this._uniformBuffer.create();
         }
 
-
+        /**
+         * Unbinds the textures.
+         */
         public unbind(): void {
             if (this._reflectionTexture && this._reflectionTexture.isRenderTarget) {
                 this._uniformBuffer.setTexture("reflectionSampler", null);
@@ -1171,10 +1252,20 @@
             super.unbind();
         }
 
+        /**
+         * Binds to the world matrix.
+         * @param world - The world matrix.
+         */
         public bindOnlyWorldMatrix(world: Matrix): void {
             this._activeEffect.setMatrix("world", world);
         }
 
+        /**
+         * Binds the submesh data.
+         * @param world - The world matrix.
+         * @param mesh - The BJS mesh.
+         * @param subMesh - A submesh of the BJS mesh.
+         */
         public bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void {
             var scene = this.getScene();
 
@@ -1449,6 +1540,10 @@
             this._afterBind(mesh);
         }
 
+        /**
+         * Returns the animatable textures.
+         * @returns - Array of animatable textures.
+         */
         public getAnimatables(): IAnimatable[] {
             var results = [];
 
@@ -1494,6 +1589,10 @@
             return results;
         }
 
+        /**
+         * Returns the texture used for reflections.
+         * @returns - Reflection texture if present.  Otherwise, returns the environment texture.
+         */
         private _getReflectionTexture(): BaseTexture {
             if (this._reflectionTexture) {
                 return this._reflectionTexture;
@@ -1502,6 +1601,11 @@
             return this.getScene().environmentTexture;
         }
 
+        /**
+         * Returns the texture used for refraction or null if none is used.
+         * @returns - Refection texture if present.  If no refraction texture and refraction 
+         * is linked with transparency, returns environment texture.  Otherwise, returns null.
+         */
         private _getRefractionTexture(): Nullable<BaseTexture> {
             if (this._refractionTexture) {
                 return this._refractionTexture;
@@ -1514,6 +1618,11 @@
             return null;
         }
 
+        /**
+         * Disposes the resources of the material.
+         * @param forceDisposeEffect - Forces the disposal of effects.
+         * @param forceDisposeTextures - Forces the disposal of all textures.
+         */
         public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
             if (forceDisposeTextures) {
                 if (this._albedoTexture) {

+ 67 - 0
src/Materials/PBR/babylon.pbrMaterial.ts

@@ -15,7 +15,11 @@
             return this._PBRMATERIAL_OPAQUE;
         }
 
+        /**
+         * Alpha Test mode, pixel are discarded below a certain threshold defined by the alpha cutoff value.
+         */
         private static _PBRMATERIAL_ALPHATEST = 1;
+
         /**
          * PBRMaterialTransparencyMode: Alpha Test mode, pixel are discarded below a certain threshold defined by the alpha cutoff value.
          */
@@ -23,7 +27,11 @@
             return this._PBRMATERIAL_ALPHATEST;
         }
 
+        /**
+         * Represents the value for Alpha Blend.  Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
+         */
         private static _PBRMATERIAL_ALPHABLEND = 2;
+
         /**
          * PBRMaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
          */
@@ -31,7 +39,12 @@
             return this._PBRMATERIAL_ALPHABLEND;
         }
 
+        /**
+         * Represents the value for Alpha Test and Blend.  Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
+         * They are also discarded below the alpha cutoff threshold to improve performances.
+         */
         private static _PBRMATERIAL_ALPHATESTANDBLEND = 3;
+
         /**
          * PBRMaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
          * They are also discarded below the alpha cutoff threshold to improve performances.
@@ -100,14 +113,23 @@
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         public ambientTextureStrength: number = 1.0;
 
+        /**
+         * Stores the alpha values in a texture.
+         */
         @serializeAsTexture()
         @expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty")
         public opacityTexture: BaseTexture;
 
+        /**
+         * Stores the reflection values in a texture.
+         */
         @serializeAsTexture()
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         public reflectionTexture: Nullable<BaseTexture>;
 
+        /**
+         * Stores the emissive values in a texture.
+         */
         @serializeAsTexture()
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         public emissiveTexture: BaseTexture;
@@ -150,18 +172,30 @@
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         public microSurfaceTexture: BaseTexture;
 
+        /**
+         * Stores surface normal data used to displace a mesh in a texture.
+         */
         @serializeAsTexture()
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         public bumpTexture: BaseTexture;
 
+        /**
+         * Stores the pre-calculated light information of a mesh in a texture.
+         */
         @serializeAsTexture()
         @expandToProperty("_markAllSubMeshesAsTexturesDirty", null)
         public lightmapTexture: BaseTexture;
 
+        /**
+         * Stores the refracted light information in a texture.
+         */
         @serializeAsTexture()
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         public refractionTexture: BaseTexture;
 
+        /**
+         * The color of a material in ambient lighting.
+         */
         @serializeAsColor3("ambient")
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         public ambientColor = new Color3(0, 0, 0);
@@ -180,10 +214,16 @@
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         public reflectivityColor = new Color3(1, 1, 1);
 
+        /**
+         * The color reflected from the material.
+         */
         @serializeAsColor3("reflection")
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         public reflectionColor = new Color3(1.0, 1.0, 1.0);
 
+        /**
+         * The color emitted from the material.
+         */
         @serializeAsColor3("emissive")
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         public emissiveColor = new Color3(0, 0, 0);
@@ -560,10 +600,17 @@
             this._environmentBRDFTexture = TextureTools.GetEnvironmentBRDFTexture(scene);
         }
 
+        /**
+         * Returns the name of this material class.
+         */
         public getClassName(): string {
             return "PBRMaterial";
         }
 
+        /**
+         * Returns an array of the actively used textures.
+         * @returns - Array of BaseTextures
+         */
         public getActiveTextures(): BaseTexture[] {
             var activeTextures = super.getActiveTextures();
 
@@ -614,6 +661,11 @@
             return activeTextures;
         }
 
+        /**
+         * Checks to see if a texture is used in the material.
+         * @param texture - Base texture to use.
+         * @returns - Boolean specifying if a texture is used in the material.
+         */
         public hasTexture(texture: BaseTexture): boolean {
             if (super.hasTexture(texture)) {
                 return true;
@@ -662,6 +714,10 @@
             return false;
         }
 
+        /**
+         * Makes a duplicate of the current material.
+         * @param name - name to use for the new material.
+         */
         public clone(name: string): PBRMaterial {
             var clone = SerializationHelper.Clone(() => new PBRMaterial(name, this.getScene()), this);
 
@@ -671,6 +727,10 @@
             return clone;
         }
 
+        /**
+         * Serializes this PBR Material.
+         * @returns - An object with the serialized material.
+         */
         public serialize(): any {
             var serializationObject = SerializationHelper.Serialize(this);
             serializationObject.customType = "BABYLON.PBRMaterial";
@@ -678,6 +738,13 @@
         }
 
         // Statics
+        /**
+         * Parses a PBR Material from a serialized object.
+         * @param source - Serialized object.
+         * @param scene - BJS scene instance.
+         * @param rootUrl - url for the scene object
+         * @returns - PBRMaterial
+         */
         public static Parse(source: any, scene: Scene, rootUrl: string): PBRMaterial {
             return SerializationHelper.Parse(() => new PBRMaterial(source.name, scene), source, scene, rootUrl);
         }

+ 9 - 0
src/Materials/PBR/babylon.pbrMetallicRoughnessMaterial.ts

@@ -87,6 +87,11 @@
             return activeTextures;
         }
 
+        /**
+         * Checks to see if a texture is used in the material.
+         * @param texture - Base texture to use.
+         * @returns - Boolean specifying if a texture is used in the material.
+         */
         public hasTexture(texture: BaseTexture): boolean {
             if (super.hasTexture(texture)) {
                 return true;
@@ -103,6 +108,10 @@
             return false;
         }
 
+        /**
+         * Makes a duplicate of the current material.
+         * @param name - name to use for the new material.
+         */
         public clone(name: string): PBRMetallicRoughnessMaterial {
             var clone = SerializationHelper.Clone(() => new PBRMetallicRoughnessMaterial(name, this.getScene()), this);
 

+ 9 - 0
src/Materials/PBR/babylon.pbrSpecularGlossinessMaterial.ts

@@ -78,6 +78,11 @@
             return activeTextures;
         }
 
+        /**
+         * Checks to see if a texture is used in the material.
+         * @param texture - Base texture to use.
+         * @returns - Boolean specifying if a texture is used in the material.
+         */
         public hasTexture(texture: BaseTexture): boolean {
             if (super.hasTexture(texture)) {
                 return true;
@@ -94,6 +99,10 @@
             return false;
         }
 
+        /**
+         * Makes a duplicate of the current material.
+         * @param name - name to use for the new material.
+         */
         public clone(name: string): PBRSpecularGlossinessMaterial {
             var clone = SerializationHelper.Clone(() => new PBRSpecularGlossinessMaterial(name, this.getScene()), this);
 

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

@@ -125,7 +125,7 @@
                 this._blurX.autoClear = false;
 
                 if (this._blurRatio === 1 && this.samples < 2 && this._texture) {
-                    this._blurX.outputTexture = this._texture;
+                    this._blurX.inputTexture = this._texture;
                 } else {
                     this._blurX.alwaysForcePOT = true;
                 }

+ 396 - 4
src/Materials/babylon.effect.ts

@@ -1,4 +1,8 @@
 module BABYLON {
+    /**
+	 * EffectFallbacks can be used to add fallbacks (properties to disable) to certain properties when desired to improve performance.
+     * (Eg. Start at high quality with reflection and fog, if fps is low, remove reflection, if still low remove fog)
+     */
     export class EffectFallbacks {
         private _defines: { [key: string]: Array<String> } = {};
 
@@ -7,10 +11,18 @@
 
         private _mesh: Nullable<AbstractMesh>;
 
+        /**
+         * Removes the fallback from the bound mesh.
+         */
         public unBindMesh() {
             this._mesh = null;
         }
 
+        /**
+         * Adds a fallback on the specified property.
+         * @param rank The rank of the fallback (Lower ranks will be fallbacked to first)
+         * @param define The name of the define in the shader
+         */
         public addFallback(rank: number, define: string): void {
             if (!this._defines[rank]) {
                 if (rank < this._currentRank) {
@@ -27,6 +39,11 @@
             this._defines[rank].push(define);
         }
 
+        /**
+         * Sets the mesh to use CPU skinning when needing to fallback.
+         * @param rank The rank of the fallback (Lower ranks will be fallbacked to first)
+         * @param mesh The mesh to use the fallbacks.
+         */
         public addCPUSkinningFallback(rank: number, mesh: AbstractMesh) {
             this._mesh = mesh;
 
@@ -38,10 +55,18 @@
             }
         }
 
+        /**
+         * Checks to see if more fallbacks are still availible.
+         */
         public get isMoreFallbacks(): boolean {
             return this._currentRank <= this._maxRank;
         }
 
+        /**
+         * Removes the defines that shoould be removed when falling back.
+         * @param currentDefines The current define statements for the shader.
+         * @returns The resulting defines with defines of the current rank removed.
+         */
         public reduce(currentDefines: string): string {
             // First we try to switch to CPU skinning
             if (this._mesh && this._mesh.computeBonesUsingShaders && this._mesh.numBoneInfluencers > 0) {
@@ -73,29 +98,95 @@
         }
     }
 
+    /**
+	 * Options to be used when creating an effect.
+     */
     export class EffectCreationOptions {
+        /**
+         * Atrributes that will be used in the shader.
+         */
         public attributes: string[];
+        /**
+         * Uniform varible names that will be set in the shader.
+         */
         public uniformsNames: string[];
+        /**
+         * Uniform buffer varible names that will be set in the shader.
+         */
         public uniformBuffersNames: string[];
+        /**
+         * Sampler texture variable names that will be set in the shader.
+         */
         public samplers: string[];
+        /**
+         * Define statements that will be set in the shader.
+         */
         public defines: any;
+        /**
+         * Possible fallbacks for this effect to improve performance when needed.
+         */
         public fallbacks: Nullable<EffectFallbacks>;
+        /**
+         * Callback that will be called when the shader is compiled.
+         */
         public onCompiled: Nullable<(effect: Effect) => void>;
+        /**
+         * Callback that will be called if an error occurs during shader compilation.
+         */
         public onError: Nullable<(effect: Effect, errors: string) => void>;
+        /**
+         * Parameters to be used with Babylons include syntax to iterate over an array (eg. {lights: 10})
+         */
         public indexParameters: any;
+        /**
+         * Max number of lights that can be used in the shader.
+         */
         public maxSimultaneousLights: number;
+        /**
+         * See https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/transformFeedbackVaryings
+         */
         public transformFeedbackVaryings: Nullable<string[]>;
     }
 
+    /**
+     * Effect containing vertex and fragment shader that can be executed on an object.
+     */
     export class Effect {
+        /**
+         * Name of the effect.
+         */
         public name: any;
+        /**
+         * String container all the define statements that should be set on the shader.
+         */
         public defines: string;
+        /**
+         * Callback that will be called when the shader is compiled.
+         */
         public onCompiled: Nullable<(effect: Effect) => void>;
+        /**
+         * Callback that will be called if an error occurs during shader compilation.
+         */
         public onError: Nullable<(effect: Effect, errors: string) => void>;
+        /**
+         * Callback that will be called when effect is bound.
+         */
         public onBind: Nullable<(effect: Effect) => void>;
+        /**
+         * Unique ID of the effect.
+         */
         public uniqueId = 0;
+        /**
+         * Observable that will be called when the shader is compiled.
+         */
         public onCompileObservable = new Observable<Effect>();
+        /**
+         * Observable that will be called if an error occurs during shader compilation.
+         */
         public onErrorObservable = new Observable<Effect>();
+        /**
+         * Observable that will be called when effect is bound.
+         */
         public onBindObservable = new Observable<Effect>();
 
         private static _uniqueIdSeed = 0;
@@ -108,6 +199,9 @@
         private _attributesNames: string[];
         private _attributes: number[];
         private _uniforms: Nullable<WebGLUniformLocation>[];
+        /**
+         * Key for the effect.
+         */
         public _key: string;
         private _indexParameters: any;
         private _fallbacks: Nullable<EffectFallbacks>;
@@ -117,10 +211,27 @@
         private _fragmentSourceCodeOverride: string;
         private _transformFeedbackVaryings: Nullable<string[]>;
 
+        /**
+         * Compiled shader to webGL program.
+         */
         public _program: WebGLProgram;
         private _valueCache: { [key: string]: any };
         private static _baseCache: { [key: number]: WebGLBuffer } = {};
 
+        /**
+         * Instantiates an effect.
+         * An effect can be used to create/manage/execute vertex and fragment shaders.
+         * @param baseName Name of the effect.
+         * @param attributesNamesOrOptions List of attribute names that will be passed to the shader or set of all options to create the effect.
+         * @param uniformsNamesOrEngine List of uniform variable names that will be passed to the shader or the engine that will be used to render effect.
+         * @param samplers List of sampler variables that will be passed to the shader.
+         * @param engine Engine to be used to render the effect
+         * @param defines Define statements to be added to the shader.
+         * @param fallbacks Possible fallbacks for this effect to improve performance when needed.
+         * @param onCompiled Callback that will be called when the shader is compiled.
+         * @param onError Callback that will be called if an error occurs during shader compilation.
+         * @param indexParameters Parameters to be used with Babylons include syntax to iterate over an array (eg. {lights: 10})
+         */
         constructor(baseName: any, attributesNamesOrOptions: string[] | EffectCreationOptions, uniformsNamesOrEngine: string[] | Engine, samplers: Nullable<string[]> = null, engine?: Engine, defines: Nullable<string> = null,
             fallbacks: Nullable<EffectFallbacks> = null, onCompiled: Nullable<(effect: Effect) => void> = null, onError: Nullable<(effect: Effect, errors: string) => void> = null, indexParameters?: any) {
             this.name = baseName;
@@ -212,58 +323,110 @@
             });
         }
 
+        /**
+         * Unique key for this effect
+         */
         public get key(): string {
             return this._key;
         }
 
-        // Properties
+        /**
+         * If the effect has been compiled and prepared.
+         * @returns if the effect is compiled and prepared.
+         */
         public isReady(): boolean {
             return this._isReady;
         }
 
+        /**
+         * The engine the effect was initialized with.
+         * @returns the engine.
+         */
         public getEngine(): Engine {
             return this._engine;
         }
 
+        /**
+         * The compiled webGL program for the effect
+         * @returns the webGL program.
+         */
         public getProgram(): WebGLProgram {
             return this._program;
         }
-
+        /**
+         * The set of names of attribute variables for the shader.
+         * @returns An array of attribute names.
+         */
         public getAttributesNames(): string[] {
             return this._attributesNames;
         }
 
+        /**
+         * Returns the attribute at the given index.
+         * @param index The index of the attribute.
+         * @returns The location of the attribute.
+         */
         public getAttributeLocation(index: number): number {
             return this._attributes[index];
         }
 
+        /**
+         * Returns the attribute based on the name of the variable.
+         * @param name of the attribute to look up.
+         * @returns the attribute location.
+         */
         public getAttributeLocationByName(name: string): number {
             var index = this._attributesNames.indexOf(name);
 
             return this._attributes[index];
         }
 
+        /**
+         * The number of attributes.
+         * @returns the numnber of attributes.
+         */
         public getAttributesCount(): number {
             return this._attributes.length;
         }
 
+        /**
+         * Gets the index of a uniform variable.
+         * @param uniformName of the uniform to look up.
+         * @returns the index.
+         */
         public getUniformIndex(uniformName: string): number {
             return this._uniformsNames.indexOf(uniformName);
         }
 
+        /**
+         * Returns the attribute based on the name of the variable.
+         * @param uniformName of the uniform to look up.
+         * @returns the location of the uniform.
+         */
         public getUniform(uniformName: string): Nullable<WebGLUniformLocation> {
             return this._uniforms[this._uniformsNames.indexOf(uniformName)];
         }
 
+        /**
+         * Returns an array of sampler variable names
+         * @returns The array of sampler variable neames.
+         */
         public getSamplers(): string[] {
             return this._samplers;
         }
 
+        /**
+         * The error from the last compilation.
+         * @returns the error string.
+         */
         public getCompilationError(): string {
             return this._compilationError;
         }
 
-        // Methods
+        /**
+         * Adds a callback to the onCompiled observable and call the callback imediatly if already ready.
+         * @param func The callback to be used.
+         */
         public executeWhenCompiled(func: (effect: Effect) => void): void {
             if (this.isReady()) {
                 func(this);
@@ -521,6 +684,13 @@
             return source;
         }
 
+        /**
+         * Recompiles the webGL program
+         * @param vertexSourceCode The source code for the vertex shader.
+         * @param fragmentSourceCode The source code for the fragment shader.
+         * @param onCompiled Callback called when completed.
+         * @param onError Callback called on error.
+         */
         public _rebuildProgram(vertexSourceCode: string, fragmentSourceCode: string, onCompiled: (program: WebGLProgram) => void, onError: (message: string) => void) {
             this._isReady = false;
 
@@ -545,11 +715,19 @@
             this._prepareEffect();
         }
 
+        /**
+         * Gets the uniform locations of the the specified variable names
+         * @param names THe names of the variables to lookup.
+         * @returns Array of locations in the same order as variable names.
+         */
         public getSpecificUniformLocations(names: string[]): Nullable<WebGLUniformLocation>[] {
             let engine = this._engine;
             return engine.getUniforms(this._program, names);
         }
 
+        /**
+         * Prepares the effect
+         */
         public _prepareEffect() {
             let attributesNames = this._attributesNames;
             let defines = this.defines;
@@ -648,18 +826,35 @@
             }
         }
 
+        /**
+         * Checks if the effect is supported. (Must be called after compilation)
+         */
         public get isSupported(): boolean {
             return this._compilationError === "";
         }
 
+        /**
+         * Binds a texture to the engine to be used as output of the shader.
+         * @param channel Name of the output variable.
+         * @param texture Texture to bind.
+         */
         public _bindTexture(channel: string, texture: InternalTexture): void {
             this._engine._bindTexture(this._samplers.indexOf(channel), texture);
         }
 
+        /**
+         * Sets a texture on the engine to be used in the shader.
+         * @param channel Name of the sampler variable.
+         * @param texture Texture to set.
+         */
         public setTexture(channel: string, texture: Nullable<BaseTexture>): void {
             this._engine.setTexture(this._samplers.indexOf(channel), this.getUniform(channel), texture);
         }
-
+        /**
+         * Sets an array of textures on the engine to be used in the shader.
+         * @param channel Name of the variable.
+         * @param textures Textures to set.
+         */
         public setTextureArray(channel: string, textures: BaseTexture[]): void {
             if (this._samplers.indexOf(channel + "Ex") === -1) {
                 var initialPos = this._samplers.indexOf(channel);
@@ -671,10 +866,16 @@
             this._engine.setTextureArray(this._samplers.indexOf(channel), this.getUniform(channel), textures);
         }
 
+        /**
+         * Sets a texture to be the input of the specified post process. (To use the output, pass in the next post process in the pipeline)
+         * @param channel Name of the sampler variable.
+         * @param postProcess Post process to get the input texture from.
+         */
         public setTextureFromPostProcess(channel: string, postProcess: Nullable<PostProcess>): void {
             this._engine.setTextureFromPostProcess(this._samplers.indexOf(channel), postProcess);
         }
 
+        /** @ignore */
         public _cacheMatrix(uniformName: string, matrix: Matrix): boolean {
             var cache = this._valueCache[uniformName];
             var flag = matrix.updateFlag;
@@ -687,6 +888,7 @@
             return true;
         }
 
+        /** @ignore */
         public _cacheFloat2(uniformName: string, x: number, y: number): boolean {
             var cache = this._valueCache[uniformName];
             if (!cache) {
@@ -708,6 +910,7 @@
             return changed;
         }
 
+        /** @ignore */
         public _cacheFloat3(uniformName: string, x: number, y: number, z: number): boolean {
             var cache = this._valueCache[uniformName];
             if (!cache) {
@@ -733,6 +936,7 @@
             return changed;
         }
 
+        /** @ignore */
         public _cacheFloat4(uniformName: string, x: number, y: number, z: number, w: number): boolean {
             var cache = this._valueCache[uniformName];
             if (!cache) {
@@ -762,6 +966,11 @@
             return changed;
         }
 
+        /**
+         * Binds a buffer to a uniform.
+         * @param buffer Buffer to bind.
+         * @param name Name of the uniform variable to bind to.
+         */
         public bindUniformBuffer(buffer: WebGLBuffer, name: string): void {
             let bufferName = this._uniformBuffersNames[name];
             if (bufferName === undefined || Effect._baseCache[bufferName] === buffer) {
@@ -771,10 +980,21 @@
             this._engine.bindUniformBufferBase(buffer, bufferName);
         }
 
+        /**
+         * Binds block to a uniform.
+         * @param blockName Name of the block to bind.
+         * @param index Index to bind.
+         */
         public bindUniformBlock(blockName: string, index: number): void {
             this._engine.bindUniformBlock(this._program, blockName, index);
         }
 
+        /**
+         * Sets an interger value on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param value Value to be set.
+         * @returns this effect.
+         */
         public setInt(uniformName: string, value: number): Effect {
             var cache = this._valueCache[uniformName];
             if (cache !== undefined && cache === value)
@@ -787,6 +1007,12 @@
             return this;
         }
 
+        /**
+         * Sets an int array on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setIntArray(uniformName: string, array: Int32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setIntArray(this.getUniform(uniformName), array);
@@ -794,6 +1020,12 @@
             return this;
         }
 
+        /**
+         * Sets an int array 2 on a uniform variable. (Array is specified as single array eg. [1,2,3,4] will result in [[1,2],[3,4]] in the shader)
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setIntArray2(uniformName: string, array: Int32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setIntArray2(this.getUniform(uniformName), array);
@@ -801,6 +1033,12 @@
             return this;
         }
 
+        /**
+         * Sets an int array 3 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6] will result in [[1,2,3],[4,5,6]] in the shader)
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setIntArray3(uniformName: string, array: Int32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setIntArray3(this.getUniform(uniformName), array);
@@ -808,6 +1046,12 @@
             return this;
         }
 
+        /**
+         * Sets an int array 4 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6,7,8] will result in [[1,2,3,4],[5,6,7,8]] in the shader)
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setIntArray4(uniformName: string, array: Int32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setIntArray4(this.getUniform(uniformName), array);
@@ -815,6 +1059,12 @@
             return this;
         }
 
+        /**
+         * Sets an float array on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setFloatArray(uniformName: string, array: Float32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setFloatArray(this.getUniform(uniformName), array);
@@ -822,6 +1072,12 @@
             return this;
         }
 
+        /**
+         * Sets an float array 2 on a uniform variable. (Array is specified as single array eg. [1,2,3,4] will result in [[1,2],[3,4]] in the shader)
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setFloatArray2(uniformName: string, array: Float32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setFloatArray2(this.getUniform(uniformName), array);
@@ -829,6 +1085,12 @@
             return this;
         }
 
+        /**
+         * Sets an float array 3 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6] will result in [[1,2,3],[4,5,6]] in the shader)
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setFloatArray3(uniformName: string, array: Float32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setFloatArray3(this.getUniform(uniformName), array);
@@ -836,6 +1098,12 @@
             return this;
         }
 
+        /**
+         * Sets an float array 4 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6,7,8] will result in [[1,2,3,4],[5,6,7,8]] in the shader)
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setFloatArray4(uniformName: string, array: Float32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setFloatArray4(this.getUniform(uniformName), array);
@@ -843,6 +1111,12 @@
             return this;
         }
 
+        /**
+         * Sets an array on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setArray(uniformName: string, array: number[]): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setArray(this.getUniform(uniformName), array);
@@ -850,6 +1124,12 @@
             return this;
         }
 
+        /**
+         * Sets an array 2 on a uniform variable. (Array is specified as single array eg. [1,2,3,4] will result in [[1,2],[3,4]] in the shader)
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setArray2(uniformName: string, array: number[]): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setArray2(this.getUniform(uniformName), array);
@@ -857,6 +1137,12 @@
             return this;
         }
 
+        /**
+         * Sets an array 3 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6] will result in [[1,2,3],[4,5,6]] in the shader)
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setArray3(uniformName: string, array: number[]): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setArray3(this.getUniform(uniformName), array);
@@ -864,6 +1150,12 @@
             return this;
         }
 
+        /**
+         * Sets an array 4 on a uniform variable. (Array is specified as single array eg. [1,2,3,4,5,6,7,8] will result in [[1,2,3,4],[5,6,7,8]] in the shader)
+         * @param uniformName Name of the variable.
+         * @param array array to be set.
+         * @returns this effect.
+         */
         public setArray4(uniformName: string, array: number[]): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setArray4(this.getUniform(uniformName), array);
@@ -871,6 +1163,12 @@
             return this;
         }
 
+        /**
+         * Sets matrices on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param matrices matrices to be set.
+         * @returns this effect.
+         */
         public setMatrices(uniformName: string, matrices: Float32Array): Effect {
             if (!matrices) {
                 return this;
@@ -882,6 +1180,12 @@
             return this;
         }
 
+        /**
+         * Sets matrix on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param matrix matrix to be set.
+         * @returns this effect.
+         */
         public setMatrix(uniformName: string, matrix: Matrix): Effect {
             if (this._cacheMatrix(uniformName, matrix)) {
                 this._engine.setMatrix(this.getUniform(uniformName), matrix);
@@ -889,6 +1193,12 @@
             return this;
         }
 
+        /**
+         * Sets a 3x3 matrix on a uniform variable. (Speicified as [1,2,3,4,5,6,7,8,9] will result in [1,2,3][4,5,6][7,8,9] matrix)
+         * @param uniformName Name of the variable.
+         * @param matrix matrix to be set.
+         * @returns this effect.
+         */
         public setMatrix3x3(uniformName: string, matrix: Float32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setMatrix3x3(this.getUniform(uniformName), matrix);
@@ -896,6 +1206,12 @@
             return this;
         }
 
+        /**
+         * Sets a 2x2 matrix on a uniform variable. (Speicified as [1,2,3,4] will result in [1,2][3,4] matrix)
+         * @param uniformName Name of the variable.
+         * @param matrix matrix to be set.
+         * @returns this effect.
+         */
         public setMatrix2x2(uniformName: string, matrix: Float32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setMatrix2x2(this.getUniform(uniformName), matrix);
@@ -903,6 +1219,12 @@
             return this;
         }
 
+        /**
+         * Sets a float on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param value value to be set.
+         * @returns this effect.
+         */
         public setFloat(uniformName: string, value: number): Effect {
             var cache = this._valueCache[uniformName];
             if (cache !== undefined && cache === value)
@@ -915,6 +1237,12 @@
             return this;
         }
 
+        /**
+         * Sets a boolean on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param bool value to be set.
+         * @returns this effect.
+         */
         public setBool(uniformName: string, bool: boolean): Effect {
             var cache = this._valueCache[uniformName];
             if (cache !== undefined && cache === bool)
@@ -927,6 +1255,12 @@
             return this;
         }
 
+        /**
+         * Sets a Vector2 on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param vector2 vector2 to be set.
+         * @returns this effect.
+         */
         public setVector2(uniformName: string, vector2: Vector2): Effect {
             if (this._cacheFloat2(uniformName, vector2.x, vector2.y)) {
                 this._engine.setFloat2(this.getUniform(uniformName), vector2.x, vector2.y);
@@ -934,6 +1268,13 @@
             return this;
         }
 
+        /**
+         * Sets a float2 on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param x First float in float2.
+         * @param y Second float in float2.
+         * @returns this effect.
+         */
         public setFloat2(uniformName: string, x: number, y: number): Effect {
             if (this._cacheFloat2(uniformName, x, y)) {
                 this._engine.setFloat2(this.getUniform(uniformName), x, y);
@@ -941,6 +1282,12 @@
             return this;
         }
 
+        /**
+         * Sets a Vector3 on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param vector3 Value to be set.
+         * @returns this effect.
+         */
         public setVector3(uniformName: string, vector3: Vector3): Effect {
             if (this._cacheFloat3(uniformName, vector3.x, vector3.y, vector3.z)) {
                 this._engine.setFloat3(this.getUniform(uniformName), vector3.x, vector3.y, vector3.z);
@@ -948,6 +1295,14 @@
             return this;
         }
 
+        /**
+         * Sets a float3 on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param x First float in float3.
+         * @param y Second float in float3.
+         * @param z Third float in float3.
+         * @returns this effect.
+         */
         public setFloat3(uniformName: string, x: number, y: number, z: number): Effect {
             if (this._cacheFloat3(uniformName, x, y, z)) {
                 this._engine.setFloat3(this.getUniform(uniformName), x, y, z);
@@ -955,6 +1310,12 @@
             return this;
         }
 
+        /**
+         * Sets a Vector4 on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param vector4 Value to be set.
+         * @returns this effect.
+         */
         public setVector4(uniformName: string, vector4: Vector4): Effect {
             if (this._cacheFloat4(uniformName, vector4.x, vector4.y, vector4.z, vector4.w)) {
                 this._engine.setFloat4(this.getUniform(uniformName), vector4.x, vector4.y, vector4.z, vector4.w);
@@ -962,6 +1323,15 @@
             return this;
         }
 
+        /**
+         * Sets a float4 on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param x First float in float4.
+         * @param y Second float in float4.
+         * @param z Third float in float4.
+         * @param w Fourth float in float4.
+         * @returns this effect.
+         */
         public setFloat4(uniformName: string, x: number, y: number, z: number, w: number): Effect {
             if (this._cacheFloat4(uniformName, x, y, z, w)) {
                 this._engine.setFloat4(this.getUniform(uniformName), x, y, z, w);
@@ -969,6 +1339,12 @@
             return this;
         }
 
+        /**
+         * Sets a Color3 on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param color3 Value to be set.
+         * @returns this effect.
+         */
         public setColor3(uniformName: string, color3: Color3): Effect {
 
             if (this._cacheFloat3(uniformName, color3.r, color3.g, color3.b)) {
@@ -977,6 +1353,13 @@
             return this;
         }
 
+        /**
+         * Sets a Color4 on a uniform variable.
+         * @param uniformName Name of the variable.
+         * @param color3 Value to be set.
+         * @param alpha Alpha value to be set.
+         * @returns this effect.
+         */
         public setColor4(uniformName: string, color3: Color3, alpha: number): Effect {
             if (this._cacheFloat4(uniformName, color3.r, color3.g, color3.b, alpha)) {
                 this._engine.setColor4(this.getUniform(uniformName), color3, alpha);
@@ -985,9 +1368,18 @@
         }
 
         // Statics
+        /**
+         * Store of each shader (The can be looked up using effect.key)
+         */
         public static ShadersStore: { [key: string]: string } = {};
+        /**
+         * Store of each included file for a shader (The can be looked up using effect.key)
+         */
         public static IncludesShadersStore: { [key: string]: string } = {};
 
+        /**
+         * Resets the cache of effects.
+         */
         public static ResetCache() {
             Effect._baseCache = {};
         }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 506 - 6
src/Materials/babylon.material.ts


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

@@ -2382,7 +2382,7 @@
             mesh.scaling = Vector3.FromArray(parsedMesh.scaling);
 
             if (parsedMesh.localMatrix) {
-                mesh.setPivotMatrix(Matrix.FromArray(parsedMesh.localMatrix));
+                mesh.setPreTransformMatrix(Matrix.FromArray(parsedMesh.localMatrix));
             } else if (parsedMesh.pivotMatrix) {
                 mesh.setPivotMatrix(Matrix.FromArray(parsedMesh.pivotMatrix));
             }

+ 28 - 25
src/Mesh/babylon.transformNode.ts

@@ -208,18 +208,31 @@ module BABYLON {
         }
 
         /**
-         * Sets a new pivot matrix to the mesh.  
-         * Returns the AbstractMesh.
+         * Sets a new matrix to apply before all other transformation
+         * @param matrix defines the transform matrix
+         * @returns the current TransformNode
+         */
+        public setPreTransformMatrix(matrix: Matrix): TransformNode {
+            return this.setPivotMatrix(matrix, false);
+        }
+
+        /**
+         * Sets a new pivot matrix to the current node
+         * @param matrix defines the new pivot matrix to use
+         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. When this parameter is set to true (default), the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @returns the current TransformNode
         */
-        public setPivotMatrix(matrix: Matrix, postMultiplyPivotMatrix = false): TransformNode {
+        public setPivotMatrix(matrix: Matrix, postMultiplyPivotMatrix = true): TransformNode {
             this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
 
-            if(!this._pivotMatrixInverse){
-                this._pivotMatrixInverse = Matrix.Invert(this._pivotMatrix);
-            } else {
-                this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+            if (this._postMultiplyPivotMatrix) {
+                if (!this._pivotMatrixInverse) {
+                    this._pivotMatrixInverse = Matrix.Invert(this._pivotMatrix);
+                } else {
+                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+                }
             }
 
             return this;
@@ -393,6 +406,12 @@ module BABYLON {
             return this;
         }
 
+        /**
+         * Sets a new pivot point to the current node
+         * @param point defines the new pivot point to use
+         * @param space defines if the point is in world or local space (local by default)
+         * @returns the current TransformNode
+        */        
         public setPivotPoint(point: Vector3, space: Space = Space.LOCAL): TransformNode {
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
@@ -406,19 +425,7 @@ module BABYLON {
                 point = Vector3.TransformCoordinates(point, tmat);
             }
 
-            Vector3.TransformCoordinatesToRef(point, wm, this.position);
-            this._pivotMatrix.m[12] = -point.x;
-            this._pivotMatrix.m[13] = -point.y;
-            this._pivotMatrix.m[14] = -point.z;
-
-            if(!this._pivotMatrixInverse){
-                this._pivotMatrixInverse = Matrix.Invert(this._pivotMatrix);
-            } else {
-                this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
-            }
-            
-            this._cache.pivotMatrixUpdated = true;
-            return this;
+            return this.setPivotMatrix(Matrix.Translation(point.x, point.y, point.z), true);
         }
 
         /**
@@ -835,10 +842,6 @@ module BABYLON {
             // Absolute position
             this._absolutePosition.copyFromFloats(this._worldMatrix.m[12], this._worldMatrix.m[13], this._worldMatrix.m[14]);
 
-            if(this._pivotMatrixInverse){
-                Vector3.TransformCoordinatesToRef(this._absolutePosition, this._pivotMatrixInverse, this._absolutePosition);
-            }
-
             // Callbacks
             this.onAfterWorldMatrixUpdateObservable.notifyObservers(this);
 
@@ -946,7 +949,7 @@ module BABYLON {
             }
 
             if (parsedTransformNode.localMatrix) {
-                transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.localMatrix));
+                transformNode.setPreTransformMatrix(Matrix.FromArray(parsedTransformNode.localMatrix));
             } else if (parsedTransformNode.pivotMatrix) {
                 transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.pivotMatrix));
             }

+ 2 - 2
src/Physics/Plugins/babylon.cannonJSPlugin.ts

@@ -435,7 +435,7 @@
 
                 //calculate the new center using a pivot (since this.BJSCANNON.js doesn't center height maps)
                 var p = Matrix.Translation(boundingInfo.boundingBox.extendSizeWorld.x, 0, -boundingInfo.boundingBox.extendSizeWorld.z);
-                mesh.setPivotMatrix(p);
+                mesh.setPreTransformMatrix(p);
                 mesh.computeWorldMatrix(true);
 
                 //calculate the translation
@@ -448,7 +448,7 @@
                 //rotation is back
                 mesh.rotationQuaternion = rotationQuaternion;
 
-                mesh.setPivotMatrix(oldPivot);
+                mesh.setPreTransformMatrix(oldPivot);
                 mesh.computeWorldMatrix(true);
             } else if (impostor.type === PhysicsImpostor.MeshImpostor) {
                 this._tmpDeltaPosition.copyFromFloats(0, 0, 0);

+ 12 - 9
src/PostProcess/babylon.postProcess.ts

@@ -179,13 +179,14 @@
         }
 
         /**
-        * The resulting output of the post process.
+        * The input texture for this post process and the output texture of the previous post process. When added to a pipeline the previous post process will
+        * render it's output into this texture and this texture will be used as textureSampler in the fragment shader of this post process.
         */
-        public get outputTexture(): InternalTexture {
+        public get inputTexture(): InternalTexture {
             return this._textures.data[this._currentRenderTextureInd];
         }
 
-        public set outputTexture(value: InternalTexture) {
+        public set inputTexture(value: InternalTexture) {
             this._forcedOutputTexture = value;
         }
 
@@ -228,7 +229,7 @@
          * @param textureType Type of textures used when performing the post process. (default: 0)
          * @param vertexUrl The url of the vertex shader to be used. (default: "postprocess")
          * @param indexParameters The index parameters to be used for babylons include syntax "#include<kernelBlurVaryingDeclaration>[0..varyingCount]". (default: undefined) See usage in babylon.blurPostProcess.ts and kernelBlur.vertex.fx
-         * @param blockCompilation If the shader should be compiled imediatly. (default: false) 
+         * @param blockCompilation If the shader should not be compiled imediatly. (default: false) 
          */
         constructor(/** Name of the PostProcess. */public name: string, fragmentUrl: string, parameters: Nullable<string[]>, samplers: Nullable<string[]>, options: number | PostProcessOptions, camera: Nullable<Camera>,
             samplingMode: number = Texture.NEAREST_SAMPLINGMODE, engine?: Engine, reusable?: boolean, defines: Nullable<string> = null, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, vertexUrl: string = "postprocess", indexParameters?: any, blockCompilation = false) {
@@ -333,6 +334,7 @@
 
         /**
          * Activates the post process by intializing the textures to be used when executed. Notifies onActivateObservable.
+         * When this post process is used in a pipeline, this is call will bind the input texture of this post process to the output of the previous.
          * @param camera The camera that will be used in the post process. This camera will be used when calling onActivateObservable.
          * @param sourceTexture The source texture to be inspected to get the width and height if not specified in the post process constructor. (default: null)
          * @param forceDepthStencil If true, a depth and stencil buffer will be generated. (default: false)
@@ -411,16 +413,17 @@
             var target: InternalTexture;
 
             if (this._shareOutputWithPostProcess) {
-                target = this._shareOutputWithPostProcess.outputTexture;
+                target = this._shareOutputWithPostProcess.inputTexture;
             } else if (this._forcedOutputTexture) {
                 target = this._forcedOutputTexture;
 
                 this.width = this._forcedOutputTexture.width;
                 this.height = this._forcedOutputTexture.height;
             } else {
-                target = this.outputTexture;
+                target = this.inputTexture;
             }
 
+            // Bind the input of this post process to be used as the output of the previous post process.
             if (this.enablePixelPerfectMode) {
                 this._scaleRatio.copyFromFloats(requiredWidth / desiredWidth, requiredHeight / desiredHeight);
                 this._engine.bindFramebuffer(target, 0, requiredWidth, requiredHeight, true);
@@ -493,14 +496,14 @@
                 this.getEngine().setAlphaConstants(this.alphaConstants.r, this.alphaConstants.g, this.alphaConstants.b, this.alphaConstants.a);
             }
 
-            // Texture            
+            // Bind the output texture of the preivous post process as the input to this post process.            
             var source: InternalTexture;
             if (this._shareOutputWithPostProcess) {
-                source = this._shareOutputWithPostProcess.outputTexture;
+                source = this._shareOutputWithPostProcess.inputTexture;
             } else if (this._forcedOutputTexture) {
                 source = this._forcedOutputTexture;
             } else {
-                source = this.outputTexture;
+                source = this.inputTexture;
             }
             this._effect._bindTexture("textureSampler", source);
 

+ 93 - 19
src/Tools/babylon.observable.ts

@@ -6,12 +6,24 @@
     export class EventState {
 
         /**
-        * If the callback of a given Observer set skipNextObservers to true the following observers will be ignored
-        */
+         * Create a new EventState
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         */
         constructor(mask: number, skipNextObservers = false, target?: any, currentTarget?: any) {
             this.initalize(mask, skipNextObservers, target, currentTarget);
         }
 
+        /**
+         * Initialize the current event state
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns the current event state
+         */
         public initalize(mask: number, skipNextObservers = false, target?: any, currentTarget?: any): EventState {
             this.mask = mask;
             this.skipNextObservers = skipNextObservers;
@@ -51,7 +63,30 @@
      * Represent an Observer registered to a given Observable object.
      */
     export class Observer<T> {
-        constructor(public callback: (eventData: T, eventState: EventState) => void, public mask: number, public scope: any = null) {
+        /**
+         * Gets or sets a property defining that the observer as to be unregistered after the next notification
+         */
+        public unregisterOnNextCall = false;
+
+        /**
+         * Creates a new observer
+         * @param callback defines the callback to call when the observer is notified
+         * @param mask defines the mask of the observer (used to filter notifications)
+         * @param scope defines the current scope used to restore the JS context
+         */
+        constructor(
+            /**
+             * Defines the callback to call when the observer is notified
+             */
+            public callback: (eventData: T, eventState: EventState) => void, 
+            /**
+             * Defines the mask of the observer (used to filter notifications)
+             */
+            public mask: number, 
+            /**
+             * Defines the current scope used to restore the JS context
+             */
+            public scope: any = null) {
         }
     }
 
@@ -62,6 +97,9 @@
         private _observers: Nullable<Observer<T>[]>;
         private _observables: Nullable<Observable<T>[]>;
 
+        /**
+         * Release associated resources
+         */
         public dispose(): void {
             if (this._observers && this._observables) {
                 for (var index = 0; index < this._observers.length; index++) {
@@ -73,6 +111,14 @@
             this._observables = null;
         }
 
+        /**
+         * Raise a callback when one of the observable will notify
+         * @param observables defines a list of observables to watch
+         * @param callback defines the callback to call on notification
+         * @param mask defines the mask used to filter notifications
+         * @param scope defines the current scope used to restore the JS context
+         * @returns the new MultiObserver
+         */
         public static Watch<T>(observables: Observable<T>[], callback: (eventData: T, eventState: EventState) => void, mask: number = -1, scope: any = null): MultiObserver<T> {
             let result = new MultiObserver<T>();
 
@@ -98,12 +144,16 @@
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      */
     export class Observable<T> {
-        _observers = new Array<Observer<T>>();
+        private _observers = new Array<Observer<T>>();
 
         private _eventState: EventState;
 
         private _onObserverAdded: Nullable<(observer: Observer<T>) => void>;
 
+        /**
+         * Creates a new observable
+         * @param onObserverAdded defines a callback to call when a new observer is added
+         */
         constructor(onObserverAdded?: (observer: Observer<T>) => void) {
             this._eventState = new EventState(0);
 
@@ -118,13 +168,16 @@
          * @param mask the mask used to filter observers
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param scope optional scope for the callback to be called from
+         * @param unregisterOnFirstCall defines if the observer as to be unregistered after the next notification
+         * @returns the new observer created for the callback
          */
-        public add(callback: (eventData: T, eventState: EventState) => void, mask: number = -1, insertFirst = false, scope: any = null): Nullable<Observer<T>> {
+        public add(callback: (eventData: T, eventState: EventState) => void, mask: number = -1, insertFirst = false, scope: any = null, unregisterOnFirstCall = false): Nullable<Observer<T>> {
             if (!callback) {
                 return null;
             }
 
             var observer = new Observer(callback, mask, scope);
+            observer.unregisterOnNextCall = unregisterOnFirstCall;
 
             if (insertFirst) {
                 this._observers.unshift(observer);
@@ -141,7 +194,8 @@
 
         /**
          * Remove an Observer from the Observable object
-         * @param observer the instance of the Observer to remove. If it doesn't belong to this Observable, false will be returned.
+         * @param observer the instance of the Observer to remove
+         * @returns false if it doesn't belong to this Observable
          */
         public remove(observer: Nullable<Observer<T>>): boolean {
             if (!observer) {
@@ -162,8 +216,9 @@
 
         /**
          * Remove a callback from the Observable object
-         * @param callback the callback to remove. If it doesn't belong to this Observable, false will be returned.
-         * @param scope optional scope. If used only the callbacks with this scope will be removed.
+         * @param callback the callback to remove
+         * @param scope optional scope. If used only the callbacks with this scope will be removed
+         * @returns false if it doesn't belong to this Observable
         */
         public removeCallback(callback: (eventData: T, eventState: EventState) => void, scope?: any): boolean {
 
@@ -177,11 +232,20 @@
             return false;
         }
 
+        private _deferUnregister(observer: Observer<T>): void {
+            Tools.SetImmediate(() => {
+                this.remove(observer);
+            })
+        }
+
         /**
          * 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
-         * @param eventData
-         * @param mask
+         * @param eventData defines the data to send to all observers
+         * @param mask defines the mask of the current notification (observers with incompatible mask (ie mask & observer.mask === 0) will not be notified)
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns false if the complete observer chain was not processed (because one observer set the skipNextObservers to true)
          */
         public notifyObservers(eventData: T, mask: number = -1, target?: any, currentTarget?: any): boolean {
             if (!this._observers.length) {
@@ -202,6 +266,10 @@
                     } else {
                         state.lastReturnValue = obs.callback(eventData, state);
                     }
+
+                    if (obs.unregisterOnNextCall) {
+                        this._deferUnregister(obs);
+                    }
                 }
                 if (state.skipNextObservers) {
                     return false;
@@ -219,8 +287,8 @@
          * 
          * @param eventData The data to be sent to each callback
          * @param mask is used to filter observers defaults to -1
-         * @param target the callback target (see EventState)
-         * @param currentTarget The current object in the bubbling phase
+         * @param target defines the callback target (see EventState)
+         * @param currentTarget defines he current object in the bubbling phase
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          */
         public notifyObserversWithPromise(eventData: T, mask: number = -1, target?: any, currentTarget?: any): Promise<T> {
@@ -256,6 +324,9 @@
                             return obs.callback(eventData, state);
                         });
                     }
+                    if (obs.unregisterOnNextCall) {
+                        this._deferUnregister(obs);
+                    }                    
                 }
             });
 
@@ -265,8 +336,9 @@
 
         /**
          * Notify a specific observer
-         * @param eventData
-         * @param mask
+         * @param observer defines the observer to notify
+         * @param eventData defines the data to be sent to each callback
+         * @param mask is used to filter observers defaults to -1
          */
         public notifyObserver(observer: Observer<T>, eventData: T, mask: number = -1): void {
             let state = this._eventState;
@@ -277,7 +349,8 @@
         }
 
         /**
-         * return true is the Observable has at least one Observer registered
+         * Gets a boolean indicating if the observable has at least one observer
+         * @returns true is the Observable has at least one Observer registered
          */
         public hasObservers(): boolean {
             return this._observers.length > 0;
@@ -292,8 +365,9 @@
         }
 
         /**
-        * Clone the current observable
-        */
+         * Clone the current observable
+         * @returns a new observable
+         */
         public clone(): Observable<T> {
             var result = new Observable<T>();
 
@@ -304,8 +378,8 @@
 
         /**
          * Does this observable handles observer registered with a given mask
-         * @param {number} trigger - the mask to be tested
-         * @return {boolean} whether or not one observer registered with the given mask is handeled 
+         * @param mask defines the mask to be tested
+         * @return whether or not one observer registered with the given mask is handeled 
         **/
         public hasSpecificMask(mask: number = -1): boolean {
             for (var obs of this._observers) {

+ 10 - 11
src/babylon.assetContainer.ts

@@ -274,17 +274,16 @@ module BABYLON {
             this._moveAssets(this.scene.meshes, this.meshes, keepAssets.meshes);
             this._moveAssets(this.scene.getGeometries(), this.geometries, keepAssets.geometries);
             this._moveAssets(this.scene.materials, this.materials, keepAssets.materials);
-    
-            Array.prototype.push.apply(this.actionManagers, this.scene._actionManagers);
-            Array.prototype.push.apply(this.animations, this.scene.animations);
-            Array.prototype.push.apply(this.lensFlareSystems, this.scene.lensFlareSystems);
-            Array.prototype.push.apply(this.lights, this.scene.lights);
-            Array.prototype.push.apply(this.morphTargetManagers, this.scene.morphTargetManagers);
-            Array.prototype.push.apply(this.multiMaterials, this.scene.multiMaterials);
-            Array.prototype.push.apply(this.skeletons, this.scene.skeletons);
-            Array.prototype.push.apply(this.particleSystems, this.scene.particleSystems);
-            Array.prototype.push.apply(this.sounds, this.scene.mainSoundTrack.soundCollection);
-            Array.prototype.push.apply(this.transformNodes, this.scene.transformNodes);
+            this._moveAssets(this.scene._actionManagers, this.actionManagers, keepAssets.actionManagers);
+            this._moveAssets(this.scene.animations, this.animations, keepAssets.animations);
+            this._moveAssets(this.scene.lensFlareSystems, this.lensFlareSystems, keepAssets.lensFlareSystems);
+            this._moveAssets(this.scene.lights, this.lights, keepAssets.lights);
+            this._moveAssets(this.scene.morphTargetManagers, this.morphTargetManagers, keepAssets.morphTargetManagers);
+            this._moveAssets(this.scene.multiMaterials, this.multiMaterials, keepAssets.multiMaterials);
+            this._moveAssets(this.scene.skeletons, this.skeletons, keepAssets.skeletons);
+            this._moveAssets(this.scene.particleSystems, this.particleSystems, keepAssets.particleSystems);
+            this._moveAssets(this.scene.mainSoundTrack.soundCollection, this.sounds, keepAssets.sounds);
+            this._moveAssets(this.scene.transformNodes, this.transformNodes, keepAssets.transformNodes);
     
             this.removeAllFromScene();
         }

+ 11 - 3
src/babylon.node.ts

@@ -411,14 +411,22 @@
             return this._ranges[name];
         }
 
-        public beginAnimation(name: string, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): void {
+        /**
+         * Will start the animation sequence
+         * @param name defines the range frames for animation sequence
+         * @param loop defines if the animation should loop (false by default)
+         * @param speedRatio defines the speed factor in which to run the animation (1 by default)
+         * @param onAnimationEnd defines a function to be executed when the animation ended (undefined by default)
+         * @returns the {BABYLON.Animatable} object created for this animation. If range does not exist, it will return null
+         */
+        public beginAnimation(name: string, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): Nullable<Animatable> {
             var range = this.getAnimationRange(name);
 
             if (!range) {
-                return;
+                return null;
             }
 
-            this._scene.beginAnimation(this, range.from, range.to, loop, speedRatio, onAnimationEnd);
+            return this._scene.beginAnimation(this, range.from, range.to, loop, speedRatio, onAnimationEnd);
         }
 
         public serializeAnimationRanges(): any {

BIN
tests/validation/ReferenceImages/LightProjectionTexture.png


BIN
tests/validation/ReferenceImages/gltfMaterialSpecularGlossiness.png


BIN
tests/validation/ReferenceImages/gltfPrimitiveAttribute.png


+ 5 - 0
tests/validation/config.json

@@ -342,6 +342,11 @@
       "functionToCall": "CreateBones2TestScene",
       "referenceImage": "instancedBones.png",
       "replace": "Dude.babylon, dude.babylon"
+    },
+    {
+      "title": "Light Projection Texture",
+      "playgroundId": "#CQNGRK",
+      "referenceImage": "LightProjectionTexture.png"
     }
   ]
 }