polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
301 lines (285 loc) • 9.47 kB
text/typescript
/**
* 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();
// }
}