UNPKG

polygonjs-engine

Version:

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

204 lines (187 loc) 7.13 kB
import {Constructor} from '../../../../types/GlobalTypes'; import {TypedObjNode} from '../_Base'; import {Matrix4} from 'three/src/math/Matrix4'; import {CoreTransform, SetParamsFromMatrixOptions, ROTATION_ORDERS, RotationOrder} from '../../../../core/Transform'; import {Object3D} from 'three/src/core/Object3D'; // import {Vector3} from 'three/src/math/Vector3'; // import {Quaternion} from 'three/src/math/Quaternion'; import {NodeParamsConfig, ParamConfig} from '../../utils/params/ParamsConfig'; import {BaseNodeType} from '../../_Base'; interface TransformedParamConfigDefaultParams { matrixAutoUpdate?: boolean; } export function TransformedParamConfig<TBase extends Constructor>( Base: TBase, default_params?: TransformedParamConfigDefaultParams ) { const matrixAutoUpdate = default_params?.matrixAutoUpdate || false; return class Mixin extends Base { transform = ParamConfig.FOLDER(); /** @param toggle on to keep world position when adding a parent or removing from one */ keepPosWhenParenting = ParamConfig.BOOLEAN(0); /** @param rotation order */ rotationOrder = ParamConfig.INTEGER(ROTATION_ORDERS.indexOf(RotationOrder.XYZ), { menu: { entries: ROTATION_ORDERS.map((order, v) => { return {name: order, value: v}; }), }, }); /** @param translate */ t = ParamConfig.VECTOR3([0, 0, 0]); /** @param rotation */ r = ParamConfig.VECTOR3([0, 0, 0]); /** @param scale */ s = ParamConfig.VECTOR3([1, 1, 1]); /** @param scale */ scale = ParamConfig.FLOAT(1); // pivot = ParamConfig.VECTOR3([0, 0, 0]); /** @param set for the matrix to be updated every frame */ matrixAutoUpdate = ParamConfig.BOOLEAN(matrixAutoUpdate ? 1 : 0); updateTransformFromObject = ParamConfig.BUTTON(null, { callback: (node: BaseNodeType) => { TransformController.PARAM_CALLBACK_update_transform_from_object(node as TransformedObjNode); }, }); tlookAt = ParamConfig.BOOLEAN(0); lookAtPos = ParamConfig.VECTOR3([0, 0, 0], { visibleIf: {tlookAt: 1}, }); // look_at = ParamConfig.OPERATOR_PATH('', { // visibleIf: {tlookAt: 1}, // nodeSelection: {context: NodeContext.OBJ}, // }); up = ParamConfig.VECTOR3([0, 1, 0], { visibleIf: {tlookAt: 1}, }); }; } class TransformedParamsConfig extends TransformedParamConfig(NodeParamsConfig) {} export class TransformedObjNode extends TypedObjNode<Object3D, TransformedParamsConfig> { readonly transform_controller: TransformController = new TransformController(this); } const HOOK_NAME = '_cook_main_without_inputs_when_dirty'; export class TransformController { constructor(private node: TransformedObjNode) {} initializeNode() { if (!this.node.dirtyController.has_hook(HOOK_NAME)) { this.node.dirtyController.addPostDirtyHook(HOOK_NAME, this._cook_main_without_inputs_when_dirty_bound); } } // TODO: this will have to be checked via the parent, when I will have obj managers at lower levels than root private _cook_main_without_inputs_when_dirty_bound = this._cook_main_without_inputs_when_dirty.bind(this); private async _cook_main_without_inputs_when_dirty() { await this.node.cookController.cook_main_without_inputs(); } update() { this.update_transform_with_matrix(); const object = this.node.object; object.matrixAutoUpdate = this.node.pv.matrixAutoUpdate; } update_transform_with_matrix(matrix?: Matrix4) { const object = this.node.object; if (matrix != null && !matrix.equals(object.matrix)) { // do not apply to cameras with control // object.matrixAutoUpdate = false; object.matrix.copy(matrix); object.dispatchEvent({type: 'change'}); } else { this._update_matrix_from_params_with_core_transform(); // this.update_transform_from_params(); } } private _core_transform = new CoreTransform(); private _update_matrix_from_params_with_core_transform() { const object = this.node.object; let prev_auto_update = object.matrixAutoUpdate; if (prev_auto_update) { object.matrixAutoUpdate = false; } const matrix = this._core_transform.matrix( this.node.pv.t, this.node.pv.r, this.node.pv.s, this.node.pv.scale, ROTATION_ORDERS[this.node.pv.rotationOrder] ); object.matrix.identity(); object.applyMatrix4(matrix); this._apply_look_at(); object.updateMatrix(); if (prev_auto_update) { object.matrixAutoUpdate = true; } object.dispatchEvent({type: 'change'}); } // private _look_at_target_t = new Vector3(); // private _look_at_target_q = new Quaternion(); // private _look_at_target_s = new Vector3(); private _apply_look_at() { const pv = this.node.pv; if (!pv.tlookAt) { return; } this.node.object.up.copy(pv.up); this.node.object.lookAt(pv.lookAtPos); // const target_node = this.node.p.look_at.found_node_with_context(NodeContext.OBJ); // if (target_node) { // const target_object = target_node.object; // target_object.ma.decompose(this._look_at_target_t, this._look_at_target_q, this._look_at_target_s); // this.node.object.up.copy(this.node.pv.up); // this.node.object.lookAt(this._look_at_target_t); // console.log('lookat', this.node.object, target_object, this._look_at_target_t); // } } set_params_from_matrix(matrix: Matrix4, options: SetParamsFromMatrixOptions = {}) { CoreTransform.set_params_from_matrix(matrix, this.node, options); } // // // KEEP POS WHEN PARENTING // // static update_node_transform_params_if_required(node: TransformedObjNode, new_parent_object: Object3D) { node.transform_controller.update_node_transform_params_if_required(new_parent_object); } // private _keep_pos_when_parenting_t = new Vector3(); // private _keep_pos_when_parenting_q = new Quaternion(); // private _keep_pos_when_parenting_s = new Vector3(); private _keep_pos_when_parenting_m_object = new Matrix4(); private _keep_pos_when_parenting_m_new_parent_inv = new Matrix4(); update_node_transform_params_if_required(new_parent_object: Object3D) { if (!this.node.pv.keepPosWhenParenting) { return; } if (!this.node.scene().loadingController.loaded()) { return; } if (new_parent_object == this.node.object.parent) { return; } const object = this.node.object; object.updateMatrixWorld(true); new_parent_object.updateMatrixWorld(true); // compute mat this._keep_pos_when_parenting_m_object.copy(object.matrixWorld); this._keep_pos_when_parenting_m_new_parent_inv.copy(new_parent_object.matrixWorld); this._keep_pos_when_parenting_m_new_parent_inv.invert(); this._keep_pos_when_parenting_m_object.premultiply(this._keep_pos_when_parenting_m_new_parent_inv); // apply mat CoreTransform.set_params_from_matrix(this._keep_pos_when_parenting_m_object, this.node, {scale: true}); } update_node_transform_params_from_object(update_matrix = false) { const object = this.node.object; if (update_matrix) { object.updateMatrix(); } CoreTransform.set_params_from_matrix(object.matrix, this.node, {scale: true}); } // // // CALLBACK // // static PARAM_CALLBACK_update_transform_from_object(node: TransformedObjNode) { node.transform_controller.update_node_transform_params_from_object(); } }