@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in
346 lines (345 loc) • 16.9 kB
TypeScript
/// <reference types="webxr" />
import 'three/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodes.js';
import type { EffectComposer } from "postprocessing";
import { BufferGeometry, Camera, DepthTexture, Group, Material, Object3D, OrthographicCamera, PerspectiveCamera, Scene, Texture, WebGLRenderer, type WebGLRendererParameters, type WebXRArrayCamera } from 'three';
import type { EffectComposer as ThreeEffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { Addressables } from './engine_addressables.js';
import { AnimationsRegistry } from './engine_animation.js';
import { Application } from './engine_application.js';
import { AssetDatabase } from './engine_assetdatabase.js';
import { Input } from './engine_input.js';
import { type ILightDataRegistry } from './engine_lightdata.js';
import { LODsManager } from "./engine_lods.js";
import { NetworkConnection } from './engine_networking.js';
import { Physics } from './engine_physics.js';
import { PlayerViewManager } from './engine_playerview.js';
import { RendererData as SceneLighting } from './engine_scenelighting.js';
import { Time } from './engine_time.js';
import type { CoroutineData, ICamera, IComponent, IContext, ILight, Model, Vec2 } from "./engine_types.js";
import type { INeedleXRSessionEventReceiver, NeedleXRSession } from './engine_xr.js';
import { NeedleMenu } from './webcomponents/needle menu/needle-menu.js';
export declare const build_scene_functions: {
[]: (context: Context) => Promise<void>;
};
export declare class LoadingProgressArgs {
/** the name or URL of the loaded file */
name: string;
/** the loading progress event from the loader */
progress: ProgressEvent;
/** the index of the loaded file */
index: number;
/** the total number of files to load */
count: number;
}
export declare class ContextCreateArgs {
/** list of glTF or GLB files to load */
files: Array<string>;
abortSignal?: AbortSignal;
/** called when loading a provided glTF file started */
onLoadingStart?: (index: number, file: string) => void;
/** called on update for each loaded glTF file */
onLoadingProgress?: (args: LoadingProgressArgs) => void;
/** Called after a gLTF file has finished loading */
onLoadingFinished?: (index: number, file: string, glTF: Model | null) => void;
}
export declare class ContextArgs {
name?: string;
/** for debugging only */
alias?: string;
/** the hash is used as a seed when initially loading the scene files */
hash?: string;
/** when true the context will not check if it's visible in the viewport and always update and render */
runInBackground?: boolean;
/** the DOM element the context belongs to or is inside of (this does not have to be the canvas. use renderer.domElement if you want to access the dom canvas) */
domElement?: HTMLElement | null;
/** externally owned renderer */
renderer?: WebGLRenderer;
/** externally owned camera */
camera?: Camera;
/** externally owned scene */
scene?: Scene;
}
export declare enum FrameEvent {
Start = -1,
EarlyUpdate = 0,
Update = 1,
LateUpdate = 2,
OnBeforeRender = 3,
OnAfterRender = 4,
PrePhysicsStep = 9,
PostPhysicsStep = 10,
Undefined = -1
}
/** threejs callback event signature */
export declare type OnRenderCallback = (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => void;
export declare function registerComponent(script: IComponent, context?: Context): void;
/**
* The context is the main object that holds all the data and state of the Needle Engine.
* It can be used to access the scene, renderer, camera, input, physics, networking, and more.
* @example
* ```typescript
* import { Behaviour } from "@needle-tools/engine";
* import { Mesh, BoxGeometry, MeshBasicMaterial } from "three";
* export class MyScript extends Behaviour {
* start() {
* console.log("Hello from MyScript");
* this.context.scene.add(new Mesh(new BoxGeometry(), new MeshBasicMaterial()));
* }
* }
* ```
*/
export declare class Context implements IContext {
private static _defaultTargetFramerate;
/** When a new context is created this is the framerate that will be used by default */
static get DefaultTargetFrameRate(): number | undefined;
/** When a new context is created this is the framerate that will be used by default */
static set DefaultTargetFrameRate(val: number | undefined);
private static _defaultWebglRendererParameters;
/** The default parameters that will be used when creating a new WebGLRenderer.
* Modify in global context to change the default parameters for all new contexts.
* @example
* ```typescript
* import { Context } from "@needle-tools/engine";
* Context.DefaultWebGLRendererParameters.antialias = false;
* ```
*/
static get DefaultWebGLRendererParameters(): WebGLRendererParameters;
/** The needle engine version */
get version(): string;
/** The currently active context. Only set during the update loops */
static get Current(): Context;
/** @internal this property should not be set by user code */
static set Current(context: Context);
static get All(): Context[];
/** The name of the context */
name: string;
/** An alias for the context */
alias: string | undefined | null;
/** When the renderer or camera are managed by an external process (e.g. when running in r3f context).
* When this is false you are responsible to call update(timestamp, xframe.
* It is also currently assumed that rendering is handled performed by an external process
* */
isManagedExternally: boolean;
/** set to true to pause the update loop. You can receive an event for it in your components.
* Note that script updates will not be called when paused */
isPaused: boolean;
/** When enabled the application will run while not visible on the page */
runInBackground: boolean;
/**
* Set to the target framerate you want your application to run in (you can use ?stats to check the fps)
* Set to undefined if you want to run at the maximum framerate
*/
targetFrameRate?: number | {
value?: number;
};
/** Use a higher number for more accurate physics simulation.
* When undefined physics steps will be 1 for mobile devices and 5 for desktop devices
* Set to 0 to disable physics updates
* TODO: changing physics steps is currently not supported because then forces that we get from the character controller and rigidbody et al are not correct anymore - this needs to be properly tested before making this configureable
*/
private physicsSteps?;
/** used to append to loaded assets */
hash?: string;
/** The `<needle-engine>` web component */
domElement: HTMLElement;
appendHTMLElement(element: HTMLElement): HTMLElement;
get resolutionScaleFactor(): number;
/** use to scale the resolution up or down of the renderer. default is 1 */
set resolutionScaleFactor(val: number);
private _resolutionScaleFactor;
private _boundingClientRectFrame;
private _boundingClientRect;
private _domX;
private _domY;
/** update bounding rects + domX, domY */
private calculateBoundingClientRect;
/** The width of the `<needle-engine>` element on the website */
get domWidth(): number;
/** The height of the `<needle-engine>` element on the website */
get domHeight(): number;
/** the X position of the Needle Engine element on the website */
get domX(): number;
/** the Y position of the Needlee Engine element on the website */
get domY(): number;
get isInXR(): boolean;
/** shorthand for `NeedleXRSession.active`
* Automatically set by NeedleXRSession when a XR session is active
* @returns the active XR session or null if no session is active
* */
xr: NeedleXRSession | null;
get xrSessionMode(): XRSessionMode | undefined;
get isInVR(): boolean;
get isInAR(): boolean;
/** If a XR session is active and in pass through mode (immersive-ar on e.g. Quest) */
get isInPassThrough(): boolean;
/** access the raw `XRSession` object (shorthand for `context.renderer.xr.getSession()`). For more control use `NeedleXRSession.active` */
get xrSession(): XRSession | null;
/** @returns the latest XRFrame (if a XRSession is currently active)
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRFrame
*/
get xrFrame(): XRFrame | null;
/** @returns the current WebXR camera while the WebXRManager is active (shorthand for `context.renderer.xr.getCamera()`) */
get xrCamera(): WebXRArrayCamera | undefined;
private _xrFrame;
get arOverlayElement(): HTMLElement;
/** Current event of the update cycle */
get currentFrameEvent(): FrameEvent;
private _currentFrameEvent;
scene: Scene;
renderer: WebGLRenderer;
composer: EffectComposer | ThreeEffectComposer | null;
readonly scripts: IComponent[];
readonly scripts_pausedChanged: IComponent[];
readonly scripts_earlyUpdate: IComponent[];
readonly scripts_update: IComponent[];
readonly scripts_lateUpdate: IComponent[];
readonly scripts_onBeforeRender: IComponent[];
readonly scripts_onAfterRender: IComponent[];
readonly scripts_WithCorroutines: IComponent[];
readonly scripts_immersive_vr: INeedleXRSessionEventReceiver[];
readonly scripts_immersive_ar: INeedleXRSessionEventReceiver[];
readonly coroutines: {
[]: Array<CoroutineData>;
};
/** callbacks called once after the context has been created */
readonly post_setup_callbacks: Function[];
/** called every frame at the beginning of the frame (after component start events and before earlyUpdate) */
readonly pre_update_callbacks: Function[];
/** called every frame before rendering (after all component events) */
readonly pre_render_callbacks: Array<(frame: XRFrame | null) => void>;
/** called every frame after rendering (after all component events) */
readonly post_render_callbacks: Function[];
/** called every frame befroe update (this list is emptied every frame) */
readonly pre_update_oneshot_callbacks: Function[];
readonly new_scripts: IComponent[];
readonly new_script_start: IComponent[];
readonly new_scripts_pre_setup_callbacks: Function[];
readonly new_scripts_post_setup_callbacks: Function[];
readonly new_scripts_xr: INeedleXRSessionEventReceiver[];
/** The main camera component of the scene - this camera is used for rendering */
mainCameraComponent: ICamera | undefined;
/** The main camera of the scene - this camera is used for rendering */
get mainCamera(): Camera;
/** Set the main camera of the scene. If set to null the camera of the {@link mainCameraComponent} will be used - this camera is used for rendering */
set mainCamera(cam: Camera | null);
private _mainCamera;
private _fallbackCamera;
application: Application;
/** access animation mixer used by components in the scene */
animations: AnimationsRegistry;
/** access timings (current frame number, deltaTime, timeScale, ...) */
time: Time;
input: Input;
/** access physics related methods (e.g. raycasting). To access the phyiscs engine use `context.physics.engine` */
physics: Physics;
/** access networking methods (use it to send or listen to messages or join a networking backend) */
connection: NetworkConnection;
/**
* @deprecated AssetDataBase is deprecated
*/
assets: AssetDatabase;
mainLight: ILight | null;
/** @deprecated Use sceneLighting */
get rendererData(): SceneLighting;
sceneLighting: SceneLighting;
addressables: Addressables;
lightmaps: ILightDataRegistry;
players: PlayerViewManager;
readonly lodsManager: LODsManager;
readonly menu: NeedleMenu;
get isCreated(): boolean;
private _sizeChanged;
private _isCreated;
private _isCreating;
private _isVisible;
private _stats;
constructor(args?: ContextArgs);
/** calling this function will dispose the current renderer and create a new one */
createNewRenderer(params?: WebGLRendererParameters): void;
private _intersectionObserver;
private internalOnUpdateVisible;
private _disposeCallbacks;
/** will request a renderer size update the next render call (will call updateSize the next update) */
requestSizeUpdate(): void;
/** Clamps the renderer max resolution. If undefined the max resolution is not clamped. Default is undefined */
maxRenderResolution?: Vec2;
/** update the renderer and canvas size */
updateSize(force?: boolean): void;
updateAspect(camera: PerspectiveCamera | OrthographicCamera, width?: number, height?: number): void;
/** This will recreate the whole needle engine context and dispose the whole scene content
* All content will be reloaded (loading times might be faster due to browser caches)
* All scripts will be recreated */
recreate(): void;
private _originalCreationArgs?;
/** @deprecated use create. This method will be removed in a future version */
onCreate(opts?: ContextCreateArgs): Promise<boolean>;
create(opts?: ContextCreateArgs): Promise<boolean>;
private onUnhandledRejection;
/** Dispatches an error */
private onError;
/** Will destroy all scenes and objects in the scene
*/
clear(): void;
dispose(): void;
/**@deprecated use dispose() */
onDestroy(): void;
private internalOnDestroy;
registerCoroutineUpdate(script: IComponent, coroutine: Generator, evt: FrameEvent): Generator;
unregisterCoroutineUpdate(coroutine: Generator, evt: FrameEvent): void;
stopAllCoroutinesFrom(script: IComponent): void;
private _cameraStack;
setCurrentCamera(cam: ICamera): void;
removeCamera(cam?: ICamera | null): void;
private _onBeforeRenderListeners;
private _onAfterRenderListeners;
/** use this to subscribe to onBeforeRender events on threejs objects */
addBeforeRenderListener(target: Object3D, callback: OnRenderCallback): void;
removeBeforeRenderListener(target: Object3D, callback: OnRenderCallback): void;
/** use this to subscribe to onAfterRender events on threejs objects */
addAfterRenderListener(target: Object3D, callback: OnRenderCallback): void;
removeAfterRenderListener(target: Object3D, callback: OnRenderCallback): void;
private _createRenderCallbackWrapper;
private _requireDepthTexture;
private _requireColorTexture;
private _renderTarget?;
private _isRendering;
get isRendering(): boolean;
setRequireDepth(val: boolean): void;
setRequireColor(val: boolean): void;
get depthTexture(): DepthTexture | null;
get opaqueColorTexture(): Texture | null;
/** returns true if the dom element is visible on screen */
get isVisibleToUser(): boolean;
private _createId;
private internalOnCreate;
private internalLoadInitialContent;
/** Sets the animation loop.
* Can not be done while creating the context or when disposed
**/
restartRenderLoop(): boolean;
private _renderlooperrors;
/** Performs a full update step including script callbacks, rendering (unless isManagedExternally is set to false) and post render callbacks */
update(timestamp: DOMHighResTimeStamp, frame?: XRFrame | null): void;
/** Call to **manually** perform physics steps.
* By default the context uses the `physicsSteps` property to perform steps during the update loop
* If you just want to increase the accuracy of physics you can instead set the `physicsSteps` property to a higher value
* */
updatePhysics(steps: number): void;
private _lastTimestamp;
private _accumulatedTime;
private _dispatchReadyAfterFrame;
private internalStep;
private internalOnBeforeRender;
private internalUpdatePhysics;
private internalOnRender;
private internalOnAfterRender;
renderNow(camera?: Camera): boolean;
private _contextRestoreTries;
private handleRendererContextLost;
/** returns true if we should return out of the frame loop */
private _wasPaused;
private onHandlePaused;
private evaluatePaused;
private renderRequiredTextures;
private executeCoroutines;
}