瀏覽代碼

loader plugin system

Raanan Weber 7 年之前
父節點
當前提交
85e5e2d843

+ 1 - 1
Viewer/src/index.ts

@@ -5,7 +5,7 @@ import { viewerManager } from './viewer/viewerManager';
 import { DefaultViewer } from './viewer/defaultViewer';
 import { AbstractViewer } from './viewer/viewer';
 import { telemetryManager } from './telemetryManager';
-import { ModelLoader } from './model/modelLoader';
+import { ModelLoader } from './loader/modelLoader';
 import { ViewerModel, ModelState } from './model/viewerModel';
 import { AnimationPlayMode, AnimationState } from './model/modelAnimation';
 

+ 34 - 4
Viewer/src/model/modelLoader.ts

@@ -1,8 +1,10 @@
 import { AbstractViewer } from "../viewer/viewer";
-import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, Tools, SceneLoader, Tags } from "babylonjs";
+import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, Tools, SceneLoader, Tags, GLTFLoaderAnimationStartMode } from "babylonjs";
 import { GLTFFileLoader } from "babylonjs-loaders";
 import { IModelConfiguration } from "../configuration/configuration";
-import { ViewerModel, ModelState } from "./viewerModel";
+import { ViewerModel, ModelState } from "../model/viewerModel";
+import { ILoaderPlugin } from './plugins/loaderPlugin';
+import { TelemetryLoaderPlugin } from './plugins/telemetryLoaderPlugin';
 
 /**
  * An instance of the class is in charge of loading the model correctly.
@@ -17,6 +19,8 @@ export class ModelLoader {
 
     private _loaders: Array<ISceneLoaderPlugin | ISceneLoaderPluginAsync>;
 
+    private _plugins: Array<ILoaderPlugin>;
+
     /**
      * Create a new Model loader
      * @param _viewer the viewer using this model loader
@@ -24,6 +28,13 @@ export class ModelLoader {
     constructor(private _viewer: AbstractViewer) {
         this._loaders = [];
         this._loadId = 0;
+        this._plugins = [];
+
+        this.addPlugin(new TelemetryLoaderPlugin());
+    }
+
+    public addPlugin(plugin: ILoaderPlugin) {
+        this._plugins.push(plugin);
     }
 
     /**
@@ -58,20 +69,31 @@ export class ModelLoader {
                 model.addAnimationGroup(animationGroup);
             }
 
+            this._checkAndRun("onLoaded", model);
             model.onLoadedObservable.notifyObserversWithPromise(model);
         }, (progressEvent) => {
+            this._checkAndRun("onProgress", progressEvent);
             model.onLoadProgressObservable.notifyObserversWithPromise(progressEvent);
-        }, (e, m, exception) => {
+        }, (scene, m, exception) => {
             model.state = ModelState.ERROR;
             Tools.Error("Load Error: There was an error loading the model. " + m);
+            this._checkAndRun("onError", m, exception);
             model.onLoadErrorObservable.notifyObserversWithPromise({ message: m, exception: exception });
         }, plugin)!;
 
         if (model.loader.name === "gltf") {
             let gltfLoader = (<GLTFFileLoader>model.loader);
-            gltfLoader.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.NONE;
+            gltfLoader.animationStartMode = GLTFLoaderAnimationStartMode.NONE;
+            gltfLoader.compileMaterials = true;
+            Object.keys(gltfLoader).filter(name => name.indexOf('on') === 0 && name.indexOf('Observable') !== -1).forEach(functionName => {
+                gltfLoader[functionName].add((payload) => {
+                    this._checkAndRun(functionName.replace("Observable", ''), payload);
+                });
+            });
         }
 
+        this._checkAndRun("onInit", model.loader, model);
+
         this._loaders.push(model.loader);
 
         return model;
@@ -102,4 +124,12 @@ export class ModelLoader {
         this._loaders.length = 0;
         this._disposed = true;
     }
+
+    private _checkAndRun(functionName: string, ...payload: Array<any>) {
+        this._plugins.filter(p => p[functionName]).forEach(plugin => {
+            try {
+                plugin[functionName].apply(this, payload);
+            } catch (e) { }
+        })
+    }
 }

+ 16 - 0
Viewer/src/loader/plugins/loaderPlugin.ts

@@ -0,0 +1,16 @@
+import { ViewerModel } from "../../model/viewerModel";
+import { IGLTFLoaderExtension, IGLTFLoaderData } from "babylonjs-loaders";
+import { AbstractMesh, ISceneLoaderPlugin, ISceneLoaderPluginAsync, SceneLoaderProgressEvent, BaseTexture, Material } from "babylonjs";
+
+export interface ILoaderPlugin {
+    onInit?: (loader: ISceneLoaderPlugin | ISceneLoaderPluginAsync, model: ViewerModel) => void;
+    onLoaded?: (model: ViewerModel) => void;
+    onError?: (message: string, exception?: any) => void;
+    onProgress?: (progressEvent: SceneLoaderProgressEvent) => void;
+    onExtensionLoaded?: (extension: IGLTFLoaderExtension) => void;
+    onParsed?: (parsedData: IGLTFLoaderData) => void;
+    onMeshLoaded?: (mesh: AbstractMesh) => void;
+    onTextureLoaded?: (texture: BaseTexture) => void;
+    onMaterialLoaded?: (material: Material) => void;
+    onComplete?: () => void;
+}

+ 46 - 0
Viewer/src/loader/plugins/telemetryLoaderPlugin.ts

@@ -0,0 +1,46 @@
+import { ILoaderPlugin } from "./loaderPlugin";
+import { telemetryManager } from "../../telemetryManager";
+import { ViewerModel } from "../..";
+import { Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs";
+
+
+export class TelemetryLoaderPlugin implements ILoaderPlugin {
+
+    private _model: ViewerModel;
+
+    private _loadStart: number;
+    private _loadEnd: number;
+
+    public onInit(loader: ISceneLoaderPlugin | ISceneLoaderPluginAsync, model: ViewerModel) {
+        this._model = model;
+        this._loadStart = Tools.Now;
+    }
+
+    public onLoaded(model: ViewerModel) {
+        telemetryManager.broadcast("Load First LOD Complete", model.getViewer(), {
+            model: model,
+            loadTime: Tools.Now - this._loadStart
+        });
+        telemetryManager.flushWebGLErrors(this._model.getViewer());
+    }
+
+    public onError(message: string, exception: any) {
+        this._loadEnd = Tools.Now;
+        telemetryManager.broadcast("Load Error", this._model.getViewer(), {
+            model: this._model,
+            loadTime: this._loadEnd - this._loadStart
+        });
+
+        telemetryManager.flushWebGLErrors(this._model.getViewer());
+    }
+
+    public onComplete() {
+        this._loadEnd = Tools.Now;
+        telemetryManager.broadcast("Load Complete", this._model.getViewer(), {
+            model: this._model,
+            loadTime: this._loadEnd - this._loadStart
+        });
+
+        telemetryManager.flushWebGLErrors(this._model.getViewer());
+    }
+}

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

@@ -104,6 +104,13 @@ export class ViewerModel implements IDisposable {
     }
 
     /**
+     * Get the viewer showing this model
+     */
+    public getViewer() {
+        return this._viewer;
+    }
+
+    /**
      * Add a mesh to this model.
      * Any mesh that has no parent will be provided with the root mesh as its new parent.
      * 

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

@@ -8,11 +8,11 @@ import { ViewerConfiguration, ISceneConfiguration, ISceneOptimizerConfiguration,
 import * as deepmerge from '../../assets/deepmerge.min.js';
 import { ViewerModel } from '../model/viewerModel';
 import { GroupModelAnimation } from '../model/modelAnimation';
-import { ModelLoader } from '../model/modelLoader';
+import { ModelLoader } from '../loader/modelLoader';
 import { CameraBehavior } from '../interfaces';
 import { viewerGlobals } from '../configuration/globals';
 import { extendClassWithConfig } from '../helper';
-import { telemetryManager } from '..';
+import { telemetryManager } from '../telemetryManager';
 
 /**
  * The AbstractViewr is the center of Babylon's viewer.