@shaderfrog/core 2.0.0-beta.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -119,9 +119,9 @@ export interface Engine {
119
119
 
120
120
  Shaderfrog works by searching each node's AST for certain patterns, like
121
121
  `uniform` variables, and creating an interface where you can replace each
122
- `uniform` variable with the output of another node.
122
+ `uniform` variable with the output of another node.
123
123
 
124
- Each fillable part of the AST is called a __hole__. Holes are found by executing
124
+ Each fillable part of the AST is called a **hole**. Holes are found by executing
125
125
  user defined _strategies_ against an AST. With a program such as:
126
126
 
127
127
  ```glsl
package/engine.d.ts CHANGED
@@ -47,6 +47,7 @@ export type EngineImporter = {
47
47
  edgeMap: {
48
48
  [oldInput: string]: string;
49
49
  };
50
+ code?: Record<string, string>;
50
51
  };
51
52
  export type EngineImporters = {
52
53
  [engine: string]: EngineImporter;
@@ -26,17 +26,23 @@ export interface NodeProperty {
26
26
  export declare const property: (displayName: string, property: string, type: GraphDataType, fillerName?: string, defaultValue?: any) => NodeProperty;
27
27
  export declare enum SourceType {
28
28
  SHADER_PROGRAM = "Shader Program",
29
- EXPRESSION = "Expression",
30
- FN_BODY_FRAGMENT = "Function Body Fragment"
29
+ FN_BODY_FRAGMENT = "Function Body Fragment",
30
+ EXPRESSION = "Expression"
31
31
  }
32
+ export type Backfillers = Record<string, {
33
+ argType: string;
34
+ targetVariable: string;
35
+ }[]>;
32
36
  export interface CodeNode extends BaseNode {
33
37
  config: NodeConfig;
38
+ type: string;
34
39
  engine: boolean;
35
40
  source: string;
36
41
  sourceType?: SourceType;
37
42
  stage?: ShaderStage;
38
43
  biStage?: boolean;
39
44
  originalEngine?: string;
45
+ backfillers?: Backfillers;
40
46
  }
41
47
  export interface BinaryNode extends CodeNode {
42
48
  operator: string;
@@ -13,6 +13,10 @@ export var property = function (displayName, property, type, fillerName, default
13
13
  export var SourceType;
14
14
  (function (SourceType) {
15
15
  SourceType["SHADER_PROGRAM"] = "Shader Program";
16
- SourceType["EXPRESSION"] = "Expression";
16
+ // Function body fragments are parsed, and parsed differently than shader
17
+ // programs. This confuses me all the time. TODO: Remove fn_body_framgent
18
+ // and just try/catch parsing a program, then try fn body fragment?
17
19
  SourceType["FN_BODY_FRAGMENT"] = "Function Body Fragment";
20
+ // Expressions are inlined as is
21
+ SourceType["EXPRESSION"] = "Expression";
18
22
  })(SourceType || (SourceType = {}));
@@ -1,9 +1,9 @@
1
1
  import { type GlslSyntaxError } from '@shaderfrog/glsl-parser';
2
- import { AstNode, Program } from '@shaderfrog/glsl-parser/ast';
2
+ import { AstNode, FunctionNode, Program } from '@shaderfrog/glsl-parser/ast';
3
3
  import { Engine, EngineContext } from '../engine';
4
4
  import { NodeInput } from './base-node';
5
5
  import { Graph, GraphNode } from './graph-types';
6
- import { InputFillers } from './parsers';
6
+ import { InputFillers } from '../strategy';
7
7
  /**
8
8
  * A node's context is the runtime / in-memory computed data associated with a
9
9
  * graph node. It includes the parsed AST representation of the node, as well as
@@ -17,6 +17,7 @@ export type NodeContext = {
17
17
  inputs?: NodeInput[];
18
18
  inputFillers: InputFillers;
19
19
  errors?: NodeErrors;
20
+ mainFn?: FunctionNode;
20
21
  };
21
22
  export type NodeErrors = {
22
23
  type: 'errors';
package/graph/context.js CHANGED
@@ -73,8 +73,9 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
73
73
  import groupBy from 'lodash.groupby';
74
74
  import { mapInputName, SourceType } from './code-nodes';
75
75
  import { NodeType } from './graph-types';
76
- import { collectConnectedNodes, filterGraphFromNode, findLinkedNode, findLinkedVertexNodes, isSourceNode, mangleEntireProgram, } from './graph';
76
+ import { collectConnectedNodes, filterGraphFromNode, findLinkedNode, findLinkedVertexNodes, isSourceNode, mangleEntireProgram, shouldNodeHaveMainFn, } from './graph';
77
77
  import { coreParsers } from './parsers';
78
+ import { findMain } from '../util/ast';
78
79
  var makeError = function (nodeId) {
79
80
  var errors = [];
80
81
  for (var _i = 1; _i < arguments.length; _i++) {
@@ -97,7 +98,7 @@ var collapseNodeInputs = function (node, updatedInputs) {
97
98
  return Object.values(groupBy(__spreadArray(__spreadArray([], __read(updatedInputs), false), __read(node.inputs), false), function (i) { return i.id; })).map(function (dupes) { return dupes.reduce(function (node, dupe) { return (__assign(__assign({}, node), dupe)); }); });
98
99
  };
99
100
  var computeNodeContext = function (engineContext, engine, graph, node) { return __awaiter(void 0, void 0, void 0, function () {
100
- var parser, sibling, onBeforeCompile, manipulateAst, inputEdges, ast, dataInputs, computedInputs, nodeContext;
101
+ var parser, sibling, onBeforeCompile, manipulateAst, inputEdges, mainFn, ast, dataInputs, computedInputs, nodeContext;
101
102
  return __generator(this, function (_a) {
102
103
  switch (_a.label) {
103
104
  case 0:
@@ -113,6 +114,10 @@ var computeNodeContext = function (engineContext, engine, graph, node) { return
113
114
  inputEdges = graph.edges.filter(function (edge) { return edge.to === node.id; });
114
115
  try {
115
116
  ast = parser.produceAst(engineContext, engine, graph, node, inputEdges);
117
+ // Find the main function before mangling
118
+ if (shouldNodeHaveMainFn(node)) {
119
+ mainFn = findMain(ast);
120
+ }
116
121
  if (manipulateAst) {
117
122
  ast = manipulateAst(engineContext, engine, graph, ast, inputEdges, node, sibling);
118
123
  }
@@ -134,13 +139,20 @@ var computeNodeContext = function (engineContext, engine, graph, node) { return
134
139
  nodeContext = {
135
140
  ast: ast,
136
141
  id: node.id,
142
+ mainFn: mainFn,
137
143
  inputFillers: computedInputs.reduce(function (acc, _a) {
138
144
  var _b;
139
- var _c = __read(_a, 3), input = _c[0], filler = _c[1], args = _c[2];
140
- return (__assign(__assign({}, acc), (_b = {}, _b[input.id] = {
145
+ var _c = __read(_a, 4), input = _c[0], filler = _c[1], fillerArgs = _c[2], fillerStmt = _c[3];
146
+ // This is intentionally broken out into an explicit return to force
147
+ // this type declaration. Inlining the object in [input.id]: {...}
148
+ // doesn't force it to be an InputFillerGroup, and it can contain extra
149
+ // arguments by accident
150
+ var fillerGroup = {
141
151
  filler: filler,
142
- args: args,
143
- }, _b)));
152
+ fillerArgs: fillerArgs,
153
+ fillerStmt: fillerStmt,
154
+ };
155
+ return __assign(__assign({}, acc), (_b = {}, _b[input.id] = fillerGroup, _b));
144
156
  }, {}),
145
157
  };
146
158
  // Skip mangling if the node tells us to, which probably means it's an engine
@@ -206,7 +218,7 @@ export var computeAllContexts = function (engineContext, engine, graph) { return
206
218
  * Used to only (re)-compute context for any actively used nodes
207
219
  */
208
220
  export var computeGraphContext = function (engineContext, engine, graph) { return __awaiter(void 0, void 0, void 0, function () {
209
- var outputFrag, outputVert, vertexIds, fragmentIds, unlinkedNodes, vertNodesOrError, fragNodesOrError;
221
+ var outputFrag, outputVert, vertexes, fragments, vertexIds, unlinkedNodes, vertNodesOrError, fragNodesOrError;
210
222
  return __generator(this, function (_a) {
211
223
  switch (_a.label) {
212
224
  case 0:
@@ -218,12 +230,13 @@ export var computeGraphContext = function (engineContext, engine, graph) { retur
218
230
  if (!outputVert) {
219
231
  throw new Error('No vertex output in graph');
220
232
  }
221
- vertexIds = collectConnectedNodes(graph, outputVert);
222
- fragmentIds = collectConnectedNodes(graph, outputFrag);
223
- unlinkedNodes = findLinkedVertexNodes(graph, vertexIds);
233
+ vertexes = collectConnectedNodes(graph, outputVert);
234
+ fragments = collectConnectedNodes(graph, outputFrag);
235
+ vertexIds = new Set(Object.keys(vertexes));
236
+ unlinkedNodes = findLinkedVertexNodes(graph, vertexes);
224
237
  return [4 /*yield*/, computeContextForNodes(engineContext, engine, graph, __spreadArray(__spreadArray([
225
238
  outputVert
226
- ], __read(Object.values(vertexIds).filter(function (node) { return node.id !== outputVert.id; })), false), __read(unlinkedNodes), false))];
239
+ ], __read(Object.values(vertexes).filter(function (node) { return node.id !== outputVert.id; })), false), __read(unlinkedNodes), false))];
227
240
  case 1:
228
241
  vertNodesOrError = _a.sent();
229
242
  if (isError(vertNodesOrError)) {
@@ -231,7 +244,7 @@ export var computeGraphContext = function (engineContext, engine, graph) { retur
231
244
  }
232
245
  return [4 /*yield*/, computeContextForNodes(engineContext, engine, graph, __spreadArray([
233
246
  outputFrag
234
- ], __read(Object.values(fragmentIds).filter(function (node) { return node.id !== outputFrag.id; })), false))];
247
+ ], __read(Object.values(fragments).filter(function (node) { return node.id !== outputFrag.id && !vertexIds.has(node.id); })), false))];
235
248
  case 2:
236
249
  fragNodesOrError = _a.sent();
237
250
  if (isError(fragNodesOrError)) {
package/graph/evaluate.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import { coreParsers } from './parsers';
2
2
  export var toGlsl = function (node) {
3
3
  var type = node.type, value = node.value;
4
+ if (type === 'number') {
5
+ return value;
6
+ }
4
7
  if (type === 'vector2') {
5
8
  return "vec2(".concat(value[0], ", ").concat(value[1], ")");
6
9
  }
@@ -10,6 +13,10 @@ export var toGlsl = function (node) {
10
13
  if (type === 'vector4' || type === 'rgba') {
11
14
  return "vec4(".concat(value[0], ", ").concat(value[1], ", ").concat(value[2], ", ").concat(value[3], ")");
12
15
  }
16
+ // Right now hard coding that an array is floats. Need type from node later.
17
+ if (type === 'array') {
18
+ return "float[".concat(value.length, "](").concat(value.join(', '), ")");
19
+ }
13
20
  throw new Error("Unknown GLSL inline type: \"".concat(node.type, "\""));
14
21
  };
15
22
  export var evaluateNode = function (engine, graph, node) {
package/graph/graph.d.ts CHANGED
@@ -11,16 +11,22 @@ import { ProduceNodeFiller } from './parsers';
11
11
  import { Graph, GraphNode } from './graph-types';
12
12
  export declare const isDataNode: (node: GraphNode) => node is DataNode;
13
13
  export declare const isSourceNode: (node: GraphNode) => node is SourceNode;
14
+ /**
15
+ * Determine if a node's source code / AST should have a main function. Essentially
16
+ * check if the source code is a full program or not.
17
+ */
18
+ export declare const shouldNodeHaveMainFn: (node: GraphNode) => node is SourceNode;
14
19
  export declare const findNode: (graph: Graph, id: string) => GraphNode;
15
20
  export declare const doesLinkThruShader: (graph: Graph, node: GraphNode) => boolean;
16
21
  export declare const nodeName: (node: GraphNode) => string;
22
+ export declare const resultName: (node: GraphNode) => string;
17
23
  export declare const mangleName: (name: string, node: GraphNode, nextSibling?: GraphNode) => string;
18
24
  export declare const mangleVar: (name: string, engine: Engine, node: GraphNode, sibling?: GraphNode) => string;
19
25
  export declare const mangleEntireProgram: (engine: Engine, ast: FrogProgram, node: GraphNode, sibling?: GraphNode) => void;
20
26
  export declare const mangleMainFn: (ast: Program, node: GraphNode, sibling?: GraphNode) => void;
21
- export declare const ensureFromNode: (graph: Graph, inputEdge: Edge) => import("./data-nodes").NumberNode | import("./data-nodes").TextureNode | import("./data-nodes").SamplerCubeNode | import("./data-nodes").ArrayNode | import("./data-nodes").Vector2Node | import("./data-nodes").Vector3Node | import("./data-nodes").Vector4Node | import("./data-nodes").RgbNode | import("./data-nodes").RgbaNode | CodeNode | import("./code-nodes").BinaryNode;
27
+ export declare const ensureFromNode: (graph: Graph, inputEdge: Edge) => import("./data-nodes").NumberNode | import("./data-nodes").TextureNode | import("./data-nodes").SamplerCubeNode | import("./data-nodes").ArrayNode | import("./data-nodes").Vector2Node | import("./data-nodes").Vector3Node | import("./data-nodes").Vector4Node | import("./data-nodes").RgbNode | import("./data-nodes").RgbaNode | import("./code-nodes").BinaryNode | CodeNode;
22
28
  export declare const resetGraphIds: (graph: Graph) => Graph;
23
- export declare const findLinkedNode: (graph: Graph, id: string) => GraphNode | undefined;
29
+ export declare const findLinkedNode: (graph: Graph, id: string) => SourceNode | undefined;
24
30
  /**
25
31
  * Find any unconnected vertex nodes linked to collected fragment nodes
26
32
  */
package/graph/graph.js CHANGED
@@ -72,8 +72,8 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
72
72
  };
73
73
  import { renameBindings, renameFunctions, } from '@shaderfrog/glsl-parser/parser/utils';
74
74
  import { computeGraphContext, isError, } from './context';
75
- import { emptyShaderSections, findShaderSections, mergeShaderSections, shaderSectionsToProgram, } from './shader-sections';
76
- import { makeExpression } from '../util/ast';
75
+ import { shaderSectionsCons, findShaderSections, mergeShaderSections, shaderSectionsToProgram, } from './shader-sections';
76
+ import { backfillAst, makeExpression, } from '../util/ast';
77
77
  import { ensure } from '../util/ensure';
78
78
  import { SourceType } from './code-nodes';
79
79
  import { nodeInput } from './base-node';
@@ -96,6 +96,21 @@ export var isDataNode = function (node) {
96
96
  export var isSourceNode = function (node) {
97
97
  return !isDataNode(node);
98
98
  };
99
+ /**
100
+ * Determine if a node's source code / AST should have a main function. Essentially
101
+ * check if the source code is a full program or not.
102
+ */
103
+ export var shouldNodeHaveMainFn = function (node) {
104
+ // Some legacy shaders have an output node that does not have a sourceType,
105
+ // otherwise the sourceType second check would always work
106
+ return node.type === NodeType.OUTPUT ||
107
+ // Same for code nodes :(
108
+ (isSourceNode(node) && !node.sourceType) ||
109
+ node.sourceType === SourceType.SHADER_PROGRAM ||
110
+ // Engine nodes can have rando types like "physical", so if they are engine
111
+ // nodes, assume they have a main fn.
112
+ node.engine;
113
+ };
99
114
  export var findNode = function (graph, id) {
100
115
  return ensure(graph.nodes.find(function (node) { return node.id === id; }));
101
116
  };
@@ -120,6 +135,7 @@ export var doesLinkThruShader = function (graph, node) {
120
135
  export var nodeName = function (node) {
121
136
  return 'main_' + node.name.replace(/[^a-zA-Z0-9]/g, ' ').replace(/ +/g, '_');
122
137
  };
138
+ export var resultName = function (node) { return nodeName(node) + '_out'; };
123
139
  export var mangleName = function (name, node, nextSibling) {
124
140
  // Mangle a name to its next stage node, so the vertex suffix becomes the
125
141
  // fragment id, but not the other way around.
@@ -160,6 +176,7 @@ export var resetGraphIds = function (graph) {
160
176
  export var findLinkedNode = function (graph, id) {
161
177
  var edgeLink = graph.edges.find(function (e) { return e.type === EdgeLink.NEXT_STAGE && (e.from === id || e.to === id); });
162
178
  var otherId = (edgeLink === null || edgeLink === void 0 ? void 0 : edgeLink.from) === id ? edgeLink === null || edgeLink === void 0 ? void 0 : edgeLink.to : edgeLink === null || edgeLink === void 0 ? void 0 : edgeLink.from;
179
+ // Only source nodes can be linked, so cast it
163
180
  return graph.nodes.find(function (node) { return node.id === otherId; });
164
181
  };
165
182
  /**
@@ -264,10 +281,11 @@ export var isDataInput = function (input) {
264
281
  return (input.type === 'uniform' || input.type === 'property') && !input.baked;
265
282
  };
266
283
  export var compileNode = function (engine, graph, edges, engineContext, node, activeIds) {
267
- var _a, _b;
284
+ var _a;
268
285
  if (activeIds === void 0) { activeIds = {}; }
269
286
  // THIS DUPLICATES OTHER LINE
270
287
  var parser = __assign(__assign({}, (coreParsers[node.type] || coreParsers[NodeType.SOURCE])), (engine.parsers[node.type] || {}));
288
+ var codeNode = node;
271
289
  var inputs = node.inputs;
272
290
  if (!parser) {
273
291
  console.error(node);
@@ -276,135 +294,97 @@ export var compileNode = function (engine, graph, edges, engineContext, node, ac
276
294
  var nodeContext = isDataNode(node)
277
295
  ? null
278
296
  : ensure(engineContext.nodes[node.id], "No node context found for \"".concat(node.name, "\" (id ").concat(node.id, ")!"));
279
- var _c = (nodeContext || {}), ast = _c.ast, inputFillers = _c.inputFillers;
297
+ var _b = (nodeContext || {}), ast = _b.ast, inputFillers = _b.inputFillers;
280
298
  if (!inputs) {
281
299
  throw new Error("I'm drunk and I think this case should be impossible");
282
300
  }
283
301
  var compiledIds = activeIds;
284
302
  var inputEdges = edges.filter(function (edge) { return edge.to === node.id; });
285
- if (inputEdges.length) {
286
- var continuation_1 = emptyShaderSections();
287
- inputEdges
288
- .filter(function (edge) { return edge.type !== EdgeLink.NEXT_STAGE; })
289
- .map(function (edge) { return ({
290
- edge: edge,
291
- fromNode: ensure(graph.nodes.find(function (node) { return edge.from === node.id; }), "GraphNode for edge ".concat(edge.from, " not found")),
292
- input: ensure(inputs.find(function (_a) {
293
- var id = _a.id;
294
- return id == edge.input;
295
- }), "GraphNode \"".concat(node.name, "\"").concat(node.stage ? " (".concat(node.stage, ")") : '', " has no input ").concat(edge.input, "!\nAvailable:").concat(inputs
296
- .map(function (_a) {
297
- var id = _a.id;
298
- return id;
299
- })
300
- .join(', '))),
301
- }); })
302
- .filter(function (_a) {
303
- var input = _a.input;
304
- return !isDataInput(input);
303
+ var continuation = shaderSectionsCons();
304
+ // Compile children recursively
305
+ inputEdges
306
+ .filter(function (edge) { return edge.type !== EdgeLink.NEXT_STAGE; })
307
+ .map(function (edge) { return ({
308
+ edge: edge,
309
+ fromNode: ensure(graph.nodes.find(function (node) { return edge.from === node.id; }), "GraphNode for edge ".concat(edge.from, " not found")),
310
+ input: ensure(inputs.find(function (_a) {
311
+ var id = _a.id;
312
+ return id == edge.input;
313
+ }), "GraphNode \"".concat(node.name, "\"").concat(node.stage ? " (".concat(node.stage, ")") : '', " has no input ").concat(edge.input, "!\nAvailable:").concat(inputs
314
+ .map(function (_a) {
315
+ var id = _a.id;
316
+ return id;
305
317
  })
306
- .forEach(function (_a) {
307
- var _b;
308
- var fromNode = _a.fromNode, edge = _a.edge, input = _a.input;
309
- var _c = __read(compileNode(engine, graph, edges, engineContext, fromNode, activeIds), 3), inputSections = _c[0], fillerAst = _c[1], childIds = _c[2];
310
- if (!fillerAst) {
311
- throw new TypeError("Expected a filler ast from node ID ".concat(fromNode.id, " (").concat(fromNode.type, ") but none was returned"));
312
- }
313
- continuation_1 = mergeShaderSections(continuation_1, inputSections);
314
- compiledIds = __assign(__assign({}, compiledIds), childIds);
315
- var filler;
316
- var fillerName;
317
- if (nodeContext) {
318
- if (input.property) {
319
- fillerName = ensure((_b = (node.config.properties || []).find(function (p) { return p.property === input.property; })) === null || _b === void 0 ? void 0 : _b.fillerName, "Node \"".concat(node.name, "\" has no property named \"").concat(input.property, "\" to find the filler for"));
320
- filler = inputFillers[fillerName];
321
- }
322
- else {
323
- filler = inputFillers[input.id];
324
- }
325
- if (!filler) {
326
- console.error('No filler for property', {
327
- input: input,
328
- node: node,
329
- inputFillers: inputFillers,
330
- fillerName: fillerName,
331
- });
332
- throw new Error("Node \"".concat(node.name, "\"").concat(node.stage
333
- ? " (".concat(node.stage, ")")
334
- : '', " has no filler for input \"").concat(input.displayName, "\" named ").concat(fillerName));
335
- }
336
- /**
337
- * +------+ +------+
338
- * a -- o add o -- o tex |
339
- * b -- o | +------+
340
- * +------+
341
- *
342
- * This could produce:
343
- * main_a(v1) + main_b(v2)
344
- * I guess it has to? or it could produce
345
- * function add(v1) { return main_a(v1) + main_b(v2); }
346
- * It can't replace the arg _expression_ in the from shaders, because
347
- * the expression isn't available there.
348
- */
349
- // TODO: This is a hard coded hack for vUv backfilling. It works in
350
- // the simple case. Doesn't work for hell (based on world position).
351
- if (filler.backfillArgs &&
352
- !Array.isArray(fillerAst) &&
353
- fillerAst.type === 'function_call') {
354
- // Object.values(filterGraphFromNode(graph, node, {
355
- // node: (n) => n.type === 'source'
356
- // }).nodes).forEach(sourceNode => {
357
- if (fromNode.type === 'source') {
358
- // @ts-ignore
359
- fillerAst.args = filler.backfillArgs;
360
- // const fc = engineContext.nodes[sourceNode.id];
361
- var fc = engineContext.nodes[fromNode.id];
362
- var main = Object.values(fc.ast.scopes[0].functions.main)[0].declaration;
363
- main.prototype.parameters = [
364
- 'vec2 vv',
365
- ];
366
- // @ts-ignore
367
- var scope = fc.ast.scopes[0];
368
- // renameBindings(scope, (name, node) => {
369
- // return node.type !== 'declaration' && name === 'vUv'
370
- // ? 'vv'
371
- // : name;
372
- // });
373
- }
374
- // })
375
- }
376
- // Fill in the input! The return value is the new AST of the filled in
377
- // fromNode.
378
- nodeContext.ast = filler.filler(fillerAst);
318
+ .join(', '))),
319
+ }); })
320
+ .filter(function (_a) {
321
+ var input = _a.input;
322
+ return !isDataInput(input);
323
+ })
324
+ .forEach(function (_a) {
325
+ var _b, _c;
326
+ var fromNode = _a.fromNode, input = _a.input;
327
+ // const [inputSections, fillerFn, childIds, childDeps] = compileNode(
328
+ var _d = __read(compileNode(engine, graph, edges, engineContext, fromNode, activeIds), 3), inputSections = _d[0], fillerFn = _d[1], childIds = _d[2];
329
+ if (!fillerFn) {
330
+ throw new TypeError("Expected a filler ast from node ID ".concat(fromNode.id, " (").concat(fromNode.type, ") but none was returned"));
331
+ }
332
+ continuation = mergeShaderSections(continuation, inputSections);
333
+ compiledIds = __assign(__assign({}, compiledIds), childIds);
334
+ // Continue on if there's no context I don't know what case causes this
335
+ if (!nodeContext) {
336
+ return;
337
+ }
338
+ // Produce the input filler
339
+ var filler;
340
+ var fillerName;
341
+ if (input.property) {
342
+ fillerName = ensure((_b = (codeNode.config.properties || []).find(function (p) { return p.property === input.property; })) === null || _b === void 0 ? void 0 : _b.fillerName, "Node \"".concat(node.name, "\" has no property named \"").concat(input.property, "\" to find the filler for"));
343
+ filler = inputFillers[fillerName];
344
+ }
345
+ else {
346
+ filler = inputFillers[input.id];
347
+ }
348
+ if (!filler) {
349
+ console.error('No filler for property', {
350
+ input: input,
351
+ node: node,
352
+ inputFillers: inputFillers,
353
+ fillerName: fillerName,
354
+ });
355
+ throw new Error("Node \"".concat(node.name, "\"").concat(node.stage ? " (".concat(node.stage, ")") : '', " has no filler for input \"").concat(input.displayName, "\" named ").concat(fillerName));
356
+ }
357
+ // Test if it needs to be backfilled - this only goes one level deep
358
+ // because we're only backfilling fromNode
359
+ var backfillers = (_c = codeNode.backfillers) === null || _c === void 0 ? void 0 : _c[input.id];
360
+ if (backfillers && shouldNodeHaveMainFn(fromNode)) {
361
+ var childAst_1 = engineContext.nodes[fromNode.id].ast;
362
+ // For now we can only backfill programs
363
+ if (childAst_1.type === 'program') {
364
+ backfillers.forEach(function (backfiller) {
365
+ // This is where the variable name gets injected into the main
366
+ // function parameter of the backfilled child
367
+ backfillAst(childAst_1, backfiller.argType, backfiller.targetVariable, engineContext.nodes[fromNode.id].mainFn);
368
+ });
379
369
  }
380
- // log(generate(ast.program));
381
- });
382
- // Order matters here! *Prepend* the input nodes to this one, because
383
- // you have to declare functions in order of use in GLSL
384
- var sections = mergeShaderSections(continuation_1, isDataNode(node) ||
385
- node.sourceType === SourceType.EXPRESSION ||
386
- node.sourceType === SourceType.FN_BODY_FRAGMENT
387
- ? emptyShaderSections()
388
- : findShaderSections(ast));
389
- var filler = isDataNode(node)
390
- ? makeExpression(toGlsl(node))
391
- : parser.produceFiller(node, ast);
392
- return [sections, filler, __assign(__assign({}, compiledIds), (_a = {}, _a[node.id] = node, _a))];
393
- }
394
- else {
395
- // TODO: This duplicates the above branch, and also does this mean we
396
- // recalculate the shader sections and filler for every edge? Can I move
397
- // these lines above the loop?
398
- var sections = isDataNode(node) ||
399
- node.sourceType === SourceType.EXPRESSION ||
400
- node.sourceType === SourceType.FN_BODY_FRAGMENT
401
- ? emptyShaderSections()
402
- : findShaderSections(ast);
403
- var filler = isDataNode(node)
404
- ? makeExpression(toGlsl(node))
405
- : parser.produceFiller(node, ast);
406
- return [sections, filler, __assign(__assign({}, compiledIds), (_b = {}, _b[node.id] = node, _b))];
407
- }
370
+ nodeContext.ast = filler.filler(fillerFn);
371
+ }
372
+ else {
373
+ // Don't backfill by discarding the backfiller args
374
+ nodeContext.ast = filler.filler(function () { return fillerFn(); });
375
+ }
376
+ });
377
+ // Order matters here! *Prepend* the input nodes to this one, because
378
+ // you have to declare functions in order of use in GLSL
379
+ var sections = mergeShaderSections(continuation, isDataNode(node) ||
380
+ codeNode.sourceType === SourceType.EXPRESSION ||
381
+ codeNode.sourceType === SourceType.FN_BODY_FRAGMENT
382
+ ? shaderSectionsCons()
383
+ : findShaderSections(ast));
384
+ var filler = isDataNode(node)
385
+ ? function () { return makeExpression(toGlsl(node)); }
386
+ : parser.produceFiller(node, ast);
387
+ return [sections, filler, __assign(__assign({}, compiledIds), (_a = {}, _a[node.id] = node, _a))];
408
388
  };
409
389
  export var compileGraph = function (engineContext, engine, graph) {
410
390
  // computeGraphContext(engineContext, engine, graph);