GLTFTab.ts 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /// <reference path="../../../dist/preview release/gltf2Interface/babylon.glTF2Interface.d.ts"/>
  2. /// <reference path="../../../dist/preview release/loaders/babylon.glTF2FileLoader.d.ts"/>
  3. /// <reference path="../../../dist/preview release/serializers/babylon.glTF2Serializer.d.ts"/>
  4. declare function Split(elements: HTMLElement[], options: any): any;
  5. module INSPECTOR {
  6. interface ILoaderExtensionSettings {
  7. [extensionName: string]: {
  8. [settingName: string]: any
  9. }
  10. };
  11. export class GLTFTab extends Tab {
  12. private static _LoaderExtensionSettings: ILoaderExtensionSettings | null = null;
  13. private _inspector: Inspector;
  14. private _actions: HTMLDivElement;
  15. private _detailsPanel: DetailPanel | null = null;
  16. private _split: any;
  17. public static get IsSupported(): boolean {
  18. return !!(BABYLON.SceneLoader && BABYLON.GLTFFileLoader && BABYLON.GLTF2.GLTFLoader) || !!BABYLON.GLTF2Export;
  19. }
  20. /** @hidden */
  21. public static _Initialize(): void {
  22. // Must register with OnPluginActivatedObservable as early as possible to
  23. // override the default settings for each extension.
  24. BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
  25. if (loader.name === "gltf" && GLTFTab._LoaderExtensionSettings) {
  26. loader.onExtensionLoadedObservable.add(extension => {
  27. const settings = GLTFTab._LoaderExtensionSettings![extension.name];
  28. for (const settingName in settings) {
  29. (extension as any)[settingName] = settings[settingName];
  30. }
  31. });
  32. }
  33. });
  34. }
  35. constructor(tabbar: TabBar, inspector: Inspector) {
  36. super(tabbar, 'GLTF');
  37. this._inspector = inspector;
  38. this._panel = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
  39. this._actions = Helpers.CreateDiv('gltf-actions', this._panel) as HTMLDivElement;
  40. this._actions.addEventListener('click', event => {
  41. this._closeDetailsPanel();
  42. });
  43. if (BABYLON.SceneLoader && BABYLON.GLTFFileLoader && BABYLON.GLTF2.GLTFLoader) {
  44. this._addImport();
  45. }
  46. if (BABYLON.GLTF2Export) {
  47. this._addExport();
  48. }
  49. }
  50. public dispose() {
  51. if (this._detailsPanel) {
  52. this._detailsPanel.dispose();
  53. }
  54. }
  55. private _addImport() {
  56. const importActions = Helpers.CreateDiv(null, this._actions) as HTMLDivElement;
  57. this._getLoaderExtensionOverridesAsync().then(loaderExtensionSettings => {
  58. const title = Helpers.CreateDiv('gltf-title', importActions);
  59. title.textContent = 'Import';
  60. const extensionActions = Helpers.CreateDiv('gltf-actions', importActions) as HTMLDivElement;
  61. const extensionsTitle = Helpers.CreateDiv('gltf-title', extensionActions) as HTMLDivElement;
  62. extensionsTitle.textContent = "Extensions";
  63. for (const extensionName in loaderExtensionSettings) {
  64. const settings = loaderExtensionSettings[extensionName];
  65. const extensionAction = Helpers.CreateDiv('gltf-action', extensionActions);
  66. extensionAction.addEventListener('click', event => {
  67. if (this._updateLoaderExtensionDetails(settings)) {
  68. event.stopPropagation();
  69. }
  70. });
  71. const checkbox = Helpers.CreateElement('span', 'gltf-checkbox', extensionAction);
  72. if (settings.enabled) {
  73. checkbox.classList.add('action', 'active');
  74. }
  75. checkbox.addEventListener('click', () => {
  76. checkbox.classList.toggle('active');
  77. settings.enabled = checkbox.classList.contains('active');
  78. });
  79. const label = Helpers.CreateElement('span', null, extensionAction);
  80. label.textContent = extensionName;
  81. }
  82. });
  83. }
  84. private _getLoaderExtensionOverridesAsync(): Promise<ILoaderExtensionSettings> {
  85. if (GLTFTab._LoaderExtensionSettings) {
  86. return Promise.resolve(GLTFTab._LoaderExtensionSettings);
  87. }
  88. const loaderExtensionSettings: ILoaderExtensionSettings = {};
  89. const engine = new BABYLON.NullEngine();
  90. const scene = new BABYLON.Scene(engine);
  91. const loader = new BABYLON.GLTF2.GLTFLoader();
  92. loader.onExtensionLoadedObservable.add(extension => {
  93. loaderExtensionSettings[extension.name] = {};
  94. const settings = loaderExtensionSettings[extension.name];
  95. for (const key of Object.keys(extension)) {
  96. if (key !== "name" && key[0] !== '_') {
  97. const value = (extension as any)[key];
  98. if (typeof value !== "object") {
  99. settings[key] = value;
  100. }
  101. }
  102. }
  103. });
  104. const data = { json: {}, bin: null };
  105. return loader.importMeshAsync([], scene, data, "").then(() => {
  106. scene.dispose();
  107. engine.dispose();
  108. return (GLTFTab._LoaderExtensionSettings = loaderExtensionSettings);
  109. });
  110. }
  111. private _updateLoaderExtensionDetails(settings: { [settingName: string]: any }): boolean {
  112. if (Object.keys(settings).length === 1) {
  113. return false;
  114. }
  115. if (!this._detailsPanel) {
  116. this._detailsPanel = new DetailPanel();
  117. this._panel.appendChild(this._detailsPanel.toHtml());
  118. this._split = Split([this._actions, this._detailsPanel.toHtml()], {
  119. blockDrag: this._inspector.popupMode,
  120. sizes: [50, 50],
  121. direction: 'vertical'
  122. });
  123. }
  124. this._detailsPanel.clean();
  125. const details = new Array<PropertyLine>();
  126. for (const key in settings) {
  127. if (key !== "enabled") {
  128. details.push(new PropertyLine(new Property(key, settings)));
  129. }
  130. }
  131. this._detailsPanel.details = details;
  132. return true;
  133. }
  134. private _closeDetailsPanel(): void {
  135. if (this._detailsPanel) {
  136. this._detailsPanel.toHtml().remove();
  137. this._detailsPanel.dispose();
  138. this._detailsPanel = null;
  139. }
  140. if (this._split) {
  141. this._split.destroy();
  142. delete this._split;
  143. }
  144. }
  145. private _addExport() {
  146. const exportActions = Helpers.CreateDiv(null, this._actions) as HTMLDivElement;
  147. const title = Helpers.CreateDiv('gltf-title', exportActions);
  148. title.textContent = 'Export';
  149. const name = Helpers.CreateInput('gltf-input', exportActions);
  150. name.placeholder = "File name...";
  151. const button = Helpers.CreateElement('button', 'gltf-button', exportActions) as HTMLButtonElement;
  152. button.innerText = 'Export GLB';
  153. button.addEventListener('click', () => {
  154. BABYLON.GLTF2Export.GLBAsync(this._inspector.scene, name.value || "scene", {
  155. shouldExportTransformNode: transformNode => !GLTFTab._IsSkyBox(transformNode)
  156. }).then((glb) => {
  157. glb.downloadFiles();
  158. });
  159. });
  160. }
  161. private static _IsSkyBox(transformNode: BABYLON.TransformNode): boolean {
  162. if (transformNode instanceof BABYLON.Mesh) {
  163. if (transformNode.material) {
  164. const material = transformNode.material as BABYLON.PBRMaterial | BABYLON.StandardMaterial;
  165. const reflectionTexture = material.reflectionTexture;
  166. if (reflectionTexture && reflectionTexture.coordinatesMode === BABYLON.Texture.SKYBOX_MODE) {
  167. return true;
  168. }
  169. }
  170. }
  171. return false;
  172. }
  173. }
  174. GLTFTab._Initialize();
  175. }