defaultViewer.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import { ViewerConfiguration, IModelConfiguration, ILightConfiguration } from './../configuration/configuration';
  2. import { Template, EventCallback } from './../templateManager';
  3. import { AbstractViewer } from './viewer';
  4. import { SpotLight, MirrorTexture, Plane, ShadowGenerator, Texture, BackgroundMaterial, Observable, ShadowLight, CubeTexture, BouncingBehavior, FramingBehavior, Behavior, Light, Engine, Scene, AutoRotationBehavior, AbstractMesh, Quaternion, StandardMaterial, ArcRotateCamera, ImageProcessingConfiguration, Color3, Vector3, SceneLoader, Mesh, HemisphericLight } from 'babylonjs';
  5. import { CameraBehavior } from '../interfaces';
  6. import { ViewerModel } from '../model/viewerModel';
  7. export class DefaultViewer extends AbstractViewer {
  8. constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = { extends: 'default' }) {
  9. super(containerElement, initialConfiguration);
  10. this.onModelLoadedObservable.add(this.onModelLoaded);
  11. }
  12. public initScene(): Promise<Scene> {
  13. return super.initScene().then(() => {
  14. this.extendClassWithConfig(this.scene, this.configuration.scene);
  15. return this.scene;
  16. })
  17. }
  18. protected onTemplatesLoaded() {
  19. this.showLoadingScreen();
  20. // navbar
  21. this.initNavbar();
  22. // close overlay button
  23. let closeButton = document.getElementById('close-button');
  24. if (closeButton) {
  25. closeButton.addEventListener('pointerdown', () => {
  26. this.hideOverlayScreen();
  27. })
  28. }
  29. return super.onTemplatesLoaded();
  30. }
  31. private initNavbar() {
  32. let navbar = this.templateManager.getTemplate('navBar');
  33. if (navbar) {
  34. let navbarHeight = navbar.parent.clientHeight + 'px';
  35. let navbarShown: boolean = true;
  36. let timeoutCancel /*: number*/;
  37. let triggerNavbar = function (show: boolean = false, evt: PointerEvent) {
  38. // only left-click on no-button.
  39. if (!navbar || evt.button > 0) return;
  40. // clear timeout
  41. timeoutCancel && clearTimeout(timeoutCancel);
  42. // if state is the same, do nothing
  43. if (show === navbarShown) return;
  44. //showing? simply show it!
  45. if (show) {
  46. navbar.parent.style.bottom = show ? '0px' : '-' + navbarHeight;
  47. navbarShown = show;
  48. } else {
  49. let visibilityTimeout = 2000;
  50. if (navbar.configuration.params && navbar.configuration.params.visibilityTimeout !== undefined) {
  51. visibilityTimeout = <number>navbar.configuration.params.visibilityTimeout;
  52. }
  53. // not showing? set timeout until it is removed.
  54. timeoutCancel = setTimeout(function () {
  55. if (navbar) {
  56. navbar.parent.style.bottom = '-' + navbarHeight;
  57. }
  58. navbarShown = show;
  59. }, visibilityTimeout);
  60. }
  61. }
  62. this.templateManager.eventManager.registerCallback('viewer', triggerNavbar.bind(this, false), 'pointerout');
  63. this.templateManager.eventManager.registerCallback('viewer', triggerNavbar.bind(this, true), 'pointerdown');
  64. this.templateManager.eventManager.registerCallback('viewer', triggerNavbar.bind(this, false), 'pointerup');
  65. this.templateManager.eventManager.registerCallback('navBar', triggerNavbar.bind(this, true), 'pointerover');
  66. // other events
  67. let viewerTemplate = this.templateManager.getTemplate('viewer');
  68. let viewerElement = viewerTemplate && viewerTemplate.parent;
  69. // full screen
  70. let triggerFullscren = (eventData: EventCallback) => {
  71. if (viewerElement) {
  72. let fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement || (<any>document).mozFullScreenElement || (<any>document).msFullscreenElement;
  73. if (!fullscreenElement) {
  74. let requestFullScreen = viewerElement.requestFullscreen || viewerElement.webkitRequestFullscreen || (<any>viewerElement).msRequestFullscreen || (<any>viewerElement).mozRequestFullScreen;
  75. requestFullScreen.call(viewerElement);
  76. } else {
  77. let exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen || (<any>document).msExitFullscreen || (<any>document).mozCancelFullScreen
  78. exitFullscreen.call(document);
  79. }
  80. }
  81. }
  82. this.templateManager.eventManager.registerCallback('navBar', triggerFullscren, 'pointerdown', '#fullscreen-button');
  83. }
  84. }
  85. protected prepareContainerElement() {
  86. this.containerElement.style.position = 'relative';
  87. this.containerElement.style.display = 'flex';
  88. }
  89. protected configureModel(modelConfiguration: Partial<IModelConfiguration>, model: ViewerModel) {
  90. super.configureModel(modelConfiguration, model);
  91. let navbar = this.templateManager.getTemplate('navBar');
  92. if (!navbar) return;
  93. let metadataContainer = navbar.parent.querySelector('#model-metadata');
  94. if (metadataContainer) {
  95. if (modelConfiguration.title !== undefined) {
  96. let element = metadataContainer.querySelector('span.model-title');
  97. if (element) {
  98. element.innerHTML = modelConfiguration.title;
  99. }
  100. }
  101. if (modelConfiguration.subtitle !== undefined) {
  102. let element = metadataContainer.querySelector('span.model-subtitle');
  103. if (element) {
  104. element.innerHTML = modelConfiguration.subtitle;
  105. }
  106. }
  107. if (modelConfiguration.thumbnail !== undefined) {
  108. (<HTMLDivElement>metadataContainer.querySelector('.thumbnail')).style.backgroundImage = `url('${modelConfiguration.thumbnail}')`;
  109. }
  110. }
  111. }
  112. public loadModel(model: any = this.configuration.model): Promise<Scene> {
  113. this.showLoadingScreen();
  114. return super.loadModel(model, true).catch((error) => {
  115. console.log(error);
  116. this.hideLoadingScreen();
  117. this.showOverlayScreen('error');
  118. return this.scene;
  119. });
  120. }
  121. private onModelLoaded = (model: ViewerModel) => {
  122. // with a short timeout, making sure everything is there already.
  123. let hideLoadingDelay = 500;
  124. if (this.configuration.lab && this.configuration.lab.hideLoadingDelay !== undefined) {
  125. hideLoadingDelay = this.configuration.lab.hideLoadingDelay;
  126. }
  127. setTimeout(() => {
  128. this.hideLoadingScreen();
  129. }, hideLoadingDelay);
  130. return;
  131. }
  132. public showOverlayScreen(subScreen: string) {
  133. let template = this.templateManager.getTemplate('overlay');
  134. if (!template) return Promise.resolve('Overlay template not found');
  135. return template.show((template => {
  136. var canvasRect = this.containerElement.getBoundingClientRect();
  137. var canvasPositioning = window.getComputedStyle(this.containerElement).position;
  138. template.parent.style.display = 'flex';
  139. template.parent.style.width = canvasRect.width + "px";
  140. template.parent.style.height = canvasRect.height + "px";
  141. template.parent.style.opacity = "1";
  142. let subTemplate = this.templateManager.getTemplate(subScreen);
  143. if (!subTemplate) {
  144. return Promise.reject(subScreen + ' template not found');
  145. }
  146. return subTemplate.show((template => {
  147. template.parent.style.display = 'flex';
  148. return Promise.resolve(template);
  149. }));
  150. }));
  151. }
  152. public hideOverlayScreen() {
  153. let template = this.templateManager.getTemplate('overlay');
  154. if (!template) return Promise.resolve('Overlay template not found');
  155. return template.hide((template => {
  156. template.parent.style.opacity = "0";
  157. let onTransitionEnd = () => {
  158. template.parent.removeEventListener("transitionend", onTransitionEnd);
  159. template.parent.style.display = 'none';
  160. }
  161. template.parent.addEventListener("transitionend", onTransitionEnd);
  162. let overlays = template.parent.querySelectorAll('.overlay');
  163. if (overlays) {
  164. for (let i = 0; i < overlays.length; ++i) {
  165. let htmlElement = <HTMLElement>overlays.item(i);
  166. htmlElement.style.display = 'none';
  167. }
  168. }
  169. /*return this.templateManager.getTemplate(subScreen).show((template => {
  170. template.parent.style.display = 'none';
  171. return Promise.resolve(template);
  172. }));*/
  173. return Promise.resolve(template);
  174. }));
  175. }
  176. public showLoadingScreen() {
  177. let template = this.templateManager.getTemplate('loadingScreen');
  178. if (!template) return Promise.resolve('Loading Screen template not found');
  179. return template.show((template => {
  180. var canvasRect = this.containerElement.getBoundingClientRect();
  181. var canvasPositioning = window.getComputedStyle(this.containerElement).position;
  182. template.parent.style.display = 'flex';
  183. template.parent.style.width = canvasRect.width + "px";
  184. template.parent.style.height = canvasRect.height + "px";
  185. template.parent.style.opacity = "1";
  186. // from the configuration!!!
  187. template.parent.style.backgroundColor = "black";
  188. return Promise.resolve(template);
  189. }));
  190. }
  191. public hideLoadingScreen() {
  192. let template = this.templateManager.getTemplate('loadingScreen');
  193. if (!template) return Promise.resolve('Loading Screen template not found');
  194. return template.hide((template => {
  195. template.parent.style.opacity = "0";
  196. let onTransitionEnd = () => {
  197. template.parent.removeEventListener("transitionend", onTransitionEnd);
  198. template.parent.style.display = 'none';
  199. }
  200. template.parent.addEventListener("transitionend", onTransitionEnd);
  201. return Promise.resolve(template);
  202. }));
  203. }
  204. protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean } = {}, model: ViewerModel) {
  205. super.configureLights(lightsConfiguration, model);
  206. // labs feature - flashlight
  207. if (this.configuration.lab && this.configuration.lab.flashlight) {
  208. let pointerPosition = BABYLON.Vector3.Zero();
  209. let lightTarget;
  210. let angle = 0.5;
  211. let exponent = Math.PI / 2;
  212. if (typeof this.configuration.lab.flashlight === "object") {
  213. exponent = this.configuration.lab.flashlight.exponent || exponent;
  214. angle = this.configuration.lab.flashlight.angle || angle;
  215. }
  216. var flashlight = new SpotLight("flashlight", Vector3.Zero(),
  217. Vector3.Zero(), exponent, angle, this.scene);
  218. if (typeof this.configuration.lab.flashlight === "object") {
  219. flashlight.intensity = this.configuration.lab.flashlight.intensity || flashlight.intensity;
  220. if (this.configuration.lab.flashlight.diffuse) {
  221. flashlight.diffuse.r = this.configuration.lab.flashlight.diffuse.r;
  222. flashlight.diffuse.g = this.configuration.lab.flashlight.diffuse.g;
  223. flashlight.diffuse.b = this.configuration.lab.flashlight.diffuse.b;
  224. }
  225. if (this.configuration.lab.flashlight.specular) {
  226. flashlight.specular.r = this.configuration.lab.flashlight.specular.r;
  227. flashlight.specular.g = this.configuration.lab.flashlight.specular.g;
  228. flashlight.specular.b = this.configuration.lab.flashlight.specular.b;
  229. }
  230. }
  231. this.scene.constantlyUpdateMeshUnderPointer = true;
  232. this.scene.onPointerObservable.add((eventData, eventState) => {
  233. if (eventData.type === 4 && eventData.pickInfo) {
  234. lightTarget = (eventData.pickInfo.pickedPoint);
  235. } else {
  236. lightTarget = undefined;
  237. }
  238. });
  239. let updateFlashlightFunction = () => {
  240. if (this.camera && flashlight) {
  241. flashlight.position.copyFrom(this.camera.position);
  242. if (lightTarget) {
  243. lightTarget.subtractToRef(flashlight.position, flashlight.direction);
  244. }
  245. }
  246. }
  247. this.scene.registerBeforeRender(updateFlashlightFunction);
  248. this.registeredOnBeforerenderFunctions.push(updateFlashlightFunction);
  249. }
  250. }
  251. }