polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
302 lines (269 loc) • 10.4 kB
text/typescript
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) {}
}