UNPKG

polygonjs-engine

Version:

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

412 lines (411 loc) 13.8 kB
import {CoreGraphNode as CoreGraphNode2} from "../../../../core/graph/CoreGraphNode"; import {ParamType as ParamType2} from "../../../poly/ParamType"; import {ParamConstructorByType as ParamConstructorByType2} from "../../../params/types/ParamConstructorByType"; import {NodeEvent as NodeEvent2} from "../../../poly/NodeEvent"; import {ParamsLabelController as ParamsLabelController2} from "./ParamsLabelController"; import {Poly as Poly2} from "../../../Poly"; const NODE_SIMPLE_NAME = "params"; export class ParamsController { constructor(node) { this.node = node; this._param_create_mode = false; this._params_created = false; this._params_by_name = {}; this._params_list = []; this._param_names = []; this._non_spare_params = []; this._spare_params = []; this._non_spare_param_names = []; this._spare_param_names = []; this._params_added_since_last_params_eval = false; } get label() { return this._label_controller = this._label_controller || new ParamsLabelController2(); } has_label_controller() { return this._label_controller != null; } dispose() { if (this._params_node) { this._params_node.dispose(); } for (let param of this.all) { param.dispose(); } this._post_create_params_hook_names = void 0; this._post_create_params_hooks = void 0; this._on_scene_load_hooks = void 0; this._on_scene_load_hook_names = void 0; this._label_controller?.dispose(); } init_dependency_node() { if (!this._params_node) { this._params_node = new CoreGraphNode2(this.node.scene(), NODE_SIMPLE_NAME); this.node.addGraphInput(this._params_node, false); } } init() { this.init_dependency_node(); this._param_create_mode = true; this.init_from_params_config(); this.node.create_params(); this._post_create_params(); } _post_create_params() { this._update_caches(); this.init_param_accessors(); this._param_create_mode = false; this._params_created = true; this.run_post_create_params_hooks(); } post_create_spare_params() { this._update_caches(); this.init_param_accessors(); } update_params(options) { let has_created_a_param = false; let has_deleted_a_param = false; if (options.names_to_delete) { for (let param_name of options.names_to_delete) { if (this.has(param_name)) { this.delete_param(param_name); has_deleted_a_param = true; } } } if (options.to_add) { for (let param_data of options.to_add) { const param = this.addParam(param_data.type, param_data.name, param_data.init_value, param_data.options); if (param) { if (param_data.raw_input != null) { param.set(param_data.raw_input); } has_created_a_param = true; } } } if (has_deleted_a_param || has_created_a_param) { this.post_create_spare_params(); this.node.scene().referencesController.notify_params_updated(this.node); this.node.emit(NodeEvent2.PARAMS_UPDATED); } } init_from_params_config() { const params_config = this.node.params_config; let init_values_used = false; if (params_config) { for (let name of Object.keys(params_config)) { const config = params_config[name]; let init_value; if (this.node.params_init_value_overrides) { init_value = this.node.params_init_value_overrides[name]; init_values_used = true; } this.addParam(config.type, name, config.init_value, config.options, init_value); } } if (init_values_used) { this.node.setDirty(); } this.node.params_init_value_overrides = void 0; } init_param_accessors() { let current_names_in_accessor = Object.getOwnPropertyNames(this.node.pv); this._remove_unneeded_accessors(current_names_in_accessor); current_names_in_accessor = Object.getOwnPropertyNames(this.node.pv); for (let param of this.all) { const is_spare = param.options.is_spare(); const param_not_yet_in_accessors = !current_names_in_accessor.includes(param.name()); if (param_not_yet_in_accessors || is_spare) { Object.defineProperty(this.node.pv, param.name(), { get: () => { return param.value; }, configurable: is_spare }); Object.defineProperty(this.node.p, param.name(), { get: () => { return param; }, configurable: is_spare }); } } } _remove_unneeded_accessors(current_names_in_accessor) { const current_param_names = this._param_names; const names_to_remove = []; for (let current_name_in_accessor of current_names_in_accessor) { if (!current_param_names.includes(current_name_in_accessor)) { names_to_remove.push(current_name_in_accessor); } } for (let name_to_remove of names_to_remove) { Object.defineProperty(this.node.pv, name_to_remove, { get: () => { return void 0; }, configurable: true }); Object.defineProperty(this.node.p, name_to_remove, { get: () => { return void 0; }, configurable: true }); } } get params_node() { return this._params_node; } get all() { return this._params_list; } get non_spare() { return this._non_spare_params; } get spare() { return this._spare_params; } get names() { return this._param_names; } get non_spare_names() { return this._non_spare_param_names; } get spare_names() { return this._spare_param_names; } set_with_type(param_name, value, type) { const param = this.param_with_type(param_name, type); if (param) { param.set(value); } else { Poly2.warn(`param ${param_name} not found with type ${type}`); } } set_float(param_name, value) { this.set_with_type(param_name, value, ParamType2.FLOAT); } set_vector3(param_name, value) { this.set_with_type(param_name, value, ParamType2.VECTOR3); } has_param(param_name) { return this._params_by_name[param_name] != null; } has(param_name) { return this.has_param(param_name); } get(param_name) { return this.param(param_name); } param_with_type(param_name, type) { const param = this.param(param_name); if (param && param.type() == type) { return param; } } get_float(param_name) { return this.param_with_type(param_name, ParamType2.FLOAT); } get_operator_path(param_name) { return this.param_with_type(param_name, ParamType2.OPERATOR_PATH); } value(param_name) { return this.param(param_name)?.value; } value_with_type(param_name, type) { return this.param_with_type(param_name, type)?.value; } boolean(param_name) { return this.value_with_type(param_name, ParamType2.BOOLEAN); } float(param_name) { return this.value_with_type(param_name, ParamType2.FLOAT); } integer(param_name) { return this.value_with_type(param_name, ParamType2.INTEGER); } string(param_name) { return this.value_with_type(param_name, ParamType2.STRING); } vector2(param_name) { return this.value_with_type(param_name, ParamType2.VECTOR2); } vector3(param_name) { return this.value_with_type(param_name, ParamType2.VECTOR3); } color(param_name) { return this.value_with_type(param_name, ParamType2.COLOR); } param(param_name) { const p = this._params_by_name[param_name]; if (p != null) { return p; } else { Poly2.warn(`tried to access param '${param_name}' in node ${this.node.fullPath()}, but existing params are: ${this.names} on node ${this.node.fullPath()}`); return null; } } delete_param(param_name) { const param = this._params_by_name[param_name]; if (param) { if (this._params_node) { this._params_node.removeGraphInput(this._params_by_name[param_name]); } param._setup_node_dependencies(null); delete this._params_by_name[param_name]; if (param.is_multiple && param.components) { for (let component of param.components) { const child_name = component.name(); delete this._params_by_name[child_name]; } } } else { throw new Error(`param '${param_name}' does not exist on node ${this.node.fullPath()}`); } } addParam(type, param_name, default_value, options = {}, init_data) { const is_spare = options["spare"] || false; if (this._param_create_mode === false && !is_spare) { Poly2.warn(`node ${this.node.fullPath()} (${this.node.type()}) param '${param_name}' cannot be created outside of create_params`); } if (this.node.scene() == null) { Poly2.warn(`node ${this.node.fullPath()} (${this.node.type()}) has no scene assigned`); } const constructor = ParamConstructorByType2[type]; if (constructor != null) { const existing_param = this._params_by_name[param_name]; if (existing_param) { if (is_spare) { if (existing_param.type() != type) { this.delete_param(existing_param.name()); } } else { Poly2.warn(`a param named ${param_name} already exists`, this.node); } } const param = new constructor(this.node.scene(), this.node); param.options.set(options); param.setName(param_name); param.set_init_value(default_value); param.init_components(); if (init_data == null) { param.set(default_value); } else { if (param.options.is_expression_for_entities()) { param.set(default_value); } if (init_data.raw_input != null) { param.set(init_data.raw_input); } else { if (init_data.simple_data != null) { param.set(init_data.simple_data); } else { if (init_data.complex_data != null) { const raw_input = init_data.complex_data.raw_input; if (raw_input) { param.set(raw_input); } else { param.set(default_value); } const overriden_options = init_data.complex_data.overriden_options; if (overriden_options != null) { const keys = Object.keys(overriden_options); for (let key of keys) { param.options.set_option(key, overriden_options[key]); } } } } } } param._setup_node_dependencies(this.node); this._params_by_name[param.name()] = param; if (param.is_multiple && param.components) { for (let component of param.components) { this._params_by_name[component.name()] = component; } } this._params_added_since_last_params_eval = true; return param; } } _update_caches() { this._params_list = Object.values(this._params_by_name); this._param_names = Object.keys(this._params_by_name); this._non_spare_params = Object.values(this._params_by_name).filter((p) => !p.options.is_spare()); this._spare_params = Object.values(this._params_by_name).filter((p) => p.options.is_spare()); this._non_spare_param_names = Object.values(this._params_by_name).filter((p) => !p.options.is_spare()).map((p) => p.name()); this._spare_param_names = Object.values(this._params_by_name).filter((p) => p.options.is_spare()).map((p) => p.name()); } async _eval_param(param) { if (param.isDirty()) { await param.compute(); if (param.states.error.active()) { this.node.states.error.set(`param '${param.name()}' error: ${param.states.error.message()}`); } } else { } } async eval_params(params) { const promises = []; for (let i = 0; i < params.length; i++) { if (params[i].isDirty()) { promises.push(this._eval_param(params[i])); } } await Promise.all(promises); if (this.node.states.error.active()) { this.node.setContainer(null); } } params_eval_required() { return this._params_node && (this._params_node.isDirty() || this._params_added_since_last_params_eval); } async eval_all() { if (this.params_eval_required()) { await this.eval_params(this._params_list); this._params_node?.removeDirtyState(); this._params_added_since_last_params_eval = false; } } onParamsCreated(hook_name, hook) { if (this._params_created) { hook(); } else { if (this._post_create_params_hook_names && this._post_create_params_hook_names.includes(hook_name)) { Poly2.error(`hook name ${hook_name} already exists`); return; } this._post_create_params_hook_names = this._post_create_params_hook_names || []; this._post_create_params_hook_names.push(hook_name); this._post_create_params_hooks = this._post_create_params_hooks || []; this._post_create_params_hooks.push(hook); } } add_on_scene_load_hook(param_name, method) { this._on_scene_load_hook_names = this._on_scene_load_hook_names || []; this._on_scene_load_hooks = this._on_scene_load_hooks || []; if (!this._on_scene_load_hook_names.includes(param_name)) { this._on_scene_load_hook_names.push(param_name); this._on_scene_load_hooks.push(method); } else { Poly2.warn(`hook with name ${param_name} already exists`, this.node); } } run_post_create_params_hooks() { if (this._post_create_params_hooks) { for (let hook of this._post_create_params_hooks) { hook(); } } } run_on_scene_load_hooks() { if (this._on_scene_load_hooks) { for (let hook of this._on_scene_load_hooks) { hook(); } } } }