UNPKG

@babylonjs/viewer

Version:

The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.

1,156 lines (1,154 loc) 76.3 kB
import { HotSpotQuery, Nullable, IDisposable, IColor4Like, IReadonlyObservable } from '@babylonjs/core/index.js'; import { Observable } from '@babylonjs/core/Misc/observable.js'; import { EngineContext } from '@babylonjs/lite'; import * as lit from 'lit'; import { LitElement, CSSResultGroup, PropertyValues, TemplateResult } from 'lit'; import * as lit_html from 'lit-html'; /** * Flags for selectively resetting parts of the viewer state. */ type ResetFlag = "source" | "environment" | "camera" | "animation" | "post-processing" | "material-variant" | "shadow"; declare const shadowQualityOptions: readonly ["none", "normal", "high"]; /** * Shadow quality levels. */ type ShadowQuality = (typeof shadowQualityOptions)[number]; declare const toneMappingOptions: readonly ["none", "standard", "aces", "neutral"]; /** * Tone mapping modes. */ type ToneMapping = (typeof toneMappingOptions)[number]; declare const ssaoOptions: readonly ["enabled", "disabled", "auto"]; /** * Screen-space ambient occlusion options. */ type SSAOOptions = (typeof ssaoOptions)[number]; /** * Camera orbit as [alpha, beta, radius]. */ type CameraOrbit = [alpha: number, beta: number, radius: number]; /** * Camera target as [x, y, z]. */ type CameraTarget = [x: number, y: number, z: number]; /** * Camera auto-orbit configuration. */ type CameraAutoOrbit = { /** * Whether the camera should automatically orbit around the model when idle. */ enabled: boolean; /** * The speed at which the camera orbits around the model when idle. */ speed: number; /** * The delay in milliseconds before the camera starts orbiting around the model when idle. */ delay: number; }; /** * Environment configuration parameters. */ type EnvironmentParams = { /** * The intensity of the environment lighting. */ intensity: number; /** * The blur applied to the environment lighting. */ blur: number; /** * The rotation of the environment lighting in radians. */ rotation: number; }; /** * Shadow configuration parameters. */ type ShadowParams = { /** * The quality of shadow being used. */ quality: ShadowQuality; }; /** * Post-processing configuration. */ type PostProcessing = { /** * The tone mapping to use for rendering the scene. */ toneMapping: ToneMapping; /** * The contrast applied to the scene. */ contrast: number; /** * The exposure applied to the scene. */ exposure: number; /** * Whether to enable screen space ambient occlusion (SSAO). */ ssao: SSAOOptions; }; /** * Options for controlling which parts of the environment to update. */ type EnvironmentOptions = Partial<Readonly<{ /** * Whether to use the environment for lighting (e.g. IBL). */ lighting: boolean; /** * Whether to use the environment for the skybox. */ skybox: boolean; }>>; /** * Options for loading an environment. */ type LoadEnvironmentOptions = EnvironmentOptions & Partial<Readonly<{ /** * Specifies the extension of the environment texture to load. * This must be specified when the extension cannot be determined from the url. */ extension: string; }>>; /** * @internal `LoadEnvironmentOptions` after the base class has resolved the optional `lighting` and * `skybox` flags to definite booleans (defaults to `true` for both when omitted, otherwise honors the * caller's choice). Engine-specific extras such as `extension` are forwarded as-is. Passed to the * subclass `_loadEnvironmentImpl` so it doesn't repeat the default-resolution logic. */ type ResolvedLoadEnvironmentOptions = Omit<LoadEnvironmentOptions, "lighting" | "skybox"> & { readonly lighting: boolean; readonly skybox: boolean; }; /** * A hot spot query specifying either a surface point or a fixed world position. */ type ViewerHotSpotQuery = ({ /** * The type of the hot spot. */ type: "surface"; /** * The index of the mesh within the loaded model. */ meshIndex: number; } & HotSpotQuery) | { /** * The type of the hot spot. */ type: "world"; /** * The fixed world space position of the hot spot. */ position: [x: number, y: number, z: number]; /** * The fixed world space normal of the hot spot. */ normal: [x: number, y: number, z: number]; }; /** * A hot spot definition with an optional camera pose. */ type HotSpot = ViewerHotSpotQuery & { /** * An optional camera pose to associate with the hotspot. */ cameraOrbit?: CameraOrbit; }; /** * Provides the result of a hot spot query. */ declare class ViewerHotSpotResult { /** * 2D canvas position in pixels. */ readonly screenPosition: [x: number, y: number]; /** * 3D world coordinates. */ readonly worldPosition: [x: number, y: number, z: number]; /** * Visibility range is [-1..1]. A value of 0 means camera eye is on the plane. */ visibility: number; } /** * Backend-agnostic options for loading a model. * @remarks * The full Viewer accepts the wider LoadAssetContainerOptions from core. * This type captures the subset that both backends support. */ type ViewerLoadModelOptions = Partial<Readonly<{ /** * The file extension to use for determining the loader plugin (e.g. ".glb", ".gltf"). */ pluginExtension: string; /** * If true, load glTF files using the OpenPBR material instead of the default PBR material. * Overrides the corresponding constructor option for this load. * @experimental */ useOpenPBR: boolean; }>>; /** * Backend-agnostic options shared by all viewer implementations. */ type ViewerBaseOptions = Partial<{ /** * The default clear color of the scene. */ clearColor: [r: number, g: number, b: number, a?: number]; /** * When enabled, rendering will be suspended when no scene state driven by the Viewer has changed. * This can reduce resource CPU/GPU pressure when the scene is static. * Enabled by default. */ autoSuspendRendering: boolean; /** * The default source model to load into the viewer. */ source: string; /** * The file extension to use for determining the loader plugin for the default source model (e.g. ".glb", ".obj"). * @remarks * If not set, the extension is inferred from the source URL when possible. This is needed for sources whose * extension cannot be inferred from the URL (e.g. data URLs or extension-less URLs). */ pluginExtension: string; /** * The default environment to load into the viewer for lighting (IBL). */ environmentLighting: string; /** * The default environment to load into the viewer for the skybox. */ environmentSkybox: string; /** * The default environment configuration. */ environmentConfig: Partial<EnvironmentParams>; /** * The default camera orbit. * @remarks The default camera orbit is restored when a new model is loaded. */ cameraOrbit: Partial<CameraOrbit>; /** * The default camera target. * @remarks The default camera target is restored when a new model is loaded. */ cameraTarget: Partial<CameraTarget>; /** * Automatically rotates a 3D model or scene without requiring user interaction. * @remarks The default camera auto orbit is restored when a new model is loaded. */ cameraAutoOrbit: Partial<CameraAutoOrbit>; /** * Whether to play the default animation immediately after loading. * @remarks The default animation auto play is restored when a new model is loaded. */ animationAutoPlay: boolean; /** * The default speed of the animation. * @remarks The default animation speed is restored when a new model is loaded. */ animationSpeed: number; /** * The default selected animation. * @remarks The default selected animation is restored when a new model is loaded. */ selectedAnimation: number; /** * The default post processing configuration. */ postProcessing: Partial<PostProcessing>; /** * Shadow configuration. */ shadowConfig: Partial<ShadowParams>; /** * The default selected material variant. * @remarks The default material variant is restored when a new model is loaded. */ selectedMaterialVariant: string; /** * The default hotspots. */ hotSpots: Record<string, HotSpot>; /** * Boolean indicating if the scene must use right-handed coordinates system. */ useRightHandedSystem: boolean; /** * If true, load glTF files using the OpenPBR material instead of the default PBR material. * @experimental */ useOpenPBR: boolean; /** * Called when a fatal error occurs that prevents the viewer from functioning. */ onFaulted: (error: Error) => void; }>; /** * The subset of the Viewer API that ViewerElementBase depends on. * Both the full Babylon.js Viewer and ViewerLite implement this contract. */ interface IViewer extends IDisposable { /** * Fired when the environment has changed. */ readonly onEnvironmentChanged: IReadonlyObservable<void>; /** * Fired when the environment configuration has changed. */ readonly onEnvironmentConfigurationChanged: IReadonlyObservable<void>; /** * Fired when an error occurs while loading the environment. */ readonly onEnvironmentError: IReadonlyObservable<unknown>; /** * Fired when the shadows configuration changes. */ readonly onShadowsConfigurationChanged: IReadonlyObservable<void>; /** * Fired when the post processing state changes. */ readonly onPostProcessingChanged: IReadonlyObservable<void>; /** * Fired when a model is loaded into the viewer (or unloaded from the viewer). */ readonly onModelChanged: IReadonlyObservable<Nullable<string | File | ArrayBufferView>>; /** * Fired when an error occurs while loading a model. */ readonly onModelError: IReadonlyObservable<unknown>; /** * Fired when progress changes on loading activity. */ readonly onLoadingProgressChanged: IReadonlyObservable<void>; /** * Fired when the camera auto orbit state changes. */ readonly onCameraAutoOrbitChanged: IReadonlyObservable<void>; /** * Fired when the selected animation changes. */ readonly onSelectedAnimationChanged: IReadonlyObservable<void>; /** * Fired when the animation speed changes. */ readonly onAnimationSpeedChanged: IReadonlyObservable<void>; /** * Fired when the selected animation is playing or paused. */ readonly onIsAnimationPlayingChanged: IReadonlyObservable<void>; /** * Fired when the current point on the selected animation timeline changes. */ readonly onAnimationProgressChanged: IReadonlyObservable<void>; /** * Fired when the selected material variant changes. */ readonly onSelectedMaterialVariantChanged: IReadonlyObservable<void>; /** * Fired when the hot spots object changes to a complete new object instance. */ readonly onHotSpotsChanged: IReadonlyObservable<void>; /** * Fired when the cameras as hot spots property changes. */ readonly onCamerasAsHotSpotsChanged: IReadonlyObservable<void>; /** * Fired after each frame is rendered. */ readonly onAfterRenderObservable: IReadonlyObservable<void>; /** * Fired when the clear color changes. */ readonly onClearColorChanged: IReadonlyObservable<void>; /** * Gets or sets the clear color (background color) of the viewer. */ clearColor: IColor4Like; /** * Gets the camera auto-orbit configuration. */ get cameraAutoOrbit(): Readonly<CameraAutoOrbit>; /** * Sets the camera auto-orbit configuration. Only specified fields are updated. */ set cameraAutoOrbit(value: Partial<Readonly<CameraAutoOrbit>>); /** * Resets the camera to its default state. * @param reframe If true, reframes the camera to fit the model. If undefined, automatically determined. */ resetCamera(reframe?: boolean): void; /** * Updates the camera pose. * @param pose The new pose of the camera. Any unspecified values are left unchanged. */ updateCamera(pose: { alpha?: number; beta?: number; radius?: number; targetX?: number; targetY?: number; targetZ?: number; }): void; /** * Gets the environment configuration. */ get environmentConfig(): Readonly<EnvironmentParams>; /** * Sets the environment configuration. Only specified fields are updated. */ set environmentConfig(value: Partial<Readonly<EnvironmentParams>>); /** * Loads an environment from the specified URL. * @param url The URL of the environment to load. * @param options The options for loading the environment. * @param abortSignal An optional signal that can be used to abort the load. */ loadEnvironment(url: string, options?: LoadEnvironmentOptions, abortSignal?: AbortSignal): Promise<void>; /** * Resets the environment to its default state. * @param options The options to use when resetting the environment. * @param abortSignal An optional signal that can be used to abort the reset. */ resetEnvironment(options?: EnvironmentOptions, abortSignal?: AbortSignal): Promise<void>; /** * Gets the post-processing configuration. */ get postProcessing(): Readonly<PostProcessing>; /** * Sets the post-processing configuration. Only specified fields are updated. */ set postProcessing(value: Partial<Readonly<PostProcessing>>); /** * Gets the current shadow configuration. */ readonly shadowConfig: Readonly<ShadowParams>; /** * Updates the shadow configuration. * @param value The new shadow configuration. * @param abortSignal Optional signal that can be used to abort the update. */ updateShadows(value: Partial<Readonly<ShadowParams>>, abortSignal?: AbortSignal): Promise<void>; /** * Loads a 3D model from the specified source. * @param source The source of the model to load. * @param options The options for loading the model. * @param abortSignal An optional signal that can be used to abort the load. */ loadModel(source: string | File | ArrayBufferView, options?: ViewerLoadModelOptions, abortSignal?: AbortSignal): Promise<void>; /** * Unloads the current 3D model if one is loaded. * @param abortSignal An optional signal that can be used to abort the reset. */ resetModel(abortSignal?: AbortSignal): Promise<void>; /** * The list of animation names for the currently loaded model. */ readonly animations: readonly string[]; /** * Gets or sets the index of the selected animation. */ selectedAnimation: number; /** * Gets or sets the speed scale at which animations are played. */ animationSpeed: number; /** * True if an animation is currently playing. */ readonly isAnimationPlaying: boolean; /** * Gets or sets the current point on the selected animation timeline, normalized between 0 and 1. */ animationProgress: number; /** * Toggles between playing and pausing the selected animation. */ toggleAnimation(): void; /** * Plays the selected animation. */ playAnimation(): void; /** * Pauses the selected animation. */ pauseAnimation(): Promise<void>; /** * The list of material variant names for the currently loaded model. */ readonly materialVariants: readonly string[]; /** * Gets or sets the selected material variant. */ selectedMaterialVariant: Nullable<string>; /** * Gets or sets the hot spots configuration. */ hotSpots: Record<string, HotSpot>; /** * Gets or sets whether cameras embedded in the model should be exposed as hot spots. */ camerasAsHotSpots: boolean; /** * Queries a named hot spot and returns its screen and world positions. * @param name The name of the hot spot to query. * @param result The result object to populate. * @returns True if the hot spot was found. */ queryHotSpot(name: string, result: ViewerHotSpotResult): boolean; /** * Updates the camera to focus on a named hotspot. * @param name The name of the hotspot to focus on. * @returns True if the hotspot was found and the camera was updated. */ focusHotSpot(name: string): boolean; /** * True if a model is currently loaded. */ readonly isModelLoaded: boolean; /** * The current loading progress. False when not loading, true when loading with indeterminate progress, or a number between 0 and 1. */ readonly loadingProgress: boolean | number; /** * Resets the viewer to its initial state based on the options passed in to the constructor. * @param flags The flags that specify which parts of the viewer to reset. If no flags are provided, all parts will be reset. */ reset(...flags: ResetFlag[]): void; /** * Disposes the viewer and releases all resources. */ dispose(): void; } /** * Common base for the full Babylon.js {@link Viewer} and the lite Viewer. * * Encapsulates the pieces that are identical between both engine backends: * - The 18 public observables exposed by the viewer surface area * - In-flight load operation tracking (used to compute aggregate `loadingProgress`) * - The `_throwIfDisposedOrAborted` helper used at the start of every async operation * - The disposed flag and observable teardown in `dispose()` * * Subclasses are responsible for everything engine-specific (scene/engine creation, * model + environment loading orchestration, camera, post-processing, shadows, etc.) * and for declaring `implements IViewer` themselves so the public API contract is * verified at the leaf class level. */ declare abstract class ViewerBase { /** * Fired when the environment has changed. */ readonly onEnvironmentChanged: Observable<void>; /** * Fired when the environment configuration has changed. */ readonly onEnvironmentConfigurationChanged: Observable<void>; /** * Fired when an error occurs while loading the environment. */ readonly onEnvironmentError: Observable<unknown>; /** * Fired when the shadows configuration changes. */ readonly onShadowsConfigurationChanged: Observable<void>; /** * Fired when the post processing state changes. */ readonly onPostProcessingChanged: Observable<void>; /** * Fired when a model is loaded into the viewer (or unloaded from the viewer). * @remarks * The event argument is the source that was loaded, or null if no model is loaded. */ readonly onModelChanged: Observable<Nullable<string | File | ArrayBufferView<ArrayBufferLike>>>; /** * Fired when an error occurs while loading a model. */ readonly onModelError: Observable<unknown>; /** * Fired when progress changes on loading activity. */ readonly onLoadingProgressChanged: Observable<void>; /** * Fired when the camera auto orbit state changes. */ readonly onCameraAutoOrbitChanged: Observable<void>; /** * Fired when the selected animation changes. */ readonly onSelectedAnimationChanged: Observable<void>; /** * Fired when the animation speed changes. */ readonly onAnimationSpeedChanged: Observable<void>; /** * Fired when the selected animation is playing or paused. */ readonly onIsAnimationPlayingChanged: Observable<void>; /** * Fired when the current point on the selected animation timeline changes. */ readonly onAnimationProgressChanged: Observable<void>; /** * Fired when the selected material variant changes. */ readonly onSelectedMaterialVariantChanged: Observable<void>; /** * Fired when the hot spots object changes to a complete new object instance. */ readonly onHotSpotsChanged: Observable<void>; /** * Fired when the cameras as hot spots property changes. */ readonly onCamerasAsHotSpotsChanged: Observable<void>; /** * Fired after each frame is rendered. */ readonly onAfterRenderObservable: Observable<void>; /** * Fired when the clear color changes. */ readonly onClearColorChanged: Observable<void>; /** * @internal Tracks in-flight load operations (model + environment + shadows) so that * `loadingProgress` can return either an aggregate progress number, `true` (indeterminate), * or `false` (no operations in flight). */ protected readonly _loadOperations: Set<Readonly<{ progress: Nullable<number>; }>>; /** @internal True after `dispose()` has been called. */ protected _isDisposed: boolean; /** * @internal Backend-agnostic viewer options stored at construction time. Subclasses declare this * with their own (narrower) options type that extends {@link ViewerBaseOptions} and assign it from * their own constructor (typically via a parameter property). */ protected abstract readonly _options?: Readonly<ViewerBaseOptions>; /** * The current loading progress. False when no load is in flight, true when at least one * load is in flight with indeterminate progress, or a number between 0 and 1 representing * the average of all in-flight loads' progress. */ get loadingProgress(): boolean | number; /** * Begin tracking a new load operation. Subclasses call this at the start of an async load * and dispose the returned handle when it completes (or fails). The handle exposes a * `progress` setter that, when assigned, fires `onLoadingProgressChanged`. * @returns A handle that can be disposed when the operation completes; the `progress` setter * updates the aggregate `loadingProgress` as the operation runs. */ protected _beginLoadOperation(): IDisposable & { progress: Nullable<number>; }; /** * @internal Throws if the viewer has been disposed or any of the supplied abort signals * are aborted. Used at the start of every async operation to bail out early. * @param abortSignals Optional abort signals to check. */ protected _throwIfDisposedOrAborted(...abortSignals: (Nullable<AbortSignal> | undefined)[]): void; /** Lock guarding lighting-side environment loads. */ private readonly _loadEnvironmentLightingLock; /** Abort controller for the currently in-flight lighting-side load (null when none). */ private _loadEnvironmentLightingAbortController; /** Lock guarding skybox-side environment loads. */ private readonly _loadEnvironmentSkyboxLock; /** Abort controller for the currently in-flight skybox-side load (null when none). */ private _loadEnvironmentSkyboxAbortController; /** * @internal The abort signal of the currently in-flight lighting-side load, or `undefined` if * none. Subclasses can use this in `_throwIfDisposedOrAborted` to bail out of dependent async * work (shadows, post-processing, etc.) when the user starts a new lighting load. */ protected get _loadEnvironmentLightingAbortSignal(): AbortSignal | undefined; /** * @internal The abort signal of the currently in-flight skybox-side load, or `undefined` if * none. Subclasses can use this in `_throwIfDisposedOrAborted` to bail out of dependent async * work (shadows, post-processing, etc.) when the user starts a new skybox load. */ protected get _loadEnvironmentSkyboxAbortSignal(): AbortSignal | undefined; /** * Loads an environment from the specified URL. The lighting and skybox sides have * independent locks and abort controllers so concurrent requests for one side don't * cancel an in-flight load for the other. * @param url The URL of the environment to load. * @param options Selects which sides to update (defaults to both) and forwards engine-specific extras. * @param abortSignal Optional signal that can be used to abort the load externally. * @returns A promise that resolves when the environment has finished loading. */ loadEnvironment(url: string, options?: LoadEnvironmentOptions, abortSignal?: AbortSignal): Promise<void>; /** * Removes the loaded environment. By default removes both lighting and skybox; pass `options` * to remove only one side. Subclasses (notably the full Viewer) may override to add backend-specific * fallback behavior such as substituting a default environment for lighting when the scene contains * PBR materials. * @param options Selects which sides to remove (defaults to both). * @param abortSignal Optional signal that can be used to abort the operation externally. * @returns A promise that resolves when the environment has finished resetting. */ resetEnvironment(options?: EnvironmentOptions, abortSignal?: AbortSignal): Promise<void>; /** * @internal Internal helper exposing the dual-lock orchestration with a nullable URL. * Used by the public `loadEnvironment` (with a string URL) and by subclass `resetEnvironment` * implementations (which pass `undefined` to clear or `"auto"` to load defaults). * * Subclasses should NOT override this — override the abstract `_loadEnvironmentImpl` instead. */ protected _updateEnvironment(url: Nullable<string | undefined>, options?: LoadEnvironmentOptions, abortSignal?: AbortSignal): Promise<void>; /** * @internal Engine-specific environment loading. Subclasses implement this with their actual * texture loading / scene mutation logic. The base class handles only the surrounding lock + * abort-prev orchestration and the composite abort signal; everything else (`onEnvironmentChanged` / * `onEnvironmentError` notifications, snapshot-helper bracketing, etc.) is the impl's responsibility. * * Implementations should: * - Throw on failure. They should fire `onEnvironmentError` themselves before throwing if they * want external observers to be notified. * - Fire `onEnvironmentChanged` on success. * - Periodically re-check abort by calling `throwIfAborted(abortSignal, compositeAbortSignal)` * at safe points within the load (e.g. after long-running awaits). * * @param url Trimmed URL string, `undefined` (caller asked to clear), or `null`. * @param options Resolved options — `lighting` and `skybox` are guaranteed booleans indicating * which sides the caller is updating; engine-specific extras (e.g. `extension`) are forwarded as-is. * @param abortSignal The caller's external abort signal (or `undefined`). * @param compositeAbortSignal Signal that fires when ALL relevant internal load operations have aborted. */ protected abstract _loadEnvironmentImpl(url: Nullable<string | undefined>, options: ResolvedLoadEnvironmentOptions, abortSignal: AbortSignal | undefined, compositeAbortSignal: AbortSignal): Promise<void>; /** @internal Current environment intensity. Initialized from options in subclass constructors. */ protected _environmentIntensity: number; /** @internal Current environment skybox blur. Initialized from options in subclass constructors. */ protected _environmentBlur: number; /** @internal Current environment rotation in radians. Initialized from options in subclass constructors. */ protected _environmentRotation: number; get environmentConfig(): Readonly<EnvironmentParams>; set environmentConfig(value: Partial<Readonly<EnvironmentParams>>); /** * @internal Push the current `_environmentIntensity` value into the engine's environment state. * Called by the public `environmentConfig` setter only when the value changes. */ protected abstract _applyEnvironmentIntensity(): void; /** * @internal Push the current `_environmentBlur` value into the engine's environment state. * Called by the public `environmentConfig` setter only when the value changes. */ protected abstract _applyEnvironmentBlur(): void; /** * @internal Push the current `_environmentRotation` value into the engine's environment state. * Called by the public `environmentConfig` setter only when the value changes. */ protected abstract _applyEnvironmentRotation(): void; /** @internal Initialized from options in subclass constructors. */ protected _autoOrbitEnabled: boolean; /** @internal Initialized from options in subclass constructors. */ protected _autoOrbitSpeed: number; /** @internal Initialized from options in subclass constructors. */ protected _autoOrbitDelay: number; get cameraAutoOrbit(): Readonly<CameraAutoOrbit>; set cameraAutoOrbit(value: Partial<Readonly<CameraAutoOrbit>>); /** * @internal Push the current `_autoOrbitEnabled` value into engine state. * Called by the public `cameraAutoOrbit` setter only when the value changes. */ protected abstract _applyCameraAutoOrbitEnabled(): void; /** * @internal Push the current `_autoOrbitSpeed` value into engine state. * Called by the public `cameraAutoOrbit` setter only when the value changes. */ protected abstract _applyCameraAutoOrbitSpeed(): void; /** * @internal Push the current `_autoOrbitDelay` value into engine state. * Called by the public `cameraAutoOrbit` setter only when the value changes. */ protected abstract _applyCameraAutoOrbitDelay(): void; /** * @internal The current scene clear color, stored as a stable mutable record so consumers * holding a reference returned by the `clearColor` getter see updates from the setter and from * `reset("environment")`. The setter mutates this object in-place rather than replacing it. */ protected readonly _clearColor: IColor4Like; /** * The viewer clear color (e.g. background). */ get clearColor(): Readonly<IColor4Like>; set clearColor(value: Readonly<IColor4Like>); /** * @internal Push the current `_clearColor` value into the engine's scene state. Called by the * public `clearColor` setter; subclasses may also call this directly during construction to sync * engine state to the initial field values. */ protected abstract _applyClearColor(): void; /** @internal Pure state — no engine state. Subclasses initialize via the public `hotSpots` setter in their constructor body. */ private _hotSpots; /** * The set of defined hotspots. */ get hotSpots(): Record<string, HotSpot>; set hotSpots(value: Record<string, HotSpot>); /** Lock guarding model loads (and resets). */ private readonly _loadModelLock; /** Abort controller for the currently in-flight model load (null when none). */ private _loadModelAbortController; /** * @internal The abort signal of the currently in-flight model load, or `undefined` if none. * Subclasses can use this in `_throwIfDisposedOrAborted` to bail out of dependent async work * (shadows, environment fallback, etc.) when the user starts a new model load. */ protected get _loadModelAbortSignal(): AbortSignal | undefined; /** * Loads a 3D model from the specified source. * @param source The source of the model to load. * @param options Engine-specific options for loading the model. * @param abortSignal Optional signal that can be used to abort the load externally. * @returns A promise that resolves when the model has finished loading. */ loadModel(source: string | File | ArrayBufferView, options?: ViewerLoadModelOptions, abortSignal?: AbortSignal): Promise<void>; /** * Unloads the current 3D model if one is loaded. * @param abortSignal Optional signal that can be used to abort the reset. * @returns A promise that resolves when the current model has been unloaded. */ resetModel(abortSignal?: AbortSignal): Promise<void>; /** * @internal Internal helper exposing the model load orchestration with `source: undefined` meaning * "unload the current model". Subclasses should NOT override this — override `_loadModelImpl` instead. */ protected _updateModel(source: string | File | ArrayBufferView | undefined, options?: ViewerLoadModelOptions, abortSignal?: AbortSignal): Promise<void>; /** * @internal Engine-specific model loading. Subclasses implement this with their actual model * loading logic. The base class handles only the surrounding lock + abort-prev orchestration; * everything else (load-operation progress tracking, `onModelChanged` / `onModelError` * notifications, snapshot-helper bracketing) is the impl's responsibility. * * Implementations should: * - Throw on failure. They should fire `onModelError` themselves before throwing if they want * external observers to be notified. * - Fire `onModelChanged` on success. * - Manage their own `_beginLoadOperation` / dispose pair if they want to contribute to * `loadingProgress`. * - Periodically re-check abort by calling `throwIfAborted(abortSignal, internalAbortSignal)` * at safe points within the load (e.g. after long-running awaits). * - Treat `source === undefined` as "unload the current model" — this is how `resetModel` flows * through. They should still fire `onModelChanged(null)` so consumers see the unload. * * @param source Source URL/File/ArrayBufferView, or `undefined` to unload the current model. * @param options Caller's options (or undefined). May contain engine-specific extras. * @param abortSignal The caller's external abort signal (or `undefined`). * @param internalAbortSignal Signal that fires when a NEWER model load supersedes this one. */ protected abstract _loadModelImpl(source: string | File | ArrayBufferView | undefined, options: ViewerLoadModelOptions | undefined, abortSignal: AbortSignal | undefined, internalAbortSignal: AbortSignal): Promise<void>; /** * @internal Optional post-lock hook invoked AFTER the model load lock is released, allowing * subclasses to do follow-up work that needs other locks (e.g. environment fallback). The base * skips this hook if the load was superseded by a newer one before we got here. * * Implementations that await additional work should re-check `internalAbortSignal.aborted` * after each await to avoid acting on stale state (a newer load may have started during the * await window). * * Default: no-op. */ protected _afterLoadModel(source: string | File | ArrayBufferView | undefined, options: ViewerLoadModelOptions | undefined, abortSignal: AbortSignal | undefined, internalAbortSignal: AbortSignal): Promise<void>; /** Lock guarding shadow updates. */ private readonly _updateShadowsLock; /** Abort controller for the currently in-flight shadow update (null when none). */ private _shadowsAbortController; /** * @internal The abort signal of the currently in-flight shadow update, or `undefined` if none. * Subclasses can use this in `_throwIfDisposedOrAborted` to bail out of dependent async work * when the user starts a new shadow update. */ protected get _shadowsAbortSignal(): AbortSignal | undefined; /** * @internal The currently committed shadow quality. Subclasses initialize this from their * options in their constructor and read it in their `_loadModelImpl` etc. The base class * commits a new value here only after `_updateShadowsImpl` succeeds, so failed/aborted * shadow updates don't leave this field out of sync with engine state. */ protected _shadowQuality: ShadowQuality; /** @internal */ abstract get selectedMaterialVariant(): Nullable<string>; /** @internal */ abstract set selectedMaterialVariant(value: Nullable<string>); /** * Gets the current shadow configuration. */ get shadowConfig(): Readonly<ShadowParams>; /** * Updates the shadow configuration. Skips work if the requested value matches the currently * committed one. Subclasses can override this to validate the requested quality (e.g. throw on * unsupported combinations) before delegating to `super.updateShadows(value, abortSignal)`. * @param value The new shadow configuration. * @param abortSignal Optional signal that can be used to abort the update externally. * @returns A promise that resolves when the shadow update completes. */ updateShadows(value: Partial<Readonly<ShadowParams>>, abortSignal?: AbortSignal): Promise<void>; /** * Runs the engine-specific shadow update at the given quality under the shared lock, with * abort-prev semantics. Subclasses should call this (rather than `_updateShadowsImpl` directly) * when they need to re-run the shadow setup (e.g. after a model change or environment change). * The public `updateShadows` also routes through this helper. * @param quality The shadow quality to apply. Defaults to the currently committed quality * (`this._shadowQuality`), which is the right choice for re-running shadow setup without * changing the committed quality. The public `updateShadows` passes a resolved new quality. * @param abortSignal Optional external abort signal. * @returns A promise that resolves when the shadow update completes. */ protected _updateShadows(quality?: ShadowQuality, abortSignal?: AbortSignal): Promise<void>; /** * @internal Engine-specific shadow setup. Subclasses implement this with their actual shadow * generation logic. The base class handles all surrounding orchestration: lock acquisition, * abort-prev semantics, quality resolution, and the success-only commit of `_shadowQuality`. * * Implementations should: * - Throw on failure; the base class propagates the error to the caller without committing the new quality. * - Use `quality` (not `this._shadowQuality`, which still holds the pre-update value) to drive the setup. * - Periodically re-check abort by calling `throwIfAborted(abortSignal, internalAbortSignal)` at safe points. */ protected abstract _updateShadowsImpl(quality: ShadowQuality, abortSignal: AbortSignal | undefined, internalAbortSignal: AbortSignal): Promise<void>; /** * Disposes the viewer and releases shared resources (observables, disposed flag). * Subclasses MUST override this method to dispose their own engine-specific state * (engine, scene, abort controllers, models, etc.) and call `super.dispose()` last * so observable consumers see the engine-specific notifications before observables clear. * * Subclasses should also early-return if `_isDisposed` is already true. */ dispose(): void; /** * Resets the viewer to its initial state based on the options passed in to the constructor. * @param flags The flags that specify which parts of the viewer to reset. If no flags are provided, all parts will be reset. * - "source": Reset the loaded model. * - "environment": Reset environment related state. * - "shadow": Reset shadow related state. * - "animation": Reset animation related state. * - "camera": Reset camera related state. * - "post-processing": Reset post-processing related state. * - "material-variant": Reset material variant related state. */ reset(...flags: ResetFlag[]): void; /** * @internal * Orchestrates the reset operation in canonical flag order. The {@link interpolate} parameter is * forwarded to per-flag hooks (currently only `_resetCamera`) so internal callers can reset * without camera animation. */ protected _reset(interpolate: boolean, ...flags: ResetFlag[]): void; /** * @internal Resets the loaded model to the source specified at construction (or no model if no source was specified). */ protected _resetModel(): void; /** @internal */ protected abstract _resetEnvironment(): void; /** * @internal Resets the shadow configuration to the value specified at construction. */ protected _resetShadows(): void; /** @internal */ protected abstract _resetAnimation(): void; /** * @internal * @param interpolate If true, animate camera transitions when supported. Subclasses without bounds-based * reframing may ignore this parameter. */ protected abstract _resetCamera(interpolate: boolean): void; /** @internal */ protected abstract _resetPostProcessing(): void; /** * @internal Resets the selected material variant to the value specified at construction (or null if not specified). */ protected _resetMaterialVariant(): void; } /** * The options for the Lite Viewer. */ type ViewerOptions = ViewerBaseOptions; /** * Options for {@link Viewer.loadModel} on the Lite Viewer. */ type LoadModelOptions = ViewerLoadModelOptions; /** * Options for creating a Lite Viewer bound to a canvas. */ type CanvasViewerOptions = ViewerBaseOptions; /** * The default options for the Lite Viewer. */ declare const DefaultViewerOptions: { readonly clearColor: [0, 0, 0, 0]; readonly autoSuspendRendering: true; readonly environmentConfig: { readonly intensity: 1; readonly blur: 0.3; readonly rotation: 0; }; readonly environmentLighting: "auto"; readonly environmentSkybox: "none"; readonly cameraAutoOrbit: { readonly enabled: false; readonly delay: 2000; readonly speed: 0.05; }; readonly animationAutoPlay: false; readonly animationSpeed: 1; readonly shadowConfig: { readonly quality: "none"; }; readonly postProcessing: { readonly toneMapping: "neutral"; readonly contrast: 1; readonly exposure: 1; readonly ssao: "auto"; }; readonly useRightHandedSystem: false; readonly useOpenPBR: false; }; /** * A lightweight implementation of the {@link IViewer} interface built on the Babylon Lite API. * * @remarks * Babylon Lite is a WebGPU-only engine that provides a subset of the full Babylon.js feature set. * Features that are not available in Lite (SSAO, "high" shadow quality, hot spots, File/ArrayBufferView model sources) * will log warnings and fall back gracefully. */ declare class Viewer extends ViewerBase implements IViewer { private readonly _engine; protected readonly _options?: ViewerOptions | undefined; private readonly _scene; private readonly _camera; private _detachControl; private _renderLoopRunning; private _autoOrbitIdleTime; private _lastPointerTime; /** The currently-loaded lighting URL ("auto" resolves to the embedded default). null = no lighting loaded. */ private _currentLightingUrl; /** The currently-loaded skybox URL ("auto" resolves to the embedded default). null = no skybox loaded. */ private _currentSkyboxUrl; private _toneMapping; private _contrast; private _exposure; private _ssaoOption; /** Serializes the async PBR-pipeline rebuilds triggered by image-processing updates * (`setSceneImageProcessing`) and environment relights (`rebuildScenePbrPipelines`), so overlapping * changes can't run concurrent rebuilds (which race on the scene's renderable list and leak). */ private readonly _pbrRebuildLock; private _shadowGenerator; private _shadowLight; private _shadowGround; private _container; /** GPU picker for double-click focus, created lazily on first double-click. Disposed with the viewer. */ private _picker; /** The source that was passed to the most recent {@link loadModel} call, for notifications. */ private _modelSource; /** * True once the first model load has built its Lite material group (via `registerScene`). Because * `_scene` is created once and never recreated, and glTF models all share Lite's singleton PBR group * builder, later model loads reuse that already-built group: their meshes are enqueued into the * per-frame material-swap queue and the running render loop materializes them, so those loads must * NOT re-register the scene (re-registration clears the swap queue and would drop the model). See the * (re-)registration decision in {@link _loadModelImpl}. */ private _modelMaterialGroupBuilt; /** Cached animation-aware model bounds for the current model. Reset on unload. See {@link _computeModelBounds}. */ private _cachedModelBounds; private _selectedAnimation; private _animationSpeed; private _wasPlaying; private _lastProgress; private _selectedMaterialVariant; private _camerasAsHotSpots; /** * Aborts the in-flight camera interpolation (from {@link focusHotSpot}) when a new one starts or * the viewer is disposed. Null when no interpolation is running. */ private _cameraInterpolationAbort; private _defaultAlpha; private _defaultBeta; private _defaultRadius; private _defaultTarget; /** * Creates a new Viewer instance. * @param _engine The Babylon Lite engine context. * @param _options Optional viewer configuration. */ constructor(_engine: EngineContext, _options?: ViewerOptions | undefined); /** @internal */ protected _applyClearColor(): void; /** @internal Lite stores auto-orbit state on the base class fields and consults them in its idle loop. No engine push needed. */ protected _applyCameraAutoOrbitEnabled(): void; /** @internal Lite stores auto-orbit state on the base class fields. */ protected _applyCameraAutoOrbitSpeed(): void; /** @internal Lite stores auto-orbit state on the base class fields. */ protected _applyCameraAutoOrbitDelay(): void; resetCamera(reframe?: boolean): void; /** * Shared implementation of camera reset. Resolves the reframe default (matching the full Viewer: * reframe to model bounds when the selected animation differs from the default, otherwise return to * the explicit default pose) and moves the camera there, optionally animating the transition. * @param reframe Whether to reframe to model bounds; when undefined, decided from animation state. * @param interpolate Whether to animate the camera to the reset pose. */ private _resetCameraCore; updateCamera(pose: { alpha?: number; beta?: number; radius?: number; targetX?: number; targetY?: number; targetZ?: number; }): void; /** * Moves the camera to a goal pose, either by animating (via {@link interpolateArcRotateCamera}) or by * snapping directly. Either way, any in-flight interpolation is first canceled so it can't fight the * new pose. Omitted or NaN goal fields keep the camera's current value for that channel. * @param goal The destination camera pose. * @param interpolate Whether to animate the transition. */ private _moveCameraTo; /** * Frames the camera to the loaded model's bounds, matching the full Viewer's framing math. Near/far * planes and zoom limits are applied immediately; the orbit pose is moved (snapped or animated) via * {@link _moveCameraTo}. * * When `applyDefaultPoseOverrides` is true, the bounds-derived orbit pose is overridden per-channel by * any explicit `cameraOrbit`/`cameraTarget` options — mirroring the full Viewer's * `_resetCamera` -> `_reframeCameraFromBounds`. With no such options this equals the pure bounds * framing used on model load, so a reset returns to exactly the load-time framing. * @param interpolate Whether to animate the camera to the framing pose. * @param applyDefaultPoseOverrides Whether to override the bounds pose with explicit camera options. * @returns True if the model had bounds and the camera was framed; false if there is no model to frame. */ private _frameCameraToModel; /** * Compute the aggregate world-space bounding box of the loaded model, accounting for * animation. * * Delegates to Lite's {@link computeMaxExtents}, which steps through the currently-selected * animation group and unions every sampled pose. This captures the full swept volume of * node (TRS), skeletal, and morph-target animation — so skinned models like the * acrobaticPlane glTF frame correctly instead of reporting their (much smaller) bind-pose * AABB. Meshes are gathered with {@link getContainerMeshes} so Viewer-added meshes (e.g. the * shadow-receiver disc) are excluded. * * The result is cached for the lifetime of the loaded model (reset in * `_unloadCurrentModel`) so the two consumers — `_frameCameraToModel` (camera target + * radius + near/far planes) and `_setupShadows` (light positioning, ground placement, * frustum sizing) — share a single animation sweep rather than stepping it twice. * * @returns aggregate `min`, `max`, `center`, and bounding-sphere `radius` * (= half the diagonal), or `null` if the model has no bounds info. */ private _computeModelBounds; /** @inte