diff --git a/packages/dev/inspector-v2/src/components/tools/importTools.tsx b/packages/dev/inspector-v2/src/components/tools/import/gltfAnimationImport.tsx similarity index 89% rename from packages/dev/inspector-v2/src/components/tools/importTools.tsx rename to packages/dev/inspector-v2/src/components/tools/import/gltfAnimationImport.tsx index 69708ba9aba..4e91854f053 100644 --- a/packages/dev/inspector-v2/src/components/tools/importTools.tsx +++ b/packages/dev/inspector-v2/src/components/tools/import/gltfAnimationImport.tsx @@ -17,7 +17,13 @@ const AnimationGroupLoadingModes = [ { label: "NoSync", value: SceneLoaderAnimationGroupLoadingMode.NoSync }, ] as const satisfies DropdownOption[]; -export const ImportAnimationsTools: FunctionComponent<{ scene: Scene }> = ({ scene }) => { +/** + * Component for importing animations into an existing scene. + * Allows configuration of animation merge behavior and provides file upload interface. + * @param props - Component props + * @returns The animation import UI + */ +export const GLTFAnimationImport: FunctionComponent<{ scene: Scene }> = ({ scene }) => { const [importDefaults, setImportDefaults] = useState({ overwriteAnimations: true, animationGroupLoadingMode: SceneLoaderAnimationGroupLoadingMode.Clean, diff --git a/packages/dev/inspector-v2/src/components/tools/import/gltfLoaderOptions.tsx b/packages/dev/inspector-v2/src/components/tools/import/gltfLoaderOptions.tsx new file mode 100644 index 00000000000..2630a0d947e --- /dev/null +++ b/packages/dev/inspector-v2/src/components/tools/import/gltfLoaderOptions.tsx @@ -0,0 +1,365 @@ +import { useCallback, useState, useEffect, useRef } from "react"; +import type { FunctionComponent } from "react"; +import { GLTFLoaderCoordinateSystemMode, GLTFLoaderAnimationStartMode } from "loaders/glTF/glTFFileLoader"; +import type { DropdownOption } from "shared-ui-components/fluent/primitives/dropdown"; +import { PropertyLine } from "shared-ui-components/fluent/hoc/propertyLines/propertyLine"; +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 { IGLTFLoaderService } from "../../../services/panes/tools/gltfLoaderService"; +import { useObservableState } from "../../../hooks/observableHooks"; +import { Collapse } from "shared-ui-components/fluent/primitives/collapse"; +import { MessageBar } from "shared-ui-components/fluent/primitives/messageBar"; + +const AnimationStartModeOptions: DropdownOption[] = [ + { label: "None", value: GLTFLoaderAnimationStartMode.NONE }, + { label: "First", value: GLTFLoaderAnimationStartMode.FIRST }, + { label: "All", value: GLTFLoaderAnimationStartMode.ALL }, +]; + +const CoordinateSystemModeOptions: DropdownOption[] = [ + { label: "Auto", value: GLTFLoaderCoordinateSystemMode.AUTO }, + { label: "Right Handed", value: GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED }, +]; + +/** + * Displays global loader configuration options in a collapsible section. + * @param props - Component props including service and current config + * @returns Loader settings UI + */ +const GLTFGlobalOptions: FunctionComponent<{ gltfLoaderService: IGLTFLoaderService; loaderConfig: ReturnType }> = ({ + gltfLoaderService, + loaderConfig, +}) => { + return ( + + gltfLoaderService.updateLoaderConfig("alwaysComputeBoundingBox", value)} + /> + gltfLoaderService.updateLoaderConfig("alwaysComputeSkeletonRootNode", value)} + /> + gltfLoaderService.updateLoaderConfig("animationStartMode", value)} + /> + gltfLoaderService.updateLoaderConfig("capturePerformanceCounters", value)} + /> + gltfLoaderService.updateLoaderConfig("compileMaterials", value)} + /> + gltfLoaderService.updateLoaderConfig("compileShadowGenerators", value)} + /> + gltfLoaderService.updateLoaderConfig("coordinateSystemMode", value)} + /> + gltfLoaderService.updateLoaderConfig("createInstances", value)} + /> + gltfLoaderService.updateLoaderConfig("loggingEnabled", value)} + /> + gltfLoaderService.updateLoaderConfig("loadAllMaterials", value)} + /> + gltfLoaderService.updateLoaderConfig("targetFps", value)} + min={1} + max={120} + step={1} + /> + gltfLoaderService.updateLoaderConfig("transparencyAsCoverage", value)} + /> + gltfLoaderService.updateLoaderConfig("useClipPlane", value)} + /> + gltfLoaderService.updateLoaderConfig("useSRGBBuffers", value)} + /> + + } + /> + ); +}; + +/** + * Displays glTF extension configuration options in a collapsible section. + * Allows enabling/disabling extensions and configuring extension-specific properties. + * @param props - Component props including service and extension states + * @returns Extension settings UI + */ +const GLTFExtensionOptions: FunctionComponent<{ gltfLoaderService: IGLTFLoaderService; extensionStates: ReturnType }> = ({ + gltfLoaderService, + extensionStates, +}) => { + return ( + <> + + gltfLoaderService.updateExtensionState("EXT_lights_image_based", value)} + /> + gltfLoaderService.updateExtensionState("EXT_mesh_gpu_instancing", value)} + /> + gltfLoaderService.updateExtensionState("EXT_texture_webp", value)} + /> + gltfLoaderService.updateExtensionState("EXT_texture_avif", value)} + /> + gltfLoaderService.updateExtensionState("KHR_draco_mesh_compression", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_pbrSpecularGlossiness", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_clearcoat", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_iridescence", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_anisotropy", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_emissive_strength", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_ior", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_sheen", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_specular", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_unlit", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_variants", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_transmission", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_diffuse_transmission", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_volume", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_dispersion", value)} + /> + gltfLoaderService.updateExtensionState("KHR_materials_diffuse_roughness", value)} + /> + gltfLoaderService.updateExtensionState("KHR_mesh_quantization", value)} + /> + gltfLoaderService.updateExtensionState("KHR_lights_punctual", value)} + /> + gltfLoaderService.updateExtensionState("EXT_lights_area", value)} + /> + gltfLoaderService.updateExtensionState("KHR_texture_basisu", value)} + /> + gltfLoaderService.updateExtensionState("KHR_texture_transform", value)} + /> + gltfLoaderService.updateExtensionState("KHR_xmp_json_ld", value)} + /> + gltfLoaderService.updateExtensionState("MSFT_lod", value)} + /> + + gltfLoaderService.updateExtensionProperty("MSFT_lod", "maxLODsToLoad", value)} + min={1} + max={10} + step={1} + /> + + gltfLoaderService.updateExtensionState("MSFT_minecraftMesh", value)} + /> + gltfLoaderService.updateExtensionState("MSFT_sRGBFactors", value)} + /> + gltfLoaderService.updateExtensionState("MSFT_audio_emitter", value)} + /> + + } + /> + + ); +}; + +/** + * Main component for glTF loader configuration. + * + * This component subscribes to service state via observables and tracks whether + * the configuration has changed since the last file load. Components in the inspector + * stay mounted (but hidden) when tabs switch, so we use the service to persist state + * across the inspector's lifetime while using React state for UI-specific concerns. + * @param props - Component props + * @returns The loader configuration UI + */ +export const GLTFLoaderOptions: FunctionComponent<{ gltfLoaderService: IGLTFLoaderService }> = ({ gltfLoaderService }) => { + const [hasChanges, setHasChanges] = useState(false); + // Store stringified initial state to efficiently compare for changes + const initialStateRef = useRef<{ loader: string; extensions: string } | null>(null); + + // Track current state + const loaderConfig = useObservableState( + useCallback(() => gltfLoaderService.getLoaderConfig(), [gltfLoaderService]), + gltfLoaderService.onLoaderConfigChangedObservable + ); + + const extensionStates = useObservableState( + useCallback(() => gltfLoaderService.getExtensionStates(), [gltfLoaderService]), + gltfLoaderService.onExtensionConfigChangedObservable + ); + + // Store initial state when file loads + useEffect(() => { + const observer = gltfLoaderService.onLoaderActivatedObservable.add(() => { + initialStateRef.current = { + loader: JSON.stringify(gltfLoaderService.getLoaderConfig()), + extensions: JSON.stringify(gltfLoaderService.getExtensionStates()), + }; + setHasChanges(false); + }); + + return () => observer.remove(); + }, [gltfLoaderService]); + + // Compare current state with initial state to determine if reload message should be shown + // Using JSON.stringify for deep equality check since these are complex nested objects + useEffect(() => { + if (!initialStateRef.current) { + return; + } + + const currentLoader = JSON.stringify(loaderConfig); + const currentExtensions = JSON.stringify(extensionStates); + + const isDifferent = currentLoader !== initialStateRef.current.loader || currentExtensions !== initialStateRef.current.extensions; + setHasChanges(isDifferent); + }, [loaderConfig, extensionStates]); + + return ( + <> + {hasChanges && } + + + + ); +}; diff --git a/packages/dev/inspector-v2/src/components/tools/import/gltfValidator.tsx b/packages/dev/inspector-v2/src/components/tools/import/gltfValidator.tsx new file mode 100644 index 00000000000..54f87aa65d5 --- /dev/null +++ b/packages/dev/inspector-v2/src/components/tools/import/gltfValidator.tsx @@ -0,0 +1,59 @@ +import { useCallback } from "react"; +import type { FunctionComponent } from "react"; +import { ButtonLine } from "shared-ui-components/fluent/hoc/buttonLine"; +import type { IGLTFLoaderService } from "../../../services/panes/tools/gltfLoaderService"; +import { useObservableState } from "../../../hooks/observableHooks"; +import { StringifiedPropertyLine } from "shared-ui-components/fluent/hoc/propertyLines/stringifiedPropertyLine"; +import { MessageBar } from "shared-ui-components/fluent/primitives/messageBar"; + +/** + * Component that displays glTF validation results. + * Shows validation status (errors, warnings, hints, info) and allows viewing detailed report. + * @param props - Component props + * @returns The validation results UI + */ +export const GLTFValidationTools: FunctionComponent<{ gltfLoaderService: IGLTFLoaderService }> = ({ gltfLoaderService }) => { + const validationResults = useObservableState( + useCallback(() => gltfLoaderService.getValidationResults(), [gltfLoaderService]), + gltfLoaderService.onValidationResultsObservable + ); + + const openValidationDetails = useCallback(() => { + if (!validationResults) { + return; + } + + const win = window.open("", "_blank"); + if (win) { + win.document.title = `${validationResults.uri} - glTF Validation Results`; + win.document.body.style.backgroundColor = "#322e2eff"; + win.document.body.style.color = "#fff"; + win.document.body.style.padding = "1rem"; + const pre = win.document.createElement("pre"); + const code = win.document.createElement("code"); + const textNode = win.document.createTextNode(JSON.stringify(validationResults, null, 2)); + code.append(textNode); + pre.append(code); + win.document.body.append(pre); + win.focus(); + } + }, [validationResults]); + + if (!validationResults) { + return ; + } + + const issues = validationResults.issues; + const hasErrors = issues.numErrors > 0; + + return ( + <> + + + + + + + + ); +}; diff --git a/packages/dev/inspector-v2/src/extensibility/defaultInspectorExtensionFeed.ts b/packages/dev/inspector-v2/src/extensibility/defaultInspectorExtensionFeed.ts index 921a2fa3ab2..ccb1081fcd5 100644 --- a/packages/dev/inspector-v2/src/extensibility/defaultInspectorExtensionFeed.ts +++ b/packages/dev/inspector-v2/src/extensibility/defaultInspectorExtensionFeed.ts @@ -39,7 +39,7 @@ export const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspecto description: "Adds new features related to importing Babylon assets.", keywords: ["import", "tools"], ...BabylonWebResources, - author: { name: "Alex Chuber", forumUserName: "alexchuber" }, + author: { name: "Alex Huber", forumUserName: "alexchuber" }, // Q: Any reason to not put Babylon.js here? getExtensionModuleAsync: async () => await import("../services/panes/tools/importService"), }, { diff --git a/packages/dev/inspector-v2/src/inspector.tsx b/packages/dev/inspector-v2/src/inspector.tsx index 08a25457eb2..96fc93f6f84 100644 --- a/packages/dev/inspector-v2/src/inspector.tsx +++ b/packages/dev/inspector-v2/src/inspector.tsx @@ -59,6 +59,7 @@ import { SceneContextIdentity } from "./services/sceneContext"; import { SelectionServiceDefinition } from "./services/selectionService"; import { ShellServiceIdentity } from "./services/shellService"; import { UserFeedbackServiceDefinition } from "./services/userFeedbackService"; +import { GLTFLoaderServiceDefinition } from "./services/panes/tools/gltfLoaderService"; export type InspectorOptions = Omit & { autoResizeEngine?: boolean }; @@ -264,6 +265,7 @@ export function ShowInspector(scene: Scene, options: Partial = // Tools pane tab and related services. ToolsServiceDefinition, + GLTFLoaderServiceDefinition, // Settings pane tab and related services. SettingsServiceDefinition, diff --git a/packages/dev/inspector-v2/src/services/panes/tools/gltfLoaderService.tsx b/packages/dev/inspector-v2/src/services/panes/tools/gltfLoaderService.tsx new file mode 100644 index 00000000000..9ae33b26aea --- /dev/null +++ b/packages/dev/inspector-v2/src/services/panes/tools/gltfLoaderService.tsx @@ -0,0 +1,290 @@ +import type { ServiceDefinition } from "../../../modularity/serviceDefinition"; +import type { IService } from "../../../modularity/serviceDefinition"; +import type { ISceneContext } from "../../sceneContext"; +import { SceneContextIdentity } from "../../sceneContext"; +import type { Observable } from "core/Misc/observable"; +import { Observable as BabylonObservable } from "core/Misc/observable"; +import { SceneLoader } from "core/Loading/sceneLoader"; +import type { ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "core/Loading/sceneLoader"; +import type { GLTFFileLoader, IGLTFLoaderExtension } from "loaders/glTF/glTFFileLoader"; +import { GLTFLoaderAnimationStartMode, GLTFLoaderCoordinateSystemMode } from "loaders/glTF/glTFFileLoader"; +import type { IGLTFValidationResults } from "babylonjs-gltf2interface"; +import type { Nullable } from "core/types"; + +export const GLTFLoaderServiceIdentity = Symbol("GLTFLoaderService"); + +/** + * Configuration state for the glTF loader. + * Uses the actual property types from GLTFFileLoader for type safety. + */ +export type IGLTFLoaderState = Pick< + GLTFFileLoader, + | "alwaysComputeBoundingBox" + | "alwaysComputeSkeletonRootNode" + | "animationStartMode" + | "capturePerformanceCounters" + | "compileMaterials" + | "compileShadowGenerators" + | "coordinateSystemMode" + | "createInstances" + | "loggingEnabled" + | "loadAllMaterials" + | "targetFps" + | "transparencyAsCoverage" + | "useClipPlane" + | "useSRGBBuffers" +>; + +/** + * State for a single glTF extension. + * Uses the actual enabled property from IGLTFLoaderExtension with additional extension-specific properties. + */ +export type IGLTFExtensionState = Pick & { + /** Additional extension-specific properties */ + [key: string]: any; +}; + +/** + * Collection of extension states keyed by extension name + */ +export interface IGLTFExtensionStates { + [key: string]: IGLTFExtensionState; +} + +/** + * Service for managing all aspects of the glTF loader including configuration, + * extensions, validation, and plugin lifecycle + */ +export interface IGLTFLoaderService extends IService { + // Plugin lifecycle + /** + * Observable that fires when a glTF loader plugin is activated + */ + readonly onLoaderActivatedObservable: Observable; + + // Loader configuration + /** + * Observable that fires when loader configuration changes + */ + readonly onLoaderConfigChangedObservable: Observable; + + /** + * Get the current loader configuration + */ + getLoaderConfig(): IGLTFLoaderState; + + /** + * Update a specific loader configuration property + */ + updateLoaderConfig(key: K, value: IGLTFLoaderState[K]): void; + + // Extensions configuration + /** + * Observable that fires when extension configuration changes + */ + readonly onExtensionConfigChangedObservable: Observable; + + /** + * Get the current extension configurations + */ + getExtensionStates(): IGLTFExtensionStates; + + /** + * Update a specific extension's enabled state + */ + updateExtensionState(extensionName: string, enabled: boolean): void; + + /** + * Update a specific property on an extension + */ + updateExtensionProperty(extensionName: string, property: K, value: any): void; + + // Validation + /** + * Observable that fires when new validation results are received + */ + readonly onValidationResultsObservable: Observable>; + + /** + * Get the most recent validation results + */ + getValidationResults(): Nullable; +} + +/** + * Creates default glTF loader state + * @returns Default loader configuration + */ +const CreateDefaultLoaderState = (): IGLTFLoaderState => ({ + alwaysComputeBoundingBox: false, + alwaysComputeSkeletonRootNode: false, + animationStartMode: GLTFLoaderAnimationStartMode.FIRST, + capturePerformanceCounters: false, + compileMaterials: false, + compileShadowGenerators: false, + coordinateSystemMode: GLTFLoaderCoordinateSystemMode.AUTO, + createInstances: true, + loggingEnabled: false, + loadAllMaterials: false, + targetFps: 60, + transparencyAsCoverage: false, + useClipPlane: false, + useSRGBBuffers: false, +}); + +/** + * Creates default extension states + * @returns Default extension configuration + */ +const CreateDefaultExtensionStates = (): IGLTFExtensionStates => { + /* eslint-disable @typescript-eslint/naming-convention */ + return { + 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: Number.MAX_VALUE }, + MSFT_minecraftMesh: { enabled: true }, + MSFT_sRGBFactors: { enabled: true }, + MSFT_audio_emitter: { enabled: true }, + }; +}; + +/** + * Unified service for managing all aspects of the glTF loader + */ +export const GLTFLoaderServiceDefinition: ServiceDefinition<[IGLTFLoaderService], [ISceneContext]> = { + friendlyName: "glTF Loader", + produces: [GLTFLoaderServiceIdentity], + consumes: [SceneContextIdentity], + factory: (_sceneContext) => { + // Plugin lifecycle + const onLoaderActivatedObservable = new BabylonObservable(); + + // Loader configuration + const onLoaderConfigChangedObservable = new BabylonObservable(); + let loaderState = CreateDefaultLoaderState(); + + // Extensions configuration + const onExtensionConfigChangedObservable = new BabylonObservable(); + let extensionStates = CreateDefaultExtensionStates(); + + // Validation + const onValidationResultsObservable = new BabylonObservable>(); + let validationResults: Nullable = null; + + // Subscribe to plugin activation + const pluginObserver = SceneLoader.OnPluginActivatedObservable.add((plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync) => { + if (plugin.name === "gltf") { + const loader = plugin as GLTFFileLoader; + + // Subscribe to loader + loader.onValidatedObservable.add((results: IGLTFValidationResults) => { + validationResults = results; + onValidationResultsObservable.notifyObservers(results); + }); + + // Subscribe to extension loading + loader.onExtensionLoadedObservable.add((extension: IGLTFLoaderExtension) => { + const extensionState = extensionStates[extension.name]; + if (extensionState) { + // Apply all extension properties + Object.keys(extensionState).forEach((key) => { + (extension as any)[key] = extensionState[key]; + }); + } + }); + + // Apply loader configuration + Object.keys(loaderState).forEach((key) => { + (loader as any)[key] = loaderState[key as keyof IGLTFLoaderState]; + }); + + // Always enable validation to provide feedback in the UI + // This ensures validation results are available regardless of user settings + loader.validate = true; + + // Notify observers after everything is set up + onLoaderActivatedObservable.notifyObservers(loader); + } + }); + + return { + // Plugin lifecycle + onLoaderActivatedObservable, + + // Loader configuration + onLoaderConfigChangedObservable, + + getLoaderConfig: () => ({ ...loaderState }), + + updateLoaderConfig: (key: K, value: IGLTFLoaderState[K]) => { + loaderState = { ...loaderState, [key]: value }; + onLoaderConfigChangedObservable.notifyObservers(loaderState); + }, + + // Extensions configuration + onExtensionConfigChangedObservable, + + getExtensionStates: () => { + // Return a deep copy to prevent external mutation + return JSON.parse(JSON.stringify(extensionStates)); + }, + + updateExtensionState: (extensionName: string, enabled: boolean) => { + // Ensure extension state exists before updating + const currentState = extensionStates[extensionName] ?? { enabled: true }; + extensionStates = { + ...extensionStates, + [extensionName]: { ...currentState, enabled }, + }; + onExtensionConfigChangedObservable.notifyObservers(extensionStates); + }, + + updateExtensionProperty: (extensionName: string, property: K, value: any) => { + // Ensure extension state exists before updating + const currentState = extensionStates[extensionName] ?? { enabled: true }; + extensionStates = { + ...extensionStates, + [extensionName]: { ...currentState, [property]: value }, + }; + onExtensionConfigChangedObservable.notifyObservers(extensionStates); + }, + + // Validation + onValidationResultsObservable, + + getValidationResults: () => validationResults, + + dispose: () => { + pluginObserver.remove(); + onLoaderActivatedObservable.clear(); + onLoaderConfigChangedObservable.clear(); + onExtensionConfigChangedObservable.clear(); + onValidationResultsObservable.clear(); + }, + }; + }, +}; diff --git a/packages/dev/inspector-v2/src/services/panes/tools/importService.tsx b/packages/dev/inspector-v2/src/services/panes/tools/importService.tsx index e474c3440c3..9eb97a95ec5 100644 --- a/packages/dev/inspector-v2/src/services/panes/tools/importService.tsx +++ b/packages/dev/inspector-v2/src/services/panes/tools/importService.tsx @@ -1,16 +1,20 @@ import type { ServiceDefinition } from "../../../modularity/serviceDefinition"; import { ToolsServiceIdentity } from "../toolsService"; import type { IToolsService } from "../toolsService"; -import { ImportAnimationsTools } from "../../../components/tools/importTools"; +import { GLTFAnimationImport } from "../../../components/tools/import/gltfAnimationImport"; +import { GLTFLoaderOptions } from "../../../components/tools/import/gltfLoaderOptions"; +import { GLTFLoaderServiceIdentity } from "./gltfLoaderService"; +import type { IGLTFLoaderService } from "./gltfLoaderService"; +import { GLTFValidationTools } from "../../../components/tools/import/gltfValidator"; -export const SceneImportServiceDefinition: ServiceDefinition<[], [IToolsService]> = { - friendlyName: "Import Tool", +export const GLTFAnimationImportServiceDefinition: ServiceDefinition<[], [IToolsService]> = { + friendlyName: "Animation Import Tool", consumes: [ToolsServiceIdentity], factory: (toolsService) => { const contentRegistration = toolsService.addSectionContent({ key: "AnimationImport", section: "Animation Import", - component: ({ context }) => , + component: ({ context }) => , }); return { @@ -21,6 +25,31 @@ export const SceneImportServiceDefinition: ServiceDefinition<[], [IToolsService] }, }; +export const GLTFToolsServiceDefinition: ServiceDefinition<[], [IToolsService, IGLTFLoaderService]> = { + friendlyName: "GLTF Tools", + consumes: [ToolsServiceIdentity, GLTFLoaderServiceIdentity], + factory: (toolsService, gltfLoaderService) => { + const loaderToolsRegistration = toolsService.addSectionContent({ + key: "GLTFLoader", + section: "GLTF Loader", + component: () => , + }); + + const validationToolsRegistration = toolsService.addSectionContent({ + key: "GLTFValidation", + section: "GLTF Validation", + component: () => , + }); + + return { + dispose: () => { + loaderToolsRegistration.dispose(); + validationToolsRegistration.dispose(); + }, + }; + }, +}; + export default { - serviceDefinitions: [SceneImportServiceDefinition], + serviceDefinitions: [GLTFAnimationImportServiceDefinition, GLTFToolsServiceDefinition], } as const; diff --git a/packages/dev/inspector-v2/src/services/panes/toolsService.tsx b/packages/dev/inspector-v2/src/services/panes/toolsService.tsx index fc719c53190..eb26d020ee6 100644 --- a/packages/dev/inspector-v2/src/services/panes/toolsService.tsx +++ b/packages/dev/inspector-v2/src/services/panes/toolsService.tsx @@ -70,7 +70,6 @@ export const ToolsServiceDefinition: ServiceDefinition<[IToolsService], [IShellS /** * Left TODO: Implement the following sections from toolsTabComponent.tsx - * - GLTF Validator (see glTFComponent.tsx) (consider putting in Import tools) * - Reflector * - GIF (consider putting in Capture Tools) * - Replay (consider putting in Capture Tools) diff --git a/packages/dev/sharedUiComponents/src/fluent/primitives/messageBar.tsx b/packages/dev/sharedUiComponents/src/fluent/primitives/messageBar.tsx index 0d7547d2abb..2b80ec7e32c 100644 --- a/packages/dev/sharedUiComponents/src/fluent/primitives/messageBar.tsx +++ b/packages/dev/sharedUiComponents/src/fluent/primitives/messageBar.tsx @@ -11,8 +11,8 @@ const useClasses = makeStyles({ }); type MessageBarProps = { - message: string; - title: string; + message?: string; + title?: string; docLink?: string; intent: "info" | "success" | "warning" | "error"; }; @@ -25,7 +25,7 @@ export const MessageBar: FunctionComponent = (props) => {
- {header} + {header && {header}} {message} {docLink && ( <>