UNPKG

polygonjs-engine

Version:

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

301 lines (285 loc) 9.47 kB
/** * Sets its transform based on the point position in a referenced object. * * */ import {TypedObjNode} from './_Base'; import {Group} from 'three/src/objects/Group'; import {FlagsControllerD} from '../utils/FlagsController'; import {AxesHelper} from 'three/src/helpers/AxesHelper'; import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig'; import {BaseNodeType} from '../_Base'; import {GeoObjNode} from './Geo'; import {HierarchyController} from './utils/HierarchyController'; import {NodeContext} from '../../poly/NodeContext'; import {WebGLRenderer} from 'three/src/renderers/WebGLRenderer'; import {Scene} from 'three/src/scenes/Scene'; import {Camera} from 'three/src/cameras/Camera'; import {BufferGeometry} from 'three/src/core/BufferGeometry'; import {Geometry} from 'three/src/core/Geometry'; import {Material} from 'three/src/materials/Material'; import {Mesh} from 'three/src/objects/Mesh'; import {Vector3} from 'three/src/math/Vector3'; import {Object3DWithGeometry} from '../../../core/geometry/Group'; import {RenderHook} from '../../../core/geometry/Material'; import {TypeAssert} from '../../poly/Assert'; enum RivetUpdateMode { ON_RENDER = 'On Every Render', MANUAL = 'Manual', } const UPDATE_MODES: RivetUpdateMode[] = [RivetUpdateMode.ON_RENDER, RivetUpdateMode.MANUAL]; class RivetObjParamConfig extends NodeParamsConfig { object = ParamConfig.OPERATOR_PATH('', { nodeSelection: { context: NodeContext.OBJ, }, dependentOnFoundNode: false, computeOnDirty: true, callback: (node: BaseNodeType) => { RivetObjNode.PARAM_CALLBACK_update_resolved_object(node as RivetObjNode); }, }); pointIndex = ParamConfig.INTEGER(0, { range: [0, 100], // callback: (node: BaseNodeType) => { // RivetObjNode.PARAM_CALLBACK_update_object_position(node as RivetObjNode); // }, }); // active = ParamConfig.BOOLEAN(true, { // callback: (node: BaseNodeType) => { // RivetObjNode.PARAM_CALLBACK_update_active_state(node as RivetObjNode); // }, // }); updateMode = ParamConfig.INTEGER(UPDATE_MODES.indexOf(RivetUpdateMode.ON_RENDER), { callback: (node: BaseNodeType) => { RivetObjNode.PARAM_CALLBACK_update_updateMode(node as RivetObjNode); }, menu: { entries: UPDATE_MODES.map((name, value) => { return {name, value}; }), }, // visibleIf: {active: true}, }); update = ParamConfig.BUTTON(null, { callback: (node: BaseNodeType) => { RivetObjNode.PARAM_CALLBACK_update(node as RivetObjNode); }, visibleIf: {updateMode: UPDATE_MODES.indexOf(RivetUpdateMode.MANUAL)}, }); // update = ParamConfig.BUTTON(null, { // callback: (node: BaseNodeType) => { // RivetObjNode.PARAM_CALLBACK_update_object_position(node as RivetObjNode); // }, // }); } const ParamsConfig = new RivetObjParamConfig(); export class RivetObjNode extends TypedObjNode<Mesh, RivetObjParamConfig> { params_config = ParamsConfig; static type(): Readonly<'rivet'> { return 'rivet'; } readonly hierarchy_controller: HierarchyController = new HierarchyController(this); public readonly flags: FlagsControllerD = new FlagsControllerD(this); private _helper = new AxesHelper(1); private _resolved_sop_group: Group | undefined; private _found_point_post = new Vector3(); create_object() { const mesh = new Mesh(); mesh.matrixAutoUpdate = false; return mesh; } initializeNode() { this.hierarchy_controller.initializeNode(); // this.io.inputs.add_on_set_input_hook('on_input_updated:update_object_position', () => { // this.update_object_position(); // }); // register hooks // this.lifecycle.add_on_add_hook(() => { // this._add_render_hook(); // // this.scene.rivets_register.register_rivet(this); // }); // this.lifecycle.add_delete_hook(() => { // this._remove_render_hook(); // // this.scene.rivets_register.deregister_rivet(this); // }); // this.nameController.add_post_set_name_hook(() => { // this.scene.rivets_register.sort_rivets(); // }); // setup frame dependency to update the matrix // this._time_graph_node.addPostDirtyHook('rivet_on_frame_change', () => { // setTimeout(() => { // this.update_object_position(); // }, 0); // }); this.addPostDirtyHook('rivet_on_dirty', () => { this.cookController.cook_main_without_inputs(); }); // this.params.set_post_create_params_hook(() => { // this._update_render_hook(); // }); // helper this._updateHelperHierarchy(); this.flags.display.add_hook(() => { this._updateHelperHierarchy(); }); } private _updateHelperHierarchy() { if (this.flags.display.active()) { this.object.add(this._helper); } else { this.object.remove(this._helper); } } async cook() { await this._update_resolved_object(); this._update_render_hook(); // this._update_updateMode(); this.cookController.end_cook(); } // private _remove_render_hook() { // if (this._previous_on_before_render) { // this.object.onBeforeRender = this._previous_on_before_render; // } // } // private _update_updateMode(){ // The problem with a frame dependency is that if there is a hierarchy // of rivets, the update chain will be in the wrong order, and therefore wrong. // It is then better to update it via a code node. // const mode = UPDATE_MODES[this.pv.mode] // switch(mode){ // case RivetUpdateMode.ON_RENDER: { // this._remove_frame_dependency() // return this._add_render_hook() // } // case RivetUpdateMode.ON_FRAME_CHANGE: { // this._remove_render_hook() // return this._add_frame_depedency() // } // } // TypeAssert.unreachable(mode) // } // private _remove_frame_dependency(){ // const frame_graph_node = this.scene.time_controller.graph_node // this.removeGraphInput(frame_graph_node) // } // private _add_frame_depedency(){ // const frame_graph_node = this.scene.time_controller.graph_node // this.addGraphInput(frame_graph_node) // } private _update_render_hook() { const mode = UPDATE_MODES[this.pv.updateMode]; switch (mode) { case RivetUpdateMode.ON_RENDER: { return this._add_render_hook(); } case RivetUpdateMode.MANUAL: { return this._remove_render_hook(); } } TypeAssert.unreachable(mode); } private _add_render_hook() { // check if the new hook is different than the current one // because if we were to add it 2x, previous hook would not be valid anymore // if (this.object.onBeforeRender !== this._on_object_before_render_bound) { // this._previous_on_before_render = this.object.onBeforeRender; this.object.onBeforeRender = this._on_object_before_render_bound; // not being frustumCulled ensures that the render hook is still used even though when the object is not on screen (as the children might very well be) this.object.frustumCulled = false; } private _remove_render_hook() { this.object.onBeforeRender = () => {}; } private _on_object_before_render_bound: RenderHook = this._update.bind(this); // private _previous_on_before_render: RenderHook | undefined; private _update( renderer?: WebGLRenderer, scene?: Scene, camera?: Camera, geometry?: BufferGeometry | Geometry, material?: Material, group?: Group | null ) { const resolved_object = this._resolved_object(); if (resolved_object) { const geometry = resolved_object.geometry; if (geometry) { const position_attrib = geometry.attributes['position']; if (position_attrib) { const position_array = position_attrib.array; this._found_point_post.fromArray(position_array, this.pv.pointIndex * 3); // ensures that parent objects have their matrices up to date resolved_object.updateWorldMatrix(true, false); resolved_object.localToWorld(this._found_point_post); this.object.matrix.makeTranslation( this._found_point_post.x, this._found_point_post.y, this._found_point_post.z ); } } } } // // // RESOLVE // // static PARAM_CALLBACK_update_resolved_object(node: RivetObjNode) { node._update_resolved_object(); } private async _update_resolved_object() { if (this.p.object.isDirty()) { await this.p.object.compute(); } const node = this.p.object.found_node(); if (node) { if (node.nodeContext() == NodeContext.OBJ && node.type() == GeoObjNode.type()) { const geo_node = node as GeoObjNode; // this._remove_render_hook(); this._resolved_sop_group = geo_node.children_display_controller.sop_group; // I can't really use _resolved_sop_group_child // because it may not exist when this method is execute // this._resolved_sop_group_child = this._resolved_sop_group.children[0] as Object3DWithGeometry; } else { this.states.error.set('found node is not a geo node'); } } } private _resolved_object() { if (!this._resolved_sop_group) { return; } const object = this._resolved_sop_group.children[0]; if (object) { return object as Object3DWithGeometry; } // return this._resolved_sop_group_child; } // // // ACTIVE // // static PARAM_CALLBACK_update_updateMode(node: RivetObjNode) { node._update_render_hook(); } // private async _update_active_state() { // // await this.p.active.compute(); // this._update_render_hook(); // } // // // UPDATE // // static PARAM_CALLBACK_update(node: RivetObjNode) { node._update(); } // private _reset() { // this._resolved_sop_group = undefined; // // this._resolved_sop_group_child = undefined; // this._update_resolved_object(); // } }