viewer.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import { viewerManager } from './viewerManager';
  2. import { TemplateManager } from './../templateManager';
  3. import configurationLoader from './../configuration/loader';
  4. import { Observable, Engine, Scene, ArcRotateCamera, Vector3, SceneLoader, AbstractMesh, Mesh, HemisphericLight, Database } from 'babylonjs';
  5. import { ViewerConfiguration } from '../configuration/configuration';
  6. import { PromiseObservable } from '../util/promiseObservable';
  7. export abstract class AbstractViewer {
  8. public templateManager: TemplateManager;
  9. public engine: Engine;
  10. public scene: Scene;
  11. public baseId: string;
  12. protected configuration: ViewerConfiguration;
  13. // observables
  14. public onSceneInitObservable: PromiseObservable<Scene>;
  15. public onEngineInitObservable: PromiseObservable<Engine>;
  16. public onModelLoadedObservable: PromiseObservable<AbstractMesh[]>;
  17. constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = {}) {
  18. // if exists, use the container id. otherwise, generate a random string.
  19. if (containerElement.id) {
  20. this.baseId = containerElement.id;
  21. } else {
  22. this.baseId = containerElement.id = 'bjs' + Math.random().toString(32).substr(2, 8);
  23. }
  24. this.onSceneInitObservable = new PromiseObservable();
  25. this.onEngineInitObservable = new PromiseObservable();
  26. this.onModelLoadedObservable = new PromiseObservable();
  27. // add this viewer to the viewer manager
  28. viewerManager.addViewer(this);
  29. // create a new template manager. TODO - singleton?
  30. this.templateManager = new TemplateManager(containerElement);
  31. this.prepareContainerElement();
  32. // extend the configuration
  33. configurationLoader.loadConfiguration(initialConfiguration).then((configuration) => {
  34. this.configuration = configuration;
  35. // adding preconfigured functions
  36. if (this.configuration.observers) {
  37. if (this.configuration.observers.onEngineInit) {
  38. this.onEngineInitObservable.add(window[this.configuration.observers.onEngineInit]);
  39. }
  40. if (this.configuration.observers.onSceneInit) {
  41. this.onSceneInitObservable.add(window[this.configuration.observers.onSceneInit]);
  42. }
  43. if (this.configuration.observers.onModelLoaded) {
  44. this.onModelLoadedObservable.add(window[this.configuration.observers.onModelLoaded]);
  45. }
  46. }
  47. // initialize the templates
  48. let templateConfiguration = this.configuration.templates || {};
  49. this.templateManager.initTemplate(templateConfiguration);
  50. // when done, execute onTemplatesLoaded()
  51. this.templateManager.onAllLoaded.add(() => {
  52. this.onTemplatesLoaded();
  53. });
  54. });
  55. }
  56. public getBaseId(): string {
  57. return this.baseId;
  58. }
  59. protected abstract prepareContainerElement();
  60. /**
  61. * This function will execute when the HTML templates finished initializing.
  62. * It should initialize the engine and continue execution.
  63. *
  64. * @protected
  65. * @returns {Promise<AbstractViewer>} The viewer object will be returned after the object was loaded.
  66. * @memberof AbstractViewer
  67. */
  68. protected onTemplatesLoaded(): Promise<AbstractViewer> {
  69. return this.initEngine().then(() => {
  70. return this.loadModel();
  71. }).then(() => {
  72. return this;
  73. });
  74. }
  75. /**
  76. * Initialize the engine. Retruns a promise in case async calls are needed.
  77. *
  78. * @protected
  79. * @returns {Promise<Engine>}
  80. * @memberof Viewer
  81. */
  82. protected initEngine(): Promise<Engine> {
  83. let canvasElement = this.templateManager.getCanvas();
  84. if (!canvasElement) {
  85. return Promise.reject('Canvas element not found!');
  86. }
  87. let config = this.configuration.engine || {};
  88. // TDO enable further configuration
  89. this.engine = new Engine(canvasElement, !!config.antialiasing);
  90. // Disable manifest checking
  91. Database.IDBStorageEnabled = false;
  92. window.addEventListener('resize', () => {
  93. this.engine.resize();
  94. });
  95. this.engine.runRenderLoop(() => {
  96. this.scene && this.scene.render();
  97. });
  98. var scale = Math.max(0.5, 1 / (window.devicePixelRatio || 2));
  99. this.engine.setHardwareScalingLevel(scale);
  100. return this.onEngineInitObservable.notifyWithPromise(this.engine).then(() => {
  101. return this.engine;
  102. });
  103. }
  104. protected initScene(): Promise<Scene> {
  105. // if the scen exists, dispose it.
  106. if (this.scene) {
  107. this.scene.dispose();
  108. }
  109. // create a new scene
  110. this.scene = new Scene(this.engine);
  111. // make sure there is a default camera and light.
  112. this.scene.createDefaultCameraOrLight(true, true, true);
  113. if (this.configuration.scene && this.configuration.scene.debug) {
  114. this.scene.debugLayer.show();
  115. }
  116. return this.onSceneInitObservable.notifyWithPromise(this.scene).then(() => {
  117. return this.scene;
  118. });
  119. }
  120. public loadModel(model: any = this.configuration.model, clearScene: boolean = true): Promise<Scene> {
  121. this.configuration.model = model;
  122. let modelUrl = (typeof model === 'string') ? model : model.url;
  123. let parts = modelUrl.split('/');
  124. let filename = parts.pop();
  125. let base = parts.join('/') + '/';
  126. let plugin = (typeof model === 'string') ? undefined : model.loader;
  127. return Promise.resolve().then(() => {
  128. if (!this.scene || clearScene) return this.initScene();
  129. else return this.scene;
  130. }).then(() => {
  131. return new Promise<Array<AbstractMesh>>((resolve, reject) => {
  132. SceneLoader.ImportMesh(undefined, base, filename, this.scene, (meshes) => {
  133. resolve(meshes);
  134. }, undefined, (e, m, exception) => {
  135. console.log(m, exception);
  136. reject(m);
  137. }, plugin);
  138. });
  139. }).then((meshes: Array<AbstractMesh>) => {
  140. return this.onModelLoadedObservable.notifyWithPromise(meshes).then(() => {
  141. return this.scene;
  142. });
  143. });
  144. }
  145. }