modelLoader.ts 7.5 KB

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