Quellcode durchsuchen

Merge pull request #4336 from bghgary/gltf-tab

Add glTF loader extension settings to GLTF tab in inspector
David Catuhe vor 7 Jahren
Ursprung
Commit
6b8f31eaa4

+ 1 - 0
dist/preview release/what's new.md

@@ -24,6 +24,7 @@
 
 - Added support for KHR_texture_transform ([bghgary](http://www.github.com/bghgary))
 - Added `onNodeLODsLoadedObservable` and `onMaterialLODsLoadedObservable` to MSFT_lod loader extension ([bghgary](http://www.github.com/bghgary))
+- Added glTF loader settings to the GLTF tab in the debug layer ([bghgary](http://www.github.com/bghgary))
 
 ### Viewer
 

+ 38 - 0
inspector/sass/tabs/_gltfTab.scss

@@ -10,6 +10,44 @@
             margin        : 10px 0 10px 0;
         }
 
+        .gltf-action {
+            height     : 20px;
+            line-height: 20px;
+            width      : 100%;
+            cursor     : pointer;
+            white-space: nowrap;
+
+            &:hover {
+                background-color: $background-lighter;
+            }
+        }
+
+        .gltf-checkbox {
+            @extend .gltf-action;
+            &:before {
+                width      : 1em;
+                height     : 1em;
+                line-height: 1em;
+                display    : inline-block;
+                font-family: 'FontAwesome', sans-serif;
+                content    : "\f096";
+                margin-right:10px;
+            }
+
+            &.active {
+                &:before {
+                    width      : 1em;
+                    height     : 1em;
+                    line-height: 1em;
+                    display    : inline-block;
+                    font-family: 'FontAwesome', sans-serif;
+                    content    : "\f14a";
+                    color      : $color-bot;
+                    margin-right:10px;
+                }
+            }
+        }
+
         .gltf-input {
             background-color: $background-lighter;
             border          : none;

+ 165 - 9
inspector/src/tabs/GLTFTab.ts

@@ -1,32 +1,186 @@
 /// <reference path="../../../dist/preview release/gltf2Interface/babylon.glTF2Interface.d.ts"/>
+/// <reference path="../../../dist/preview release/loaders/babylon.glTF2FileLoader.d.ts"/>
 /// <reference path="../../../dist/preview release/serializers/babylon.glTF2Serializer.d.ts"/>
 
+declare function Split(elements: HTMLElement[], options: any): any;
+
 module INSPECTOR {
+    interface ILoaderExtensionSettings {
+        [extensionName: string]: {
+            [settingName: string]: any
+        }
+    };
+
     export class GLTFTab extends Tab {
+        private static _LoaderExtensionSettings: ILoaderExtensionSettings | null = null;
+
+        private _inspector: Inspector;
+        private _actions: HTMLDivElement;
+        private _detailsPanel: DetailPanel | null = null;
+        private _split: any;
+
+        /** @hidden */
+        public static _Initialize(): void {
+            // Must register with OnPluginActivatedObservable as early as possible to
+            // override the default settings for each extension.
+            BABYLON.SceneLoader.OnPluginActivatedObservable.add((loader: BABYLON.GLTFFileLoader) => {
+                if (loader.name === "gltf" && GLTFTab._LoaderExtensionSettings) {
+                    loader.onExtensionLoadedObservable.add(extension => {
+                        const settings = GLTFTab._LoaderExtensionSettings![extension.name];
+                        for (const settingName in settings) {
+                            (extension as any)[settingName] = settings[settingName];
+                        }
+                    });
+                }
+            });
+        }
+
         constructor(tabbar: TabBar, inspector: Inspector) {
             super(tabbar, 'GLTF');
 
+            this._inspector = inspector;
             this._panel = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
-            const actions = Helpers.CreateDiv('gltf-actions', this._panel) as HTMLDivElement;
-            this._addExport(inspector, actions);
+            this._actions = Helpers.CreateDiv('gltf-actions', this._panel) as HTMLDivElement;
+            this._actions.addEventListener('click', event => {
+                this._closeDetailsPanel();
+            });
+
+            this._addImport();
+            this._addExport();
         }
 
         public dispose() {
-            // Nothing to dispose
+            if (this._detailsPanel) {
+                this._detailsPanel.dispose();
+            }
         }
 
-        private _addExport(inspector: Inspector, actions: HTMLDivElement) {
-            const title = Helpers.CreateDiv('gltf-title', actions);
+        private _addImport() {
+            const importActions = Helpers.CreateDiv(null, this._actions) as HTMLDivElement;
+
+            this._getLoaderExtensionOverridesAsync().then(loaderExtensionSettings => {
+                const title = Helpers.CreateDiv('gltf-title', importActions);
+                title.textContent = 'Import';
+
+                const extensionActions = Helpers.CreateDiv('gltf-actions', importActions) as HTMLDivElement;
+
+                const extensionsTitle = Helpers.CreateDiv('gltf-title', extensionActions) as HTMLDivElement;
+                extensionsTitle.textContent = "Extensions";
+
+                for (const extensionName in loaderExtensionSettings) {
+                    const settings = loaderExtensionSettings[extensionName];
+
+                    const extensionAction = Helpers.CreateDiv('gltf-action', extensionActions);
+                    extensionAction.addEventListener('click', event => {
+                        if (this._updateLoaderExtensionDetails(settings)) {
+                            event.stopPropagation();
+                        }
+                    });
+
+                    const checkbox = Helpers.CreateElement('span', 'gltf-checkbox', extensionAction);
+
+                    if (settings.enabled) {
+                        checkbox.classList.add('action', 'active');
+                    }
+
+                    checkbox.addEventListener('click', () => {
+                        checkbox.classList.toggle('active');
+                        settings.enabled = checkbox.classList.contains('active');
+                    });
+
+                    const label = Helpers.CreateElement('span', null, extensionAction);
+                    label.textContent = extensionName;
+                }
+            });
+        }
+
+        private _getLoaderExtensionOverridesAsync(): Promise<ILoaderExtensionSettings> {
+            if (GLTFTab._LoaderExtensionSettings) {
+                return Promise.resolve(GLTFTab._LoaderExtensionSettings);
+            }
+
+            const loaderExtensionSettings: ILoaderExtensionSettings = {};
+
+            const engine = new BABYLON.NullEngine();
+            const scene = new BABYLON.Scene(engine);
+            const loader = new BABYLON.GLTF2.GLTFLoader();
+            loader.onExtensionLoadedObservable.add(extension => {
+                loaderExtensionSettings[extension.name] = {};
+                const settings = loaderExtensionSettings[extension.name];
+                for (const key of Object.keys(extension)) {
+                    if (key !== "name" && key[0] !== '_') {
+                        const value = (extension as any)[key];
+                        if (typeof value !== "object") {
+                            settings[key] = value;
+                        }
+                    }
+                }
+            });
+
+            const data = { json: {}, bin: null };
+            return loader.importMeshAsync([], scene, data, "").then(() => {
+                scene.dispose();
+                engine.dispose();
+
+                return (GLTFTab._LoaderExtensionSettings = loaderExtensionSettings);
+            });
+        }
+
+        private _updateLoaderExtensionDetails(settings: { [settingName: string]: any }): boolean {
+            if (Object.keys(settings).length === 1) {
+                return false;
+            }
+
+            if (!this._detailsPanel) {
+                this._detailsPanel = new DetailPanel();
+                this._panel.appendChild(this._detailsPanel.toHtml());
+
+                this._split = Split([this._actions, this._detailsPanel.toHtml()], {
+                    blockDrag: this._inspector.popupMode,
+                    sizes: [50, 50],
+                    direction: 'vertical'
+                });
+            }
+
+            this._detailsPanel.clean();
+
+            const details = new Array<PropertyLine>();
+            for (const key in settings) {
+                if (key !== "enabled") {
+                    details.push(new PropertyLine(new Property(key, settings)));
+                }
+            }
+            this._detailsPanel.details = details;
+
+            return true;
+        }
+
+        private _closeDetailsPanel(): void {
+            if (this._detailsPanel) {
+                this._detailsPanel.toHtml().remove();
+                this._detailsPanel.dispose();
+                this._detailsPanel = null;
+            }
+
+            if (this._split) {
+                this._split.destroy();
+                delete this._split;
+            }
+        }
+
+        private _addExport() {
+            const exportActions = Helpers.CreateDiv(null, this._actions) as HTMLDivElement;
+
+            const title = Helpers.CreateDiv('gltf-title', exportActions);
             title.textContent = 'Export';
 
-            const name = Helpers.CreateInput('gltf-input', actions);
+            const name = Helpers.CreateInput('gltf-input', exportActions);
             name.placeholder = "File name...";
 
-            const button = Helpers.CreateElement('button', 'gltf-button', actions) as HTMLButtonElement;
+            const button = Helpers.CreateElement('button', 'gltf-button', exportActions) as HTMLButtonElement;
             button.innerText = 'Export GLB';
-
             button.addEventListener('click', () => {
-                BABYLON.GLTF2Export.GLBAsync(inspector.scene, name.value || "scene", {
+                BABYLON.GLTF2Export.GLBAsync(this._inspector.scene, name.value || "scene", {
                     shouldExportTransformNode: transformNode => !GLTFTab._IsSkyBox(transformNode)
                 }).then((glb) => {
                     glb.downloadFiles();
@@ -48,4 +202,6 @@ module INSPECTOR {
             return false;
         }
     }
+
+    GLTFTab._Initialize();
 }

+ 4 - 3
inspector/src/tsconfig.json

@@ -1,12 +1,13 @@
 {
     "compilerOptions": {
         "experimentalDecorators": true,
-        "module": "commonjs", 
+        "module": "commonjs",
         "target": "es5",
         "noImplicitAny": true,
         "noImplicitReturns": true,
         "noImplicitThis": true,
-        "noUnusedLocals": true,    
-        "strictNullChecks": true
+        "noUnusedLocals": true,
+        "strictNullChecks": true,
+        "lib": ["dom", "es2015.promise", "es5"]
     }
 }

+ 17 - 8
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -164,6 +164,11 @@ module BABYLON.GLTF2 {
          */
         public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             return Promise.resolve().then(() => {
+                this._babylonScene = scene;
+                this._rootUrl = rootUrl;
+                this._progressCallback = onProgress;
+                this._loadData(data);
+
                 let nodes: Nullable<Array<_ILoaderNode>> = null;
 
                 if (meshesNames) {
@@ -187,7 +192,7 @@ module BABYLON.GLTF2 {
                     });
                 }
 
-                return this._loadAsync(nodes, scene, data, rootUrl, onProgress).then(() => {
+                return this._loadAsync(nodes).then(() => {
                     return {
                         meshes: this._getMeshes(),
                         particleSystems: [],
@@ -207,17 +212,19 @@ module BABYLON.GLTF2 {
          * @returns a promise which completes when objects have been loaded to the scene
          */
         public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
-            return this._loadAsync(null, scene, data, rootUrl, onProgress);
-        }
-
-        private _loadAsync(nodes: Nullable<Array<_ILoaderNode>>, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
             return Promise.resolve().then(() => {
                 this._babylonScene = scene;
                 this._rootUrl = rootUrl;
                 this._progressCallback = onProgress;
+                this._loadData(data);
+                return this._loadAsync(null);
+            });
+        }
+
+        private _loadAsync(nodes: Nullable<Array<_ILoaderNode>>): Promise<void> {
+            return Promise.resolve().then(() => {
                 this._state = GLTFLoaderState.LOADING;
 
-                this._loadData(data);
                 this._loadExtensions();
                 this._checkExtensions();
 
@@ -246,7 +253,9 @@ module BABYLON.GLTF2 {
                 });
 
                 resultPromise.then(() => {
-                    this._rootBabylonMesh.setEnabled(true);
+                    if (this._rootBabylonMesh) {
+                        this._rootBabylonMesh.setEnabled(true);
+                    }
 
                     Tools.SetImmediate(() => {
                         if (!this._disposed) {
@@ -375,7 +384,7 @@ module BABYLON.GLTF2 {
             return rootNode;
         }
 
-        private _loadNodesAsync(nodes: _ILoaderNode[], ): Promise<void> {
+        private _loadNodesAsync(nodes: _ILoaderNode[]): Promise<void> {
             const promises = new Array<Promise<void>>();
 
             for (let node of nodes) {

+ 6 - 8
sandbox/index.js

@@ -83,17 +83,15 @@ if (BABYLON.Engine.isSupported()) {
 
     var sceneLoaded = function (sceneFile, babylonScene) {
         function displayDebugLayerAndLogs() {
-            currentScene.debugLayer._displayLogs = true;
             enableDebugLayer = true;
             currentScene.debugLayer.show();
         };
         function hideDebugLayerAndLogs() {
-            currentScene.debugLayer._displayLogs = false;
             enableDebugLayer = false;
             currentScene.debugLayer.hide();
         };
-    
-            if (enableDebugLayer) {
+
+        if (enableDebugLayer) {
             hideDebugLayerAndLogs();
         }
 
@@ -248,9 +246,9 @@ if (BABYLON.Engine.isSupported()) {
         }).bind(this);
         filesInput.monitorElementForDragNDrop(canvas);
 
-        window.addEventListener("keydown", function (evt) {
+        window.addEventListener("keydown", function (event) {
             // Press R to reload
-            if (evt.keyCode === 82 && !enableDebugLayer) {
+            if (event.keyCode === 82 && event.target.nodeName !== "INPUT") {
                 filesInput.reload();
             }
         });
@@ -290,9 +288,9 @@ if (BABYLON.Engine.isSupported()) {
         }
     }, false);
 
-    window.addEventListener("keydown", function (evt) {
+    window.addEventListener("keydown", function (event) {
         // Press space to toggle footer
-        if (evt.keyCode === 32 && !enableDebugLayer) {
+        if (event.keyCode === 32 && event.target.nodeName !== "INPUT") {
             if (footer.style.display === "none") {
                 footer.style.display = "block";
             }

+ 16 - 1
tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

@@ -66,6 +66,22 @@ describe('Babylon Scene Loader', function () {
             });
         });
 
+        it('Load TwoQuads with ImportMesh and one node name', () => {
+            const scene = new BABYLON.Scene(subject);
+            return BABYLON.SceneLoader.ImportMeshAsync("node0", "http://models.babylonjs.com/Tests/TwoQuads/", "TwoQuads.gltf", scene).then(() => {
+                expect(scene.getMeshByName("node0"), "node0").to.exist;
+                expect(scene.getMeshByName("node1"), "node1").to.not.exist;
+            });
+        });
+
+        it('Load TwoQuads with ImporMesh and two node names', () => {
+            const scene = new BABYLON.Scene(subject);
+            return BABYLON.SceneLoader.ImportMeshAsync(["node0", "node1"], "http://models.babylonjs.com/Tests/TwoQuads/", "TwoQuads.gltf", scene).then(() => {
+                expect(scene.getMeshByName("node0"), "node0").to.exist;
+                expect(scene.getMeshByName("node1"), "node1").to.exist;
+            });
+        });
+
         it('Load BoomBox with callbacks', () => {
             let parsedCount = 0;
             let meshCount = 0;
@@ -482,7 +498,6 @@ describe('Babylon Scene Loader', function () {
 
         // TODO: test animation group callback
         // TODO: test material instancing
-        // TODO: test ImportMesh with specific node name
         // TODO: test KHR_materials_pbrSpecularGlossiness
         // TODO: test KHR_lights
     });