@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
425 lines (424 loc) • 17.1 kB
JavaScript
"use strict";
import { BaseGlShaderAssembler } from "../_Base";
import { ThreeToGl } from "../../../../../../core/ThreeToGl";
import { ShaderName } from "../../../../utils/shaders/ShaderName";
import { UniformGLDefinition, VaryingGLDefinition } from "../../../utils/GLDefinition";
import { GlConnectionPointType } from "../../../../utils/io/connections/Gl";
import {
pushOnArrayAtEntry
} from "../../../../../../core/MapUtils";
import { Material } from "three";
import { GlNodeFinder } from "../../utils/NodeFinder";
import { assignOnBeforeCompileDataAndFunction } from "./OnBeforeCompile";
import { GlobalsOutput } from "./common/GlobalOutput";
const FRAGMENT_GLOBALS_OUTPUT = [
/*GlobalsOutput.GL_POSITION,*/
GlobalsOutput.GL_FRAGCOORD,
GlobalsOutput.GL_POINTCOORD
];
const COMPILE_CUSTOM_MATERIALS = true;
export class ShaderAssemblerMaterial extends BaseGlShaderAssembler {
constructor() {
super(...arguments);
this._assemblersByCustomName = /* @__PURE__ */ new Map();
this._filterFragmentShaderCallbacks = /* @__PURE__ */ new Map();
// private _additionalUniformNames:string[]=[]
// setAdditionalUniformNames(uniformNames:string[]){
// this._additionalUniformNames = [...uniformNames]
// }
this._additionalTextureUniforms = {};
}
createMaterial() {
return new Material();
}
customAssemblerClassByCustomName() {
return void 0;
}
traverseCustomAssemblers(callback) {
this._assemblersByCustomName.forEach(callback);
}
_addCustomMaterials(material) {
const map = this.customAssemblerClassByCustomName();
if (map) {
map.forEach((assemblerClass, customName) => {
this._addCustomMaterial(material, customName, assemblerClass);
});
}
}
_addCustomMaterial(material, customName, assemblerClass) {
material.customMaterials = material.customMaterials || {};
const matNode = this.currentGlParentNode();
const matBuilderNode = matNode;
if (matBuilderNode.customMaterialRequested && matBuilderNode.customMaterialRequested(customName) == false) {
delete material.customMaterials[customName];
return;
}
let customAssembler = this._assemblersByCustomName.get(customName);
if (!customAssembler) {
customAssembler = new assemblerClass(this.currentGlParentNode());
this._assemblersByCustomName.set(customName, customAssembler);
}
const mat = customAssembler.createMaterial();
mat.name = customName;
material.customMaterials[customName] = mat;
}
compileCustomMaterials(material, parentAssembler) {
const matNode = this.currentGlParentNode();
const matBuilderNode = matNode;
const class_by_custom_name = this.customAssemblerClassByCustomName();
if (class_by_custom_name) {
class_by_custom_name.forEach(
(assemblerClass, customName) => {
if (matBuilderNode.customMaterialRequested && matBuilderNode.customMaterialRequested(customName) == false) {
delete material.customMaterials[customName];
return;
} else {
if (!(customName in material.customMaterials)) {
this._addCustomMaterial(material, customName, assemblerClass);
}
}
if (this._codeBuilder) {
let assembler = this._assemblersByCustomName.get(customName);
if (!assembler) {
assembler = new assemblerClass(this.currentGlParentNode());
this._assemblersByCustomName.set(customName, assembler);
}
assembler._setAdditionalTextureUniforms(this._additionalTextureUniforms);
assembler.set_root_nodes(this._root_nodes);
assembler.setGlParentNode(parentAssembler.currentGlParentNode());
assembler.set_param_configs_owner(this._codeBuilder);
assembler.set_shader_configs(this.shaderConfigs());
assembler.set_variable_configs(this.variable_configs());
const custom_material = material.customMaterials[customName];
if (custom_material) {
assembler.setFilterFragmentShaderMethodOwner(this);
assembler.compileMaterial(custom_material);
assembler.setFilterFragmentShaderMethodOwner(void 0);
}
}
}
);
}
}
_resetFilterFragmentShaderCallbacks() {
this._filterFragmentShaderCallbacks.clear();
}
_addFilterFragmentShaderCallback(callbackName, callback) {
this._filterFragmentShaderCallbacks.set(callbackName, callback);
}
_removeFilterFragmentShaderCallback(callbackName) {
this._filterFragmentShaderCallbacks.delete(callbackName);
}
setFilterFragmentShaderMethodOwner(owner) {
this._filterFragmentShaderMethodOwner = owner;
}
filterFragmentShader(fragmentShader) {
this._filterFragmentShaderCallbacks.forEach((callback, callbackName) => {
fragmentShader = callback(fragmentShader);
});
return fragmentShader;
}
processFilterFragmentShader(fragmentShader) {
if (this._filterFragmentShaderMethodOwner) {
return this._filterFragmentShaderMethodOwner.filterFragmentShader(fragmentShader);
} else {
return this.filterFragmentShader(fragmentShader);
}
}
onBeforeCompileData() {
return this._onBeforeCompileData;
}
clearAdditionalTextureUniforms() {
this._additionalTextureUniforms = {};
}
addAdditionalTextureUniforms(uniformName, uniform) {
this._additionalTextureUniforms[uniformName] = uniform;
}
_setAdditionalTextureUniforms(uniforms) {
this.clearAdditionalTextureUniforms();
const uniformNames = Object.keys(uniforms);
for (const uniformName of uniformNames) {
this._additionalTextureUniforms[uniformName] = uniforms[uniformName];
}
}
compileMaterial(material, codeBuilderOptions) {
if (!this.compileAllowed()) {
return;
}
const outputNodes = GlNodeFinder.findOutputNodes(this.currentGlParentNode());
if (outputNodes.length == 0) {
this.currentGlParentNode().states.error.set("one output node is required");
}
if (outputNodes.length > 1) {
this.currentGlParentNode().states.error.set("only one output node allowed");
}
const varyingNodes = GlNodeFinder.findVaryingNodes(this.currentGlParentNode());
const rootNodes = outputNodes.concat(varyingNodes);
this.set_root_nodes(rootNodes);
this.updateShaders(codeBuilderOptions);
this.prepareOnBeforeCompileData(material);
if (COMPILE_CUSTOM_MATERIALS) {
if (material.customMaterials) {
this.compileCustomMaterials(material, this);
}
}
}
prepareOnBeforeCompileData(material) {
const scene = this.currentGlParentNode().scene();
const vertexShader = this._shaders_by_name.get(ShaderName.VERTEX);
const fragmentShader = this._shaders_by_name.get(ShaderName.FRAGMENT);
if (vertexShader && fragmentShader) {
const processedFragmentShader = this.processFilterFragmentShader(fragmentShader);
this._onBeforeCompileData = {
vertexShader,
fragmentShader: processedFragmentShader,
paramConfigs: this.param_configs(),
additionalTextureUniforms: this._additionalTextureUniforms,
timeDependent: this.uniformsTimeDependent(),
resolutionDependent: this.uniformsResolutionDependent(),
raymarchingLightsWorldCoordsDependent: this._raymarchingLightsWorldCoordsDependent()
};
assignOnBeforeCompileDataAndFunction(scene, material, this._onBeforeCompileData);
material.needsUpdate = true;
}
}
updateShaders(codeBuilderOptions) {
this._shaders_by_name.clear();
this._lines.clear();
for (const shaderName of this.shaderNames()) {
const template = this._template_shader_for_shader_name(shaderName);
if (template) {
this._lines.set(shaderName, template.split("\n"));
}
}
if (this._root_nodes.length > 0) {
this.buildCodeFromNodes(this._root_nodes, codeBuilderOptions);
this._buildLines();
}
for (const shader_name of this.shaderNames()) {
const lines = this._lines.get(shader_name);
if (lines) {
this._shaders_by_name.set(shader_name, lines.join("\n"));
}
}
}
shadow_assembler_class_by_custom_name() {
return {};
}
add_output_body_line(output_node, shaders_collection_controller, input_name) {
var _a;
const input = output_node.io.inputs.named_input(input_name);
const var_input = output_node.variableForInput(input_name);
const variable_config = this.variable_config(input_name);
let new_var = null;
if (input) {
new_var = ThreeToGl.vector3(var_input);
} else {
if (variable_config.default_from_attribute()) {
const connection_point = output_node.io.inputs.namedInputConnectionPointsByName(input_name);
if (connection_point) {
const gl_type = connection_point.type();
const attr_read = (_a = this.globalsHandler()) == null ? void 0 : _a.readAttribute(
output_node,
gl_type,
input_name,
shaders_collection_controller
);
if (attr_read) {
new_var = attr_read;
}
}
} else {
const variable_config_default = variable_config.default();
if (variable_config_default) {
new_var = variable_config_default;
}
}
}
if (new_var) {
const prefix = variable_config.prefix();
const suffix = variable_config.suffix();
const if_condition = variable_config.if_condition();
if (if_condition) {
shaders_collection_controller.addBodyLines(output_node, [`#if ${if_condition}`]);
}
shaders_collection_controller.addBodyLines(output_node, [`${prefix}${new_var}${suffix}`]);
const postLines = variable_config.postLines();
if (postLines) {
shaders_collection_controller.addBodyLines(output_node, postLines);
}
if (if_condition) {
shaders_collection_controller.addBodyLines(output_node, [`#endif`]);
}
}
}
set_node_lines_output(output_node, shadersCollectionController) {
var _a;
const shader_name = shadersCollectionController.currentShaderName();
const input_names = (_a = this.shader_config(shader_name)) == null ? void 0 : _a.input_names();
if (input_names) {
for (const input_name of input_names) {
if (output_node.io.inputs.has_named_input(input_name)) {
this.add_output_body_line(output_node, shadersCollectionController, input_name);
}
}
}
}
setNodeLinesAttribute(attribute_node, shadersCollectionController) {
var _a;
const gl_type = attribute_node.glType();
const new_var = (_a = this.globalsHandler()) == null ? void 0 : _a.readAttribute(
attribute_node,
gl_type,
attribute_node.attributeName(),
shadersCollectionController
);
const var_name = attribute_node.glVarName(attribute_node.outputName());
shadersCollectionController.addBodyLines(attribute_node, [`${gl_type} ${var_name} = ${new_var}`]);
}
handle_globals_output_name(options) {
var _a;
switch (options.output_name) {
case GlobalsOutput.TIME:
this.handleTime(options);
return;
case GlobalsOutput.RESOLUTION:
this.handle_resolution(options);
return;
case GlobalsOutput.MV_POSITION:
this.handle_mvPosition(options);
return;
case GlobalsOutput.GL_POSITION:
this.handle_gl_Position(options);
return;
case GlobalsOutput.GL_FRAGCOORD:
this.handle_gl_FragCoord(options);
return;
case GlobalsOutput.GL_POINTCOORD:
this.handle_gl_PointCoord(options);
return;
default:
(_a = this.globalsHandler()) == null ? void 0 : _a.handleGlobalsNode(
options.globals_node,
options.output_name,
options.shaders_collection_controller
);
}
}
handleTime(options) {
const definition = new UniformGLDefinition(
options.globals_node,
GlConnectionPointType.FLOAT,
options.output_name
);
if (options.globals_shader_name) {
pushOnArrayAtEntry(options.definitions_by_shader_name, options.globals_shader_name, definition);
}
const body_line = `float ${options.var_name} = ${options.output_name}`;
for (const dependency of options.dependencies) {
pushOnArrayAtEntry(options.definitions_by_shader_name, dependency, definition);
pushOnArrayAtEntry(options.body_lines_by_shader_name, dependency, body_line);
}
options.body_lines.push(body_line);
this.setUniformsTimeDependent();
}
handle_resolution(options) {
options.body_lines.push(`vec2 ${options.var_name} = resolution`);
const definition = new UniformGLDefinition(
options.globals_node,
GlConnectionPointType.VEC2,
options.output_name
);
if (options.globals_shader_name) {
pushOnArrayAtEntry(options.definitions_by_shader_name, options.globals_shader_name, definition);
}
for (const dependency of options.dependencies) {
pushOnArrayAtEntry(options.definitions_by_shader_name, dependency, definition);
}
this.setUniformsResolutionDependent();
}
handle_mvPosition(options) {
if (options.shader_name == ShaderName.FRAGMENT) {
const globals_node = options.globals_node;
const shaders_collection_controller = options.shaders_collection_controller;
const definition = new VaryingGLDefinition(globals_node, GlConnectionPointType.VEC4, options.var_name);
const vertex_body_line = `${options.var_name} = modelViewMatrix * vec4(position, 1.0)`;
shaders_collection_controller.addDefinitions(globals_node, [definition], ShaderName.VERTEX);
shaders_collection_controller.addBodyLines(globals_node, [vertex_body_line], ShaderName.VERTEX);
shaders_collection_controller.addDefinitions(globals_node, [definition]);
}
}
handle_gl_Position(options) {
if (options.shader_name == ShaderName.FRAGMENT) {
const globals_node = options.globals_node;
const shaders_collection_controller = options.shaders_collection_controller;
const definition = new VaryingGLDefinition(globals_node, GlConnectionPointType.VEC4, options.var_name);
const vertex_body_line = `${options.var_name} = projectionMatrix * modelViewMatrix * vec4(position, 1.0)`;
shaders_collection_controller.addDefinitions(globals_node, [definition], ShaderName.VERTEX);
shaders_collection_controller.addBodyLines(globals_node, [vertex_body_line], ShaderName.VERTEX);
shaders_collection_controller.addDefinitions(globals_node, [definition]);
}
}
handle_gl_FragCoord(options) {
if (options.shader_name == ShaderName.FRAGMENT) {
options.body_lines.push(`vec4 ${options.var_name} = gl_FragCoord`);
}
}
handle_gl_PointCoord(options) {
if (options.shader_name == ShaderName.FRAGMENT) {
options.body_lines.push(`vec2 ${options.var_name} = gl_PointCoord`);
} else {
options.body_lines.push(`vec2 ${options.var_name} = vec2(0.0, 0.0)`);
}
}
set_node_lines_globals(globals_node, shaders_collection_controller) {
const body_lines = [];
const shader_name = shaders_collection_controller.currentShaderName();
const shader_config = this.shader_config(shader_name);
if (!shader_config) {
return;
}
const dependencies = shader_config.dependencies();
const definitions_by_shader_name = /* @__PURE__ */ new Map();
const body_lines_by_shader_name = /* @__PURE__ */ new Map();
const used_output_names = this.used_output_names_for_shader(globals_node, shader_name);
for (const output_name of used_output_names) {
const var_name = globals_node.glVarName(output_name);
const globals_shader_name = shaders_collection_controller.currentShaderName();
const options = {
globals_node,
shaders_collection_controller,
output_name,
globals_shader_name,
definitions_by_shader_name,
body_lines,
var_name,
shader_name,
dependencies,
body_lines_by_shader_name
};
this.handle_globals_output_name(options);
}
definitions_by_shader_name.forEach((definitions, shader_name2) => {
shaders_collection_controller.addDefinitions(globals_node, definitions, shader_name2);
});
body_lines_by_shader_name.forEach((body_lines2, shader_name2) => {
shaders_collection_controller.addBodyLines(globals_node, body_lines2, shader_name2);
});
shaders_collection_controller.addBodyLines(globals_node, body_lines);
}
used_output_names_for_shader(globals_node, shader_name) {
const used_output_names = globals_node.io.outputs.used_output_names();
const filtered_names = [];
for (const name of used_output_names) {
if (shader_name == ShaderName.VERTEX) {
if (!FRAGMENT_GLOBALS_OUTPUT.includes(name)) {
filtered_names.push(name);
}
} else {
filtered_names.push(name);
}
}
return filtered_names;
}
}