@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
245 lines (218 loc) • 7.96 kB
text/typescript
/**
* processes input events
*
*
*/
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {JsConnectionPoint, JsConnectionPointType, JS_CONNECTION_POINT_IN_NODE_DEF} from '../utils/io/connections/Js';
// import {ParamType} from '../../poly/ParamType';
// import {JsNodeParamConstructorMap} from './utils/JsNodeInputParam';
// import {ParamValuesTypeMap} from '../../params/types/ParamValuesTypeMap';
import {TRIGGER_CONNECTION_NAME, TypedJsNode} from './_Base';
import {StringParamLanguage} from '../../params/utils/OptionsController';
import {TranspiledFilter} from '../utils/code/controllers/TranspiledFilter';
// import {Poly} from '../../Poly';
import {BaseCodeProcessor, buildCodeNodeFunction} from '../../../core/code/FunctionBuilderUtils';
import {JsLinesCollectionController} from './code/utils/JsLinesCollectionController';
import {inputObject3D} from './_BaseObject3D';
import {JsAssemblerActor} from './code/assemblers/actor/ActorAssembler';
import {JsFunctionName} from '../utils/shaders/ShaderName';
import {JsType} from '../../poly/registers/nodes/types/Js';
import {BaseNodeType} from '../_Base';
// import {BaseNodeType} from '../_Base';
const CONNECTION_OPTIONS = JS_CONNECTION_POINT_IN_NODE_DEF;
interface CompileOptions {
triggerFunctionNode: boolean;
}
export const JS_CODE_DEFAULT_TS = `
export class CodeJsProcessor extends BaseCodeJsProcessor {
override initializeProcessor(){
this.io.inputs.setNamedInputConnectionPoints([
new JsConnectionPoint('myBoolParam', JsConnectionPointType.BOOLEAN),
]);
this.io.outputs.setNamedOutputConnectionPoints([
new JsConnectionPoint(JsConnectionPointType.TRIGGER, JsConnectionPointType.TRIGGER),
]);
}
override setTriggerableLines(controller: JsLinesCollectionController) {
const object3D = this.inputObject3D(this, controller);
const myBoolParam = this.variableForInput(controller, 'myBoolParam');
const bodyLines = [
object3D + '.position.y += ' + myBoolParam + ' ? -1 : 1;',
object3D + '.updateMatrix()'
];
this.addTriggerableLines(controller, bodyLines);
}
}
`;
const DEFAULT_JS = JS_CODE_DEFAULT_TS.replace(/\:\sJsLinesCollectionController/g, '').replace(/override\s/g, '');
export class BaseCodeJsProcessor extends BaseCodeProcessor {
constructor(protected override node: CodeJsNode) {
super(node);
}
get pv() {
return this.node.pv;
}
get p() {
return this.node.p;
}
get io() {
return this.node.io;
}
initializeProcessor() {}
setTriggerableLines(controller: JsLinesCollectionController) {}
inputObject3D(processor: BaseCodeJsProcessor, controller: JsLinesCollectionController) {
return inputObject3D(this.node, controller);
}
protected variableForInput(controller: JsLinesCollectionController, inputName: string) {
return this.node.variableForInput(controller, inputName);
}
protected addTriggerableLines(controller: JsLinesCollectionController, bodyLines: string[]) {
return controller.addTriggerableLines(this.node, bodyLines);
}
}
class CodeJsParamsConfig extends NodeParamsConfig {
// compile = ParamConfig.BUTTON(null, {
// callback: (node: BaseNodeType) => {
// CodeJsNode.PARAM_CALLBACK_compile(node as CodeJsNode);
// },
// cook: false,
// });
codeTypescript = ParamConfig.STRING(JS_CODE_DEFAULT_TS, {
hideLabel: true,
language: StringParamLanguage.TYPESCRIPT,
cook: false,
});
codeJavascript = ParamConfig.STRING(DEFAULT_JS, {
hidden: true,
cook: false,
callback: (node: BaseNodeType) => {
CodeJsNode.PARAM_CALLBACK_requestCompile(node as CodeJsNode);
},
});
}
const ParamsConfig = new CodeJsParamsConfig();
export class CodeJsNode extends TypedJsNode<CodeJsParamsConfig> {
override paramsConfig = ParamsConfig;
// adding BaseCodeJsProcessor seems necessary to have the bundled types include it
static BaseCodeJsProcessor = BaseCodeJsProcessor;
static override type() {
return JsType.CODE;
}
protected _processor: BaseCodeJsProcessor | undefined;
override initializeNode() {
this.io.connection_points.spare_params.setInputlessParamNames(['codeTypescript', 'codeJavascript']);
this.io.inputs.setNamedInputConnectionPoints([
new JsConnectionPoint(TRIGGER_CONNECTION_NAME, JsConnectionPointType.TRIGGER, CONNECTION_OPTIONS),
new JsConnectionPoint(JsConnectionPointType.OBJECT_3D, JsConnectionPointType.OBJECT_3D, CONNECTION_OPTIONS),
]);
this.io.outputs.setNamedOutputConnectionPoints([
new JsConnectionPoint(TRIGGER_CONNECTION_NAME, JsConnectionPointType.TRIGGER),
]);
this.params.onParamsCreated('compile', () => {
this.compile({triggerFunctionNode: false});
});
// this.addPostDirtyHook('compile', () => {
// this.compile({triggerFunctionNode: true});
// });
}
// override cook() {
// // try {
// // }catch(err){
// // }
// this.compile({triggerFunctionNode: false});
// console.log('COOK: this.compiled()', this.compiled());
// // if (this.compiled()) {
// // } // else {
// // this.states.error.set(`cannot generate function`);
// //}
// this.cookController.endCook();
// }
override setTriggerableLines(controller: JsLinesCollectionController) {
this._processor?.setTriggerableLines(controller);
}
private _lastCompiledCode: string | undefined;
private _compilationSuccessful = false;
compiled(): boolean {
return this._compilationSuccessful;
}
compile(options: CompileOptions) {
const content = TranspiledFilter.filter(this.pv.codeJavascript);
if (this._lastCompiledCode == content) {
return;
}
this._compilationSuccessful = false;
this._lastCompiledCode = undefined;
this._processor = undefined;
this.states.error.clear();
const _preventSelfCompilation = () => {
this._setFunctionNodeToRecompileAllowed(false);
// this.dirtyController.setForbiddenTriggerNodes([this, this.io.inputs.graphNode()]);
this.setSelfDirtyForbidden(true);
this.io.inputs.graphNode().setForbiddenTriggerNodes(this);
};
const _restore = () => {
this._setFunctionNodeToRecompileAllowed(true);
// this.dirtyController.setForbiddenTriggerNodes([]);
this.setSelfDirtyForbidden(false);
this.io.inputs.graphNode().clearForbiddenTriggerNodes();
};
try {
const functionBody = `try {
${content}
} catch(e) {
states.error.set(e);
}`;
const ProcessorClass = buildCodeNodeFunction<BaseCodeJsProcessor>({
BaseCodeProcessor: BaseCodeJsProcessor,
BaseCodeProcessorName: 'BaseCodeJsProcessor',
node: this,
functionBody,
otherVariables: {
['JsConnectionPoint']: JsConnectionPoint,
['JsConnectionPointType']: JsConnectionPointType,
},
});
if (!ProcessorClass) {
throw new Error(`cannot generate function`);
// this.states.error.set(`cannot generate function (${e})`);
// return
}
if (ProcessorClass) {
this._processor = new ProcessorClass(this);
_preventSelfCompilation();
this._processor.initializeProcessor();
// test the generated processor
const dummyAssembler = new JsAssemblerActor(this.functionNode()!);
const dummyShadersCollectionController = new JsLinesCollectionController(
[JsFunctionName.MAIN],
JsFunctionName.MAIN,
dummyAssembler
);
this._processor.setTriggerableLines(dummyShadersCollectionController);
_restore();
this._compilationSuccessful = true;
this._lastCompiledCode = content;
this.states.error.clear();
if (options.triggerFunctionNode) {
this._setFunctionNodeToRecompile();
}
} // else {
// this.states.error.set(`cannot generate function`);
// Poly.warn(functionBody);
//}
// console.log('BADDD');
} catch (e) {
_restore();
// Poly.warn(e);
this.states.error.set(`cannot generate function (${e})`);
if (options.triggerFunctionNode) {
this._setFunctionNodeToRecompile();
}
// throw new Error(`cannot generate function`);
}
}
static PARAM_CALLBACK_requestCompile(node: CodeJsNode) {
node.compile({triggerFunctionNode: true});
}
}