modelLoader.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import { AbstractViewer } from "../viewer/viewer";
  2. import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, Tools, SceneLoader, Tags } from "babylonjs";
  3. import { GLTFFileLoader, GLTFLoaderAnimationStartMode } from "babylonjs-loaders";
  4. import { IModelConfiguration } from "../configuration/configuration";
  5. import { ViewerModel, ModelState } from "../model/viewerModel";
  6. import { ILoaderPlugin } from './plugins/loaderPlugin';
  7. import { TelemetryLoaderPlugin } from './plugins/telemetryLoaderPlugin';
  8. import { getLoaderPluginByName } from './plugins/';
  9. /**
  10. * An instance of the class is in charge of loading the model correctly.
  11. * This class will continously be expended with tasks required from the specific loaders Babylon has.
  12. *
  13. * A Model loader is unique per (Abstract)Viewer. It is being generated by the viewer
  14. */
  15. export class ModelLoader {
  16. private _loadId: number;
  17. private _disposed = false;
  18. private _loaders: Array<ISceneLoaderPlugin | ISceneLoaderPluginAsync>;
  19. private _plugins: Array<ILoaderPlugin>;
  20. /**
  21. * Create a new Model loader
  22. * @param _viewer the viewer using this model loader
  23. */
  24. constructor(private _viewer: AbstractViewer) {
  25. this._loaders = [];
  26. this._loadId = 0;
  27. this._plugins = [];
  28. }
  29. public addPlugin(plugin: ILoaderPlugin | string) {
  30. let actualPlugin: ILoaderPlugin = {};
  31. if (typeof plugin === 'string') {
  32. let loadedPlugin = getLoaderPluginByName(plugin);
  33. if (loadedPlugin) {
  34. actualPlugin = loadedPlugin;
  35. }
  36. } else {
  37. actualPlugin = plugin;
  38. }
  39. if (actualPlugin && this._plugins.indexOf(actualPlugin) === -1) {
  40. this._plugins.push(actualPlugin);
  41. }
  42. }
  43. /**
  44. * Load a model using predefined configuration
  45. * @param modelConfiguration the modelConfiguration to use to load the model
  46. */
  47. public load(modelConfiguration: IModelConfiguration): ViewerModel {
  48. const model = new ViewerModel(this._viewer, modelConfiguration);
  49. model.loadId = this._loadId++;
  50. if (!modelConfiguration.url) {
  51. model.state = ModelState.ERROR;
  52. Tools.Error("No URL provided");
  53. return model;
  54. }
  55. let filename = Tools.GetFilename(modelConfiguration.url) || modelConfiguration.url;
  56. let base = modelConfiguration.root || Tools.GetFolderPath(modelConfiguration.url);
  57. let plugin = modelConfiguration.loader;
  58. model.loader = SceneLoader.ImportMesh(undefined, base, filename, this._viewer.sceneManager.scene, (meshes, particleSystems, skeletons, animationGroups) => {
  59. meshes.forEach(mesh => {
  60. Tags.AddTagsTo(mesh, "viewerMesh");
  61. model.addMesh(mesh);
  62. });
  63. model.particleSystems = particleSystems;
  64. model.skeletons = skeletons;
  65. for (const animationGroup of animationGroups) {
  66. model.addAnimationGroup(animationGroup);
  67. }
  68. this._checkAndRun("onLoaded", model);
  69. model.onLoadedObservable.notifyObserversWithPromise(model);
  70. }, (progressEvent) => {
  71. this._checkAndRun("onProgress", progressEvent);
  72. model.onLoadProgressObservable.notifyObserversWithPromise(progressEvent);
  73. }, (scene, m, exception) => {
  74. model.state = ModelState.ERROR;
  75. Tools.Error("Load Error: There was an error loading the model. " + m);
  76. this._checkAndRun("onError", m, exception);
  77. model.onLoadErrorObservable.notifyObserversWithPromise({ message: m, exception: exception });
  78. }, plugin)!;
  79. if (model.loader.name === "gltf") {
  80. let gltfLoader = (<GLTFFileLoader>model.loader);
  81. gltfLoader.animationStartMode = GLTFLoaderAnimationStartMode.NONE;
  82. gltfLoader.compileMaterials = true;
  83. // if ground is set to "mirror":
  84. if (this._viewer.configuration.ground && typeof this._viewer.configuration.ground === 'object' && this._viewer.configuration.ground.mirror) {
  85. gltfLoader.useClipPlane = true;
  86. }
  87. Object.keys(gltfLoader).filter(name => name.indexOf('on') === 0 && name.indexOf('Observable') !== -1).forEach(functionName => {
  88. gltfLoader[functionName].add((payload) => {
  89. this._checkAndRun(functionName.replace("Observable", ''), payload);
  90. });
  91. });
  92. gltfLoader.onParsedObservable.add((data) => {
  93. if (data && data.json && data.json['asset']) {
  94. model.loadInfo = data.json['asset'];
  95. }
  96. })
  97. }
  98. this._checkAndRun("onInit", model.loader, model);
  99. this._loaders.push(model.loader);
  100. return model;
  101. }
  102. public cancelLoad(model: ViewerModel) {
  103. const loader = model.loader || this._loaders[model.loadId];
  104. // ATM only available in the GLTF Loader
  105. if (loader && loader.name === "gltf") {
  106. let gltfLoader = (<GLTFFileLoader>loader);
  107. gltfLoader.dispose();
  108. model.state = ModelState.CANCELED;
  109. } else {
  110. Tools.Warn("This type of loader cannot cancel the request");
  111. }
  112. }
  113. /**
  114. * dispose the model loader.
  115. * If loaders are registered and are in the middle of loading, they will be disposed and the request(s) will be cancelled.
  116. */
  117. public dispose() {
  118. this._loaders.forEach(loader => {
  119. if (loader.name === "gltf") {
  120. (<GLTFFileLoader>loader).dispose();
  121. }
  122. });
  123. this._loaders.length = 0;
  124. this._disposed = true;
  125. }
  126. private _checkAndRun(functionName: string, ...payload: Array<any>) {
  127. if (this._disposed) return;
  128. this._plugins.filter(p => p[functionName]).forEach(plugin => {
  129. try {
  130. plugin[functionName].apply(this, payload);
  131. } catch (e) { }
  132. })
  133. }
  134. }