@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,793 lines • 79.6 kB
TypeScript
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.
* @remarks
* Only honored by the full Viewer; the Lite Viewer has no scene-mutation tracking and ignores this
* option. Both flavors suspend rendering while the canvas is offscreen regardless of this setting.
*/
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.
*
* The `autoSuspendRendering` option is silently ignored: Lite has no scene-mutation tracking, so it cannot
* detect when a scene is idle. Rendering is still suspended while the canvas is offscreen (see
* {@link CreateViewerForCanvas}), which is the case that dominates cost on pages with multiple viewers.
*/
declare class Viewer extends ViewerBase implements IViewer {
private readonly _engine;
protected readonly _options?: ViewerOptions | undefined;
private readonly _scene;
private readonly _camera;
private readonly _deviceLostRecovery;
private _detachControl;
/** True while the engine's requestAnimationFrame loop is running. False while suspended or disposed. */
private _renderLoopRunning;
/**
* True once the scene has been registered with the engine (deferred builders run, renderables bucketed).
* Distinct from {@link _renderLoopRunning}: suspending rendering stops the rAF loop but leaves the scene
* registered, so code that needs to know "has the scene been built" must consult this instead.
*/
private _sceneRegistered;
/** Number of live suspension handles returned by {@link _suspendRendering}. Rendering runs only at zero. */
private _suspendRenderCount;
/**
* Serializes every engine start/stop and scene (un)registration, so a suspend/resume triggered by the
* offscreen observer can never interleave with {@link _beginRendering}'s stop, unregister, register,
* start sequence (which is itself re-entered from model loads, environment loads, and construction).
*/
private readonly _renderLoopLock;
/**
* Resolves the pending `startEngine` await when the render loop is stopped before its first frame.
* Lite's `startEngine` promise only settles from inside the rAF callback, so stopping the loop first
* (by suspension or disposal) would otherwise leave the await pending forever.
*/
private _renderLoopStopped;
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;
/** @internal Lite has no engine state for intensity. */
protected _applyEnvironmentIntensity(): void;
/** @internal */
protected _applyEnvironmentBlur(): void;
/** @internal */
protected _applyEnvironmentRotation(): void;
/** @internal */
protected _loadEnvironmentImpl(url: Nullable<string | undefined>, options: ResolvedLoadEnvironmentOptions, abortSignal: AbortSignal | undefined, compositeAbortSignal: AbortSignal): Promise<void>;
get postProcessing(): Readonly<PostProcessing>;
set postProcessing(value: Partial<Readonly<PostProcessing>>);
/**
* Apply the current committed post-processing state to the running scene via
* `setSceneImageProcessing`, serialized through {@link _pbrRebuildLock} so overlapping calls
* never run concurrent PBR-pipeline rebuilds. Each queued apply re-reads the latest committed state
* when it runs, so a burst of rapid changes collapses to the final state (intermediate updates that
* no longer differ are no-ops).
*/
private _applyImageProcessingDynamic;
/**
* Force a rebuild of the loaded model's PBR pipelines so they pick up the scene's current environment
* (IBL) textures. Needed because Lite bakes the environment into the PBR shaders at build time and
* `loadEnvironment` doesn't rebuild existing PBR groups, so a model built before its environment loads
* renders unlit/black. Used when an environment is added or changed AFTER a model is already displayed.
*
* `rebuildScenePbrPipelines` re-runs the PBR group builder against the scene's current `_envTextures`,
* producing pipelines pixel-identical to a model built with the environment present from the start.
*
* Serialized through {@link _pbrRebuildLock} with the other image-processing updates (which also
* rebuild PBR pipelines) so the two can't race.
* @returns A promise that resolves once the model's PBR pipelines have been rebuilt.
*/
private _rebuildModelPbrForEnvironment;
/**
* Build the Babylon Lite {@link ImageProcessingUpdate} that mirrors the Viewer's committed
* post-processing state (`_toneMapping`, `_exposure`, `_contrast`). SSAO has no
* `scene.imageProcessing` slot in Lite — it's tracked in `_ssaoOption` but doesn't render anything
* yet (Lite has no SSAO support).
* @returns The Lite image-processing update mirroring the Viewer's committed state.
*/
private _liteImageProcessingUpdate;
/**
* Push the Viewer's committed post-processing state directly into `scene.imageProcessing`. Used on
* paths where the scene is not yet registered or is about to be (re-)registered — construction, and
* after env loads (Lite's env loader overwrites `scene.imageProcessing` with its own defaults, so we
* re-push our values before re-registration bakes them into the shaders). The dynamic path (the
* `postProcessing` setter) instead uses `setSceneImageProcessing` for a targeted pipeline rebuild.
*/
private _applyImageProcessingToScene;
/**
* Updates the shadow configuration.
* @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>;
/**
* @internal
* Lite cannot cleanly add or remove shadow infrastructure (light, ground disc, shadow generator)
* after the scene has been registered. Adding meshes post-register requires re-running deferred
* GPU builders, which corrupts the existing model's pipeline state. Reloading the model breaks
* for similar reasons (the previous scene state isn't fully torn down).
*
* For now, shadow quality is effectively fixed at the value provided in the initial constructor
* options: `_setupShadows` runs once during `_loadModelImpl` (before `addToScene` and the first
* `registerScene`), so initial setup works correctly. Subsequent calls to `updateShadows` change
* the committed `_shadowQuality` field but do not re-run shadow setup. Callers that need to
* change shadow quality should recreate the viewer.
*/
protected _updateShadowsImpl(quality: ShadowQuality, abortSignal: AbortSignal | undefined, internalAbortSignal: AbortSignal): Promise<void>;
private _setupShadows;
private _rotateShadowLightWithEnvironment;
/** @internal */
protected _loadModelImpl(source: string | File | ArrayBufferView | undefined, options: ViewerLoadModelOptions | undefined, abortSignal: AbortSignal | undefined, internalAbortSignal: AbortSignal): Promise<void>;
private _unloadCurrentModel;
get animations(): readonly string[];
get selectedAnimation(): number;
set selectedAnimation(index: number);
get animationSpeed(): number;
set animationSpeed(value: number);
get isAnimationPlaying(): boolean;
get animationProgress(): number;
set animationProgress(value: number);
toggleAnimation(): void;
playAnimation(): void;
pauseAnimation(): Promise<void>;
private _getActiveAnimationGroup;
/**
* Enforces the "only the selected animation may be playing" invariant by stopping every
* non-selected animation group (`stopAnimation` blocks subsequent ticks) and pausing the
* selected one (so its tick still runs and applies the current-time pose).
*
* Lite's `tickAnimation` writes bone TRS every frame regardless of `playing`, so a merely
* paused non-selected group would still pollute the mesh transforms with its current frame's
* pose. Only `stopAnimation` blocks tick entirely.
*
* The selected group's tick must be allowed to run (so switching between animations updates
* the pose). Lite's `pauseAnimation` doesn't un-stop a previously-stopped group, so we run
* `playAnimation` then `pauseAnimation` to clear the stopped flag while ending up paused —
* the tick fires next frame and applies the time-0 pose.
*/
private _isolateSelectedAnimation;
private _setupAnimations;
private _pollAnimationState;
get materialVariants(): readonly string[];
get selectedMaterialVariant(): Nullable<string>;
set selectedMaterialVariant(value: Nullable<string>);
get camerasAsHotSpots(): boolean;
set camerasAsHotSpots(value: boolean);
queryHotSpot(name: string, result: ViewerHotSpotResult): boolean;
focusHotSpot(name: string): boolean;
/**
* Starts a camera interpolation toward the given goal pose, canceling any interpolation already in
* flight. Lite's arc-rotate camera has no built-in interpolation, so this drives
* {@link interpolateArcRotateCamera} from the scene render loop. The returned promise is intentionally
* swallowed: it rejects when the transition is superseded, aborted, or interrupted by user input,
* none of which are error conditions here.
* @param goal The destination camera pose; omitted fields keep the current value.
*/
private _interpolateCameraTo;
/**
* Resolves a named hotspot to its world position, screen position, and visibility, writing the
* result into `result`. Returns the hotspot definition on success (so callers like
* {@link focusHotSpot} can read its `cameraOrbit`), or `null` if the hotspot is unknown or cannot
* be resolved (e.g. an out-of-range surface vertex).
*
* Surface hotspots track skeletal + morph animation: the three referenced vertices are deformed
* for the current frame via {@link computeDeformedPositionToRef} (mesh-local), barycentric-
* blended, then transformed to world space by the mesh world matrix — mirroring Babylon.js core's
* `GetHotSpotToRef`. World hotspots use their fixed position/normal.
* @param name The name of the hotspot to resolve.
* @param result The result object to write the world position, screen position, and visibility into.
* @returns The hotspot definition on success, or `null` if it cannot be resolved.
*/
private _queryHotSpot;
/**
* Computes the world-space position and normal of a surface hotspot on `mesh` from three vertex
* indices and barycentric weights, applying the mesh's current animation pose. Mirrors core's
* `GetHotSpotToRef`: deform each vertex to mesh-local space, blend by barycentric, then transform
* the single blended point (and the local triangle normal) to world space.
* @param mesh The mesh the hotspot is anchored to.
* @param pointIndex The three vertex indices defining the hotspot's triangle.
* @param barycentric The barycentric weights blending the three vertices.
* @param outPos Receives the world-space hotspot position.
* @param outNormal Receives the world-space hotspot normal.
* @returns `true` if the position and normal were computed, or `false` if a vertex is out of range.
*/
private _getSurfaceHotSpotToRef;
get isModelLoaded(): boolean;
/** @internal */
protected _resetEnvironment(): void;
/** @internal */
protected _resetAnimation(): void;
/**
* @internal
* Resets the camera to its default/framing pose, animating the transition when `interpolate` is true
* (e.g. a user-initiated reset) and snapping when false (e.g. an initial reset before the first frame).
*/
protected _resetCamera(interpolate: boolean): void;
/** @internal */
protected _resetPostProcessing(): void;
/**
* Registers the scene with the engine and starts the render loop.
* Safe to call multiple times — stops and re-registers if already running.
* @remarks
* Serialized against suspend/resume via {@link _renderLoopLock} so a scroll-driven suspension can never
* land in the middle of the stop, unregister, register, start sequence below.
*/
private _beginRendering;
/**
* Starts the engine's render loop, unless rendering is suspended (or the viewer is disposed), and waits
* for the first frame.
* @remarks
* Lite's `startEngine` promise resolves from inside the rAF callback, so it never settles if the loop is
* stopped before that first frame. {@link _renderLoopStopped} races against it so a suspension or a
* disposal arriving in that window can't leave this await (and any model load awaiting it) pending forever.
*/
private _startRenderLoop;
/** Stops the engine's render loop (if running) and releases anyone awaiting its first frame. */
private _stopRenderLoop;
/**
* Suspends rendering until the returned disposable is disposed.
* @remarks
* Reference counted, mirroring the full Viewer: rendering only resumes once every suspension handle has
* been disposed. The scene stays registered while suspended, so resuming does not rebuild anything.
* @returns A disposable that resumes rendering (when no other suspensions are outstanding).
* @internal
*/
_suspendRendering(): IDisposable;
dispose(): void;
private _onPointerActivity;
/**
* Handles a canvas double-click: GPU-picks the model at the cursor and, on a hit, focuses the camera
* on the picked point; on a miss (background), reframes the camera. Mirrors the full Viewer's
* `POINTERDOUBLETAP` handler.
* @param event The double-click mouse event; its offset coordinates locate the pick on the canvas.
*/
private _onCanvasDoubleClick;
/**
* Picks the model at the given canvas coordinates and either focuses the picked point (hit) or
* reframes the camera (miss). Only the loaded model's meshes are pickable, so Viewer-added meshes
* (e.g. the shadow-receiver disc) never swallow a pick or count as a "model" hit.
* @param x The canvas-relative CSS x coordinate of the double-click.
* @param y The canvas-relative CSS y coordinate of the double-click.
*/
private _handleDoubleClick;
/**
* Focuses the camera on a world-space point, mirroring the full Viewer's double-tap-on-model behavior.
* The target and radius are first snapped so the point lies on the current view axis at its picked
* depth — this preserves the camera position and avoids a dolly along the view axis — then the target
* is interpolated to the actual point (orbit angles and radius held).
* @param point The world-space point to focus on.
*/
private _focusCameraOnPoint;
private _updateAutoOrbit;
}
/**
* Creates a new {@link Viewer} instance for the given canvas element.
* @param canvas The HTML canvas element to render into.
* @param options Optional viewer configuration.
* @returns A promise that resolves to the initialized Viewer.
*/
declare function CreateViewerForCanvas(canvas: HTMLCanvasElement, options?: CanvasViewerOptions): Promise<Viewer>;
type ResetMode = "auto" | "reframe" | [ResetFlag, ...flags: ResetFlag[]];
interface ViewerElementEventMap extends HTMLElementEventMap {
viewerready: Event;
viewerrender: Event;
environmentchange: Event;
environmentconfigurationchange: Event;
environmenterror: ErrorEvent;
shadowsconfigurationchange: Event;
modelchange: CustomEvent<Nullable<string | File | ArrayBufferView>>;
modelerror: ErrorEvent;
loadingprogresschange: Event;
selectedanimationchange: Event;
animationspeedchange: Event;
animationplayingchange: Event;
animationprogresschange: Event;
selectedmaterialvariantchange: Event;
}
interface ViewerElementBase {
addEventListener<K extends keyof ViewerElementEventMap>(type: K, listener: (this: HTMLElement, ev: ViewerElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof ViewerElementEventMap>(type: K, listener: (this: HTMLElement, ev: ViewerElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
/**
* Abstract base class for viewer custom elements.
* Contains all shared UI logic and depends only on IViewer.
*/
declare abstract class ViewerElementBase<ViewerClass extends IViewer = IViewer, Options extends ViewerBaseOptions = ViewerBaseOptions> extends LitElement {
protected readonly _options: Options;
private readonly _viewerLock;
private _animationSliderResizeObserver;
/** @internal */
protected _viewer?: ViewerClass;
/**
* Creates an instance of a ViewerElementBase subclass.
* @param _options The options to use when creating the Viewer.
*/
protected constructor(_options?: Options);
private readonly _propertyBindings;
/** @internal */
static get observedAttributes(): string[];
/** @internal */
static styles: CSSResultGroup;
/**
* Get hotspot world and screen values from a named hotspot
* @param name slot of the hot spot
* @param result resulting world and screen positions
* @returns world position, world normal and screen space coordinates
*/
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, false otherwise.
*/
focusHotSpot(name: string): boolean;
protected accessor _isFaultedBacking: boolean;
protected get _isFaulted(): boolean;
/**
* When true, the scene will be rendered even if no scene state has changed.
*/
accessor renderWhenIdle: boolean;
/**
* The model URL.
*/
accessor source: Nullable<string>;
/**
* Forces the model to be loaded with the specified extension.
* @remarks
* If this property is not set, the extension will be inferred from the model URL when possible.
*/
accessor extension: Nullable<string>;
/**
* If true, load glTF files using the OpenPBR material instead of the default PBR material.
* @experimental
*/
accessor useOpenPBR: boolean;
/**
* The texture URLs used for lighting and skybox. Setting this property will set both environmentLighting and environmentSkybox.
*/
get environment(): {
lighting: Nullable<string>;
skybox: Nullable<string>;
};
set environment(url: string);
/**
* The texture URL for lighting.
*/
accessor environmentLighting: Nullable<string>;
/**
* The texture URL for the skybox.
*/
accessor environmentSkybox: Nullable<string>;
/**
* A value between 0 and 2 that specifies the intensity of the environment lighting.
*/
accessor environmentIntensity: Nullable<number>;
/**
* A value in radians that specifies the rotation of the environment.
*/
accessor environmentRotation: Nullable<number>;
/**
* The type of shadows to use.
*/
accessor shadowQuality: Nullable<ShadowQuality>;
private accessor _loadingProgress;
/**
* Gets information about loading activity.
* @remarks
* false indicates no loading activity.
* true indicates loading activity with no progress information.
* A number between 0 and 1 indicates loading activity with progress information.
*/
get loadingProgress(): boolean | number;
/**
* A value between 0 and 1 that specifies how much to blur the skybox.
*/
accessor skyboxBlur: Nullable<number>;
/**
* The tone mapping to use for rendering the scene.
*/
accessor toneMapping: Nullable<ToneMapping>;
/**
* The contrast applied to the scene.
*/
accessor contrast: Nullable<number>;
/**
* The exposure applied to the scene.
*/
accessor exposure: Nullable<number>;
/**
* Enables or disables screen space ambient occlusion (SSAO).
*/
accessor ssao: Nullable<SSAOOptions>;
/**
* The clear color (e.g. background color) for the viewer.
*/
accessor clearColor: Nullable<IColor4Like>;
/**
* Enables or disables camera auto-orbit.
*/
accessor cameraAutoOrbit: boolean;
/**
* The speed at which the camera auto-orbits around the target.
*/
accessor cameraAutoOrbitSpeed: Nullable<number>;
/**
* The delay in milliseconds before the camera starts auto-orbiting.
*/
accessor cameraAutoOrbitDelay: Nullable<number>;
/**
* The set of defined hot spots.
*/
accessor hotSpots: Record<string, HotSpot>;
/**
* True if the viewer has any hotspots.
*/
protected get _hasHotSpots(): boolean;
/**
* True if the default animation should play automatically when a model is loaded.
*/
accessor animationAutoPlay: boolean;
/**
* The list of animation names for the currently loaded model.
*/
get animations(): readonly string[];
/**
* True if the loaded model has any animations.
*/
protected get _hasAnimations(): boolean;
/**
* The currently selected animation index.
*/
accessor selectedAnimation: Nullable<number>;
/**
* True if an animation is currently playing.
*/
get isAnimationPlaying(): boolean;
/**
* The speed scale at which animations are played.
*/
accessor animationSpeed: number;
/**
* The current point on the selected animation timeline, normalized between 0 and 1.
*/
accessor animationProgress: number;
private accessor _animations;
private accessor _isAnimationPlaying;
private accessor _showAnimationSlider;
/**
* The list of material variants for the currently loaded model.
*/
get materialVariants(): readonly string[];
/**
* The currently selected material variant.
*/
accessor selectedMaterialVariant: Nullable<string>;
/**
* True if scene cameras should be used as hotspots.
*/
accessor camerasAsHotSpots: boolean;
/**
* Determines the behavior of the reset function, and the associated default reset button.
* @remarks
* - "auto" - Resets the camera to the initial pose if it makes sense given other viewer state, such as the selected animation.
* - "reframe" - Reframes the camera based on the current viewer state (ignores the initial pose).
* - [ResetFlag] - A space separated list of reset flags that reset various aspects of the viewer state.
*/
accessor resetMode: ResetMode;
private accessor _canvasContainer;
private accessor _hotSpotSelect;
/**
* Toggles the play/pause animation state if there is a selected animation.
*/
toggleAnimation(): void;
/**
* Resets the Viewer state based on the @see resetMode property.
*/
reset(): void;
private _reset;
/**
* Resets the camera to its initial pose.
*/
resetCamera(): void;
/**
* Reloads the viewer. This is typically only needed when the viewer is in a faulted state (e.g. due to the context being lost).
*/
reload(): void;
/** @internal */
connectedCallback(): void;
/** @internal */
disconnectedCallback(): void;
/** @internal */
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
/** @internal */
protected update(changedProperties: PropertyValues<this>): void;
/**
* Determines whether a full viewer reload is required for the given property changes.
* Subclasses can override to add additional reload triggers.
* @param changedProperties The properties that have changed.
* @returns True if the viewer needs to be reloaded.
*/
protected _needsReload(changedProperties: Map<PropertyKey, unknown>): boolean;
/** @internal */
protected render(): TemplateResult<1>;
/**
* Renders the progress bar.
* @returns The template result for the progress bar.
*/
protected _renderProgressBar(): TemplateResult;
/**
* Renders the toolbar.
* @returns The template result for the toolbar.
*/
protected _renderToolbar(): TemplateResult;
/**
* Renders the reload button.
* @returns The template result for the reload button.
*/
protected _renderReloadButton(): TemplateResult;
/**
* Renders UI elements that overlay the viewer.
* Override this method to provide additional rendering for the component.
* @returns TemplateResult The rendered template result.
*/
protected _renderOverlay(): TemplateResult;
/**
* Dispatches a custom event.
* @param type The type of the event.
* @param event A function that creates the event.
*/
protected _dispatchCustomEvent<TEvent extends keyof ViewerElementEventMap>(type: TEvent, event: (type: TEvent) => ViewerElementEventMap[TEvent]): void;
/**
* Handles changes to the selected animation.
* @param event The change event.
*/
protected _onSelectedAnimationChanged(event: Event): void;
/**
* Handles changes to the animation speed.
* @param event The change event.
*/
protected _onAnimationSpeedChanged(event: Event): void;
/**
* Handles changes to the animation timeline.
* @param event The change event.
*/
protected _onAnimationTimelineChanged(event: Event): void;
/**
* Handles pointer down events on the animation timeline.
* @param event The pointer down event.
*/
protected _onAnimationTimelinePointerDown(event: Event): void;
/**
* Handles changes to the selected material variant.
* @param event The change event.
*/
protected _onMaterialVariantChanged(event: Event): void;
/**
* Handles changes to the hot spot list.
* @param event The change event.
*/
protected _onHotSpotsChanged(event: Event): void;
private _onAnimationSliderChanged;
private _createPropertyBinding;
/**
* Creates a viewer for the specified canvas.
* Subclasses must implement this to return an appropriate IViewer instance.
* @param canvas The canvas to create the viewer for.
* @returns The created viewer.
*/
protected abstract _createViewer(canvas: HTMLCanvasElement, options: Options): Promise<ViewerClass>;
private _setupViewer;
private _tearDownViewer;
/**
* Called during teardown after the viewer has been disposed.
* Subclasses can override to clean up additional state.
*/
protected _onViewerTornDown(): void;
private _updateModel;
private _updateEnv;
private _updateShadows;
}
/**
* Viewer custom element backed by the Babylon Lite engine (WebGPU-only).
* Provides the same `<babylon-viewer>` tag as the full viewer — the two are mutually exclusive.
*/
declare abstract class ViewerElement extends ViewerElementBase<Viewer, CanvasViewerOptions> {
protected constructor(options?: CanvasViewerOptions);
/**
* Gets the underlying Viewer instance (when the viewer is in a loaded state).
*/
get viewer(): Viewer | undefined;
protected _createViewer(canvas: HTMLCanvasElement, options: CanvasViewerOptions): Promise<Viewer>;
}
/**
* Displays a 3D model using the Babylon Lite Viewer (WebGPU-only).
* @remarks
* This element registers as `<babylon-viewer>` and is mutually exclusive with the full Babylon.js viewer element.
* Import `@babylonjs/viewer/lite` instead of `@babylonjs/viewer` to use the Lite viewer.
*/
declare class HTML3DElement extends ViewerElement {
/**
* Creates a new HTML3DElement backed by the Lite viewer.
* @param options The options to use for the viewer.
*/
constructor(options?: Readonly<CanvasViewerOptions>);
}
/**
* Creates a custom HTML element that creates an HTML3DElement with the specified name and configuration.
* @param elementName The name of the custom element.
* @param options The options to use for the viewer.
*/
declare function ConfigureCustomViewerElement(elementName: string, options: Readonly<CanvasViewerOptions>): void;
/**
* Displays child elements at the screen space location of a hotspot in a babylon-viewer.
* @remarks
* The babylon-viewer-annotation element must be a child of a babylon-viewer element.
*/
declare class HTML3DAnnotationElement extends LitElement {
/** @internal */
static styles: lit.CSSResult;
private readonly _internals;
private readonly _mutationObserver;
private _viewerAttachment;
private _connectingAbortController;
private _updateAnnotation;
/**
* The name of the hotspot to track.
*/
accessor hotSpot: string;
/** @internal */
connectedCallback(): void;
/** @internal */
disconnectedCallback(): void;
/** @internal */
protected render(): lit_html.TemplateResult<1>;
/** @internal */
protected update(changedProperties: PropertyValues<this>): void;
private _sanitizeInnerHTML;
}
export { ConfigureCustomViewerElement, CreateViewerForCanvas, DefaultViewerOptions, HTML3DAnnotationElement, HTML3DElement, Viewer, ViewerElement, ViewerHotSpotResult };
export type { CameraAutoOrbit, CanvasViewerOptions, EnvironmentOptions, HotSpot, LoadModelOptions, PostProcessing, ShadowQuality, ToneMapping, ViewerElementEventMap, ViewerHotSpotQuery, ViewerOptions };