@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
612 lines (611 loc) • 21.3 kB
JavaScript
"use strict";
import { LineType } from "../utils/LineType";
import { VariableConfig } from "../configs/VariableConfig";
import { JsCodeBuilder } from "../utils/CodeBuilder";
import { TypedJsNode } from "../../_Base";
import { JsShaderConfig } from "../configs/ShaderConfig";
import { TypedAssembler } from "../../../utils/shaders/BaseAssembler";
import { JsFunctionName } from "../../../utils/shaders/ShaderName";
import { TypedNodeTraverser } from "../../../utils/shaders/NodeTraverser";
import { JsNodeFinder } from "../utils/NodeFinder";
import { JsType } from "../../../../poly/registers/nodes/types/Js";
import { SopType } from "../../../../poly/registers/nodes/types/Sop";
export const INSERT_MEMBERS_AFTER = "// insert members";
export const INSERT_DEFINE_AFTER = "// insert defines";
export const INSERT_CONSTRUCTOR_AFTER = "// insert after constructor";
export const INSERT_BODY_AFTER = "// insert body";
const INSERT_MEMBER_AFTER_MAP = /* @__PURE__ */ new Map([
// [ShaderName.VERTEX, '#include <common>'],
[JsFunctionName.MAIN, INSERT_MEMBERS_AFTER],
[JsFunctionName.VELOCITY, INSERT_MEMBERS_AFTER],
[JsFunctionName.COLLIDER, INSERT_MEMBERS_AFTER]
]);
const INSERT_DEFINE_AFTER_MAP = /* @__PURE__ */ new Map([
// [ShaderName.VERTEX, '#include <common>'],
[JsFunctionName.MAIN, INSERT_DEFINE_AFTER],
[JsFunctionName.VELOCITY, INSERT_DEFINE_AFTER],
[JsFunctionName.COLLIDER, INSERT_DEFINE_AFTER]
]);
const INSERT_CONSTRUCTOR_AFTER_MAP = /* @__PURE__ */ new Map([
// [ShaderName.VERTEX, '#include <common>'],
[JsFunctionName.MAIN, INSERT_CONSTRUCTOR_AFTER],
[JsFunctionName.VELOCITY, INSERT_CONSTRUCTOR_AFTER],
[JsFunctionName.COLLIDER, INSERT_CONSTRUCTOR_AFTER]
]);
const INSERT_BODY_AFTER_MAP = /* @__PURE__ */ new Map([
// [ShaderName.VERTEX, '#include <color_vertex>'],
[JsFunctionName.MAIN, INSERT_BODY_AFTER],
[JsFunctionName.VELOCITY, INSERT_BODY_AFTER],
[JsFunctionName.COLLIDER, INSERT_BODY_AFTER]
]);
const LINES_TO_REMOVE_MAP = /* @__PURE__ */ new Map([
// [ShaderName.VERTEX, ['#include <begin_vertex>', '#include <beginnormal_vertex>']],
[JsFunctionName.MAIN, []],
[JsFunctionName.VELOCITY, []],
[JsFunctionName.COLLIDER, []]
]);
const SPACED_LINES = 3;
const PER_POINT_PARENT_TYPES = /* @__PURE__ */ new Set([SopType.ACTOR_INSTANCE, SopType.ACTOR_POINT]);
export class BaseJsShaderAssembler extends TypedAssembler {
constructor(_jsParentNode) {
super();
this._jsParentNode = _jsParentNode;
this._shaders_by_name = /* @__PURE__ */ new Map();
this._lines = /* @__PURE__ */ new Map();
this._root_nodes = [];
this._leaf_nodes = [];
this._uniformsTimeDependent = false;
this._uniformsResolutionDependent = false;
this._computedVarNames = /* @__PURE__ */ new Set();
//
//
// REGISTERED VARIABLES
//
//
this._registeredVariables = /* @__PURE__ */ new Map();
this._registeredVariablesCountByNode = /* @__PURE__ */ new Map();
//
//
// REGISTERED FUNCTIONS
//
//
this._registeredFunctions = /* @__PURE__ */ new Map();
}
perPoint() {
return PER_POINT_PARENT_TYPES.has(this._jsParentNode.type());
}
setJsParentNode(parentNode) {
this._overridenJsParentNode = parentNode;
}
currentJsParentNode() {
return this._overridenJsParentNode || this._jsParentNode;
}
addComputedVarName(varName) {
if (!this.computedVariablesAllowed()) {
return;
}
this._computedVarNames.add(varName);
}
registeredAsComputed(varName) {
if (varName.trim().length == 0) {
console.warn(`attempt to read an empty variable ('${varName}')`);
}
return this._computedVarNames.has(varName);
}
computedVariablesAllowed() {
return false;
}
memberReference(varName) {
if (this.computedVariablesAllowed()) {
return `this.${varName}.value`;
} else {
return `this.${varName}`;
}
}
compile() {
}
// abstract defaultEntityIndexVariable(): string;
// private get material() {
// return (this._material = this._material || this._createMaterial());
// }
// async get_material(/*master_assembler?: BaseGlShaderAssembler*/) {
// this._material = this._material || this._createMaterial();
// await this._update_material(/*master_assembler*/);
// return this._material;
// }
_template_shader_for_shader_name(shaderName) {
var _a, _b, _c;
switch (shaderName) {
case JsFunctionName.MAIN:
return (_a = this.templateShader()) == null ? void 0 : _a.main;
case JsFunctionName.VELOCITY:
return (_b = this.templateShader()) == null ? void 0 : _b.velocity;
case JsFunctionName.COLLIDER:
return (_c = this.templateShader()) == null ? void 0 : _c.collider;
}
}
globalsHandler() {
var _a;
return (_a = this.currentJsParentNode().assemblerController()) == null ? void 0 : _a.globalsHandler();
}
compileAllowed() {
var _a;
return ((_a = this.currentJsParentNode().assemblerController()) == null ? void 0 : _a.globalsHandler()) != null;
}
shaders_by_name() {
return this._shaders_by_name;
}
// protected createMaterial(): ShaderMaterial | undefined {
// return undefined;
// }
_buildLines() {
for (const shaderName of this.shaderNames()) {
const template = this._template_shader_for_shader_name(shaderName);
if (template) {
this._replaceTemplate(template, shaderName);
}
}
}
// protected _build_lines_for_shader_name(shader_name: ShaderName){
// const template = this._template_shader()
// this._replace_template(template[`${shader_name}Shader`], shader_name)
// }
set_root_nodes(root_nodes) {
this._root_nodes = root_nodes;
}
templateShader() {
return void 0;
}
_reset() {
this._resetRegisteredFunctions();
this._resetRegisteredVariables();
this._computedVarNames.clear();
}
updateFunction() {
this._reset();
}
// protected addUniforms(uniforms: IUniforms) {
// for (let param_config of this.param_configs()) {
// uniforms[param_config.uniformName()] = param_config.uniform();
// }
// if (this.uniformsTimeDependent()) {
// uniforms[UniformName.TIME] = uniforms[UniformName.TIME] || {
// // type: '1f',
// value: this.currentGlParentNode().scene().time(),
// };
// }
// if (this.uniformsResolutionDependent()) {
// uniforms[UniformName.RESOLUTION] = uniforms[UniformName.RESOLUTION] || {
// value: new Vector2(1000, 1000),
// };
// }
// }
//
//
// ROOT NODES AND SHADER NAMES
//
//
rootNodesByShaderName(shaderName, rootNodes) {
const list = [];
for (const node of rootNodes) {
switch (node.type()) {
case JsType.PARAM:
case JsType.OUTPUT: {
list.push(node);
break;
}
case JsType.OUTPUT_AMBIENT_LIGHT:
case JsType.OUTPUT_AREA_LIGHT:
case JsType.OUTPUT_DIRECTIONAL_LIGHT:
case JsType.OUTPUT_HEMISPHERE_LIGHT:
case JsType.OUTPUT_POINT_LIGHT:
case JsType.OUTPUT_SPOT_LIGHT: {
list.push(node);
break;
}
case JsType.ATTRIBUTE: {
list.push(node);
break;
}
}
}
return list;
}
// leafNodesByShaderName(shaderName: ShaderName): BaseGlNodeType[] {
// const list = [];
// for (let node of this._leaf_nodes) {
// switch (node.type()) {
// case GlobalsGlNode.type(): {
// list.push(node);
// break;
// }
// case AttributeGlNode.type(): {
// break;
// }
// }
// }
// return list;
// }
setNodeLinesGlobals(globalsNode, linesController) {
}
setNodeLinesOutput(outputNode, linesController) {
}
setNodeLinesAttribute(attributeNode, linesController) {
}
//
//
// CHILDREN NODES PARAMS
//
//
codeBuilder() {
return this._codeBuilder = this._codeBuilder || this._createCodeBuilder();
}
_createCodeBuilder() {
var _a, _b;
const computedVariablesAllowed = (_b = (_a = this._jsParentNode.assemblerController()) == null ? void 0 : _a.assembler) == null ? void 0 : _b.computedVariablesAllowed();
const nodeTraverser = new TypedNodeTraverser(
this.currentJsParentNode(),
this.shaderNames(),
(rootNode, shaderName) => {
return this.inputNamesForShaderName(rootNode, shaderName);
},
{
// we do traverse children if computed(()=>{}) is used.
// If not, we don't need to traverse children,
// and the code is handled by the subnet internal code builder
traverseChildren: computedVariablesAllowed == true ? true : false
}
);
return new JsCodeBuilder(
nodeTraverser,
(shaderName, rootNodes) => {
return this.rootNodesByShaderName(shaderName, rootNodes);
},
this
);
}
buildCodeFromNodes(rootNodes, codeBuilderOptions) {
const paramNodes = JsNodeFinder.findParamGeneratingNodes(this.currentJsParentNode());
this.codeBuilder().buildFromNodes(rootNodes, paramNodes, codeBuilderOptions);
}
allow_new_param_configs() {
this.codeBuilder().allow_new_param_configs();
}
disallow_new_param_configs() {
this.codeBuilder().disallow_new_param_configs();
}
builder_param_configs() {
return this.codeBuilder().param_configs();
}
builder_lines(shader_name, line_type) {
return this.codeBuilder().lines(shader_name, line_type);
}
all_builder_lines() {
return this.codeBuilder().all_lines();
}
param_configs() {
const code_builder = this._param_config_owner || this.codeBuilder();
return code_builder.param_configs();
}
set_param_configs_owner(param_config_owner) {
this._param_config_owner = param_config_owner;
if (this._param_config_owner) {
this.codeBuilder().disallow_new_param_configs();
} else {
this.codeBuilder().allow_new_param_configs();
}
}
//
//
// CHILDREN NODES PARAMS
//
//
static output_input_connection_points() {
return [
// new JsConnectionPoint('position', JsConnectionPointType.VEC3),
// new JsConnectionPoint('normal', JsConnectionPointType.VEC3),
// new JsConnectionPoint('color', JsConnectionPointType.VEC3),
// new JsConnectionPoint('alpha', JsConnectionPointType.FLOAT),
// new JsConnectionPoint('uv', JsConnectionPointType.VEC2),
];
}
add_output_inputs(output_child) {
output_child.io.inputs.setNamedInputConnectionPoints(BaseJsShaderAssembler.output_input_connection_points());
}
static create_globals_node_output_connections() {
return [
// new JsConnectionPoint('position', JsConnectionPointType.VEC3),
// new JsConnectionPoint('normal', JsConnectionPointType.VEC3),
// new JsConnectionPoint('color', JsConnectionPointType.VEC3),
// new JsConnectionPoint('uv', JsConnectionPointType.VEC2),
// new JsConnectionPoint(GlobalsOutput.MV_POSITION, JsConnectionPointType.VEC4),
// // Maybe I should not add worldPosition, worldNormal, I just now
// // as those could add computation overhead when always present in the shader.
// // But hopefully in the soon future, they will only be added when the code builder
// // adds lines based on connections, as opposed to the whole node
// new JsConnectionPoint('worldPosition', JsConnectionPointType.VEC4), // vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
// new JsConnectionPoint('worldNormal', JsConnectionPointType.VEC3), // vec3 worldNormal = normalize( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );
// // new GlConnectionPoint('I', GlConnectionPointType.VEC3), // vec3 I = worldPosition.xyz - cameraPosition;
// new JsConnectionPoint(GlobalsOutput.GL_POSITION, JsConnectionPointType.VEC4),
// new JsConnectionPoint(GlobalsOutput.GL_FRAGCOORD, JsConnectionPointType.VEC4),
// new JsConnectionPoint('cameraPosition', JsConnectionPointType.VEC3),
// new JsConnectionPoint(GlobalsOutput.RESOLUTION, JsConnectionPointType.VEC2),
// new JsConnectionPoint(GlobalsOutput.TIME, JsConnectionPointType.FLOAT),
];
}
create_globals_node_output_connections() {
return BaseJsShaderAssembler.create_globals_node_output_connections();
}
add_globals_outputs(globals_node) {
globals_node.io.outputs.setNamedOutputConnectionPoints(this.create_globals_node_output_connections());
}
allow_attribute_exports() {
return false;
}
//
//
// CONFIGS
//
//
resetConfigs() {
this._reset_shader_configs();
this._reset_variable_configs();
this._resetUniformsTimeDependency();
this._resetUniformsResolutionDependency();
}
shaderConfigs() {
return this._shader_configs = this._shader_configs || this.create_shader_configs();
}
set_shader_configs(shader_configs) {
this._shader_configs = shader_configs;
}
shaderNames() {
var _a;
return ((_a = this.shaderConfigs()) == null ? void 0 : _a.map((sc) => sc.name())) || [];
}
_reset_shader_configs() {
this._shader_configs = void 0;
}
create_shader_configs() {
return [
// new ShaderConfig(ShaderName.VERTEX, ['position', 'normal', 'uv', VaryingWriteGlNode.INPUT_NAME], []),
new JsShaderConfig(JsFunctionName.MAIN, ["color", "alpha"], [])
];
}
shader_config(name) {
var _a;
return (_a = this.shaderConfigs()) == null ? void 0 : _a.filter((sc) => {
return sc.name() == name;
})[0];
}
variable_configs() {
return this._variable_configs = this._variable_configs || this.create_variable_configs();
}
set_variable_configs(variable_configs) {
this._variable_configs = variable_configs;
}
variable_config(name) {
return this.variable_configs().filter((vc) => {
return vc.name() == name;
})[0];
}
static create_variable_configs() {
return [
new VariableConfig("d", {
// default_from_attribute: true,
// default: this.globalsHandler().variable_config_default('position'),
// required_definitions: this.globalsHandler().variable_config_required_definitions('position'),
prefix: "return "
})
// new VariableConfig('normal', {
// default_from_attribute: true,
// prefix: 'vec3 objectNormal = ',
// postLines: ['#ifdef USE_TANGENT', ' vec3 objectTangent = vec3( tangent.xyz );', '#endif'],
// }),
// new VariableConfig('color', {
// prefix: 'diffuseColor.xyz = ',
// }),
// new VariableConfig('alpha', {
// prefix: 'diffuseColor.a = ',
// }),
// new VariableConfig('uv', {
// // default_from_attribute: true,
// prefix: 'vUv = ',
// // if: GlobalsGeometryHandler.IF_RULE.uv,
// }),
];
}
create_variable_configs() {
return BaseJsShaderAssembler.create_variable_configs();
}
_reset_variable_configs() {
this._variable_configs = void 0;
this.variable_configs();
}
inputNamesForShaderName(rootNode, shaderName) {
var _a;
if (shaderName == JsFunctionName.MAIN) {
return (rootNode.io.inputs.namedInputConnectionPoints() || []).map((c) => c.name());
} else {
return ((_a = this.shader_config(shaderName)) == null ? void 0 : _a.input_names()) || [];
}
}
// time dependency
_resetUniformsTimeDependency() {
this._uniformsTimeDependent = false;
}
setUniformsTimeDependent() {
this._uniformsTimeDependent = true;
}
uniformsTimeDependent() {
return this._uniformsTimeDependent;
}
// resolution dependency
_resetUniformsResolutionDependency() {
this._uniformsResolutionDependent = false;
}
setUniformsResolutionDependent() {
this._uniformsResolutionDependent = true;
}
uniformsResolutionDependent() {
return this._uniformsResolutionDependent;
}
_raymarchingLightsWorldCoordsDependent() {
return false;
}
//
//
// TEMPLATE HOOKS
//
//
insertMemberAfter(shaderName) {
return INSERT_MEMBER_AFTER_MAP.get(shaderName);
}
insertDefineAfter(shaderName) {
return INSERT_DEFINE_AFTER_MAP.get(shaderName);
}
insertConstructorAfter(shaderName) {
return INSERT_CONSTRUCTOR_AFTER_MAP.get(shaderName);
}
insertBodyAfter(shaderName) {
return INSERT_BODY_AFTER_MAP.get(shaderName);
}
// protected insertTriggerAfter(shaderName: ShaderName): string | undefined {
// return INSERT_TRIGGER_AFTER_MAP.get(shaderName);
// }
// protected insertTriggerableAfter(shaderName: ShaderName): string | undefined {
// return INSERT_TRIGGERABLE_AFTER_MAP.get(shaderName);
// }
linesToRemove(shaderName) {
return LINES_TO_REMOVE_MAP.get(shaderName);
}
//
//
// TEMPLATE CODE REPLACEMENT
//
//
_replaceTemplate(template, shaderName) {
const memberLines = this.builder_lines(shaderName, LineType.MEMBER);
const constructorLines = this.builder_lines(shaderName, LineType.CONSTRUCTOR);
const defineLines = this.builder_lines(shaderName, LineType.DEFINE);
const body = this.builder_lines(shaderName, LineType.BODY);
let templateLines = template.split("\n");
const newLines = [
// `#define FPS ${ThreeToGl.float(scene.time_controller.fps)}`,
// `#define TIME_INCREMENT (1.0/${ThreeToGl.float(scene.time_controller.fps)})`,
// `#define FRAME_RANGE_START ${ThreeToGl.float(scene.time_controller.frame_range[0])}`,
// `#define FRAME_RANGE_END ${ThreeToGl.float(scene.time_controller.frame_range[1])}`,
];
const lineBeforeMember = this.insertMemberAfter(shaderName);
const lineBeforeDefine = this.insertDefineAfter(shaderName);
const lineBeforeConstructor = this.insertConstructorAfter(shaderName);
const lineBeforeBody = this.insertBodyAfter(shaderName);
const linesToRemove = this.linesToRemove(shaderName);
let lineBeforeMemberFound = false;
let lineBeforeDefineFound = false;
let lineBeforeConstructorFound = false;
let lineBeforeBodyFoundOnPreviousLine = false;
let lineBeforeBodyFound = false;
for (const templateLine of templateLines) {
if (lineBeforeMemberFound == true) {
if (memberLines) {
this._insertLines(newLines, memberLines);
}
lineBeforeMemberFound = false;
}
if (lineBeforeDefineFound == true) {
if (defineLines) {
this._insertLines(newLines, defineLines);
}
lineBeforeDefineFound = false;
}
if (lineBeforeConstructorFound == true) {
if (constructorLines) {
this._insertLines(newLines, constructorLines);
}
lineBeforeConstructorFound = false;
}
if (lineBeforeBodyFoundOnPreviousLine == true) {
if (body) {
this._insertLines(newLines, body);
}
lineBeforeBodyFoundOnPreviousLine = false;
}
let line_remove_required = false;
if (linesToRemove) {
for (const line_to_remove of linesToRemove) {
if (templateLine.indexOf(line_to_remove) >= 0) {
line_remove_required = true;
}
}
}
if (!line_remove_required) {
newLines.push(templateLine);
} else {
newLines.push("// removed:");
newLines.push(`//${templateLine}`);
}
if (lineBeforeDefine && templateLine.indexOf(lineBeforeDefine) >= 0) {
lineBeforeDefineFound = true;
}
if (lineBeforeConstructor && templateLine.indexOf(lineBeforeConstructor) >= 0) {
lineBeforeConstructorFound = true;
}
if (lineBeforeMember && templateLine.indexOf(lineBeforeMember) >= 0) {
lineBeforeMemberFound = true;
}
if (lineBeforeBody && templateLine.indexOf(lineBeforeBody) >= 0) {
lineBeforeBodyFoundOnPreviousLine = true;
lineBeforeBodyFound = true;
}
}
if (lineBeforeBody) {
if (!lineBeforeBodyFound) {
console.warn(`line '${lineBeforeBody}' was not found in shader '${shaderName}'`, template, this);
} else {
}
}
this._lines.set(shaderName, newLines);
}
_insertLines(newLines, linesToAdd) {
if (linesToAdd.length == 0) {
return;
}
for (let i = 0; i < SPACED_LINES; i++) {
newLines.push("");
}
for (const lineToAdd of linesToAdd) {
newLines.push(lineToAdd);
}
for (let i = 0; i < SPACED_LINES; i++) {
newLines.push("");
}
}
addVariable(node, variable, varName) {
const count = this._registeredVariablesCountByNode.get(node) || 0;
this._registeredVariablesCountByNode.set(node, count + 1);
const varFullName = varName ? varName : "VAR_" + TypedJsNode.inputVarName(node, count == 0 ? "" : `_${count}`);
this._registeredVariables.set(varFullName, variable);
return varFullName;
}
traverseRegisteredVariables(callback) {
this._registeredVariables.forEach(callback);
}
_resetRegisteredVariables() {
this._registeredVariables.clear();
this._registeredVariablesCountByNode.clear();
}
addFunction(node, namedFunction) {
const existingFunctionName = this._registeredFunctions.get(namedFunction.type());
if (existingFunctionName) {
return;
}
this._registeredFunctions.set(namedFunction.type(), namedFunction);
}
traverseRegisteredFunctions(callback) {
this._registeredFunctions.forEach(callback);
}
_resetRegisteredFunctions() {
this._registeredFunctions.clear();
}
}