@wonderlandengine/api
Version:
Wonderland Engine's JavaScript API.
834 lines (833 loc) • 27.8 kB
TypeScript
/// <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;
}