UNPKG

@wonderlandengine/api

Version:

Wonderland Engine's JavaScript API.

834 lines (833 loc) 27.8 kB
/// <reference types="webxr" /> import { Component, ComponentConstructor, Object as Object3D, Physics, I18N, MorphTargets, Font, ParticleEffect } from './wonderland.js'; import { Emitter, RetainEmitter } from './utils/event.js'; import { Version } from './version.js'; import { WASM } from './wasm.js'; import { Logger } from './utils/logger.js'; import { MaterialManager } from './resources/material-manager.js'; import { MeshManager } from './resources/mesh-manager.js'; import { ResourceManager } from './resources/resource.js'; import { TextureManager } from './resources/texture-manager.js'; import { ImageLike, ProgressCallback } from './types.js'; import { XRSessionState, WebXR } from './webxr.js'; import { Prefab, InMemoryLoadOptions, LoadOptions } from './prefab.js'; import { Scene, ActivateOptions } from './scene.js'; import { PrefabGLTF, GLTFOptions } from './scene-gltf.js'; /** * Ensures that this API is compatible with the given * runtime version. * * We only enforce compatibility for major and minor components, i.e., * the runtime and the API must both be of the form `x.y.*`. * * @throws If the major or the minor components are different. * * @param version The target version */ export declare function checkRuntimeCompatibility(version: Version): void; /** * Main Wonderland Engine instance. * * Controls the canvas, rendering, and JS <-> WASM communication. */ export declare class WonderlandEngine { #private; /** * {@link Emitter} for WebXR session end events. * * Usage from within a component: * * ```js * this.engine.onXRSessionEnd.add(() => console.log("XR session ended.")); * ``` */ get onXRSessionEnd(): Emitter; /** * {@link RetainEmitter} for WebXR session start events. * * Usage from within a component: * * ```js * this.engine.onXRSessionStart.add((session, mode) => console.log(session, mode)); * ``` * * By default, this emitter is retained and will automatically call any callback added * while a session is already started: * * ```js * // XR session is already active. * this.engine.onXRSessionStart.add((session, mode) => { * console.log(session, mode); // Triggered immediately. * }); * ``` */ get onXRSessionStart(): Emitter<[XRSession, XRSessionMode]>; /** * {@link Emitter} for canvas / main framebuffer resize events. * * Usage from within a component: * * ```js * this.engine.onResize.add(() => { * const canvas = this.engine.canvas; * console.log(`New Size: ${canvas.width}, ${canvas.height}`); * }); * ``` * * @note The size of the canvas is in physical pixels, and is set via {@link WonderlandEngine.resize}. */ readonly onResize: Emitter; /** Whether AR is supported by the browser. */ get arSupported(): boolean; /** Whether VR is supported by the browser. */ get vrSupported(): boolean; /** * {@link RetainEmitter} signalling the end of the loading screen. * * Listeners get notified when the first call to {@link Scene#load()} is * invoked. At this point the new scene has not become active, and none of * its resources or components are initialized. * * Compared to {@link onSceneLoaded}, this does not wait for all components * to be fully initialized and activated. Any handler added inside * {@link Component#init()}, {@link Component#start()} or * {@link Component#onActivate()} will be called immediately. * * Usage: * * ```js * this.engine.onLoadingScreenEnd.add(() => console.log("Wait is over!")); * ``` */ readonly onLoadingScreenEnd: RetainEmitter<void[]>; /** * {@link Emitter} for scene loaded events. * * Listeners get notified when a call to {@link Scene#load()} finishes. At * this point all resources are loaded and all components had their * {@link Component#init()} as well as (if active) * {@link Component#start()} and {@link Component#onActivate()} methods * called. * * Usage from within a component: * * ```js * this.engine.onSceneLoaded.add(() => console.log("Scene switched!")); * ``` * * @deprecated Use {@link onSceneActivated} instead. */ readonly onSceneLoaded: Emitter<void[]>; /** * {@link Emitter} for scene activated events. * * This listener is notified with the old scene as first parameter, and * the new scene as second. * * This listener is notified after all resources are loaded and all components had their * {@link Component#init()} as well as (if active) * {@link Component#start()} and {@link Component#onActivate()} methods * called. * * Usage from within a component: * * ```js * this.engine.onSceneActivated.add((oldScene, newScene) => { * console.log(`Scene switch from ${oldScene.filename} to ${newScene.filename}`); * }); * ``` */ readonly onSceneActivated: Emitter<[Scene, Scene]>; /** * Triggered when a hot reload is requested. * * When using the Wonderland Editor, this will be triggered when packaging * the project. * * @note When {@link autoHotReload} is `true`, the emitter is notified after the * scene is reloaded. * * To customize the hot reload behavior, set the {@link autoHotReload} to `false`, * and use this emitter: * * ```js * this.engine.onHotReloadRequest.add(async (filename: string) => { * // This handler can be used to re-download a scene, download components, * // fetch an API, anything! * await this.loadMainScene(filename); * }); * ``` */ readonly onHotReloadRequest: Emitter<[string]>; /** * Access to internationalization. */ readonly i18n: I18N; /** * WebXR related state, `null` if no XR session is active. */ get xr(): XRSessionState | null; /** * If `true`, {@link Texture}, {@link Object3D}, and {@link Component} * instances have their prototype erased upon destruction. * * #### Object * * ```js * engine.erasePrototypeOnDestroy = true; * * const obj = engine.scene.addObject(); * obj.name = 'iamalive'; * console.log(obj.name); // Prints 'iamalive' * * obj.destroy(); * console.log(obj.name); // Throws an error * ``` * * #### Component * * Components will also be affected: * * ```js * class MyComponent extends Component { * static TypeName = 'my-component'; * static Properties = { * alive: Property.bool(true) * }; * * start() { * this.destroy(); * console.log(this.alive) // Throws an error * } * } * ``` * * A component is also destroyed if its ancestor gets destroyed: * * ```js * class MyComponent extends Component { * ... * start() { * this.object.parent.destroy(); * console.log(this.alive) // Throws an error * } * } * ``` * * @note Native components will not be erased if destroyed via object destruction: * * ```js * const mesh = obj.addComponent('mesh'); * obj.destroy(); * console.log(mesh.active) // Doesn't throw even if the mesh is destroyed * ``` * * @since 1.1.1 */ erasePrototypeOnDestroy: boolean; /** * If `true`, the materials will be wrapped in a proxy to support pre-1.2.0 * material access, i.e., * * ```js * const material = new Material(engine, 'Phong Opaque'); * material.diffuseColor = [1.0, 0.0, 0.0, 1.0]; * ``` * * If `false`, property accessors will not be available and material * properties should be accessed via methods, i.e., * * ```js * const PhongOpaque = engine.materials.getTemplate('Phong Opaque'); * const material = new PhongOpaque(); * material.setDiffuseColor([1.0, 0.0, 0.0, 1.0]); * ... * const diffuse = material.getDiffuseColor(); * ``` * * When disabled, reading/writing to materials is slightly more efficient on the CPU. */ legacyMaterialSupport: boolean; /** * If `true`, automatically reloads the scene sent by the editor socket. * * @note The {@link onHotReloadRequest} emitter is notified **after** the scene is re-loaded. * * To use custom hot-reload code, set this boolean to `false` and add the reloading code * */ autoHotReload: boolean; /** * If `true`, uncompressed images will be automatically converted * to `ImageBitmap` before upload to the GPU. * * For more information, have a look at the * [ImageBitmap](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap) documentation. */ loadUncompressedImagesAsBitmap: boolean; /** * Scene cache in scene manager. * * @hidden */ _scenes: (Prefab | null)[]; /** * Currently active scene. * * @hidden */ _scene: Scene; /** Incremented every time a new main scene is loaded. */ _mainSceneVersion: number; /** @hidden */ private _textures; /** @hidden */ private _materials; /** @hidden */ private _meshes; /** @hidden */ private _morphTargets; /** @hidden */ private _fonts; /** @hidden */ private _particleEffects; /** * Current loading uncompressed images. * * @note Indexed by js image index. * * @hidden */ private _uncompressedPromises; /** * Create a new engine instance. * * @param wasm Wasm bridge instance * @param loadingScreen Loading screen .bin file data * * @hidden */ constructor(wasm: WASM, loadingScreen: ArrayBuffer | null, withRenderer: boolean); /** * Start the engine if it's not already running. * * When using the {@link loadRuntime} function, this method is called * automatically. */ start(): void; /** * Register a custom JavaScript component type. * * You can register a component directly using a class inheriting from {@link Component}: * * ```js * import { Component, Type } from '@wonderlandengine/api'; * * export class MyComponent extends Component { * static TypeName = 'my-component'; * static Properties = { * myParam: {type: Type.Float, default: 42.0}, * }; * init() {} * start() {} * update(dt) {} * onActivate() {} * onDeactivate() {} * onDestroy() {} * }); * * // Here, we assume we have an engine already instantiated. * // In general, the registration occurs in the `index.js` file in your * // final application. * engine.registerComponent(MyComponent); * ``` * * {@label CLASSES} * @param classes Custom component(s) extending {@link Component}. * * @since 1.0.0 */ registerComponent(...classes: ComponentConstructor[]): void; /** * Register a component bundle * * The module must be of the form: * * ```js * export default function(engine) { * engine.registerComponent(MyComponent); * ... * } * ``` * * Alternatively, you can manually register a component bundle: * * ```js * const registerBundle = (await import(url)).default; * registerBundle(engine); * ``` * * @param url The URL of the module to register. * @param nocache If `true`, force browser to re-download the file. */ registerBundle(url: string, nocache?: boolean): Promise<void>; /** * Update the loading screen progress bar. * * @param value Current loading percentage, in the range [0; 1]. * * @since 1.2.1 */ setLoadingProgress(percentage: number): void; /** * Switch the current active scene. * * Once active, the scene will be updated and rendered on the canvas. * * The currently active scene is accessed via {@link WonderlandEngine.scene}: * * ```js * import {Component} from '@wonderlandengine/api'; * * class MyComponent extends Component{ * start() { * console.log(this.scene === this.engine.scene); // Prints `true` * } * } * ``` * * @note This method will throw if the scene isn't activatable. * * #### Component Lifecycle * * Marking a scene as active will: * * Call {@link Component#onDeactivate} for all active components of the previous scene * * Call {@link Component#onActivate} for all active components of the new scene * * #### Usage * * ```js * const scene = await engine.loadScene('Scene.bin'); * engine.switchTo(scene); * ``` * * @returns A promise that resolves once the scene is ready. * * @since 1.2.0 */ switchTo(scene: Scene, opts?: ActivateOptions): Promise<void>; /** * Load the scene from a URL, as the main scene of a new {@link Scene}. * * #### Usage * * ```js * // The method returns the main scene * const scene = await engine.loadMainScene(); * ``` * * #### Destruction * * Loading a new main scene entirely resets the state of the engine, and destroys: * - All loaded scenes, prefabs, and gltf files * - Meshes * - Textures * - Materials * * @note This method can only load Wonderland Engine `.bin` files. * * @param options The URL pointing to the scene to load or an object * holding additional loading options. * @param progress Optional progress callback. When setting a custom * callback, you need to manually call {@link setLoadingProgress} to * get progress updates in the loading screen. * @returns The main scene of the new {@link Scene}. */ loadMainScene(options: LoadOptions & ActivateOptions, progress?: ProgressCallback): Promise<Scene>; /** * Similar to {@link WonderlandEngine.loadMainScene}, but loading is done * from an `ArrayBuffer`. * * @param options An object containing the buffer and extra metadata. * @returns The main scene of the new {@link Scene}. */ loadMainSceneFromBuffer(options: InMemoryLoadOptions & ActivateOptions): Promise<Scene>; /** * Load a {@link Prefab} from a URL. * * #### Usage * * ```js * const prefab = await engine.loadPrefab('Prefab.bin'); * ``` * * @note This method can only load Wonderland Engine `.bin` files. * @note This method is a wrapper around {@link WonderlandEngine.loadPrefabFromBuffer}. * * @param url The URL pointing to the prefab to load. * @param progress Optional progress callback. * @returns The loaded {@link Prefab}. */ loadPrefab(options: LoadOptions, progress?: ProgressCallback): Promise<Prefab>; /** * Similar to {@link WonderlandEngine.loadPrefab}, but loading is done from * an `ArrayBuffer`. * * @param options An object containing the buffer and extra metadata. * @returns A new loaded {@link Prefab}. */ loadPrefabFromBuffer(options: InMemoryLoadOptions): Prefab; /** * Load a scene from a URL. * * At the opposite of {@link WonderlandEngine.loadMainScene}, the scene loaded * will be added to the list of existing scenes, and its resources will be made * available for other scenes/prefabs/gltf to use. * * #### Resources Sharing * * Upon loading, the scene resources are added in the engine, and references * to those resources are updated. * * It's impossible for a scene loaded with this method to import pipelines. * Thus, the loaded scene will reference existing pipelines in the main scene, * based on their names. * * #### Usage * * ```js * const scene = await engine.loadScene('Scene.bin'); * ``` * * @note This method can only load Wonderland Engine `.bin` files. * * @param options The URL pointing to the scene to load or an object * holding additional loading options. * @param progress Optional progress callback. * @returns A new loaded {@link Scene}. */ loadScene(options: LoadOptions, progress?: ProgressCallback): Promise<Scene>; /** * Create a glTF scene from a URL. * * @note Loading glTF files requires `enableRuntimeGltf` to be checked in * the editor Project Settings. * * @note This method is a wrapper around {@link WonderlandEngine.loadGLTFFromBuffer}. * * @param options The URL pointing to the scene to load or an object * holding additional loading options, optionally including * {@link GLTFOptions}. {@link LoadOptions.streamed} is ignored as * streamed glTF parsing isn't supported. * @param progress Optional progress callback. * @returns A new loaded {@link PrefabGLTF}. */ loadGLTF(opts: LoadOptions & GLTFOptions, progress?: ProgressCallback): Promise<PrefabGLTF>; /** * Similar to {@link WonderlandEngine.loadScene}, but loading is done from * an `ArrayBuffer`. * * @throws If the scene is streamable. * * @param options An object containing the buffer and extra metadata. * @returns A new loaded {@link Scene}. */ loadSceneFromBuffer(options: InMemoryLoadOptions): Scene; /** * Similar to {@link WonderlandEngine.loadGLTF}, but loading is done from * an `ArrayBuffer`. * * @note Loading glTF files requires `enableRuntimeGltf` to be checked in * the editor Project Settings. * * @param options An object containing the buffer and extra glTF metadata. * @returns A new loaded {@link PrefabGLTF}. */ loadGLTFFromBuffer(options: InMemoryLoadOptions & GLTFOptions): PrefabGLTF; /** * Checks whether the given component is registered or not. * * @param typeOrClass A string representing the component typename (e.g., `'cursor-component'`), * or a component class (e.g., `CursorComponent`). * @returns `true` if the component is registered, `false` otherwise. */ isRegistered(typeOrClass: string | ComponentConstructor): boolean; /** * Retrieve the registered component from its type name. * * @param typename The component {@link Component.TypeName} property * @returns The class if a component was registered with given name, `null` otherwise. */ getComponentClass<T extends Component>(typename: string): ComponentConstructor<T> | null; /** * Resize the canvas and the rendering context. * * @note The `width` and `height` parameters will be scaled by the * `devicePixelRatio` value. By default, the pixel ratio used is * [window.devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio). * * @param width The width, in CSS pixels. * @param height The height, in CSS pixels. * @param devicePixelRatio The pixel ratio factor. */ resize(width: number, height: number, devicePixelRatio?: number): void; /** * Run the next frame. * * @param fixedDelta The elapsed time between this frame and the previous one. * * @note The engine automatically schedules next frames. You should only * use this method for testing. */ nextFrame(fixedDelta?: number): void; /** * Request an XR session. * * @note Please use this call instead of directly calling `navigator.xr.requestSession()`. * Wonderland Engine requires to be aware that a session is started, and this * is done through this call. * * @param mode The XR mode. * @param features An array of required features, e.g., `['local-floor', 'hit-test']`. * @param optionalFeatures An array of optional features, e.g., `['bounded-floor', 'depth-sensing']`. * @returns A promise resolving with the `XRSession`, a string error message otherwise. */ requestXRSession(mode: XRSessionMode, features: string[], optionalFeatures?: string[]): Promise<XRSession>; /** * Offer an XR session. * * Adds an interactive UI element to the browser interface to start an XR * session. Browser support is optional, so it's advised to still allow * requesting a session with a UI element on the website itself. * * @note Please use this call instead of directly calling `navigator.xr.offerSession()`. * Wonderland Engine requires to be aware that a session is started, and this * is done through this call. * * @param mode The XR mode. * @param features An array of required features, e.g., `['local-floor', 'hit-test']`. * @param optionalFeatures An array of optional features, e.g., `['bounded-floor', 'depth-sensing']`. * @returns A promise resolving with the `XRSession`, a string error message otherwise. * * @since 1.1.5 */ offerXRSession(mode: XRSessionMode, features: string[], optionalFeatures?: string[]): Promise<XRSession>; /** * Wrap an object ID using {@link Object}. * * @note This method performs caching and will return the same * instance on subsequent calls. * * @param objectId ID of the object to create. * @returns The object * * @deprecated Use {@link Scene#wrap} instead. * */ wrapObject(objectId: number): Object3D; toString(): string; /** Currently active scene. */ get scene(): Scene; /** * WebAssembly bridge. * * @note Use with care. This object is used to communicate * with the WebAssembly code throughout the api. * * @hidden */ get wasm(): WASM; /** * WebXR api. * * @hidden */ get webxr(): WebXR; /** Canvas element that Wonderland Engine renders to. */ get canvas(): HTMLCanvasElement; /** * Current WebXR session or `null` if no session active. * * @deprecated Use {@link XRSessionState.session} on the {@link xr} * object instead. */ get xrSession(): XRSession | null; /** * Current WebXR frame or `null` if no session active. * * @deprecated Use {@link XRSessionState.frame} on the {@link xr} * object instead. */ get xrFrame(): XRFrame | null; /** * Current WebXR base layer or `null` if no session active. * * @deprecated Use {@link XRSessionState.baseLayer} on the {@link xr} * object instead. */ get xrBaseLayer(): XRProjectionLayer | XRWebGLLayer | null; /** * Current WebXR framebuffer or `null` if no session active. * * @deprecated Use {@link XRSessionState.framebuffers} on the {@link xr} * object instead. */ get xrFramebuffer(): WebGLFramebuffer | null; /** * WebXR framebuffer scale factor. */ get xrFramebufferScaleFactor(): number; set xrFramebufferScaleFactor(value: number); /** Physics manager, only available when physx is enabled in the runtime. */ get physics(): Physics | null; /** Texture resources */ get textures(): TextureManager; /** Material resources */ get materials(): MaterialManager; /** Mesh resources */ get meshes(): MeshManager; /** Morph target set resources */ get morphTargets(): ResourceManager<MorphTargets>; /** Font resources */ get fonts(): ResourceManager<Font>; /** Particle effect resources */ get particleEffects(): ResourceManager<ParticleEffect>; /** Get all uncompressed images. */ get images(): ImageLike[]; /** * Promise that resolve once all uncompressed images are loaded. * * This is equivalent to calling {@link WonderlandEngine.images}, and wrapping each * `load` listener into a promise. */ get imagesPromise(): Promise<ImageLike[]>; /** * `true` if the texture streaming is currently idle, i.e., * not attempting to upload any textures. * * @hidden */ get isTextureStreamingIdle(): boolean; /** * `true` if reverse-Z rendering is supported and enabled. * * @hidden */ get isReverseZEnabled(): boolean; set autoResizeCanvas(flag: boolean); /** `true` if the canvas is automatically resized by the engine. */ get autoResizeCanvas(): boolean; /** Retrieves the runtime version. */ get runtimeVersion(): Version; /** Engine {@link Logger}. Use it to turn on / off logging. */ get log(): Logger; /** * Initialize the engine. * * @note Should be called after the WebAssembly is fully loaded. * * @hidden */ _init(withRenderer: boolean): boolean; /** * Reset the runtime state, including: * - Component cache * - Images * - Callbacks * * @note This api is meant to be used internally. * * @hidden */ _reset(): Promise<void>; /** * Attempt to hot reload the engine. * * @param filename URL of the file to use for the reload. * If `null`, forces a full page reload. * * @hidden */ _reloadRequest(filename: string | null): Promise<void>; /** * Add an empty scene. * * @returns The newly created scene * * @hidden */ _createEmpty(): Scene; /** @hidden */ _destroyScene(instance: Prefab): void; /** * Reload the state of the engine with a new main scene. * * @param index Scene index. * * @hidden */ private _reload; /** * Helper to load a main scene from an `ArrayBuffer` or `ReadableStream`. * * @param data Buffer or stream. * @param url Base URL. * @param nocache If `true`, force a browser reload of files to download. * Only affects components loading for now. * @param options Activation options. * @returns The loaded main scene. * * @hidden */ private _loadMainScene; /** * Helper to load prefab and activatable scene from an `ArrayBuffer`. * * @param PrefabClass Scene constructor. * @param options Loading options. * @returns The loaded prefab. * * @hidden */ private _loadSceneFromBuffer; /** * Helper to load prefab and activatable scene from a `ReadableStream`. * * @param PrefabClass Scene constructor. * @param options Loading options. * @returns The loaded prefab. * * @hidden */ private _loadSceneFromStream; /** * Checks if the loaded scene is a prefab and throws an error if not. * * @param scene The loaded scene * * @hidden */ private _validateLoadedPrefab; /** * Checks if the loaded scene is a scene (and not a prefab) and throws an * error if not. * * @param scene The loaded scene * * @hidden */ private _validateLoadedScene; /** * Set the scene data on the engine, before the activation occurs. * * @hidden */ private _preactivate; /** * Notify the runtime when images are ready to be uploaded. * * @param sceneIndex Index of the scene where the image originated * @hidden */ private _loadUncompressedImages; }