@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
290 lines (289 loc) • 10.8 kB
JavaScript
;
import { TextureAllocation } from "./TextureAllocation";
import { TextureVariable } from "./TextureVariable";
import {
GlConnectionPointComponentsCountMap,
GlConnectionPointType
} from "../../../utils/io/connections/Gl";
import { AttributeGlNode } from "../../Attribute";
import { GlobalsGlNode } from "../../Globals";
import { OutputGlNode } from "../../Output";
import { arrayUniq } from "../../../../../core/ArrayUtils";
import { pushOnArrayAtEntry } from "../../../../../core/MapUtils";
import { GlType } from "../../../../poly/registers/nodes/types/Gl";
const OUTPUT_NAME_ATTRIBUTES = ["position", "normal", "color", "uv"];
export class TextureAllocationsController {
// private _next_allocation_index: number = 0;
constructor() {
this._writableAllocations = [];
this._readonlyAllocations = [];
}
dispose() {
this._writableAllocations.splice(0, this._writableAllocations.length);
this._readonlyAllocations.splice(0, this._readonlyAllocations.length);
}
static _sortNodes(rootNodes) {
const outputNodes = rootNodes.filter((node) => node.type() == OutputGlNode.type());
const sortedRootNodes = outputNodes;
const nonOutputNodes = rootNodes.filter((node) => node.type() != OutputGlNode.type());
const nonOutputNodeNames = nonOutputNodes.map((n) => n.path()).sort();
const nonOutputNodesByName = /* @__PURE__ */ new Map();
for (const node of nonOutputNodes) {
nonOutputNodesByName.set(node.path(), node);
}
for (const nodeName of nonOutputNodeNames) {
const node = nonOutputNodesByName.get(nodeName);
if (node) {
sortedRootNodes.push(node);
}
}
return sortedRootNodes;
}
allocateConnectionsFromRootNodes(rootNodes, leafNodes) {
const variables = [];
rootNodes = TextureAllocationsController._sortNodes(rootNodes);
leafNodes = TextureAllocationsController._sortNodes(leafNodes);
for (const node of rootNodes) {
const node_id = node.graphNodeId();
switch (node.type()) {
case OutputGlNode.type(): {
const connectionPoints = node.io.inputs.namedInputConnectionPoints();
if (connectionPoints) {
for (const connection_point of connectionPoints) {
const input = node.io.inputs.named_input(connection_point.name());
if (input) {
const variable = new TextureVariable(
connection_point.name(),
GlConnectionPointComponentsCountMap[connection_point.type()]
);
variable.addGraphNodeId(node_id);
variables.push(variable);
}
}
}
break;
}
case AttributeGlNode.type(): {
const attrib_node = node;
const named_input = attrib_node.connected_input_node();
const connection_point = attrib_node.connected_input_connection_point();
if (named_input && connection_point) {
const variable = new TextureVariable(
attrib_node.attributeName(),
GlConnectionPointComponentsCountMap[connection_point.type()]
);
variable.addGraphNodeId(node_id);
variables.push(variable);
}
break;
}
case GlType.ADJACENT_POINTS_ATTRIB_SMOOTH: {
const adjacentPointsAttribSmoothNode = node;
const data = adjacentPointsAttribSmoothNode.textureAllocationData();
for (const attribName of data) {
const variable = new TextureVariable(
attribName,
GlConnectionPointComponentsCountMap[GlConnectionPointType.VEC2]
);
variable.setReadonly(true);
variable.addGraphNodeId(node_id);
variables.push(variable);
}
break;
}
}
}
for (const node of leafNodes) {
const node_id = node.graphNodeId();
switch (node.type()) {
case GlobalsGlNode.type(): {
const globals_node = node;
for (const output_name of globals_node.io.outputs.used_output_names()) {
const is_attribute = OUTPUT_NAME_ATTRIBUTES.includes(output_name);
if (is_attribute) {
const connection_point = globals_node.io.outputs.namedOutputConnectionPointsByName(output_name);
if (connection_point) {
const gl_type = connection_point.type();
const variable = new TextureVariable(
output_name,
GlConnectionPointComponentsCountMap[gl_type]
);
variable.addGraphNodeId(node_id);
variables.push(variable);
}
}
}
break;
}
case AttributeGlNode.type(): {
const attribute_node = node;
const connection_point = attribute_node.output_connection_point();
if (connection_point) {
const variable = new TextureVariable(
attribute_node.attributeName(),
GlConnectionPointComponentsCountMap[connection_point.type()]
);
if (!attribute_node.isExporting()) {
variable.setReadonly(true);
}
variable.addGraphNodeId(node_id);
variables.push(variable);
}
break;
}
}
}
this._allocateVariables(variables);
}
_allocateVariables(variables) {
const uniqVariables = this._ensureVariablesAreUnique(variables);
const variablesBySizeInverse = uniqVariables.sort((v0, v1) => {
return v0.size() < v1.size() ? 1 : -1;
});
for (const variable of variablesBySizeInverse) {
if (variable.readonly()) {
this._allocateVariable(variable, this._readonlyAllocations);
} else {
this._allocateVariable(variable, this._writableAllocations);
}
}
}
_ensureVariablesAreUnique(variables) {
const variableByName = /* @__PURE__ */ new Map();
for (const variable of variables) {
pushOnArrayAtEntry(variableByName, variable.name(), variable);
}
const uniqVariables = [];
variableByName.forEach((variablesForName, variableName) => {
const firstVariable = variablesForName[0];
uniqVariables.push(firstVariable);
for (let i = 1; i < variablesForName.length; i++) {
const otherVariable = variablesForName[i];
firstVariable.merge(otherVariable);
}
});
return uniqVariables;
}
_allocateVariable(newVariable, allocations) {
let isAllocated = this.hasVariable(newVariable.name());
if (isAllocated) {
throw "no variable should be allocated since they have been made unique before";
} else {
for (const allocation of allocations) {
if (!isAllocated && allocation.hasSpaceForVariable(newVariable)) {
allocation.addVariable(newVariable);
isAllocated = true;
}
}
if (!isAllocated) {
const newAllocation = new TextureAllocation(
/*this.nextAllocationName()*/
);
allocations.push(newAllocation);
newAllocation.addVariable(newVariable);
}
}
}
_addWritableAllocation(allocation) {
this._writableAllocations.push(allocation);
}
_addReadonlyAllocation(allocation) {
this._readonlyAllocations.push(allocation);
}
readonlyAllocations() {
return this._readonlyAllocations;
}
// private _nextAllocationName(): ShaderName {
// const name = ParticleShaderNames[this._next_allocation_index];
// this._next_allocation_index += 1;
// return name;
// }
shaderNames() {
const explicitShaderNames = this._writableAllocations.map((a) => a.shaderName());
const uniqShaderNames = [];
arrayUniq(explicitShaderNames, uniqShaderNames);
return uniqShaderNames;
}
createShaderConfigs() {
return [
// new ShaderConfig('position', ['position'], []),
// new ShaderConfig('fragment', ['color', 'alpha'], ['vertex']),
];
}
allocationForShaderName(shader_name) {
const writeableAllocation = this._writableAllocations.filter((a) => a.shaderName() == shader_name)[0];
if (writeableAllocation) {
return writeableAllocation;
}
return this._readonlyAllocations.filter((a) => a.shaderName() == shader_name)[0];
}
inputNamesForShaderName(root_node, shader_name) {
const allocation = this.allocationForShaderName(shader_name);
if (allocation) {
return allocation.inputNamesForNode(root_node);
}
}
// find_variable(root_node: BaseNodeGl, shader_name: ShaderName, input_name: string): TextureVariable{
// const allocation = this.allocation_for_shader_name(shader_name)
// if(allocation){
// return allocation.find_variable_with_node(root_node, input_name)
// }
// }
variable(variable_name) {
for (const allocation of this._writableAllocations) {
const variable = allocation.variable(variable_name);
if (variable) {
return variable;
}
}
for (const allocation of this._readonlyAllocations) {
const variable = allocation.variable(variable_name);
if (variable) {
return variable;
}
}
}
variables() {
const writableVariables = this._writableAllocations.map((a) => a.variables() || []).flat();
const readonlyVariables = this._writableAllocations.map((a) => a.variables() || []).flat();
return writableVariables.concat(readonlyVariables);
}
hasVariable(name) {
const names = this.variables().map((v) => v.name());
return names.includes(name);
}
static fromJSON(data) {
const controller = new TextureAllocationsController();
for (const datum of data.writable) {
const shader_name = Object.keys(datum)[0];
const allocation_data = datum[shader_name];
const new_allocation = TextureAllocation.fromJSON(allocation_data);
controller._addWritableAllocation(new_allocation);
}
for (const datum of data.readonly) {
const shader_name = Object.keys(datum)[0];
const allocation_data = datum[shader_name];
const new_allocation = TextureAllocation.fromJSON(allocation_data);
controller._addReadonlyAllocation(new_allocation);
}
return controller;
}
toJSON(scene) {
const writable = this._writableAllocations.map((allocation) => {
const data = {
[allocation.shaderName()]: allocation.toJSON(scene)
};
return data;
});
const readonly = this._readonlyAllocations.map((allocation) => {
const data = {
[allocation.shaderName()]: allocation.toJSON(scene)
};
return data;
});
return { writable, readonly };
}
print(scene) {
console.warn(JSON.stringify(this.toJSON(scene), [""], 2));
}
}