modelLoader.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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 base: string;
  56. let filename: any;
  57. if (modelConfiguration.file) {
  58. base = "file:";
  59. filename = modelConfiguration.file;
  60. }
  61. else {
  62. filename = Tools.GetFilename(modelConfiguration.url) || modelConfiguration.url;
  63. base = modelConfiguration.root || Tools.GetFolderPath(modelConfiguration.url);
  64. }
  65. let plugin = modelConfiguration.loader;
  66. model.loader = SceneLoader.ImportMesh(undefined, base, filename, this._viewer.sceneManager.scene, (meshes, particleSystems, skeletons, animationGroups) => {
  67. meshes.forEach(mesh => {
  68. Tags.AddTagsTo(mesh, "viewerMesh");
  69. model.addMesh(mesh);
  70. });
  71. model.particleSystems = particleSystems;
  72. model.skeletons = skeletons;
  73. for (const animationGroup of animationGroups) {
  74. model.addAnimationGroup(animationGroup);
  75. }
  76. this._checkAndRun("onLoaded", model);
  77. this._viewer.sceneManager.scene.executeWhenReady(() => {
  78. model.onLoadedObservable.notifyObservers(model);
  79. });
  80. }, (progressEvent) => {
  81. this._checkAndRun("onProgress", progressEvent);
  82. model.onLoadProgressObservable.notifyObserversWithPromise(progressEvent);
  83. }, (scene, m, exception) => {
  84. model.state = ModelState.ERROR;
  85. Tools.Error("Load Error: There was an error loading the model. " + m);
  86. this._checkAndRun("onError", m, exception);
  87. model.onLoadErrorObservable.notifyObserversWithPromise({ message: m, exception: exception });
  88. }, plugin)!;
  89. if (model.loader.name === "gltf") {
  90. let gltfLoader = (<GLTFFileLoader>model.loader);
  91. gltfLoader.animationStartMode = GLTFLoaderAnimationStartMode.NONE;
  92. gltfLoader.compileMaterials = true;
  93. if (!modelConfiguration.file) {
  94. gltfLoader.rewriteRootURL = (rootURL, responseURL) => {
  95. return modelConfiguration.root || Tools.GetFolderPath(responseURL || modelConfiguration.url || '');
  96. };
  97. }
  98. // if ground is set to "mirror":
  99. if (this._viewer.configuration.ground && typeof this._viewer.configuration.ground === 'object' && this._viewer.configuration.ground.mirror) {
  100. gltfLoader.useClipPlane = true;
  101. }
  102. Object.keys(gltfLoader).filter(name => name.indexOf('on') === 0 && name.indexOf('Observable') !== -1).forEach(functionName => {
  103. gltfLoader[functionName].add((payload) => {
  104. this._checkAndRun(functionName.replace("Observable", ''), payload);
  105. });
  106. });
  107. gltfLoader.onParsedObservable.add((data) => {
  108. if (data && data.json && data.json['asset']) {
  109. model.loadInfo = data.json['asset'];
  110. }
  111. });
  112. gltfLoader.onCompleteObservable.add(() => {
  113. model.loaderDone = true;
  114. });
  115. } else {
  116. model.loaderDone = true;
  117. }
  118. this._checkAndRun("onInit", model.loader, model);
  119. this._loaders.push(model.loader);
  120. return model;
  121. }
  122. public cancelLoad(model: ViewerModel) {
  123. const loader = model.loader || this._loaders[model.loadId];
  124. // ATM only available in the GLTF Loader
  125. if (loader && loader.name === "gltf") {
  126. let gltfLoader = (<GLTFFileLoader>loader);
  127. gltfLoader.dispose();
  128. model.state = ModelState.CANCELED;
  129. } else {
  130. Tools.Warn("This type of loader cannot cancel the request");
  131. }
  132. }
  133. /**
  134. * dispose the model loader.
  135. * If loaders are registered and are in the middle of loading, they will be disposed and the request(s) will be cancelled.
  136. */
  137. public dispose() {
  138. this._loaders.forEach(loader => {
  139. if (loader.name === "gltf") {
  140. (<GLTFFileLoader>loader).dispose();
  141. }
  142. });
  143. this._loaders.length = 0;
  144. this._disposed = true;
  145. }
  146. private _checkAndRun(functionName: string, ...payload: Array<any>) {
  147. if (this._disposed) return;
  148. this._plugins.filter(p => p[functionName]).forEach(plugin => {
  149. try {
  150. plugin[functionName].apply(this, payload);
  151. } catch (e) { }
  152. })
  153. }
  154. }