UNPKG

threepipe

Version:

A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.

125 lines (104 loc) 3.8 kB
import {IObject3D} from '../../../core' import {UiObjectConfig} from 'uiconfig.js' import {ComponentCtx, ComponentDefn, ComponentJSON} from './componentTypes' import {refreshAllStateProperties} from './setupComponent' import {EntityComponentPlugin} from '../EntityComponentPlugin' import {ViewerEventMap} from '../../../viewer/ThreeViewer' export type TObject3DComponent = typeof Object3DComponent export class Object3DComponent { declare ['constructor']: TObject3DComponent & ComponentDefn // enabled = true static StateProperties: ComponentDefn['StateProperties'] = [/* 'enabled'*/] static ComponentType = 'Object3DComponent' readonly isObject3DComponent = true protected _object: IObject3D | null = null get object() { if (!this._object) { throw new Error('Cannot access object at this point.') } return this._object } set object(_: IObject3D) { console.error('Object3DComponent: object is read-only') } // name: string // uuid = generateUUID() uuid = Math.random().toString(32).slice(2, 10) declare uiConfig?: UiObjectConfig declare state: never // so that this name can never be used protected _state: ComponentJSON['state'] get stateRef() { return this._state } // getState() { // return {...this._state} // } setState(state: ComponentJSON['state']) { const oldState = this._state if (state === oldState) return if (state && typeof state === 'object') { this._state = state } else { this._state = {} } for (const key in oldState) { if (!(key in this._state)) { this._state[key] = oldState[key] } } refreshAllStateProperties(this, oldState) return this } declare ctx: ComponentCtx constructor() { this._state = { // ...stateFuncs, } // todo this has to be done after the subclass constructor, not before. move to the plugin // this.name = this.type } // hooks start() { // called when component is added to an object, usually when the scene is played } stop() { // called when scene is stopped or component is removed from an object when scene is playing } update(_e: ViewerEventMap['preFrame']): boolean|void { // called every frame } preFrame(_e: ViewerEventMap['preFrame']): boolean|void { // called every frame, before update, even when not running(paused/edit mode) } init(object: IObject3D, state: ComponentJSON['state']) { if (!object) throw new Error('Object3DComponent: no object') this._object = object this.setState(state) } destroy() { this._object = null as any this.stateChangeHandlers = {} const state = this._state this._state = {} return state } stateChangeHandlers: Record<string, ((value: any, oldValue: any, key?: string) => void)[]> = {} onStateChange<K extends keyof this & string>( key: K, fn: (value: this[K], oldValue: this[K], key?: K) => void ) { if (!this.stateChangeHandlers[key]) this.stateChangeHandlers[key] = [] this.stateChangeHandlers[key].push(fn) } /** * @internal */ ['_sType']?: string getComponent<T extends TObject3DComponent>(type: T | string, self = false) { const obj = this._object if (!obj) return self ? null : this.ctx.ecp.getComponentOfType(type) if (self) return EntityComponentPlugin.GetComponent(obj, type) return EntityComponentPlugin.GetComponentInParent(obj, type) || this.ctx.ecp.getComponentOfType(type) } }