import { Color, Scene, Box3, ColorRepresentation, Texture } from 'three/webgpu'; import { DIVERoot } from './root/Root.ts'; import { DIVEGridSettings, GridComponent } from '../../components/grid/GridComponent.ts'; import { DIVEComponent } from '../component/Component.ts'; import { DIVETicker } from '../clock/Clock.ts'; /** * The light a scene gets from an equirectangular image around it. */ export type DIVEImageBasedLightSettings = { /** * The URL of the image, `null` for the bundled default HDR. * * @default null */ image: string | null; /** * Rotation around Y in radians. * * @default 0 */ rotation: number; /** * @default 1 */ intensity: number; }; export declare const DIVEImageBasedLightDefaultSettings: DIVEImageBasedLightSettings; /** * The image based light as requested, its image possibly still loading. */ export type DIVEImageBasedLight = Readonly; /** * An equirectangular image at infinity behind the scene. */ export type DIVEBackgroundImageSettings = { /** * The URL of the image. */ image: string; /** * Rotation around Y in radians. * * @default 0 */ rotation?: number; }; /** * What to show behind the scene: a colour, an image, or `null` for nothing, * so that the page behind the canvas shows through. The colour * `'transparent'` is read as `null`. */ export type DIVEBackgroundSettings = ColorRepresentation | DIVEBackgroundImageSettings | null; /** * The background as requested, `null` being transparent. An image may still * be loading. */ export type DIVEBackground = Readonly<{ type: 'color'; color: Color; }> | Readonly<{ type: 'image'; image: string; rotation: number; }> | null; export type DIVESceneSettings = { /** * Whether to add a floor to the scene. * * @default false */ displayFloor: boolean; /** * Whether to add a grid to the scene. * * @default false */ displayGrid: boolean; /** * How the grid looks: cell size, major line spacing, line colors. * * Whatever it leaves out keeps its default. Line colors come as a preset -- * `DIVEGridOnLightColors`, `DIVEGridOnDarkColors`, `DIVEGridOnAnyColors` -- * because which one reads well is a question about the ground the grid sits * on, and the engine cannot see it: that ground is a white floor in one * scene and the page behind a transparent canvas in the next. * * @default DIVEGridDefaultSettings */ grid: Partial; /** * What to show behind the scene: a colour, an image, or `null` for * transparent. Wins over `backgroundColor` when both are given. * * @default undefined, which leaves the background to `backgroundColor` */ background: DIVEBackgroundSettings | undefined; /** * The background color of the scene. * * @deprecated Use `background` instead, which also takes an image or * `null` for transparent. * @default transparent */ backgroundColor: ColorRepresentation; /** * The image based light, or `null` for none. Whatever it leaves out keeps * its default. * * @default DIVEImageBasedLightDefaultSettings */ imageBasedLight: Partial | null; }; export declare const DIVESceneDefaultSettings: Required; /** * A basic scene class. * * Comes with a root object that contains all the scene objects. * * @module */ /** * A basic scene class. * * Comes with a root object that contains all the scene objects, and drives the * per-frame tick of every component attached anywhere below it. */ export declare class DIVEScene extends Scene implements DIVETicker { readonly isDIVEScene: true; private _settings; private _root; private _grid; /** * Components that asked for a per-frame callback. * * Flat and enrolment-based: nothing walks the scene tree per frame, and * components without a `tick` never appear here at all. Nodes enrol and * withdraw their components as they enter and leave the tree, so the only * per-frame cost is iterating this array. */ private _tickingComponents; /** Set while `tick` is iterating, so mutations are deferred. */ private _isTicking; /** * What was withdrawn while `tick` was iterating. * * Splicing under the loop would shift the indices, so a withdrawal that * arrives mid-frame is recorded and applied afterwards. Recorded by identity, * not derived from `tick`/`tickEnabled` state: a component that is removed * from its node still has both, so a state-based compaction kept it enrolled * and it went on ticking with no owner -- and, after a move, in two scenes. */ private _withdrawn; /** the image based light as requested */ private _imageBasedLight; private _lightImage; /** the background as requested */ private _background; private _backgroundImage; /** the newest image request of each kind, until it has settled */ private _loading; constructor(settings?: Partial); /** * Settles once every image requested so far is in place or has failed. * * Never rejects; a failure is reported by the request that started it. * A request made later is not covered, reading this again covers it. */ get loaded(): Promise; /** Whether no requested image is still loading. */ get isLoaded(): boolean; /** * Keeps a request as the newest of its kind until it settles. * * @param kind - Which image the request is for. * @param request - The request, or `null` when nothing loads. */ private _track; private _untrack; /** * The image based light as requested, or `null` for none. * * A new image shows up here at once and lights the scene once it has * loaded, the previous one lighting it until then. */ get imageBasedLight(): DIVEImageBasedLight | null; /** * The texture the scene is lit by, `null` before the first image has * loaded. * * @internal */ get imageBasedLightTexture(): Texture | null; /** * Sets the image based light, or turns it off with `null`. * * Fields left out keep their value; after `null` they start from the * defaults again. An `image` of `null` is the bundled default HDR. Of * overlapping image changes the last one wins. An image used in several * places is loaded once. * * @param settings - What to change, or `null` for no image based light. * @returns Resolves once the image has loaded; it lights the scene from * the next render on. */ setImageBasedLight(settings: Partial | null): Promise; get root(): DIVERoot; /** * The grid, built on first use. * * Lazy because a scene without one should not pay for the plane, the shader * material and its uniforms. */ get grid(): GridComponent; /** * Puts the grid on its own node in the scene. * * Its own node, and not the root: the grid is furniture the viewer sees, not * scene content, so it must stay out of everything that walks the root -- * bounds, exports, the entity tree. */ private _buildGrid; /** * The background as requested, `null` being transparent. * * A new image shows up here at once and is shown once it has loaded, the * previous background staying until then. */ getBackground(): DIVEBackground; /** * Sets what is shown behind the scene: a colour, an equirectangular image, * or `null` for transparent. * * An image is shown as it is, without a cube capture, and rotated through * the scene's background rotation, so a new rotation for the same image * costs no reload. Of overlapping calls the last one wins. An image used * in several places is loaded once. * * @param value - The new background. * @returns Resolves once the background is on the scene, for an image * once it has loaded. */ setBackground(value: DIVEBackgroundSettings): Promise; /** * Puts the requested background image on the scene, unless it was * overtaken or has not loaded yet. * * Runs once a request has settled, outside any render, so passes that * hide the background need nothing but a plain save and restore. */ private _showBackgroundImage; computeSceneBB(): Box3; /** * Drives every enrolled component once per frame. * * @param deltaTime - Seconds since the previous frame. */ tick(deltaTime: number): void; /** * Registers a component for the per-frame tick. * * Called by {@link DIVENode} when a component becomes live; not part of the * component-authoring surface. * * @param component - The component to enrol. * @internal */ enlistComponent(component: DIVEComponent): void; /** * Removes a component from the per-frame tick. * * @param component - The component to withdraw. * @internal */ withdrawComponent(component: DIVEComponent): void; /** * Gives up everything in the scene, GPU resources included. * * Every component gets its `dispose` called, which is what actually frees * geometries, materials and textures: three hangs a `dispose` listener on each * of those and destroys the GPU object when it fires. Its own * `Renderer.dispose` does not do this -- it only drops its bookkeeping -- so * without this pass the only thing that ever released GPU memory was ending * the canvas's WebGL context, which is not something DIVE may do to a canvas * it was handed. * * Found through the nodes rather than by looking for components in the graph: * a component is not in the graph -- only what it contributed is -- so * `node.components` is the only place they can be reached. * * Collects first and disposes afterwards, because a component is free to * change the tree it sits in while being disposed. */ dispose(): void; }