@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
463 lines (462 loc) • 14.9 kB
JavaScript
"use strict";
import { CoreGraphNode } from "../../../../core/graph/CoreGraphNode";
import { ParamType } from "../../../poly/ParamType";
import { ParamConstructorByType } from "../../../params/types/ParamConstructorByType";
import { NodeEvent } from "../../../poly/NodeEvent";
import { Poly } from "../../../Poly";
import { arrayCopy } from "../../../../core/ArrayUtils";
const NODE_SIMPLE_NAME = "params";
const _tmpAll = [];
export class ParamsController {
constructor(node) {
this.node = node;
this._param_create_mode = false;
this._params_created = false;
this._paramsByName = /* @__PURE__ */ new Map();
// caches
this._paramsList = [];
this._paramNames = [];
this._non_spare_params = [];
this._spare_params = [];
this._non_spare_param_names = [];
this._spare_param_names = [];
// private _params_eval_key: string;
this._params_added_since_last_params_eval = false;
this._promises = [];
}
dispose() {
if (this._params_node) {
this._params_node.dispose();
}
arrayCopy(this.all, _tmpAll);
for (const param of _tmpAll) {
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;
}
initDependencyNode() {
if (!this._params_node) {
this._params_node = new CoreGraphNode(this.node.scene(), NODE_SIMPLE_NAME);
this.node.addGraphInput(this._params_node, false);
}
}
init() {
this.initDependencyNode();
this._param_create_mode = true;
this._initFromParamsConfig();
this.node.createParams();
this._postCreateParams();
}
_postCreateParams() {
this._updateCaches();
this._initParamAccessors();
this._param_create_mode = false;
this._params_created = true;
this._runPostCreateParamsHooks();
}
postCreateSpareParams() {
this._updateCaches();
this._initParamAccessors();
this.node.scene().referencesController.notifyParamsUpdated(this.node);
this.node.emit(NodeEvent.PARAMS_UPDATED);
}
updateParams(options) {
let hasCreatedAParam = false;
let hasDeletedAParam = false;
const addedParams = [];
if (options.namesToDelete) {
for (const param_name of options.namesToDelete) {
if (this.has(param_name)) {
this._deleteParam(param_name);
hasDeletedAParam = true;
}
}
}
if (options.toAdd) {
for (const paramData of options.toAdd) {
const param = this.addParam(paramData.type, paramData.name, paramData.initValue, paramData.options);
if (param) {
if (paramData.rawInput != null) {
param.set(paramData.rawInput);
}
hasCreatedAParam = true;
addedParams.push(param);
}
}
}
if (hasDeletedAParam || hasCreatedAParam) {
this.postCreateSpareParams();
}
for (const param of addedParams) {
this.node.scene().missingExpressionReferencesController.checkForMissingParamReferences(param);
}
}
_initFromParamsConfig() {
var _a;
const paramsConfig = this.node.paramsConfig;
let init_values_used = false;
if (paramsConfig) {
const paramsInitValueOverrides = (_a = this.node.createOptions) == null ? void 0 : _a.paramsInitValueOverrides;
const names = Object.keys(paramsConfig);
for (const name of names) {
const config = paramsConfig[name];
let init_value;
if (paramsInitValueOverrides) {
init_value = paramsInitValueOverrides[name];
init_values_used = true;
}
this.addParam(config.type, name, config.init_value, config.options, init_value);
}
}
if (init_values_used) {
this.node.setDirty();
}
if (this.node.createOptions) {
this.node.createOptions.paramsInitValueOverrides = void 0;
}
}
_initParamAccessors() {
let currentNamesInAccessor = Object.getOwnPropertyNames(this.node.pv);
this._removeUnneededAccessors(currentNamesInAccessor);
currentNamesInAccessor = Object.getOwnPropertyNames(this.node.pv);
const all = this.all;
for (const param of all) {
const isSpare = param.options.isSpare();
const paramNotYetInAccessors = !currentNamesInAccessor.includes(param.name());
try {
if (paramNotYetInAccessors || isSpare) {
Object.defineProperty(this.node.pv, param.name(), {
get: () => {
return param.value;
},
// only spare parameters can be removed
configurable: isSpare
});
Object.defineProperty(this.node.p, param.name(), {
get: () => {
return param;
},
configurable: isSpare
});
}
} catch (err) {
}
}
}
_removeUnneededAccessors(current_names_in_accessor) {
const current_param_names = this._paramNames;
const names_to_remove = [];
for (const 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 (const 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._paramsList;
}
get non_spare() {
return this._non_spare_params;
}
get spare() {
return this._spare_params;
}
get names() {
return this._paramNames;
}
get non_spare_names() {
return this._non_spare_param_names;
}
get spare_names() {
return this._spare_param_names;
}
set_with_type(paramName, value, type) {
const param = this.paramWithType(paramName, type);
if (param) {
param.set(value);
} else {
Poly.warn(`param ${paramName} not found with type ${type}`);
}
}
set_float(paramName, value) {
this.set_with_type(paramName, value, ParamType.FLOAT);
}
set_vector3(paramName, value) {
this.set_with_type(paramName, value, ParamType.VECTOR3);
}
has_param(paramName) {
return this._paramsByName.has(paramName);
}
has(paramName) {
return this.has_param(paramName);
}
get(paramName) {
return this.param(paramName);
}
paramWithType(param_name, type) {
const param = this.param(param_name);
if (param && param.type() == type) {
return param;
}
}
getFloat(param_name) {
return this.paramWithType(param_name, ParamType.FLOAT);
}
// get_operator_path(param_name: string): OperatorPathParam {
// return this.paramWithType(param_name, ParamType.OPERATOR_PATH) as OperatorPathParam;
// }
value(paramName) {
var _a;
return (_a = this.param(paramName)) == null ? void 0 : _a.value;
}
valueWithType(paramName, type) {
var _a;
return (_a = this.paramWithType(paramName, type)) == null ? void 0 : _a.value;
}
boolean(paramName) {
return this.valueWithType(paramName, ParamType.BOOLEAN);
}
float(paramName) {
return this.valueWithType(paramName, ParamType.FLOAT);
}
integer(paramName) {
return this.valueWithType(paramName, ParamType.INTEGER);
}
string(paramName) {
return this.valueWithType(paramName, ParamType.STRING);
}
vector2(paramName) {
return this.valueWithType(paramName, ParamType.VECTOR2);
}
vector3(paramName) {
return this.valueWithType(paramName, ParamType.VECTOR3);
}
color(paramName) {
return this.valueWithType(paramName, ParamType.COLOR);
}
param(paramName) {
const p = this._paramsByName.get(paramName);
if (p != null) {
return p;
} else {
Poly.warn(
`tried to access param '${paramName}' in node ${this.node.path()}, but existing params are: ${this.names} on node ${this.node.path()}`
);
return null;
}
}
// param_cache_name(param_name: string) {
// return `_param_${param_name}`;
// }
// delete_params(param_names: string[]) {
// for (let param_name of param_names) {
// this.delete_param(param_name);
// }
// }
// call update_params instead
_deleteParam(paramName) {
const param = this._paramsByName.get(paramName);
if (param) {
if (this._params_node) {
this._params_node.removeGraphInput(param);
}
param._setupNodeDependencies(null);
this._paramsByName.delete(paramName);
if (param.isMultiple() && param.components) {
for (const component of param.components) {
const childName = component.name();
this._paramsByName.delete(childName);
}
}
param.dispose();
param.scene().graphNodesController.notifyParamPathChanged(param);
} else {
throw new Error(`param '${paramName}' does not exist on node ${this.node.path()}`);
}
}
addParam(type, paramName, defaultValue, options = {}, initData) {
const isSpare = options["spare"] || false;
if (this._param_create_mode === false && !isSpare) {
Poly.warn(
`node ${this.node.path()} (${this.node.type()}) param '${paramName}' cannot be created outside of createParams`
);
}
if (this.node.scene() == null) {
Poly.warn(`node ${this.node.path()} (${this.node.type()}) has no scene assigned`);
}
const constructor = ParamConstructorByType[type];
if (constructor != null) {
const existing_param = this._paramsByName.get(paramName);
if (existing_param) {
if (isSpare) {
if (existing_param.type() != type) {
this._deleteParam(existing_param.name());
}
} else {
Poly.warn(`a param named ${paramName} already exists`, this.node);
}
}
const param = new constructor(this.node.scene(), this.node, {
serializerClass: this.node.scene().paramSerializerClass()
});
param.options.set(options);
param.setName(paramName);
param.setInitValue(defaultValue);
param.initComponents();
if (initData == null) {
param.set(defaultValue);
} else {
if (param.options.isExpressionForEntities()) {
param.set(defaultValue);
}
if (initData.raw_input != null) {
param.set(initData.raw_input);
} else {
if (initData.simple_data != null) {
param.set(initData.simple_data);
} else {
if (initData.complex_data != null) {
const raw_input = initData.complex_data.raw_input;
if (raw_input) {
param.set(raw_input);
} else {
param.set(defaultValue);
}
const overriden_options = initData.complex_data.overriden_options;
if (overriden_options != null) {
const keys = Object.keys(overriden_options);
for (const key of keys) {
param.options.setOption(key, overriden_options[key]);
}
}
}
}
}
}
param.postOptionsInitialize();
param._setupNodeDependencies(this.node);
this._paramsByName.set(param.name(), param);
if (param.isMultiple() && param.components) {
for (const component of param.components) {
this._paramsByName.set(component.name(), component);
}
}
this._params_added_since_last_params_eval = true;
param.scene().graphNodesController.notifyParamPathChanged(param);
return param;
}
}
_updateCaches() {
this._paramsList.splice(0, this._paramsList.length);
this._paramsByName.forEach((param) => {
this._paramsList.push(param);
});
this._paramNames = this._paramsList.map((param) => param.name());
this._non_spare_params = this._paramsList.filter((p) => !p.options.isSpare());
this._spare_params = this._paramsList.filter((p) => p.options.isSpare());
this._non_spare_param_names = this._non_spare_params.map((p) => p.name());
this._spare_param_names = this._spare_params.map((p) => p.name());
}
async _evalParam(param) {
if (param.isDirty()) {
await param.compute();
if (param.states.error.active() && param.disposed() == false) {
this.node.states.error.set(`param '${param.name()}' error: ${param.states.error.message()}`);
}
}
}
async evalParams(params) {
let dirtyParamsCount = 0;
for (const param of params) {
if (param.isDirty()) {
dirtyParamsCount += 1;
}
}
this._promises.length = dirtyParamsCount;
let i = 0;
for (const param of params) {
if (param.isDirty()) {
this._promises[i] = this._evalParam(param);
i += 1;
}
}
await Promise.all(this._promises);
if (this.node.states.error.active()) {
this.node._setContainer(null);
}
}
paramsEvalRequired() {
return this._params_node != null && (this._params_node.isDirty() || this._params_added_since_last_params_eval);
}
async evalAll() {
var _a;
if (this.paramsEvalRequired()) {
await this.evalParams(this._paramsList);
(_a = this._params_node) == null ? void 0 : _a.removeDirtyState();
this._params_added_since_last_params_eval = false;
}
}
//
//
// HOOKS
//
//
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)) {
Poly.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);
}
}
addOnSceneLoadHook(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 {
Poly.warn(`hook with name ${param_name} already exists`, this.node);
}
}
_runPostCreateParamsHooks() {
if (this._post_create_params_hooks) {
for (const hook of this._post_create_params_hooks) {
hook();
}
}
}
runOnSceneLoadHooks() {
if (this._on_scene_load_hooks) {
for (const hook of this._on_scene_load_hooks) {
hook();
}
}
}
}