Explorar el Código

Merge branch 'master' into morePostProcessDocs

Trevor Baron hace 7 años
padre
commit
5729d7c9be

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 7522 - 7513
Playground/babylon.d.txt


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 23 - 23
Viewer/dist/viewer.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 23 - 23
Viewer/dist/viewer.min.js


+ 153 - 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;
-
-    };
-
-    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;
-        };
+    model?: IModelConfiguration | string;
 
-    };
-    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;
@@ -189,7 +66,150 @@ export interface ViewerConfiguration {
     }
 }
 
-export interface SceneOptimizerParameters {
+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 ISceneOptimizerParameters {
     priority?: number;
     maximumSize?: number;
     step?: number;

+ 32 - 174
Viewer/src/viewer/defaultViewer.ts

@@ -1,6 +1,6 @@
 
 
-import { ViewerConfiguration } from './../configuration/configuration';
+import { ViewerConfiguration, IModelConfiguration, ILightConfiguration } from './../configuration/configuration';
 import { Template, EventCallback } from './../templateManager';
 import { AbstractViewer } from './viewer';
 import { SpotLight, MirrorTexture, Plane, ShadowGenerator, Texture, BackgroundMaterial, Observable, ShadowLight, CubeTexture, BouncingBehavior, FramingBehavior, Behavior, Light, Engine, Scene, AutoRotationBehavior, AbstractMesh, Quaternion, StandardMaterial, ArcRotateCamera, ImageProcessingConfiguration, Color3, Vector3, SceneLoader, Mesh, HemisphericLight } from 'babylonjs';
@@ -8,8 +8,6 @@ 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,6 +103,34 @@ export class DefaultViewer extends AbstractViewer {
         this.containerElement.style.display = 'flex';
     }
 
+    protected configureModel(modelConfiguration: Partial<IModelConfiguration>) {
+        super.configureModel(modelConfiguration);
+
+        let navbar = this.templateManager.getTemplate('navBar');
+        if (!navbar) return;
+
+        let metadataContainer = navbar.parent.querySelector('#model-metadata');
+        if (metadataContainer) {
+            if (modelConfiguration.title !== undefined) {
+                let element = metadataContainer.querySelector('span.model-title');
+                if (element) {
+                    element.innerHTML = modelConfiguration.title;
+                }
+            }
+
+            if (modelConfiguration.subtitle !== undefined) {
+                let element = metadataContainer.querySelector('span.model-subtitle');
+                if (element) {
+                    element.innerHTML = modelConfiguration.subtitle;
+                }
+            }
+
+            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) => {
@@ -116,10 +142,6 @@ export class DefaultViewer extends AbstractViewer {
     }
 
     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.
         let hideLoadingDelay = 500;
         if (this.configuration.lab && this.configuration.lab.hideLoadingDelay !== undefined) {
@@ -129,48 +151,11 @@ export class DefaultViewer extends AbstractViewer {
             this.hideLoadingScreen();
         }, hideLoadingDelay);
 
-
-        // 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);
     }
 
-    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) {
-                let element = metadataContainer.querySelector('span.model-title');
-                if (element) {
-                    element.innerHTML = this.configuration.model.title;
-                }
-            }
-
-            if (this.configuration.model.subtitle) {
-                let element = metadataContainer.querySelector('span.model-subtitle');
-                if (element) {
-                    element.innerHTML = this.configuration.model.subtitle;
-                }
-            }
-
-            if (this.configuration.model.thumbnail) {
-                (<HTMLDivElement>metadataContainer.querySelector('.thumbnail')).style.backgroundImage = `url('${this.configuration.model.thumbnail}')`;
-            }
-        }
-
-    }
-
     /*protected initEnvironment(focusMeshes: Array<AbstractMesh> = []): Promise<Scene> {
         if (this.configuration.skybox) {
             // Define a general environment textue
@@ -360,10 +345,9 @@ export class DefaultViewer extends AbstractViewer {
         }));
     }
 
-    private setupLights(focusMeshes: Array<AbstractMesh> = []) {
-
-        let sceneConfig = this.configuration.scene || { defaultLight: true };
-
+    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();
@@ -409,131 +393,5 @@ export class DefaultViewer extends AbstractViewer {
             this.scene.registerBeforeRender(updateFlashlightFunction);
             this.registeredOnBeforerenderFunctions.push(updateFlashlightFunction);
         }
-
-        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);
-                }
-
-                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]);
-                        }
-                    }
-                }
-            });
-        }
-    }
-
-    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;
-        }
     }
 }

+ 493 - 155
Viewer/src/viewer/viewer.ts

@@ -1,8 +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 { 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 {
 
@@ -10,6 +13,7 @@ export abstract class AbstractViewer {
 
     public engine: Engine;
     public scene: Scene;
+    public camera: ArcRotateCamera;
     public sceneOptimizer: SceneOptimizer;
     public baseId: string;
 
@@ -35,6 +39,7 @@ export abstract class AbstractViewer {
     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>;
 
@@ -54,6 +59,7 @@ export abstract class AbstractViewer {
         this.onEngineInitObservable = new Observable();
         this.onModelLoadedObservable = new Observable();
         this.onModelLoadProgressObservable = new Observable();
+        this.onModelLoadErrorObservable = new Observable();
         this.onInitDoneObservable = new Observable();
         this.onLoaderInitObservable = new Observable();
 
@@ -70,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 || {};
@@ -123,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() {
@@ -233,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);
     }
 
@@ -287,8 +661,14 @@ 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) => {
@@ -297,14 +677,23 @@ export abstract class AbstractViewer {
                     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.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;
                 });
@@ -312,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);
     }
@@ -492,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;
+        }
+    }
 }

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 8909 - 8900
dist/preview release/babylon.d.ts


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 2 - 2
dist/preview release/babylon.js


+ 27 - 16
dist/preview release/babylon.max.js

@@ -15195,19 +15195,23 @@ var BABYLON;
             configurable: true
         });
         /**
-         * Sets a new pivot matrix to the mesh.
-         * Returns the AbstractMesh.
+         * 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. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @returns the current TransformNode
         */
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
             if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
-            if (!this._pivotMatrixInverse) {
-                this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-            }
-            else {
-                this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+            if (this._postMultiplyPivotMatrix) {
+                if (!this._pivotMatrixInverse) {
+                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
+                }
+                else {
+                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+                }
             }
             return this;
         };
@@ -15371,8 +15375,16 @@ var BABYLON;
             BABYLON.Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
             return this;
         };
-        TransformNode.prototype.setPivotPoint = function (point, space) {
+        /**
+         * 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)
+         * @param postMultiplyPivotMatrix defines if the pivot transformation must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @returns the current TransformNode
+        */
+        TransformNode.prototype.setPivotPoint = function (point, space, postMultiplyPivotMatrix) {
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
+            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
             }
@@ -15386,11 +15398,13 @@ var BABYLON;
             this._pivotMatrix.m[12] = -point.x;
             this._pivotMatrix.m[13] = -point.y;
             this._pivotMatrix.m[14] = -point.z;
-            if (!this._pivotMatrixInverse) {
-                this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-            }
-            else {
-                this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+            if (this._postMultiplyPivotMatrix) {
+                if (!this._pivotMatrixInverse) {
+                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
+                }
+                else {
+                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+                }
             }
             this._cache.pivotMatrixUpdated = true;
             return this;
@@ -15764,9 +15778,6 @@ var BABYLON;
             this._afterComputeWorldMatrix();
             // Absolute position
             this._absolutePosition.copyFromFloats(this._worldMatrix.m[12], this._worldMatrix.m[13], this._worldMatrix.m[14]);
-            if (this._pivotMatrixInverse) {
-                BABYLON.Vector3.TransformCoordinatesToRef(this._absolutePosition, this._pivotMatrixInverse, this._absolutePosition);
-            }
             // Callbacks
             this.onAfterWorldMatrixUpdateObservable.notifyObservers(this);
             if (!this._poseMatrix) {

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 2 - 2
dist/preview release/babylon.worker.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 8702 - 8693
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 2 - 2
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 27 - 16
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -15195,19 +15195,23 @@ var BABYLON;
             configurable: true
         });
         /**
-         * Sets a new pivot matrix to the mesh.
-         * Returns the AbstractMesh.
+         * 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. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @returns the current TransformNode
         */
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
             if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
-            if (!this._pivotMatrixInverse) {
-                this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-            }
-            else {
-                this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+            if (this._postMultiplyPivotMatrix) {
+                if (!this._pivotMatrixInverse) {
+                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
+                }
+                else {
+                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+                }
             }
             return this;
         };
@@ -15371,8 +15375,16 @@ var BABYLON;
             BABYLON.Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
             return this;
         };
-        TransformNode.prototype.setPivotPoint = function (point, space) {
+        /**
+         * 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)
+         * @param postMultiplyPivotMatrix defines if the pivot transformation must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @returns the current TransformNode
+        */
+        TransformNode.prototype.setPivotPoint = function (point, space, postMultiplyPivotMatrix) {
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
+            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
             }
@@ -15386,11 +15398,13 @@ var BABYLON;
             this._pivotMatrix.m[12] = -point.x;
             this._pivotMatrix.m[13] = -point.y;
             this._pivotMatrix.m[14] = -point.z;
-            if (!this._pivotMatrixInverse) {
-                this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-            }
-            else {
-                this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+            if (this._postMultiplyPivotMatrix) {
+                if (!this._pivotMatrixInverse) {
+                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
+                }
+                else {
+                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+                }
             }
             this._cache.pivotMatrixUpdated = true;
             return this;
@@ -15764,9 +15778,6 @@ var BABYLON;
             this._afterComputeWorldMatrix();
             // Absolute position
             this._absolutePosition.copyFromFloats(this._worldMatrix.m[12], this._worldMatrix.m[13], this._worldMatrix.m[14]);
-            if (this._pivotMatrixInverse) {
-                BABYLON.Vector3.TransformCoordinatesToRef(this._absolutePosition, this._pivotMatrixInverse, this._absolutePosition);
-            }
             // Callbacks
             this.onAfterWorldMatrixUpdateObservable.notifyObservers(this);
             if (!this._poseMatrix) {

+ 27 - 16
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js

@@ -15181,19 +15181,23 @@ var BABYLON;
             configurable: true
         });
         /**
-         * Sets a new pivot matrix to the mesh.
-         * Returns the AbstractMesh.
+         * 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. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @returns the current TransformNode
         */
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
             if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
-            if (!this._pivotMatrixInverse) {
-                this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-            }
-            else {
-                this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+            if (this._postMultiplyPivotMatrix) {
+                if (!this._pivotMatrixInverse) {
+                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
+                }
+                else {
+                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+                }
             }
             return this;
         };
@@ -15357,8 +15361,16 @@ var BABYLON;
             BABYLON.Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
             return this;
         };
-        TransformNode.prototype.setPivotPoint = function (point, space) {
+        /**
+         * 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)
+         * @param postMultiplyPivotMatrix defines if the pivot transformation must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @returns the current TransformNode
+        */
+        TransformNode.prototype.setPivotPoint = function (point, space, postMultiplyPivotMatrix) {
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
+            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
             }
@@ -15372,11 +15384,13 @@ var BABYLON;
             this._pivotMatrix.m[12] = -point.x;
             this._pivotMatrix.m[13] = -point.y;
             this._pivotMatrix.m[14] = -point.z;
-            if (!this._pivotMatrixInverse) {
-                this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-            }
-            else {
-                this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+            if (this._postMultiplyPivotMatrix) {
+                if (!this._pivotMatrixInverse) {
+                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
+                }
+                else {
+                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+                }
             }
             this._cache.pivotMatrixUpdated = true;
             return this;
@@ -15750,9 +15764,6 @@ var BABYLON;
             this._afterComputeWorldMatrix();
             // Absolute position
             this._absolutePosition.copyFromFloats(this._worldMatrix.m[12], this._worldMatrix.m[13], this._worldMatrix.m[14]);
-            if (this._pivotMatrixInverse) {
-                BABYLON.Vector3.TransformCoordinatesToRef(this._absolutePosition, this._pivotMatrixInverse, this._absolutePosition);
-            }
             // Callbacks
             this.onAfterWorldMatrixUpdateObservable.notifyObservers(this);
             if (!this._poseMatrix) {

+ 27 - 16
dist/preview release/es6.js

@@ -15181,19 +15181,23 @@ var BABYLON;
             configurable: true
         });
         /**
-         * Sets a new pivot matrix to the mesh.
-         * Returns the AbstractMesh.
+         * 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. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @returns the current TransformNode
         */
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
             if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
-            if (!this._pivotMatrixInverse) {
-                this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-            }
-            else {
-                this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+            if (this._postMultiplyPivotMatrix) {
+                if (!this._pivotMatrixInverse) {
+                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
+                }
+                else {
+                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+                }
             }
             return this;
         };
@@ -15357,8 +15361,16 @@ var BABYLON;
             BABYLON.Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
             return this;
         };
-        TransformNode.prototype.setPivotPoint = function (point, space) {
+        /**
+         * 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)
+         * @param postMultiplyPivotMatrix defines if the pivot transformation must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @returns the current TransformNode
+        */
+        TransformNode.prototype.setPivotPoint = function (point, space, postMultiplyPivotMatrix) {
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
+            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
             }
@@ -15372,11 +15384,13 @@ var BABYLON;
             this._pivotMatrix.m[12] = -point.x;
             this._pivotMatrix.m[13] = -point.y;
             this._pivotMatrix.m[14] = -point.z;
-            if (!this._pivotMatrixInverse) {
-                this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-            }
-            else {
-                this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+            if (this._postMultiplyPivotMatrix) {
+                if (!this._pivotMatrixInverse) {
+                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
+                }
+                else {
+                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+                }
             }
             this._cache.pivotMatrixUpdated = true;
             return this;
@@ -15750,9 +15764,6 @@ var BABYLON;
             this._afterComputeWorldMatrix();
             // Absolute position
             this._absolutePosition.copyFromFloats(this._worldMatrix.m[12], this._worldMatrix.m[13], this._worldMatrix.m[14]);
-            if (this._pivotMatrixInverse) {
-                BABYLON.Vector3.TransformCoordinatesToRef(this._absolutePosition, this._pivotMatrixInverse, this._absolutePosition);
-            }
             // Callbacks
             this.onAfterWorldMatrixUpdateObservable.notifyObservers(this);
             if (!this._poseMatrix) {

+ 0 - 34
dist/preview release/typedocValidationBaseline.json

@@ -36875,40 +36875,6 @@
             }
           }
         },
-        "setPivotMatrix": {
-          "Comments": {
-            "MissingReturn": true
-          },
-          "Parameter": {
-            "matrix": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "postMultiplyPivotMatrix": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "setPivotPoint": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "point": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "space": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
         "setPositionWithLocalVector": {
           "Comments": {
             "MissingReturn": true

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 6 - 6
dist/preview release/viewer/babylon.viewer.js


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

@@ -44,6 +44,7 @@
 - Documented PostProcessRenderEffect, DefaultRenderingPipeline, BlurPostProcess, DepthOfFieldEffect, PostProcess, PostProcessManager, Effect classes ([trevordev](https://github.com/trevordev))
 - SPS internal storage of each solid particle rotation matrix ([jbousquie](https://github.com/jbousquie)) 
 - (Viewer) Introducing the viewer labs - testing new features. ([RaananW](https://github.com/RaananW))
+- (Viewer) It is now possible to update parts of the configuration without rcreating the objects. ([RaananW](https://github.com/RaananW))
 
 ## Bug fixes
 - Texture extension detection in `Engine.CreateTexture` ([sebavan](https://github.com/sebavan))

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

@@ -208,18 +208,22 @@ module BABYLON {
         }
 
         /**
-         * Sets a new pivot matrix to the mesh.  
-         * Returns the AbstractMesh.
+         * 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. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, 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 {
             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,7 +397,14 @@ module BABYLON {
             return this;
         }
 
-        public setPivotPoint(point: Vector3, space: Space = Space.LOCAL): TransformNode {
+        /**
+         * 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)
+         * @param postMultiplyPivotMatrix defines if the pivot transformation must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @returns the current TransformNode
+        */        
+        public setPivotPoint(point: Vector3, space: Space = Space.LOCAL, postMultiplyPivotMatrix = false): TransformNode {
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
             }
@@ -411,12 +422,14 @@ module BABYLON {
             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);
+            if (this._postMultiplyPivotMatrix) {
+                if (!this._pivotMatrixInverse) {
+                    this._pivotMatrixInverse = Matrix.Invert(this._pivotMatrix);
+                } else {
+                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
+                }
             }
-            
+
             this._cache.pivotMatrixUpdated = true;
             return this;
         }
@@ -835,10 +848,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);