-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Inspector v2: GLTF Import tools #17580
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
alexchuber
merged 23 commits into
BabylonJS:master
from
alexchuber:inspector-v2-import-tools
Jan 27, 2026
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
604286e
Make MessageBar title/message optional
alexchuber cf332d7
Update author and leave note
alexchuber ea859ad
Add GLTF tools (settings, validator) to Import Tools
alexchuber 5f7abe7
Refactor
alexchuber 70004e5
Fix validation results being discarded whenever Inspector is recreated
alexchuber d937a5e
Nits for validation tool
alexchuber 30e0d55
Move validationResult state to GLTFValidation
alexchuber 8a0a1c6
Lazy initialize results history
alexchuber 5cb4f79
Simplify loader options state mgmt
alexchuber e6880ea
Naming & comment nits
alexchuber d56f476
Add "indentExpandedContent" option to ExpandableProperty props
alexchuber 8fc86ac
Missed a spot for indentExpandedContent
alexchuber 12f2d6e
Loop through extensionOptions to create components
alexchuber 05fc2f8
Use child window for glTF validation results
alexchuber 27da23d
Fix MessageBar optional props
alexchuber ebca3fa
Keep all weird GLTFValidation statics internal
alexchuber c7f4fee
Simplify lo-fi GLTFValidation solution
alexchuber 36f03d4
Forgot to remove import
alexchuber a95bcae
Merge branch 'master' of https://github.com/BabylonJS/Babylon.js into…
alexchuber 0523353
Merge remote-tracking branch 'origin/master' into inspector-v2-import…
ryantrem 9fb6e8b
Use ChildWindow
ryantrem 68d853e
Use GLTFLoaderDefaultOptions
ryantrem 6192840
Merge remote-tracking branch 'origin/master' into inspector-v2-import…
ryantrem File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
packages/dev/inspector-v2/src/components/tools/import/gltfLoaderOptionsTool.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| import type { FunctionComponent } from "react"; | ||
| import { GLTFLoaderCoordinateSystemMode, GLTFLoaderAnimationStartMode } from "loaders/glTF/glTFFileLoader"; | ||
| import type { DropdownOption } from "shared-ui-components/fluent/primitives/dropdown"; | ||
| import { SwitchPropertyLine } from "shared-ui-components/fluent/hoc/propertyLines/switchPropertyLine"; | ||
| import { NumberDropdownPropertyLine } from "shared-ui-components/fluent/hoc/propertyLines/dropdownPropertyLine"; | ||
| import { SyncedSliderPropertyLine } from "shared-ui-components/fluent/hoc/propertyLines/syncedSliderPropertyLine"; | ||
| import type { GLTFExtensionOptionsType, GLTFLoaderOptionsType } from "../../../services/panes/tools/import/gltfLoaderOptionsService"; | ||
| import { PropertyLine } from "shared-ui-components/fluent/hoc/propertyLines/propertyLine"; | ||
| import { BoundProperty } from "../../properties/boundProperty"; | ||
|
|
||
| const AnimationStartModeOptions: DropdownOption<number>[] = [ | ||
| { label: "None", value: GLTFLoaderAnimationStartMode.NONE }, | ||
| { label: "First", value: GLTFLoaderAnimationStartMode.FIRST }, | ||
| { label: "All", value: GLTFLoaderAnimationStartMode.ALL }, | ||
| ]; | ||
|
|
||
| const CoordinateSystemModeOptions: DropdownOption<number>[] = [ | ||
| { label: "Auto", value: GLTFLoaderCoordinateSystemMode.AUTO }, | ||
| { label: "Right Handed", value: GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED }, | ||
| ]; | ||
|
|
||
| export const GLTFLoaderOptionsTool: FunctionComponent<{ | ||
| loaderOptions: GLTFLoaderOptionsType; | ||
| }> = ({ loaderOptions }) => { | ||
| return ( | ||
| <PropertyLine | ||
| label="Loader Options" | ||
| expandByDefault={false} | ||
| indentExpandedContent={true} | ||
| expandedContent={ | ||
| <> | ||
| <BoundProperty component={SwitchPropertyLine} label="Always compute bounding box" target={loaderOptions} propertyKey="alwaysComputeBoundingBox" /> | ||
| <BoundProperty component={SwitchPropertyLine} label="Always compute skeleton root node" target={loaderOptions} propertyKey="alwaysComputeSkeletonRootNode" /> | ||
| <BoundProperty | ||
| component={NumberDropdownPropertyLine} | ||
| label="Animation start mode" | ||
| options={AnimationStartModeOptions} | ||
| target={loaderOptions} | ||
| propertyKey="animationStartMode" | ||
| /> | ||
| <BoundProperty component={SwitchPropertyLine} label="Capture performance counters" target={loaderOptions} propertyKey="capturePerformanceCounters" /> | ||
| <BoundProperty component={SwitchPropertyLine} label="Compile materials" target={loaderOptions} propertyKey="compileMaterials" /> | ||
| <BoundProperty component={SwitchPropertyLine} label="Compile shadow generators" target={loaderOptions} propertyKey="compileShadowGenerators" /> | ||
| <BoundProperty | ||
| component={NumberDropdownPropertyLine} | ||
| label="Coordinate system" | ||
| options={CoordinateSystemModeOptions} | ||
| target={loaderOptions} | ||
| propertyKey="coordinateSystemMode" | ||
| /> | ||
| <BoundProperty component={SwitchPropertyLine} label="Create instances" target={loaderOptions} propertyKey="createInstances" /> | ||
| <BoundProperty component={SwitchPropertyLine} label="Enable logging" target={loaderOptions} propertyKey="loggingEnabled" /> | ||
| <BoundProperty component={SwitchPropertyLine} label="Load all materials" target={loaderOptions} propertyKey="loadAllMaterials" /> | ||
| <BoundProperty component={SyncedSliderPropertyLine} label="Target FPS" target={loaderOptions} propertyKey="targetFps" min={1} max={120} step={1} /> | ||
| <BoundProperty component={SwitchPropertyLine} label="Transparency as coverage" target={loaderOptions} propertyKey="transparencyAsCoverage" /> | ||
| <BoundProperty component={SwitchPropertyLine} label="Use clip plane" target={loaderOptions} propertyKey="useClipPlane" /> | ||
| <BoundProperty component={SwitchPropertyLine} label="Use sRGB buffers" target={loaderOptions} propertyKey="useSRGBBuffers" /> | ||
| </> | ||
| } | ||
| /> | ||
| ); | ||
| }; | ||
|
|
||
| export const GLTFExtensionOptionsTool: FunctionComponent<{ | ||
| extensionOptions: GLTFExtensionOptionsType; | ||
| }> = ({ extensionOptions }) => { | ||
| return ( | ||
| <PropertyLine | ||
| label="Extension Options" | ||
| expandByDefault={false} | ||
| indentExpandedContent={true} | ||
| expandedContent={ | ||
| <> | ||
| {Object.entries(extensionOptions).map(([extensionName, options]) => { | ||
| return ( | ||
| <BoundProperty | ||
| key={extensionName} | ||
| component={SwitchPropertyLine} | ||
| label={extensionName} | ||
| target={options} | ||
| propertyKey="enabled" | ||
| expandedContent={ | ||
| (extensionName === "MSFT_lod" && ( | ||
| <BoundProperty | ||
| key={extensionName + "_maxLODsToLoad"} | ||
| component={SyncedSliderPropertyLine} | ||
| label="Maximum LODs" | ||
| target={extensionOptions[extensionName]} // TS can't infer that value ~ extensionOptions[extensionName] | ||
| propertyKey="maxLODsToLoad" | ||
| min={1} | ||
| max={10} | ||
| step={1} | ||
| /> | ||
| )) || | ||
| undefined | ||
| } | ||
| /> | ||
| ); | ||
| })} | ||
| </> | ||
| } | ||
| /> | ||
| ); | ||
| }; |
33 changes: 33 additions & 0 deletions
33
packages/dev/inspector-v2/src/components/tools/import/gltfValidationTool.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import type { FunctionComponent } from "react"; | ||
|
|
||
| import type { IGLTFValidationResults } from "babylonjs-gltf2interface"; | ||
|
|
||
| import { useRef } from "react"; | ||
|
|
||
| import { ButtonLine } from "shared-ui-components/fluent/hoc/buttonLine"; | ||
| import { ChildWindow } from "shared-ui-components/fluent/hoc/childWindow"; | ||
| import { StringifiedPropertyLine } from "shared-ui-components/fluent/hoc/propertyLines/stringifiedPropertyLine"; | ||
| import { MessageBar } from "shared-ui-components/fluent/primitives/messageBar"; | ||
|
|
||
| export const GLTFValidationTool: FunctionComponent<{ validationResults: IGLTFValidationResults }> = ({ validationResults }) => { | ||
| const childWindow = useRef<ChildWindow>(null); | ||
|
|
||
| const issues = validationResults.issues; | ||
| const hasErrors = issues.numErrors > 0; | ||
|
|
||
| return ( | ||
| <> | ||
| <MessageBar intent={hasErrors ? "error" : "success"} message={hasErrors ? "Your file has validation issues" : "Your file is a valid glTF file"} /> | ||
| <StringifiedPropertyLine key="NumErrors" label="Errors" value={issues.numErrors} /> | ||
| <StringifiedPropertyLine key="NumWarnings" label="Warnings" value={issues.numWarnings} /> | ||
| <StringifiedPropertyLine key="NumInfos" label="Infos" value={issues.numInfos} /> | ||
| <StringifiedPropertyLine key="NumHints" label="Hints" value={issues.numHints} /> | ||
| <ButtonLine label="View Report Details" onClick={() => childWindow.current?.open()} /> | ||
| <ChildWindow id="gltfValidationResults" imperativeRef={childWindow}> | ||
| <pre style={{ margin: 0, overflow: "auto" }}> | ||
| <code>{JSON.stringify(validationResults, null, 2)}</code> | ||
| </pre> | ||
| </ChildWindow> | ||
| </> | ||
| ); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
packages/dev/inspector-v2/src/services/panes/tools/import/gltfAnimationImportService.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import type { ServiceDefinition } from "../../../../modularity/serviceDefinition"; | ||
| import { ToolsServiceIdentity } from "../../toolsService"; | ||
| import type { IToolsService } from "../../toolsService"; | ||
| import { GLTFAnimationImportTool } from "../../../../components/tools/import/gltfAnimationImportTool"; | ||
|
|
||
| export const GLTFAnimationImportServiceDefinition: ServiceDefinition<[], [IToolsService]> = { | ||
| friendlyName: "GLTF Animation Import", | ||
| consumes: [ToolsServiceIdentity], | ||
| factory: (toolsService) => { | ||
| const contentRegistration = toolsService.addSectionContent({ | ||
| key: "AnimationImport", | ||
| order: 40, | ||
| section: "GLTF Animation Import", | ||
| component: ({ context }) => <GLTFAnimationImportTool scene={context} />, | ||
| }); | ||
|
|
||
| return { | ||
| dispose: () => { | ||
| contentRegistration.dispose(); | ||
| }, | ||
| }; | ||
| }, | ||
| }; |
107 changes: 107 additions & 0 deletions
107
packages/dev/inspector-v2/src/services/panes/tools/import/gltfLoaderOptionsService.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| import type { ISceneLoaderPlugin, ISceneLoaderPluginAsync, SceneLoaderPluginOptions } from "core/Loading/sceneLoader"; | ||
| import type { GLTFFileLoader, IGLTFLoaderExtension } from "loaders/glTF/glTFFileLoader"; | ||
| import type { ServiceDefinition } from "../../../../modularity/serviceDefinition"; | ||
| import type { IToolsService } from "../../toolsService"; | ||
|
|
||
| import { SceneLoader } from "core/Loading/sceneLoader"; | ||
| import { GLTFLoaderDefaultOptions } from "loaders/glTF/glTFFileLoader"; | ||
| import { MessageBar } from "shared-ui-components/fluent/primitives/messageBar"; | ||
| import { GLTFExtensionOptionsTool, GLTFLoaderOptionsTool } from "../../../../components/tools/import/gltfLoaderOptionsTool"; | ||
| import { ToolsServiceIdentity } from "../../toolsService"; | ||
|
|
||
| export const GLTFLoaderServiceIdentity = Symbol("GLTFLoaderService"); | ||
|
|
||
| // Options exposed in Inspector includes all the properties from the default loader options (GLTFLoaderDefaultOptions) | ||
| // plus some options that only exist directly on the GLTFFileLoader class itself. | ||
| const CurrentLoaderOptions = Object.assign( | ||
| { | ||
| capturePerformanceCounters: false, | ||
| loggingEnabled: false, | ||
| } satisfies Pick<GLTFFileLoader, "capturePerformanceCounters" | "loggingEnabled">, | ||
| GLTFLoaderDefaultOptions | ||
| ); | ||
|
|
||
| export type GLTFLoaderOptionsType = typeof CurrentLoaderOptions; | ||
|
|
||
| const CurrentExtensionOptions = { | ||
ryantrem marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /* eslint-disable @typescript-eslint/naming-convention */ | ||
| EXT_lights_image_based: { enabled: true }, | ||
| EXT_mesh_gpu_instancing: { enabled: true }, | ||
| EXT_texture_webp: { enabled: true }, | ||
| EXT_texture_avif: { enabled: true }, | ||
| KHR_draco_mesh_compression: { enabled: true }, | ||
| KHR_materials_pbrSpecularGlossiness: { enabled: true }, | ||
| KHR_materials_clearcoat: { enabled: true }, | ||
| KHR_materials_iridescence: { enabled: true }, | ||
| KHR_materials_anisotropy: { enabled: true }, | ||
| KHR_materials_emissive_strength: { enabled: true }, | ||
| KHR_materials_ior: { enabled: true }, | ||
| KHR_materials_sheen: { enabled: true }, | ||
| KHR_materials_specular: { enabled: true }, | ||
| KHR_materials_unlit: { enabled: true }, | ||
| KHR_materials_variants: { enabled: true }, | ||
| KHR_materials_transmission: { enabled: true }, | ||
| KHR_materials_diffuse_transmission: { enabled: true }, | ||
| KHR_materials_volume: { enabled: true }, | ||
| KHR_materials_dispersion: { enabled: true }, | ||
| KHR_materials_diffuse_roughness: { enabled: true }, | ||
| KHR_mesh_quantization: { enabled: true }, | ||
| KHR_lights_punctual: { enabled: true }, | ||
| EXT_lights_area: { enabled: true }, | ||
| KHR_texture_basisu: { enabled: true }, | ||
| KHR_texture_transform: { enabled: true }, | ||
| KHR_xmp_json_ld: { enabled: true }, | ||
| MSFT_lod: { enabled: true, maxLODsToLoad: 10 }, | ||
| MSFT_minecraftMesh: { enabled: true }, | ||
| MSFT_sRGBFactors: { enabled: true }, | ||
| MSFT_audio_emitter: { enabled: true }, | ||
| } satisfies SceneLoaderPluginOptions["gltf"]["extensionOptions"]; | ||
|
|
||
| export type GLTFExtensionOptionsType = typeof CurrentExtensionOptions; | ||
|
|
||
| export const GLTFLoaderOptionsServiceDefinition: ServiceDefinition<[], [IToolsService]> = { | ||
| friendlyName: "GLTF Loader Options", | ||
| consumes: [ToolsServiceIdentity], | ||
| factory: (toolsService) => { | ||
| // Subscribe to plugin activation | ||
| const pluginObserver = SceneLoader.OnPluginActivatedObservable.add((plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync) => { | ||
| if (plugin.name === "gltf") { | ||
| const loader = plugin as GLTFFileLoader; | ||
|
|
||
| // Apply loader settings | ||
| Object.assign(loader, CurrentLoaderOptions); | ||
|
|
||
| // Subscribe to extension loading | ||
| loader.onExtensionLoadedObservable.add((extension: IGLTFLoaderExtension) => { | ||
ryantrem marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const extensionOptions = CurrentExtensionOptions[extension.name as keyof GLTFExtensionOptionsType]; | ||
| if (extensionOptions) { | ||
| // Apply extension settings | ||
| Object.assign(extension, extensionOptions); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
|
|
||
| const loaderToolsRegistration = toolsService.addSectionContent({ | ||
| key: "GLTFLoaderOptions", | ||
| section: "GLTF Loader", | ||
| order: 50, | ||
| component: () => { | ||
| return ( | ||
| <> | ||
| <MessageBar intent="info" message="Reload the file for changes to take effect" /> | ||
| <GLTFLoaderOptionsTool loaderOptions={CurrentLoaderOptions} /> | ||
| <GLTFExtensionOptionsTool extensionOptions={CurrentExtensionOptions} /> | ||
| </> | ||
| ); | ||
| }, | ||
| }); | ||
|
|
||
| return { | ||
| dispose: () => { | ||
| pluginObserver.remove(); | ||
| loaderToolsRegistration.dispose(); | ||
| }, | ||
| }; | ||
| }, | ||
| }; | ||
45 changes: 45 additions & 0 deletions
45
packages/dev/inspector-v2/src/services/panes/tools/import/gltfValidationService.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import type { ServiceDefinition } from "../../../../modularity/serviceDefinition"; | ||
| import { SceneLoader } from "core/Loading/sceneLoader"; | ||
| import type { ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "core/Loading/sceneLoader"; | ||
| import type { GLTFFileLoader } from "loaders/glTF/glTFFileLoader"; | ||
| import { GLTFValidationTool } from "../../../../components/tools/import/gltfValidationTool"; | ||
| import type { IToolsService } from "../../toolsService"; | ||
| import { ToolsServiceIdentity } from "../../toolsService"; | ||
| import { MessageBar } from "shared-ui-components/fluent/primitives/messageBar"; | ||
| import { GLTFValidation } from "loaders/glTF/glTFValidation"; | ||
| import { useProperty } from "../../../../hooks/compoundPropertyHooks"; | ||
|
|
||
| export const GLTFValidationServiceDefinition: ServiceDefinition<[], [IToolsService]> = { | ||
| friendlyName: "GLTF Validation", | ||
| consumes: [ToolsServiceIdentity], | ||
| factory: (toolsService) => { | ||
| const pluginObserver = SceneLoader.OnPluginActivatedObservable.add((plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync) => { | ||
| if (plugin.name === "gltf") { | ||
| const loader = plugin as GLTFFileLoader; | ||
| loader.validate = true; | ||
| } | ||
| }); | ||
|
|
||
| const sectionRegistration = toolsService.addSectionContent({ | ||
| key: "GLTFValidation", | ||
| section: "GLTF Validation", | ||
| order: 60, | ||
| component: () => { | ||
| const validationState = useProperty(GLTFValidation, "_LastResults"); | ||
|
|
||
| if (!validationState) { | ||
| return <MessageBar intent="info" message="Reload the file to see validation results" />; | ||
| } | ||
|
|
||
| return <GLTFValidationTool validationResults={validationState} />; | ||
| }, | ||
| }); | ||
|
|
||
| return { | ||
| dispose: () => { | ||
| sectionRegistration.dispose(); | ||
| pluginObserver.remove(); | ||
| }, | ||
| }; | ||
| }, | ||
| }; |
7 changes: 7 additions & 0 deletions
7
packages/dev/inspector-v2/src/services/panes/tools/import/importService.tsx
ryantrem marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import { GLTFLoaderOptionsServiceDefinition } from "./gltfLoaderOptionsService"; | ||
| import { GLTFValidationServiceDefinition } from "./gltfValidationService"; | ||
| import { GLTFAnimationImportServiceDefinition } from "./gltfAnimationImportService"; | ||
|
|
||
| export default { | ||
| serviceDefinitions: [GLTFAnimationImportServiceDefinition, GLTFLoaderOptionsServiceDefinition, GLTFValidationServiceDefinition], | ||
| } as const; |
26 changes: 0 additions & 26 deletions
26
packages/dev/inspector-v2/src/services/panes/tools/importService.tsx
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.