UNPKG

polygonjs-engine

Version:

node-based webgl 3D engine https://polygonjs.com

302 lines (269 loc) 10.4 kB
import {Constructor, valueof} from '../../../types/GlobalTypes'; import {Camera} from 'three/src/cameras/Camera'; import {CoreTransform} from '../../../core/Transform'; import {ObjNodeRenderOrder} from './_Base'; import {ThreejsCameraControlsController} from './utils/cameras/ControlsController'; import {LayersController, LayerParamConfig} from './utils/LayersController'; import {PostProcessController, CameraPostProcessParamConfig} from './utils/cameras/PostProcessController'; import {RenderController, CameraRenderParamConfig} from './utils/cameras/RenderController'; import {TransformedParamConfig, TransformController} from './utils/TransformController'; import {ChildrenDisplayController} from './utils/ChildrenDisplayController'; import {DisplayNodeController} from '../utils/DisplayNodeController'; import {NodeContext} from '../../poly/NodeContext'; import {ThreejsViewer, ThreejsViewerProperties} from '../../viewers/Threejs'; import {FlagsControllerD} from '../utils/FlagsController'; import {BaseParamType} from '../../params/_Base'; import {BaseNodeType} from '../_Base'; import {BaseSopNodeType} from '../sop/_Base'; import {TypedObjNode} from './_Base'; import {BaseViewerType} from '../../viewers/_Base'; import {HierarchyController} from './utils/HierarchyController'; import {GeoNodeChildrenMap} from '../../poly/registers/nodes/Sop'; import {ParamsInitData} from '../utils/io/IOController'; import {Raycaster} from 'three/src/core/Raycaster'; import {Vector2} from 'three/src/math/Vector2'; import {CoreType} from '../../../core/Type'; export interface OrthoOrPerspCamera extends Camera { near: number; far: number; updateProjectionMatrix: () => void; getFocalLength?: () => void; } const EVENT_CHANGE = {type: 'change'}; export const BASE_CAMERA_DEFAULT = { near: 1.0, far: 100.0, }; export enum UpdateFromControlsMode { ON_END = 'on move end', ALWAYS = 'always', NEVER = 'never', } export const UPDATE_FROM_CONTROLS_MODES: UpdateFromControlsMode[] = [ UpdateFromControlsMode.ON_END, UpdateFromControlsMode.ALWAYS, UpdateFromControlsMode.NEVER, ]; import {ParamConfig, NodeParamsConfig} from '../utils/params/ParamsConfig'; export function CameraMasterCameraParamConfig<TBase extends Constructor>(Base: TBase) { return class Mixin extends Base { setMasterCamera = ParamConfig.BUTTON(null, { callback: (node: BaseNodeType, param: BaseParamType) => { BaseCameraObjNodeClass.PARAM_CALLBACK_setMasterCamera(node as BaseCameraObjNodeType); }, }); }; } export function ThreejsCameraTransformParamConfig<TBase extends Constructor>(Base: TBase) { return class Mixin extends Base { camera = ParamConfig.FOLDER(); controls = ParamConfig.NODE_PATH('', { nodeSelection: { context: NodeContext.EVENT, }, }); updateFromControlsMode = ParamConfig.INTEGER( UPDATE_FROM_CONTROLS_MODES.indexOf(UpdateFromControlsMode.ON_END), { menu: { entries: UPDATE_FROM_CONTROLS_MODES.map((name, value) => { return {name, value}; }), }, } ); // allowUpdateFromControls = ParamConfig.BOOLEAN(1); // target = ParamConfig.VECTOR3([0, 0, 0], {cook: false}); near = ParamConfig.FLOAT(BASE_CAMERA_DEFAULT.near, { range: [0, 100], cook: false, computeOnDirty: true, callback: (node: BaseNodeType, param: BaseParamType) => { BaseThreejsCameraObjNodeClass.PARAM_CALLBACK_update_near_far_from_param( node as BaseThreejsCameraObjNodeType, param ); }, }); far = ParamConfig.FLOAT(BASE_CAMERA_DEFAULT.far, { range: [0, 100], cook: false, computeOnDirty: true, callback: (node: BaseNodeType, param: BaseParamType) => { BaseThreejsCameraObjNodeClass.PARAM_CALLBACK_update_near_far_from_param( node as BaseThreejsCameraObjNodeType, param ); }, }); // aspect = ParamConfig.FLOAT(1); // lock_width = ParamConfig.BOOLEAN(1); // look_at = ParamConfig.OPERATOR_PATH(''); display = ParamConfig.BOOLEAN(1); }; } export class BaseCameraObjParamsConfig extends CameraMasterCameraParamConfig(NodeParamsConfig) {} export class BaseThreejsCameraObjParamsConfig extends CameraPostProcessParamConfig( CameraRenderParamConfig( TransformedParamConfig( LayerParamConfig(ThreejsCameraTransformParamConfig(CameraMasterCameraParamConfig(NodeParamsConfig))) ) ) ) {} export abstract class TypedCameraObjNode< O extends OrthoOrPerspCamera, K extends BaseCameraObjParamsConfig > extends TypedObjNode<O, K> { // public readonly flags: FlagsControllerD = new FlagsControllerD(this); public readonly render_order: number = ObjNodeRenderOrder.CAMERA; protected _object!: O; protected _aspect: number = -1; get object() { return this._object; } async cook() { this.update_camera(); this._object.dispatchEvent(EVENT_CHANGE); this.cookController.end_cook(); } on_create() {} on_delete() {} prepare_raycaster(mouse: Vector2, raycaster: Raycaster) {} camera() { return this._object; } update_camera() {} static PARAM_CALLBACK_setMasterCamera(node: BaseCameraObjNodeType) { node.set_as_master_camera(); } set_as_master_camera() { this.scene().camerasController.setMasterCameraNodePath(this.fullPath()); } setup_for_aspect_ratio(aspect: number) {} protected _update_for_aspect_ratio(): void {} update_transform_params_from_object() { // CoreTransform.set_params_from_matrix(this._object.matrix, this, {scale: false}) CoreTransform.set_params_from_object(this._object, this); } abstract createViewer(element: HTMLElement): BaseViewerType; static PARAM_CALLBACK_update_from_param(node: BaseCameraObjNodeType, param: BaseParamType) { (node.object as any)[param.name()] = (node.pv as any)[param.name()]; } } export class TypedThreejsCameraObjNode< O extends OrthoOrPerspCamera, K extends BaseThreejsCameraObjParamsConfig > extends TypedCameraObjNode<O, K> { public readonly flags: FlagsControllerD = new FlagsControllerD(this); readonly hierarchy_controller: HierarchyController = new HierarchyController(this); readonly transform_controller: TransformController = new TransformController(this); protected _controls_controller: ThreejsCameraControlsController | undefined; get controls_controller(): ThreejsCameraControlsController { return (this._controls_controller = this._controls_controller || new ThreejsCameraControlsController(this)); } protected _layers_controller: LayersController | undefined; get layers_controller() { return (this._layers_controller = this._layers_controller || new LayersController(this)); } protected _render_controller: RenderController | undefined; get renderController(): RenderController { return (this._render_controller = this._render_controller || new RenderController(this)); } protected _post_process_controller: PostProcessController | undefined; get post_process_controller(): PostProcessController { return (this._post_process_controller = this._post_process_controller || new PostProcessController(this)); } // display_node and children_display controllers public readonly children_display_controller: ChildrenDisplayController = new ChildrenDisplayController(this); public readonly display_node_controller: DisplayNodeController = new DisplayNodeController( this, this.children_display_controller.display_node_controller_callbacks() ); // protected _children_controller_context = NodeContext.SOP; initializeBaseNode() { super.initializeBaseNode(); this.io.outputs.set_has_one_output(); this.hierarchy_controller.initializeNode(); this.transform_controller.initializeNode(); this.children_display_controller.initializeNode(); } createNode<S extends keyof GeoNodeChildrenMap>( node_class: S, params_init_value_overrides?: ParamsInitData ): GeoNodeChildrenMap[S]; createNode<K extends valueof<GeoNodeChildrenMap>>( node_class: Constructor<K>, params_init_value_overrides?: ParamsInitData ): K; createNode<K extends valueof<GeoNodeChildrenMap>>( node_class: Constructor<K>, params_init_value_overrides?: ParamsInitData ): K { return super.createNode(node_class, params_init_value_overrides) as K; } children() { return super.children() as BaseSopNodeType[]; } nodesByType<K extends keyof GeoNodeChildrenMap>(type: K): GeoNodeChildrenMap[K][] { return super.nodesByType(type) as GeoNodeChildrenMap[K][]; } prepare_raycaster(mouse: Vector2, raycaster: Raycaster) { raycaster.setFromCamera(mouse, this._object); } async cook() { this.transform_controller.update(); this.layers_controller.update(); // await this.background_controller.update(); this.update_near_far(); this.renderController.update(); this.update_camera(); this.controls_controller.update_controls(); // TODO: ideally the update transform and update camera // can both return if the camera has changed // and we can run this here instead of inside the update_transform and update_camera // this._object.dispatchEvent( EVENT_CHANGE ) this._object.dispatchEvent(EVENT_CHANGE); this.cookController.end_cook(); } static PARAM_CALLBACK_update_near_far_from_param(node: BaseThreejsCameraObjNodeType, param: BaseParamType) { node.update_near_far(); } update_near_far() { if (this._object.near != this.pv.near || this._object.far != this.pv.far) { this._object.near = this.pv.near; this._object.far = this.pv.far; this._object.updateProjectionMatrix(); } } setup_for_aspect_ratio(aspect: number) { if (CoreType.isNaN(aspect)) { return; } if (aspect && this._aspect != aspect) { this._aspect = aspect; this._update_for_aspect_ratio(); } } createViewer(element: HTMLElement, viewer_properties?: ThreejsViewerProperties): ThreejsViewer { return new ThreejsViewer(element, this.scene(), this, viewer_properties); } static PARAM_CALLBACK_reset_effects_composer(node: BaseThreejsCameraObjNodeType) { node.post_process_controller.reset(); } } export type BaseCameraObjNodeType = TypedCameraObjNode<OrthoOrPerspCamera, BaseCameraObjParamsConfig>; export abstract class BaseCameraObjNodeClass extends TypedCameraObjNode< OrthoOrPerspCamera, BaseCameraObjParamsConfig > {} export type BaseThreejsCameraObjNodeType = TypedThreejsCameraObjNode< OrthoOrPerspCamera, BaseThreejsCameraObjParamsConfig >; export class BaseThreejsCameraObjNodeClass extends TypedThreejsCameraObjNode< OrthoOrPerspCamera, BaseThreejsCameraObjParamsConfig > { PARAM_CALLBACK_update_effects_composer(node: BaseNodeType) {} }