@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
180 lines (165 loc) • 6.11 kB
text/typescript
import {
BaseJsShaderAssembler,
INSERT_DEFINE_AFTER,
INSERT_BODY_AFTER,
INSERT_MEMBERS_AFTER,
INSERT_CONSTRUCTOR_AFTER,
SpareParamOptions,
} from '../_Base';
import {RegisterableVariable} from '../_BaseJsPersistedConfigUtils';
import {JsFunctionName} from '../../../../utils/shaders/ShaderName';
import {JsLinesCollectionController} from '../../utils/JsLinesCollectionController';
import {NamedFunctionMap} from '../../../../../poly/registers/functions/All';
import {ParamOptions} from '../../../../../params/utils/OptionsController';
import {AttributeJsNodeInput} from '../../../Attribute';
import {NodeContext} from '../../../../../poly/NodeContext';
import {JsType} from '../../../../../poly/registers/nodes/types/Js';
import {
EntityBuilderFunctionData,
EntityBuilderFunctionDataAttributeDataItem,
} from './_BaseEntityBuilderPersistedConfig';
import {PrettierController} from '../../../../../../core/code/PrettierController';
import {EntityBuilderAssemblerConstant} from './EntityBuilderAssemblerCommon';
import type {AttributeJsNode} from '../../../Attribute';
const TEMPLATE = `
${INSERT_DEFINE_AFTER}
${INSERT_MEMBERS_AFTER}
${INSERT_CONSTRUCTOR_AFTER}
const __EVALUATOR_NAME___ = function(){
${INSERT_BODY_AFTER}
`;
const CLOSE_CLASS_DEFINITION = `};
return __EVALUATOR_NAME___;`;
export class JsAssemblerBaseEntityBuilder extends BaseJsShaderAssembler {
makeFunctionNodeDirtyOnChange() {
return true;
}
override templateShader() {
return {
main: TEMPLATE.replace(/__EVALUATOR_NAME___/g, this._evaluatorName()),
};
}
protected _closeClassDefinition() {
return CLOSE_CLASS_DEFINITION.replace(/__EVALUATOR_NAME___/g, this._evaluatorName());
}
protected _evaluatorName(): string {
return 'BaseEntityBuilderEvaluator';
}
override spareParamsOptions(options: SpareParamOptions) {
const _options: ParamOptions = {
spare: true,
// computeOnDirty: true, // not needed if cook option is not set
// cook: false, // for SDFBuilder, the node needs to recook
// important for texture nodes
// that compute after being found by the nodepath param
dependentOnFoundNode: true,
};
return _options;
}
defaultObjectVariable(): string {
return EntityBuilderAssemblerConstant.OBJECT;
}
defaultObject3DMaterialVariable(): string {
return EntityBuilderAssemblerConstant.MATERIAL;
}
defaultPrimitiveGraph():string {
return EntityBuilderAssemblerConstant.PRIMITIVE_GRAPH;
}
functionData(): EntityBuilderFunctionData | undefined {
// const functionBody = this._shaders_by_name.get(ShaderName.FRAGMENT);
const _buildFunctionBody = () => {
const bodyLines = this._shaders_by_name.get(JsFunctionName.MAIN) || TEMPLATE;
const functionBodyElements = [
bodyLines,
// triggerableFunctionLines.join('\n'),
// triggerFunctionLines.join('\n'),
this._closeClassDefinition(),
];
const functionBody = PrettierController.formatJs(functionBodyElements.join('\n'));
return functionBody;
};
const functionBody = _buildFunctionBody();
if (!functionBody) {
return;
}
const variableNames: string[] = [];
const functionNames: Array<keyof NamedFunctionMap> = [];
const variablesByName: Record<string, RegisterableVariable> = {};
const functionsByName: Record<string, Function> = {};
this.traverseRegisteredVariables((variable, varName) => {
variableNames.push(varName);
variablesByName[varName] = variable;
});
this.traverseRegisteredFunctions((namedFunction) => {
functionNames.push(namedFunction.type() as keyof NamedFunctionMap);
functionsByName[namedFunction.type()] = namedFunction.func.bind(namedFunction);
});
// gather attribute data
const attribNodes: AttributeJsNode[] = [];
this.currentJsParentNode().childrenController?.traverseChildren((child) => {
if (child.context() == NodeContext.JS && child.type() == JsType.ATTRIBUTE) {
attribNodes.push(child as AttributeJsNode);
}
});
const attributesRead: EntityBuilderFunctionDataAttributeDataItem[] = attribNodes
.filter((n) => n.isImporting())
.map((attribNode) => attribNode.attribData());
const attributesWrite: EntityBuilderFunctionDataAttributeDataItem[] = attribNodes
.filter((n) => n.isExporting())
.map((attribNode) => attribNode.attribData());
// const paramConfigs = this.param_configs();
const paramConfigs = this.param_configs();
return {
functionBody,
variableNames,
variablesByName,
functionNames,
functionsByName,
paramConfigs: [...paramConfigs],
attributesData: {
read: attributesRead,
write: attributesWrite,
},
};
}
override updateFunction() {
super.updateFunction();
this._lines = new Map();
this._shaders_by_name = new Map();
const shaderNames = this.shaderNames();
if (this._root_nodes.length > 0) {
this.buildCodeFromNodes(this._root_nodes);
this._buildLines();
}
for (const shaderName of shaderNames) {
const lines = this._lines.get(shaderName);
if (lines) {
this._shaders_by_name.set(shaderName, lines.join('\n'));
}
}
}
override setNodeLinesAttribute(attributeNode: AttributeJsNode, linesController: JsLinesCollectionController) {
const shaderName = linesController.currentShaderName();
const shaderConfig = this.shader_config(shaderName);
if (!shaderConfig) {
return;
}
const bodyLines: string[] = [];
const attribName = attributeNode.attributeName();
// const dataType = attributeNode.jsType();
// export
if (attributeNode.isExporting()) {
const exportedValue = attributeNode.variableForInput(linesController, AttributeJsNodeInput.EXPORT);
const bodyLine = `${EntityBuilderAssemblerConstant.ATTRIBUTES_DICT}.set('${attribName}', ${exportedValue})`;
bodyLines.push(bodyLine);
}
// output
const usedOutputNames = attributeNode.io.outputs.used_output_names();
for (const outputName of usedOutputNames) {
const varName = attributeNode.jsVarName(outputName);
const bodyLine = `${varName} = ${EntityBuilderAssemblerConstant.ATTRIBUTES_DICT}.get('${attribName}')`;
bodyLines.push(bodyLine);
}
linesController._addBodyLines(attributeNode, bodyLines);
}
}