Selaa lähdekoodia

refactor render only default config out

Trevor Baron 6 vuotta sitten
vanhempi
commit
f734b01d1b

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

@@ -1,137 +1,8 @@
-import { mapperManager } from './mappers';
-import { ViewerConfiguration } from './configuration';
-import { getConfigurationType } from './types';
-import { processConfigurationCompatibility } from './configurationCompatibility';
+import { RenderOnlyConfigurationLoader } from "./renderOnlyLoader";
+import { getConfigurationType } from "./types";
 
-import { deepmerge } from '../helper/';
-import { Tools, IFileRequest } from 'babylonjs';
-
-/**
- * The configuration loader will load the configuration object from any source and will use the defined mapper to
- * parse the object and return a conform ViewerConfiguration.
- * It is a private member of the scene.
- */
-export class ConfigurationLoader {
-
-    private _configurationCache: { [url: string]: any };
-
-    private _loadRequests: Array<IFileRequest>;
-
-    constructor(private _enableCache: boolean = false) {
-        this._configurationCache = {};
-        this._loadRequests = [];
+export class ConfigurationLoader extends RenderOnlyConfigurationLoader {
+    protected getExtendedConfig(type:string|undefined){
+        return getConfigurationType(type || "extended");
     }
-
-    /**
-     * load a configuration object that is defined in the initial configuration provided.
-     * The viewer configuration can extend different types of configuration objects and have an extra configuration defined.
-     *
-     * @param initConfig the initial configuration that has the definitions of further configuration to load.
-     * @param callback an optional callback that will be called sync, if noconfiguration needs to be loaded or configuration is payload-only
-     * @returns A promise that delivers the extended viewer configuration, when done.
-     */
-    public loadConfiguration(initConfig: ViewerConfiguration = {}, callback?: (config: ViewerConfiguration) => void): Promise<ViewerConfiguration> {
-
-        let loadedConfig: ViewerConfiguration = deepmerge({}, initConfig);
-        this._processInitialConfiguration(loadedConfig);
-
-        let extendedConfiguration = getConfigurationType(loadedConfig.extends || "extended");
-
-        if (loadedConfig.configuration) {
-
-            let mapperType = "json";
-            return Promise.resolve().then(() => {
-                if (typeof loadedConfig.configuration === "string" || (loadedConfig.configuration && loadedConfig.configuration.url)) {
-                    // a file to load
-
-                    let url: string = '';
-                    if (typeof loadedConfig.configuration === "string") {
-                        url = loadedConfig.configuration;
-                    }
-
-                    // if configuration is an object
-                    if (typeof loadedConfig.configuration === "object" && loadedConfig.configuration.url) {
-                        url = loadedConfig.configuration.url;
-                        let type = loadedConfig.configuration.mapper;
-                        // empty string?
-                        if (!type) {
-                            // load mapper type from filename / url
-                            type = loadedConfig.configuration.url.split('.').pop();
-                        }
-                        mapperType = type || mapperType;
-                    }
-                    return this._loadFile(url);
-                } else {
-                    if (typeof loadedConfig.configuration === "object") {
-                        mapperType = loadedConfig.configuration.mapper || mapperType;
-                        return loadedConfig.configuration.payload || {};
-                    }
-                    return {};
-
-                }
-            }).then((data: any) => {
-                let mapper = mapperManager.getMapper(mapperType);
-                let parsed = deepmerge(mapper.map(data), loadedConfig);
-                let merged = deepmerge(extendedConfiguration, parsed);
-                processConfigurationCompatibility(merged);
-                if (callback) { callback(merged); }
-                return merged;
-            });
-        } else {
-            loadedConfig = deepmerge(extendedConfiguration, loadedConfig);
-            processConfigurationCompatibility(loadedConfig);
-            if (callback) { callback(loadedConfig); }
-            return Promise.resolve(loadedConfig);
-        }
-    }
-
-    /**
-     * Dispose the configuration loader. This will cancel file requests, if active.
-     */
-    public dispose() {
-        this._loadRequests.forEach((request) => {
-            request.abort();
-        });
-        this._loadRequests.length = 0;
-    }
-
-    /**
-     * This function will process the initial configuration and make needed changes for the viewer to work.
-     * @param config the mutable(!) initial configuration to process
-     */
-    private _processInitialConfiguration(config: ViewerConfiguration) {
-        if (config.model) {
-            if (typeof config.model === "string") {
-                config.model = {
-                    url: config.model
-                };
-            }
-        }
-    }
-
-    private _loadFile(url: string): Promise<any> {
-        let cacheReference = this._configurationCache;
-        if (this._enableCache && cacheReference[url]) {
-            return Promise.resolve(cacheReference[url]);
-        }
-
-        return new Promise((resolve, reject) => {
-            let fileRequest = Tools.LoadFile(url, (result) => {
-                let idx = this._loadRequests.indexOf(fileRequest);
-                if (idx !== -1) {
-                    this._loadRequests.splice(idx, 1);
-                }
-                if (this._enableCache) { cacheReference[url] = result; }
-                resolve(result);
-            }, undefined, undefined, false, (request, error: any) => {
-                let idx = this._loadRequests.indexOf(fileRequest);
-                if (idx !== -1) {
-                    this._loadRequests.splice(idx, 1);
-                }
-                reject(error);
-            });
-            this._loadRequests.push(fileRequest);
-        });
-    }
-
 }

+ 164 - 0
Viewer/src/configuration/renderOnlyLoader.ts

@@ -0,0 +1,164 @@
+import { mapperManager } from './mappers';
+import { ViewerConfiguration } from './configuration';
+import { processConfigurationCompatibility } from './configurationCompatibility';
+
+import { deepmerge } from '../helper';
+import { IFileRequest, Tools } from '@babylonjs/core/Misc/tools';
+import { extendedConfiguration } from './types/extended';
+import { renderOnlyDefaultConfiguration } from './types/renderOnlyDefault';
+
+/**
+ * The configuration loader will load the configuration object from any source and will use the defined mapper to
+ * parse the object and return a conform ViewerConfiguration.
+ * It is a private member of the scene.
+ */
+export class RenderOnlyConfigurationLoader {
+
+    private _configurationCache: { [url: string]: any };
+
+    private _loadRequests: Array<IFileRequest>;
+
+    constructor(private _enableCache: boolean = false) {
+        this._configurationCache = {};
+        this._loadRequests = [];
+    }
+
+    private _getConfigurationTypeExcludeTemplate (types: string): ViewerConfiguration {
+        let config: ViewerConfiguration = {};
+        let typesSeparated = types.split(",");
+        typesSeparated.forEach((type) => {
+            switch (type.trim()) {
+                case 'default':
+                    config = deepmerge(config, renderOnlyDefaultConfiguration);
+                    break;
+                case 'none':
+                    break;
+                case 'extended':
+                default:
+                    config = deepmerge(config, extendedConfiguration);
+                    break;
+            }
+            if (config.extends) {
+                config = deepmerge(config, this._getConfigurationTypeExcludeTemplate(config.extends));
+            }
+        });
+        return config;
+    };
+
+    protected getExtendedConfig(type:string|undefined){
+        return this._getConfigurationTypeExcludeTemplate(type || "extended");
+    }
+
+    /**
+     * load a configuration object that is defined in the initial configuration provided.
+     * The viewer configuration can extend different types of configuration objects and have an extra configuration defined.
+     *
+     * @param initConfig the initial configuration that has the definitions of further configuration to load.
+     * @param callback an optional callback that will be called sync, if noconfiguration needs to be loaded or configuration is payload-only
+     * @returns A promise that delivers the extended viewer configuration, when done.
+     */
+    public loadConfiguration(initConfig: ViewerConfiguration = {}, callback?: (config: ViewerConfiguration) => void): Promise<ViewerConfiguration> {
+
+        let loadedConfig: ViewerConfiguration = deepmerge({}, initConfig);
+        this._processInitialConfiguration(loadedConfig);
+        
+        let extendedConfiguration = this.getExtendedConfig(loadedConfig.extends);
+
+        if (loadedConfig.configuration) {
+
+            let mapperType = "json";
+            return Promise.resolve().then(() => {
+                if (typeof loadedConfig.configuration === "string" || (loadedConfig.configuration && loadedConfig.configuration.url)) {
+                    // a file to load
+
+                    let url: string = '';
+                    if (typeof loadedConfig.configuration === "string") {
+                        url = loadedConfig.configuration;
+                    }
+
+                    // if configuration is an object
+                    if (typeof loadedConfig.configuration === "object" && loadedConfig.configuration.url) {
+                        url = loadedConfig.configuration.url;
+                        let type = loadedConfig.configuration.mapper;
+                        // empty string?
+                        if (!type) {
+                            // load mapper type from filename / url
+                            type = loadedConfig.configuration.url.split('.').pop();
+                        }
+                        mapperType = type || mapperType;
+                    }
+                    return this._loadFile(url);
+                } else {
+                    if (typeof loadedConfig.configuration === "object") {
+                        mapperType = loadedConfig.configuration.mapper || mapperType;
+                        return loadedConfig.configuration.payload || {};
+                    }
+                    return {};
+
+                }
+            }).then((data: any) => {
+                let mapper = mapperManager.getMapper(mapperType);
+                let parsed = deepmerge(mapper.map(data), loadedConfig);
+                let merged = deepmerge(extendedConfiguration, parsed);
+                processConfigurationCompatibility(merged);
+                if (callback) { callback(merged); }
+                return merged;
+            });
+        } else {
+            loadedConfig = deepmerge(extendedConfiguration, loadedConfig);
+            processConfigurationCompatibility(loadedConfig);
+            if (callback) { callback(loadedConfig); }
+            return Promise.resolve(loadedConfig);
+        }
+    }
+
+    /**
+     * Dispose the configuration loader. This will cancel file requests, if active.
+     */
+    public dispose() {
+        this._loadRequests.forEach((request) => {
+            request.abort();
+        });
+        this._loadRequests.length = 0;
+    }
+
+    /**
+     * This function will process the initial configuration and make needed changes for the viewer to work.
+     * @param config the mutable(!) initial configuration to process
+     */
+    private _processInitialConfiguration(config: ViewerConfiguration) {
+        if (config.model) {
+            if (typeof config.model === "string") {
+                config.model = {
+                    url: config.model
+                };
+            }
+        }
+    }
+
+    private _loadFile(url: string): Promise<any> {
+        let cacheReference = this._configurationCache;
+        if (this._enableCache && cacheReference[url]) {
+            return Promise.resolve(cacheReference[url]);
+        }
+
+        return new Promise((resolve, reject) => {
+            let fileRequest = Tools.LoadFile(url, (result) => {
+                let idx = this._loadRequests.indexOf(fileRequest);
+                if (idx !== -1) {
+                    this._loadRequests.splice(idx, 1);
+                }
+                if (this._enableCache) { cacheReference[url] = result; }
+                resolve(result);
+            }, undefined, undefined, false, (request, error: any) => {
+                let idx = this._loadRequests.indexOf(fileRequest);
+                if (idx !== -1) {
+                    this._loadRequests.splice(idx, 1);
+                }
+                reject(error);
+            });
+            this._loadRequests.push(fileRequest);
+        });
+    }
+    
+}

+ 4 - 2
Viewer/src/configuration/types/default.ts

@@ -2,12 +2,14 @@ import { ViewerConfiguration } from '../configuration';
 import { defaultTemplate, fillContainer, loadingScreen, defaultViewer, navbar, overlay, help, share, error } from '../../assets';
 import { babylonFont } from '../../assets/font';
 import * as images from '../../assets/img';
+import { renderOnlyDefaultConfiguration } from './renderOnlyDefault';
+import { deepmerge } from '../../helper';
 
 /**
  * The default configuration of the viewer, including templates (canvas, overly, loading screen)
  * This configuration doesn't hold specific parameters, and only defines objects that are needed for the viewer to fully work correctly.
  */
-export let defaultConfiguration: ViewerConfiguration = {
+export let defaultConfiguration: ViewerConfiguration = deepmerge(renderOnlyDefaultConfiguration, {
     version: "3.2.0-alpha4",
     templates: {
         main: {
@@ -119,4 +121,4 @@ export let defaultConfiguration: ViewerConfiguration = {
     },
     scene: {
     }
-};
+});

+ 32 - 0
Viewer/src/configuration/types/renderOnlyDefault.ts

@@ -0,0 +1,32 @@
+/**
+ * The render only default configuration of the viewer, including templates (canvas, overly, loading screen)
+ * This configuration doesn't hold specific parameters, and only defines objects that are needed for the render only viewer viewer to fully work correctly.
+ */
+export let renderOnlyDefaultConfiguration = {
+    version: "3.2.0-alpha4",
+    camera: {
+        behaviors: {
+            autoRotate: {
+                type: 0
+            },
+            framing: {
+                type: 2,
+                zoomOnBoundingInfo: true,
+                zoomStopsAnimation: false
+            },
+            bouncing: {
+                type: 1
+            }
+        }
+    },
+    skybox: {
+    },
+    ground: {
+        receiveShadows: true
+    },
+    engine: {
+        antialiasing: true
+    },
+    scene: {
+    }
+};

+ 6 - 3
Viewer/src/loader/modelLoader.ts

@@ -1,5 +1,8 @@
-import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, SceneLoader, Tags, Tools } from 'babylonjs';
-import { GLTFFileLoader, GLTFLoaderAnimationStartMode } from 'babylonjs-loaders';
+import {GLTFFileLoader, GLTFLoaderAnimationStartMode} from "@babylonjs/loaders/glTF"
+import { ISceneLoaderPlugin, ISceneLoaderPluginAsync } from '@babylonjs/core/Loading/sceneLoader';
+import { Tools } from '@babylonjs/core/Misc/tools';
+import { SceneLoader } from '@babylonjs/core/Loading/sceneLoader';
+import { Tags } from '@babylonjs/core/Misc/tags';
 
 import { ConfigurationContainer } from '../configuration/configurationContainer';
 import { IModelConfiguration } from '../configuration/interfaces/modelConfiguration';
@@ -63,7 +66,7 @@ export class ModelLoader {
      * @param modelConfiguration the modelConfiguration to use to load the model
      */
     public load(modelConfiguration: IModelConfiguration): ViewerModel {
-
+        
         const model = new ViewerModel(this._observablesManager, modelConfiguration, this._configurationContainer);
 
         model.loadId = this._loadId++;

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 75 - 20
Viewer/src/managers/sceneManager.ts


+ 0 - 4
Viewer/src/renderOnly.ts

@@ -1,4 +0,0 @@
-import { TemplateFreeViewer } from './viewer/templateFreeViewer';
-import '@babylonjs/loaders/glTF/2.0';
-
-export { TemplateFreeViewer };

+ 15 - 0
Viewer/src/renderOnlyIndex.ts

@@ -0,0 +1,15 @@
+import { TemplateFreeViewer } from './viewer/templateFreeViewer';
+// Required side effects
+import '@babylonjs/loaders/glTF/2.0';
+import "@babylonjs/core/Lights/Shadows/shadowGeneratorSceneComponent"
+import "@babylonjs/core/Debug/debugLayer";
+import "@babylonjs/core/Meshes/meshBuilder";
+// TODO bad error message without this
+import '@babylonjs/core/Loading/Plugins'
+import "@babylonjs/core/Materials/Textures/Loaders"
+import "@babylonjs/core/PostProcesses/RenderPipeline/postProcessRenderPipelineManagerSceneComponent";
+// This was needed at some point but no longer?
+// import "@babylonjs/core/Gamepads/gamepadSceneComponent"
+
+
+export { TemplateFreeViewer };

+ 34 - 6
Viewer/src/viewer/defaultViewer.ts

@@ -1,8 +1,14 @@
 
-import { ViewerConfiguration, IModelConfiguration, ILightConfiguration } from './../configuration';
+import { ViewerConfiguration, IModelConfiguration, ILightConfiguration, ISceneConfiguration } from './../configuration';
 import { Template, EventCallback } from '../templating/templateManager';
-import { AbstractViewer } from './viewer';
-import { SpotLight, Vector3, FilesInput } from 'babylonjs';
+import { FilesInput } from '@babylonjs/core/Misc/filesInput';
+import { SpotLight } from '@babylonjs/core/Lights/spotLight';
+import { Vector3 } from '@babylonjs/core/Maths/math';
+import { TemplateManager } from '../templating/templateManager';
+import { AbstractViewerWithTemplate } from './viewerWithTemplate';
+import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial';
+import { PBRMaterial } from '@babylonjs/core/Materials/PBR/pbrMaterial';
+import { extendClassWithConfig } from '../helper';
 import { ViewerModel } from '../model/viewerModel';
 import { IModelAnimation, AnimationState } from '../model/modelAnimation';
 import { IViewerTemplatePlugin } from '../templating/viewerTemplatePlugin';
@@ -13,7 +19,11 @@ import { PrintButtonPlugin } from '../templating/plugins/printButton';
  * The Default viewer is the default implementation of the AbstractViewer.
  * It uses the templating system to render a new canvas and controls.
  */
-export class DefaultViewer extends AbstractViewer {
+export class DefaultViewer extends AbstractViewerWithTemplate {
+    /**
+     * The corresponsing template manager of this viewer.
+     */
+    public templateManager: TemplateManager;
 
     public fullscreenElement?: Element;
 
@@ -24,7 +34,7 @@ export class DefaultViewer extends AbstractViewer {
      */
     constructor(public containerElement: Element, initialConfiguration: ViewerConfiguration = { extends: 'default' }) {
         super(containerElement, initialConfiguration);
-
+        
         this.onModelLoadedObservable.add(this._onModelLoaded);
         this.onModelRemovedObservable.add(() => {
             this._configureTemplate();
@@ -37,6 +47,22 @@ export class DefaultViewer extends AbstractViewer {
         });
 
         this.onInitDoneObservable.add(() => {
+            this.sceneManager.setDefaultMaterial = function(sceneConfig: ISceneConfiguration){
+                let conf = sceneConfig.defaultMaterial;
+                if(!conf){
+                    return;
+                }
+                if ((conf.materialType === 'standard' && this.scene.defaultMaterial.getClassName() !== 'StandardMaterial') ||
+                    (conf.materialType === 'pbr' && this.scene.defaultMaterial.getClassName() !== 'PBRMaterial')) {
+                    this.scene.defaultMaterial.dispose();
+                    if (conf.materialType === 'standard') {
+                        this.scene.defaultMaterial = new StandardMaterial("defaultMaterial", this.scene);
+                    } else {
+                        this.scene.defaultMaterial = new PBRMaterial("defaultMaterial", this.scene);
+                    }
+                }
+                extendClassWithConfig(this.scene.defaultMaterial, conf);
+            }
             if (!this.sceneManager.models.length) {
                 this.hideLoadingScreen();
             }
@@ -205,7 +231,7 @@ export class DefaultViewer extends AbstractViewer {
                 }
                 var speed = element.dataset["value"];
                 if (speed) {
-                    this._updateAnimationSpeed(speed);
+                    this._updateAnimationSpeed(speed);
                 }
                 break;
             case "progress-wrapper":
@@ -601,6 +627,8 @@ export class DefaultViewer extends AbstractViewer {
 
         super._onConfigurationLoaded(configuration);
 
+        this.templateManager = new TemplateManager(this.containerElement);
+
         // initialize the templates
         let templateConfiguration = this.configuration.templates || {};
 

+ 30 - 0
Viewer/src/viewer/templateFreeViewer.ts

@@ -0,0 +1,30 @@
+
+import { ViewerConfiguration } from './../configuration';
+import { AbstractViewer } from './viewer';
+
+export class TemplateFreeViewer extends AbstractViewer {
+    constructor(public containerElement: Element, initialConfiguration: ViewerConfiguration = {}) {
+        super(containerElement, initialConfiguration);
+        this._canvas = containerElement as HTMLCanvasElement
+    }
+    public initialize(){
+        let autoLoad = typeof this.configuration.model === 'string' || (this.configuration.model && this.configuration.model.url);
+        return this._initEngine().then((engine) => {
+            return this.onEngineInitObservable.notifyObserversWithPromise(engine);
+        }).then(() => {
+            this._initTelemetryEvents();
+            if (autoLoad) {
+                return this.loadModel(this.configuration.model!).catch(() => { }).then(() => { return this.sceneManager.scene; });
+            } else {
+                return this.sceneManager.scene || this.sceneManager.initScene(this.configuration.scene);
+            }
+        }).then(() => {
+            return this.onInitDoneObservable.notifyObserversWithPromise(this);
+        }).catch((e) => {
+            console.log(e.toString());
+            return this;
+        });
+    }
+    protected _prepareContainerElement() {
+    }
+}

+ 18 - 16
Viewer/src/viewer/viewer.ts

@@ -1,16 +1,24 @@
-import { Effect, Engine, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Observable, RenderingManager, Scene, SceneLoaderProgressEvent, TargetCamera, Tools, Vector3, Observer } from 'babylonjs';
+import { Engine } from '@babylonjs/core/Engines/engine';
+import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, SceneLoaderProgressEvent } from '@babylonjs/core/Loading/sceneLoader';
+import { Observable } from '@babylonjs/core/Misc/observable';
+import { Scene } from '@babylonjs/core/scene';
+import { RenderingManager } from '@babylonjs/core/Rendering/renderingManager';
+import { Vector3 } from '@babylonjs/core/Maths/math';
+import { TargetCamera } from '@babylonjs/core/Cameras/targetCamera';
+import { Tools } from '@babylonjs/core/Misc/tools';
+import { Effect } from '@babylonjs/core/Materials/effect';
+import { ConfigurationLoader } from '../configuration/loader';
 import { IModelConfiguration, IObserversConfiguration, ViewerConfiguration } from '../configuration/';
 import { processConfigurationCompatibility } from '../configuration/configurationCompatibility';
 import { ConfigurationContainer } from '../configuration/configurationContainer';
 import { viewerGlobals } from '../configuration/globals';
-import { ConfigurationLoader } from '../configuration/loader';
+import { RenderOnlyConfigurationLoader } from '../configuration/renderOnlyLoader';
 import { deepmerge } from '../helper/';
 import { ModelLoader } from '../loader/modelLoader';
 import { ObservablesManager } from '../managers/observablesManager';
 import { SceneManager } from '../managers/sceneManager';
 import { telemetryManager } from '../managers/telemetryManager';
 import { ViewerModel } from '../model/viewerModel';
-import { TemplateManager } from '../templating/templateManager';
 import { viewerManager } from './viewerManager';
 
 /**
@@ -18,13 +26,6 @@ import { viewerManager } from './viewerManager';
  * It is the basic implementation of the default viewer and is responsible of loading and showing the model and the templates
  */
 export abstract class AbstractViewer {
-
-    /**
-     * The corresponsing template manager of this viewer.
-     */
-    public templateManager: TemplateManager;
-    // TODO get the template manager to the default viewer, if no one is extending the abstract viewer
-
     /**
      * Babylon Engine corresponding with this viewer
      */
@@ -160,7 +161,7 @@ export abstract class AbstractViewer {
     /**
      * The configuration loader of this viewer
      */
-    protected _configurationLoader: ConfigurationLoader;
+    protected _configurationLoader: RenderOnlyConfigurationLoader;
 
     /**
      * Is the viewer already initialized. for internal use.
@@ -173,6 +174,10 @@ export abstract class AbstractViewer {
         return this._configurationContainer;
     }
 
+    protected getConfigurationLoader(){
+        return new RenderOnlyConfigurationLoader();
+    }
+
     constructor(public containerElement: Element, initialConfiguration: ViewerConfiguration = {}) {
         // if exists, use the container id. otherwise, generate a random string.
         if (containerElement.id) {
@@ -192,7 +197,7 @@ export abstract class AbstractViewer {
         RenderingManager.AUTOCLEAR = false;
 
         // extend the configuration
-        this._configurationLoader = new ConfigurationLoader();
+        this._configurationLoader = this.getConfigurationLoader();
         this._configurationLoader.loadConfiguration(initialConfiguration, (configuration) => {
             this._onConfigurationLoaded(configuration);
         });
@@ -417,8 +422,6 @@ export abstract class AbstractViewer {
             }));
         }
 
-        this.templateManager = new TemplateManager(this.containerElement);
-
         this.observablesManager.onViewerInitStartedObservable.notifyObservers(this);
     }
 
@@ -680,7 +683,6 @@ export abstract class AbstractViewer {
      * @returns a ViewerModel object that is not yet fully loaded.
      */
     public initModel(modelConfig: string | File | IModelConfiguration, clearScene: boolean = true): ViewerModel {
-
         let configuration: IModelConfiguration;
         if (typeof modelConfig === 'string') {
             configuration = {
@@ -748,7 +750,7 @@ export abstract class AbstractViewer {
             // We can decide here whether or not to cancel the lst load, but the developer can do that.
             return Promise.reject("another model is curently being loaded.");
         }
-
+        
         return Promise.resolve(this.sceneManager.scene).then((scene) => {
             if (!scene) { return this.sceneManager.initScene(this.configuration.scene, this.configuration.optimizer); }
             return scene;

+ 12 - 0
Viewer/src/viewer/viewerWithTemplate.ts

@@ -0,0 +1,12 @@
+import { AbstractViewer } from "./viewer";
+import { ConfigurationLoader } from "../configuration/loader";
+
+/**
+ * The AbstractViewer is the center of Babylon's viewer.
+ * It is the basic implementation of the default viewer and is responsible of loading and showing the model and the templates
+ */
+export abstract class AbstractViewerWithTemplate extends AbstractViewer {
+    protected getConfigurationLoader(){
+        return new ConfigurationLoader();
+    }
+}