ecspresso
Version:
A minimal Entity-Component-System library for typescript and javascript.
249 lines (248 loc) • 8.65 kB
TypeScript
/**
* 3D Renderer Plugin for ECSpresso
*
* An opt-in Three.js-based 3D rendering plugin that automates scene graph wiring.
* Import from 'ecspresso/plugins/rendering/renderer3D'
*
* This plugin includes 3D transform propagation automatically.
*/
import type { WebGLRenderer, WebGLRendererParameters, Scene, Camera, Object3D, Mesh, Group, ColorRepresentation } from 'three';
import { type Plugin } from 'ecspresso';
import type { ComponentsConfig, EmptyConfig, EventsConfig, ResourcesConfig } from '../../type-utils';
import { type LocalTransform3D, type WorldTransform3D, type Transform3DComponentTypes, type Transform3DPluginOptions } from 'ecspresso/plugins/spatial/transform3D';
export type { LocalTransform3D, WorldTransform3D, Transform3DComponentTypes };
export { createTransform3D, createLocalTransform3D, createWorldTransform3D, DEFAULT_LOCAL_TRANSFORM_3D, DEFAULT_WORLD_TRANSFORM_3D, } from 'ecspresso/plugins/spatial/transform3D';
/**
* Visibility component for 3D entities.
*/
export interface Visible3D {
visible: boolean;
}
/**
* Aggregate component types for the 3D renderer plugin.
* Included automatically via `.withPlugin(createRenderer3DPlugin({ ... }))`.
*/
export interface Renderer3DComponentTypes extends Transform3DComponentTypes {
mesh: Mesh;
group: Group;
object3d: Object3D;
visible3d: Visible3D;
/** Controls Three.js Object3D.renderOrder for manual z-ordering */
renderOrder: number;
}
/**
* Events emitted by the 3D renderer plugin.
*/
export interface Renderer3DEventTypes {
hierarchyChanged: {
entityId: number;
oldParent: number | null;
newParent: number | null;
};
}
/**
* Resources provided by the 3D renderer plugin.
*/
export interface Renderer3DResourceTypes {
threeRenderer: WebGLRenderer;
scene: Scene;
camera: Camera;
}
/**
* Common options shared between both initialization modes.
*/
interface Renderer3DPluginCommonOptions<G extends string = 'renderer3d'> {
/** System group name (default: 'renderer3d') */
systemGroup?: G;
/** Priority for render sync system (default: 500) */
renderSyncPriority?: number;
/** Options for the included 3D transform plugin */
transform?: Transform3DPluginOptions;
/** When true, starts a requestAnimationFrame loop to drive ecs.update() automatically (default: true) */
startLoop?: boolean;
}
/**
* Options when providing pre-initialized Three.js objects.
*/
export interface Renderer3DPluginPreInitOptions<G extends string = 'renderer3d'> extends Renderer3DPluginCommonOptions<G> {
/** Pre-initialized WebGLRenderer */
renderer: WebGLRenderer;
/** Pre-initialized Scene */
scene: Scene;
/** Pre-initialized Camera */
camera: Camera;
container?: never;
background?: never;
width?: never;
height?: never;
antialias?: never;
shadows?: never;
cameraOptions?: never;
threeInit?: never;
}
/**
* Camera configuration for managed mode.
*
* Discriminated on `projection`. Defaults to `'perspective'` when omitted.
* Orthographic cameras use `viewSize` (world-unit height at zoom=1) to define
* the base frustum; `zoom` maps directly to Three.js's `OrthographicCamera.zoom`.
*/
export type CameraOptions = {
projection?: 'perspective';
fov?: number;
near?: number;
far?: number;
position?: {
x: number;
y: number;
z: number;
};
lookAt?: {
x: number;
y: number;
z: number;
};
} | {
projection: 'orthographic';
viewSize?: number;
zoom?: number;
near?: number;
far?: number;
position?: {
x: number;
y: number;
z: number;
};
lookAt?: {
x: number;
y: number;
z: number;
};
};
/**
* Options when letting the plugin create and manage Three.js objects.
*/
export interface Renderer3DPluginManagedOptions<G extends string = 'renderer3d'> extends Renderer3DPluginCommonOptions<G> {
renderer?: never;
scene?: never;
camera?: never;
/** Container element to append the canvas to (or CSS selector string). Defaults to `document.body`. */
container?: HTMLElement | string;
/** Scene background color. */
background?: ColorRepresentation;
/** Canvas width. When omitted, auto-sizes to container. */
width?: number;
/** Canvas height. When omitted, auto-sizes to container. */
height?: number;
/** Enable antialiasing (default: true) */
antialias?: boolean;
/** Enable shadow mapping (default: false) */
shadows?: boolean;
/** Camera configuration */
cameraOptions?: CameraOptions;
/** Escape hatch for raw WebGLRendererParameters not otherwise exposed. */
threeInit?: Partial<WebGLRendererParameters>;
}
/**
* Configuration options for the 3D renderer plugin.
*
* Supports two modes:
* 1. **Pre-initialized**: Pass already-initialized renderer, scene, camera
* 2. **Managed**: Omit them and the plugin creates everything during `ecs.initialize()`
*
* This plugin includes 3D transform propagation automatically.
*
* @example Pre-initialized mode
* ```typescript
* const renderer = new WebGLRenderer({ antialias: true });
* const scene = new Scene();
* const camera = new PerspectiveCamera(75, w / h, 0.1, 1000);
*
* const ecs = ECSpresso.create()
* .withPlugin(createRenderer3DPlugin({ renderer, scene, camera }))
* .build();
* ```
*
* @example Managed mode
* ```typescript
* const ecs = ECSpresso.create()
* .withPlugin(createRenderer3DPlugin({
* container: '#game',
* background: 0x1099bb,
* antialias: true,
* cameraOptions: { fov: 75, position: { x: 0, y: 5, z: 10 } },
* }))
* .build();
* await ecs.initialize();
* ```
*/
export type Renderer3DPluginOptions<G extends string = 'renderer3d'> = Renderer3DPluginPreInitOptions<G> | Renderer3DPluginManagedOptions<G>;
interface PositionOption3D {
x?: number;
y?: number;
z?: number;
}
interface TransformOptions3D {
rotation?: {
x?: number;
y?: number;
z?: number;
};
scale?: number | {
x: number;
y: number;
z: number;
};
visible?: boolean;
}
/**
* Create components for a mesh entity.
* Returns an object suitable for spreading into spawn().
*
* @example
* ```typescript
* const player = ecs.spawn({
* ...createMeshComponents(myMesh, { x: 10, y: 0, z: -5 }),
* velocity: { x: 0, y: 0, z: 0 },
* });
* ```
*/
export declare function createMeshComponents(mesh: Mesh, position?: PositionOption3D, options?: TransformOptions3D): Pick<Renderer3DComponentTypes, 'mesh' | 'localTransform3D' | 'worldTransform3D' | 'visible3d'>;
/**
* Create components for a group entity.
* Returns an object suitable for spreading into spawn().
*
* @example
* ```typescript
* const enemies = ecs.spawn({
* ...createGroupComponents(enemyGroup, { x: 50, y: 0, z: -30 }),
* });
* ```
*/
export declare function createGroupComponents(group: Group, position?: PositionOption3D, options?: TransformOptions3D): Pick<Renderer3DComponentTypes, 'group' | 'localTransform3D' | 'worldTransform3D' | 'visible3d'>;
/**
* Create components for a generic Object3D entity.
* Returns an object suitable for spreading into spawn().
*
* @example
* ```typescript
* const obj = ecs.spawn({
* ...createObject3DComponents(myObject, { x: 0, y: 0, z: 0 }),
* });
* ```
*/
export declare function createObject3DComponents(object3d: Object3D, position?: PositionOption3D, options?: TransformOptions3D): Pick<Renderer3DComponentTypes, 'object3d' | 'localTransform3D' | 'worldTransform3D' | 'visible3d'>;
type Renderer3DLabels = 'renderer3d-sync' | 'renderer3d-scene-graph' | 'renderer3d-render' | 'transform3d-propagation';
type Renderer3DReactiveQueryNames = 'renderer3d-meshes' | 'renderer3d-groups' | 'renderer3d-objects';
type Renderer3DWorldConfig = ComponentsConfig<Renderer3DComponentTypes> & EventsConfig<Renderer3DEventTypes> & ResourcesConfig<Renderer3DResourceTypes>;
/**
* Create a 3D rendering plugin for ECSpresso.
*
* This plugin provides:
* - 3D transform propagation (localTransform3D -> worldTransform3D)
* - Render sync system (updates Three.js objects from ECS components)
* - Scene graph management (auto-adds/removes Three.js objects)
* - Render call (renderer.render(scene, camera) each frame)
* - Optional requestAnimationFrame loop
*/
export declare function createRenderer3DPlugin<G extends string = 'renderer3d'>(options: Renderer3DPluginOptions<G>): Plugin<Renderer3DWorldConfig, EmptyConfig, Renderer3DLabels, G, never, Renderer3DReactiveQueryNames>;