@shaderfrog/core 0.0.2 → 0.1.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.
Files changed (56) hide show
  1. package/README.md +184 -1
  2. package/dist/ast/manipulate.js +328 -0
  3. package/dist/ast/shader-sections.js +256 -0
  4. package/dist/context.js +230 -0
  5. package/dist/engine.js +209 -0
  6. package/dist/evaluate.js +27 -0
  7. package/dist/graph-types.js +7 -0
  8. package/dist/graph.js +381 -0
  9. package/dist/graph.test.js +168 -0
  10. package/dist/nodes/code-nodes.js +18 -0
  11. package/dist/nodes/core-node.js +9 -0
  12. package/dist/nodes/data-nodes.js +123 -0
  13. package/dist/nodes/edge.js +1 -0
  14. package/dist/nodes/engine-node.js +189 -0
  15. package/dist/parsers.js +213 -0
  16. package/dist/plugins/babylon/bablyengine.js +582 -0
  17. package/dist/plugins/babylon/importers.js +64 -0
  18. package/{src/plugins/babylon/index.ts → dist/plugins/babylon/index.js} +0 -1
  19. package/dist/plugins/playcanvas/importers.js +28 -0
  20. package/dist/plugins/playcanvas/index.js +2 -0
  21. package/dist/plugins/playcanvas/playengine.js +510 -0
  22. package/dist/plugins/three/importers.js +15 -0
  23. package/{src/plugins/three/index.ts → dist/plugins/three/index.js} +0 -1
  24. package/dist/plugins/three/threngine.js +495 -0
  25. package/dist/strategy/assignemntTo.js +26 -0
  26. package/dist/strategy/declarationOf.js +23 -0
  27. package/dist/strategy/hardCode.js +23 -0
  28. package/dist/strategy/index.js +38 -0
  29. package/dist/strategy/inject.js +122 -0
  30. package/dist/strategy/namedAttribute.js +48 -0
  31. package/dist/strategy/texture2D.js +83 -0
  32. package/dist/strategy/uniform.js +190 -0
  33. package/dist/strategy/variable.js +80 -0
  34. package/dist/stratgies.test.js +164 -0
  35. package/dist/util/ast.js +9 -0
  36. package/dist/util/ensure.js +7 -0
  37. package/dist/util/id.js +2 -0
  38. package/package.json +12 -4
  39. package/src/ast/manipulate.ts +0 -392
  40. package/src/ast/shader-sections.ts +0 -323
  41. package/src/core/engine.ts +0 -214
  42. package/src/core/file.js +0 -53
  43. package/src/core/graph.ts +0 -1007
  44. package/src/core/nodes/code-nodes.ts +0 -66
  45. package/src/core/nodes/core-node.ts +0 -48
  46. package/src/core/nodes/data-nodes.ts +0 -344
  47. package/src/core/nodes/edge.ts +0 -23
  48. package/src/core/nodes/engine-node.ts +0 -266
  49. package/src/core/strategy.ts +0 -520
  50. package/src/core.test.ts +0 -312
  51. package/src/plugins/babylon/bablyengine.ts +0 -670
  52. package/src/plugins/babylon/importers.ts +0 -69
  53. package/src/plugins/three/importers.ts +0 -18
  54. package/src/plugins/three/threngine.tsx +0 -571
  55. package/src/util/ensure.ts +0 -10
  56. package/src/util/id.ts +0 -2
package/dist/engine.js ADDED
@@ -0,0 +1,209 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var __read = (this && this.__read) || function (o, n) {
13
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
14
+ if (!m) return o;
15
+ var i = m.call(o), r, ar = [], e;
16
+ try {
17
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
18
+ }
19
+ catch (error) { e = { error: error }; }
20
+ finally {
21
+ try {
22
+ if (r && !r.done && (m = i["return"])) m.call(i);
23
+ }
24
+ finally { if (e) throw e.error; }
25
+ }
26
+ return ar;
27
+ };
28
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
29
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
30
+ if (ar || !(i in from)) {
31
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
32
+ ar[i] = from[i];
33
+ }
34
+ }
35
+ return to.concat(ar || Array.prototype.slice.call(from));
36
+ };
37
+ import preprocess from '@shaderfrog/glsl-parser/preprocessor';
38
+ import { generate, parser } from '@shaderfrog/glsl-parser';
39
+ import { NodeType } from './graph-types';
40
+ import groupBy from 'lodash.groupby';
41
+ import { collectNodeProperties } from './graph';
42
+ import { evaluateNode } from './evaluate';
43
+ var log = function () {
44
+ var _a;
45
+ var args = [];
46
+ for (var _i = 0; _i < arguments.length; _i++) {
47
+ args[_i] = arguments[_i];
48
+ }
49
+ return (_a = console.log).call.apply(_a, __spreadArray([console, '\x1b[32m(core)\x1b[0m'], __read(args), false));
50
+ };
51
+ export var EngineNodeType;
52
+ (function (EngineNodeType) {
53
+ EngineNodeType["toon"] = "toon";
54
+ EngineNodeType["phong"] = "phong";
55
+ EngineNodeType["physical"] = "physical";
56
+ EngineNodeType["shader"] = "shader";
57
+ EngineNodeType["binary"] = "binary";
58
+ })(EngineNodeType || (EngineNodeType = {}));
59
+ // type EdgeUpdates = { [edgeId: string]: { oldInput: string; newInput: string } };
60
+ export var convertNode = function (node, converter) {
61
+ log("Converting ".concat(node.name, " (").concat(node.id, ")"));
62
+ var preprocessed = preprocess(node.source, {
63
+ preserveComments: true,
64
+ preserve: {
65
+ version: function () { return true; },
66
+ define: function () { return true; },
67
+ },
68
+ });
69
+ var ast = parser.parse(preprocessed);
70
+ converter.convertAst(ast, node.stage);
71
+ var source = generate(ast);
72
+ return __assign(__assign({}, node), { source: source });
73
+ };
74
+ export var convertToEngine = function (oldEngine, newEngine, graph) {
75
+ var converter = newEngine.importers[oldEngine.name];
76
+ if (!converter) {
77
+ throw new Error("The engine ".concat(newEngine.name, " has no importer for ").concat(oldEngine.name));
78
+ }
79
+ log("Attempting to convert from ".concat(newEngine.name, " to ").concat(oldEngine.name));
80
+ // const edgeUpdates: EdgeUpdates = {};
81
+ var edgesByNodeId = groupBy(graph.edges, 'to');
82
+ var edgeUpdates = {};
83
+ var nodeUpdates = {};
84
+ graph.nodes.forEach(function (node) {
85
+ // Convert engine nodes
86
+ if (node.type in EngineNodeType) {
87
+ if (node.type in newEngine.constructors) {
88
+ var source = node;
89
+ nodeUpdates[source.id] = // @ts-ignore
90
+ newEngine.constructors[source.type](source.id, source.name, source.groupId, source.position, source.config.uniforms, source.stage, source.nextStageNodeId);
91
+ // Bail if no conversion
92
+ }
93
+ else {
94
+ throw new Error("Can't convert ".concat(oldEngine.name, " to ").concat(newEngine.name, " because ").concat(newEngine.name, " does not have a \"").concat(node.type, "\" constructor"));
95
+ }
96
+ }
97
+ else if (NodeType.SOURCE === node.type) {
98
+ nodeUpdates[node.id] = convertNode(node, converter);
99
+ }
100
+ // Then update input edges. We only care about engine nodes
101
+ if (node.type in converter.nodeInputMap) {
102
+ var map_1 = converter.nodeInputMap[node.type];
103
+ (edgesByNodeId[node.id] || []).forEach(function (edge) {
104
+ if (edge.input in map_1) {
105
+ var mapped = map_1[edge.input];
106
+ log('Converting edge', edge.input, 'to', map_1[edge.input]);
107
+ edgeUpdates[edge.id] = __assign(__assign({}, edge), { input: mapped });
108
+ }
109
+ else {
110
+ log('Discarding', edge.input, 'as there is no edge mapping in the', newEngine.name, 'importer');
111
+ edgeUpdates[edge.id] = null;
112
+ }
113
+ });
114
+ }
115
+ });
116
+ graph.edges = graph.edges.reduce(function (edges, edge) {
117
+ if (edge.id in edgeUpdates) {
118
+ var res = edgeUpdates[edge.id];
119
+ if (res === null) {
120
+ return edges;
121
+ }
122
+ else {
123
+ return __spreadArray(__spreadArray([], __read(edges), false), [res], false);
124
+ }
125
+ }
126
+ return __spreadArray(__spreadArray([], __read(edges), false), [edge], false);
127
+ }, []);
128
+ graph.nodes = graph.nodes.reduce(function (nodes, node) {
129
+ if (node.id in nodeUpdates) {
130
+ var res = nodeUpdates[node.id];
131
+ if (res === null) {
132
+ return nodes;
133
+ }
134
+ else {
135
+ return __spreadArray(__spreadArray([], __read(nodes), false), [res], false);
136
+ }
137
+ }
138
+ return __spreadArray(__spreadArray([], __read(nodes), false), [node], false);
139
+ }, []);
140
+ log('Created converted graph', graph);
141
+ return graph;
142
+ };
143
+ /**
144
+ * Create the initial engine node properties for a plugin to create its initial
145
+ * material with. This finds all engine nodes in the graph, finds all their
146
+ * properties, evalutes them, and returns an object with initial properties to
147
+ * set on the new plugin material, like a three.RawShaderMaterial().
148
+ *
149
+ * Currently only PlayCanvas uses this. It's at odds with the compileResult.dataInputs
150
+ * code path. That path uses isDataNode() to check for inputs, which excludes
151
+ * baked inputs. PlayCanvas requires (at least diffusesMap?) baked input properties
152
+ * to be set to a pc.Texture() at runtime, otherwise there's an error about
153
+ * vertex_texCoord0.
154
+ */
155
+ export var collectInitialEvaluatedGraphProperties = function (engine, graph, defaultPropertySetting) {
156
+ var graphProperties = {};
157
+ // Get all the nodes with properties, meaning engine nodes, and the inputs
158
+ // for each property (property is like "diffuseMap").
159
+ var _a = collectNodeProperties(graph), nodes = _a.nodes, inputs = _a.inputs;
160
+ Object.entries(inputs).forEach(function (_a) {
161
+ var _b = __read(_a, 2), nodeId = _b[0], nodeInputs = _b[1];
162
+ // For every node with properties... There might be mulitple if there are
163
+ // uniforms plugged into both frag and vertex engine nodes, which
164
+ var node = nodes[nodeId];
165
+ nodeInputs.forEach(function (i) {
166
+ // Cast this to an input with a property specified on it, which the
167
+ // predicate search enforces
168
+ var input = i;
169
+ var edge = graph.edges.find(function (_a) {
170
+ var to = _a.to, i = _a.input;
171
+ return to === node.id && i === input.id;
172
+ });
173
+ // In the case where a node has been deleted from the graph,
174
+ // dataInputs won't have been udpated until a recompile completes
175
+ var fromNode = edge && graph.nodes.find(function (_a) {
176
+ var id = _a.id;
177
+ return id === edge.from;
178
+ });
179
+ if (fromNode) {
180
+ // If this is a baked input, we need to set the engine property to force
181
+ // whatever we're baking to generate.
182
+ if (input.baked) {
183
+ // Find the corresponding property on the node and get the default
184
+ // setting
185
+ var property = (node.config.properties || []).find(function (p) { return p.property === input.property; });
186
+ if (property) {
187
+ graphProperties[input.property] = defaultPropertySetting(property);
188
+ }
189
+ else {
190
+ console.error('Property not found on input node', node, input);
191
+ throw new Error('Property not found on input node');
192
+ }
193
+ // Other inputs should(?) be data if not baked
194
+ }
195
+ else {
196
+ try {
197
+ graphProperties[input.property] = evaluateNode(engine, graph, fromNode);
198
+ }
199
+ catch (err) {
200
+ console.error('Tried to evaluate a non-data node!', {
201
+ err: err,
202
+ });
203
+ }
204
+ }
205
+ }
206
+ });
207
+ });
208
+ return graphProperties;
209
+ };
@@ -0,0 +1,27 @@
1
+ import { coreParsers } from './parsers';
2
+ export var toGlsl = function (node) {
3
+ var type = node.type, value = node.value;
4
+ if (type === 'vector2') {
5
+ return "vec2(".concat(value[0], ", ").concat(value[1], ")");
6
+ }
7
+ if (type === 'vector3' || type === 'rgb') {
8
+ return "vec3(".concat(value[0], ", ").concat(value[1], ", ").concat(value[2], ")");
9
+ }
10
+ if (type === 'vector4' || type === 'rgba') {
11
+ return "vec4(".concat(value[0], ", ").concat(value[1], ", ").concat(value[2], ", ").concat(value[3], ")");
12
+ }
13
+ throw new Error("Unknown GLSL inline type: \"".concat(node.type, "\""));
14
+ };
15
+ export var evaluateNode = function (engine, graph, node) {
16
+ // TODO: Data nodes themselves should have evaluators
17
+ if ('value' in node) {
18
+ return engine.evaluateNode(node);
19
+ }
20
+ var evaluate = coreParsers[node.type].evaluate;
21
+ if (!evaluate) {
22
+ throw new Error("No evaluator for node ".concat(node.name, " (type: ").concat(node.type, ", id: ").concat(node.id, ")"));
23
+ }
24
+ var inputEdges = graph.edges.filter(function (edge) { return edge.to === node.id; });
25
+ var inputNodes = inputEdges.map(function (edge) { return graph.nodes.find(function (node) { return node.id === edge.from; }); });
26
+ return evaluate(node, inputEdges, inputNodes, evaluateNode.bind(null, engine, graph));
27
+ };
@@ -0,0 +1,7 @@
1
+ export var NodeType;
2
+ (function (NodeType) {
3
+ NodeType["OUTPUT"] = "output";
4
+ NodeType["BINARY"] = "binary";
5
+ NodeType["SOURCE"] = "source";
6
+ })(NodeType || (NodeType = {}));
7
+ export var MAGIC_OUTPUT_STMTS = 'mainStmts';
package/dist/graph.js ADDED
@@ -0,0 +1,381 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var __read = (this && this.__read) || function (o, n) {
13
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
14
+ if (!m) return o;
15
+ var i = m.call(o), r, ar = [], e;
16
+ try {
17
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
18
+ }
19
+ catch (error) { e = { error: error }; }
20
+ finally {
21
+ try {
22
+ if (r && !r.done && (m = i["return"])) m.call(i);
23
+ }
24
+ finally { if (e) throw e.error; }
25
+ }
26
+ return ar;
27
+ };
28
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
29
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
30
+ if (ar || !(i in from)) {
31
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
32
+ ar[i] = from[i];
33
+ }
34
+ }
35
+ return to.concat(ar || Array.prototype.slice.call(from));
36
+ };
37
+ import { renameBindings, renameFunctions, } from '@shaderfrog/glsl-parser/parser/utils';
38
+ import { emptyShaderSections, findShaderSections, mergeShaderSections, } from './ast/shader-sections';
39
+ import { makeExpression } from './ast/manipulate';
40
+ import { ensure } from './util/ensure';
41
+ import { SourceType } from './nodes/code-nodes';
42
+ import { nodeInput } from './nodes/core-node';
43
+ import { makeId } from './util/id';
44
+ import { coreParsers } from './parsers';
45
+ import { toGlsl } from './evaluate';
46
+ import { MAGIC_OUTPUT_STMTS, NodeType } from './graph-types';
47
+ var log = function () {
48
+ var _a;
49
+ var args = [];
50
+ for (var _i = 0; _i < arguments.length; _i++) {
51
+ args[_i] = arguments[_i];
52
+ }
53
+ return (_a = console.log).call.apply(_a, __spreadArray([console, '\x1b[31m(core.graph)\x1b[0m'], __read(args), false));
54
+ };
55
+ export var isDataNode = function (node) {
56
+ return 'value' in node;
57
+ };
58
+ export var isSourceNode = function (node) {
59
+ return !isDataNode(node);
60
+ };
61
+ export var findNode = function (graph, id) {
62
+ return ensure(graph.nodes.find(function (node) { return node.id === id; }));
63
+ };
64
+ export var doesLinkThruShader = function (graph, node) {
65
+ var edges = graph.edges.filter(function (edge) { return edge.from === node.id; });
66
+ if (edges.length === 0) {
67
+ return false;
68
+ }
69
+ return edges.reduce(function (foundShader, edge) {
70
+ var upstreamNode = ensure(graph.nodes.find(function (node) { return node.id === edge.to; }));
71
+ return (foundShader ||
72
+ // TODO: LARD this probably will introduce some insidius hard to track
73
+ // down bug, as I try to pull toon and phong up out of core, I need to
74
+ // know if a graph links through a "shader" which now means somehting
75
+ // different... does a config object need isShader? Can we compute it from
76
+ // inputs/ outputs/source?
77
+ (upstreamNode.sourceType !== SourceType.EXPRESSION &&
78
+ upstreamNode.type !== NodeType.OUTPUT) ||
79
+ doesLinkThruShader(graph, upstreamNode));
80
+ }, false);
81
+ };
82
+ export var nodeName = function (node) {
83
+ return 'main_' + node.name.replace(/[^a-zA-Z0-9]/g, ' ').replace(/ +/g, '_');
84
+ };
85
+ export var mangleName = function (name, node) {
86
+ // Mangle names by using the next stage id, if present
87
+ var id = ('nextStageNodeId' in node && node.nextStageNodeId) || node.id;
88
+ return "".concat(name, "_").concat(id);
89
+ };
90
+ export var mangleVar = function (name, engine, node) {
91
+ return engine.preserve.has(name) ? name : mangleName(name, node);
92
+ };
93
+ export var mangleEntireProgram = function (ast, node, engine) {
94
+ renameBindings(ast.scopes[0], function (name, n) {
95
+ return n.doNotDescope ? name : mangleVar(name, engine, node);
96
+ });
97
+ mangleMainFn(ast, node);
98
+ };
99
+ export var mangleMainFn = function (ast, node) {
100
+ renameFunctions(ast.scopes[0], function (name) {
101
+ return name === 'main' ? nodeName(node) : mangleName(name, node);
102
+ });
103
+ };
104
+ /**
105
+ * Create the inputs on a node from the properties. This used to be done at
106
+ * context time. Doing it at node creation time lets us auto-bake edges into
107
+ * the node at initial graph creation time.
108
+ */
109
+ export var prepopulatePropertyInputs = function (node) { return (__assign(__assign({}, node), { inputs: __spreadArray(__spreadArray([], __read(node.inputs), false), __read((node.config.properties || []).map(function (property) {
110
+ return nodeInput(property.displayName, "property_".concat(property.property), 'property', property.type, new Set(['data']), !!property.fillerName, // bakeable
111
+ property.property);
112
+ })), false) })); };
113
+ /**
114
+ * Recursively filter the graph, starting from a specific node, looking for
115
+ * nodes and edges that match predicates. This function returns the inputs for
116
+ * matched edges, not the edges themselves, as a convenience for the only
117
+ * consumer of this function, which is finding input names to use as uniforms.
118
+ *
119
+ * Inputs can only be filtered if the graph context has been computed, since
120
+ * inputs aren't created until then.
121
+ */
122
+ export var filterGraphFromNode = function (graph, node, predicates, depth) {
123
+ var _a;
124
+ if (depth === void 0) { depth = Infinity; }
125
+ var inputs = node.inputs;
126
+ var inputEdges = graph.edges.filter(function (edge) { return edge.to === node.id; });
127
+ var nodeAcc = __assign({}, (predicates.node && predicates.node(node, inputEdges)
128
+ ? (_a = {}, _a[node.id] = node, _a) : {}));
129
+ return inputEdges.reduce(function (acc, inputEdge) {
130
+ var _a;
131
+ var input = inputs.find(function (i) { return i.id === inputEdge.input; });
132
+ var fromNode = inputEdge
133
+ ? ensure(graph.nodes.find(function (_a) {
134
+ var id = _a.id;
135
+ return id === inputEdge.from;
136
+ }))
137
+ : undefined;
138
+ var inputAcc = __assign(__assign({}, acc.inputs), (input &&
139
+ predicates.input &&
140
+ predicates.input(input, node, inputEdge, fromNode)
141
+ ? (_a = {}, _a[node.id] = __spreadArray(__spreadArray([], __read((acc.inputs[node.id] || [])), false), [input], false), _a) : {}));
142
+ if (inputEdge && fromNode && depth > 1) {
143
+ var result = filterGraphFromNode(graph, fromNode, predicates, depth - 1);
144
+ return {
145
+ nodes: __assign(__assign({}, acc.nodes), result.nodes),
146
+ inputs: __assign(__assign(__assign({}, acc.inputs), inputAcc), result.inputs),
147
+ };
148
+ }
149
+ return __assign(__assign({}, acc), { inputs: __assign(__assign({}, acc.inputs), inputAcc) });
150
+ }, { inputs: {}, nodes: nodeAcc });
151
+ };
152
+ export var collectConnectedNodes = function (graph, node) {
153
+ return filterGraphFromNode(graph, node, { node: function () { return true; } }).nodes;
154
+ };
155
+ export var filterGraphNodes = function (graph, nodes, filter, depth) {
156
+ if (depth === void 0) { depth = Infinity; }
157
+ return nodes.reduce(function (acc, node) {
158
+ var result = filterGraphFromNode(graph, node, filter, depth);
159
+ return {
160
+ nodes: __assign(__assign({}, acc.nodes), result.nodes),
161
+ inputs: __assign(__assign({}, acc.inputs), result.inputs),
162
+ };
163
+ }, {
164
+ nodes: {},
165
+ inputs: {},
166
+ });
167
+ };
168
+ // before data inputs were known by the input.category being node or data. I
169
+ // tried updating inputs to have acepts: [code|data] and "baked" now is there a
170
+ // way to know if we're plugging in code or data?
171
+ export var isDataInput = function (input) {
172
+ return (input.type === 'uniform' || input.type === 'property') && !input.baked;
173
+ };
174
+ export var compileNode = function (engine, graph, edges, engineContext, node, activeIds) {
175
+ var _a, _b;
176
+ if (activeIds === void 0) { activeIds = {}; }
177
+ // THIS DUPLICATES OTHER LINE
178
+ var parser = __assign(__assign({}, (coreParsers[node.type] || coreParsers[NodeType.SOURCE])), (engine.parsers[node.type] || {}));
179
+ var inputs = node.inputs;
180
+ if (!parser) {
181
+ console.error(node);
182
+ throw new Error("No parser found for ".concat(node.name, " (").concat(node.type, ", id ").concat(node.id, ")"));
183
+ }
184
+ var nodeContext = isDataNode(node)
185
+ ? null
186
+ : ensure(engineContext.nodes[node.id], "No node context found for \"".concat(node.name, "\" (id ").concat(node.id, ")!"));
187
+ var _c = (nodeContext || {}), ast = _c.ast, inputFillers = _c.inputFillers;
188
+ if (!inputs) {
189
+ throw new Error("I'm drunk and I think this case should be impossible");
190
+ }
191
+ var compiledIds = activeIds;
192
+ var inputEdges = edges.filter(function (edge) { return edge.to === node.id; });
193
+ if (inputEdges.length) {
194
+ var continuation_1 = emptyShaderSections();
195
+ inputEdges
196
+ .map(function (edge) { return ({
197
+ edge: edge,
198
+ fromNode: ensure(graph.nodes.find(function (node) { return edge.from === node.id; }), "GraphNode for edge ".concat(edge.from, " not found")),
199
+ input: ensure(inputs.find(function (_a) {
200
+ var id = _a.id;
201
+ return id == edge.input;
202
+ }), "GraphNode \"".concat(node.name, "\" has no input ").concat(edge.input, "!\nAvailable:").concat(inputs.map(function (_a) {
203
+ var id = _a.id;
204
+ return id;
205
+ }).join(', '))),
206
+ }); })
207
+ .filter(function (_a) {
208
+ var input = _a.input;
209
+ return !isDataInput(input);
210
+ })
211
+ .forEach(function (_a) {
212
+ var _b;
213
+ var fromNode = _a.fromNode, edge = _a.edge, input = _a.input;
214
+ var _c = __read(compileNode(engine, graph, edges, engineContext, fromNode, activeIds), 3), inputSections = _c[0], fillerAst = _c[1], childIds = _c[2];
215
+ if (!fillerAst) {
216
+ throw new TypeError("Expected a filler ast from node ID ".concat(fromNode.id, " (").concat(fromNode.type, ") but none was returned"));
217
+ }
218
+ continuation_1 = mergeShaderSections(continuation_1, inputSections);
219
+ compiledIds = __assign(__assign({}, compiledIds), childIds);
220
+ var filler;
221
+ var fillerName;
222
+ if (nodeContext) {
223
+ if (input.property) {
224
+ 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"));
225
+ filler = inputFillers[fillerName];
226
+ }
227
+ else {
228
+ filler = inputFillers[input.id];
229
+ }
230
+ if (!filler) {
231
+ console.error('No filler for property', {
232
+ input: input,
233
+ node: node,
234
+ inputFillers: inputFillers,
235
+ fillerName: fillerName,
236
+ });
237
+ throw new Error("Node \"".concat(node.name, "\" has no filler for input \"").concat(input.displayName, "\" named ").concat(fillerName));
238
+ }
239
+ /**
240
+ * +------+ +------+
241
+ * a -- o add o -- o tex |
242
+ * b -- o | +------+
243
+ * +------+
244
+ *
245
+ * This could produce:
246
+ * main_a(v1) + main_b(v2)
247
+ * I guess it has to? or it could produce
248
+ * function add(v1) { return main_a(v1) + main_b(v2); }
249
+ * It can't replace the arg _expression_ in the from shaders, because
250
+ * the expression isn't available there.
251
+ */
252
+ // TODO: This is a hard coded hack for vUv backfilling. It works in
253
+ // the simple case. Doesn't work for hell (based on world position).
254
+ if (filler.backfillArgs &&
255
+ !Array.isArray(fillerAst) &&
256
+ fillerAst.type === 'function_call') {
257
+ // Object.values(filterGraphFromNode(graph, node, {
258
+ // node: (n) => n.type === 'source'
259
+ // }).nodes).forEach(sourceNode => {
260
+ if (fromNode.type === 'source') {
261
+ // @ts-ignore
262
+ fillerAst.args = filler.backfillArgs;
263
+ // const fc = engineContext.nodes[sourceNode.id];
264
+ var fc = engineContext.nodes[fromNode.id];
265
+ var main = Object.values(fc.ast.scopes[0].functions.main)[0].declaration;
266
+ main.prototype.parameters = [
267
+ 'vec2 vv',
268
+ ];
269
+ // @ts-ignore
270
+ var scope = fc.ast.scopes[0];
271
+ renameBindings(scope, function (name, node) {
272
+ return node.type !== 'declaration' && name === 'vUv'
273
+ ? 'vv'
274
+ : name;
275
+ });
276
+ }
277
+ // })
278
+ }
279
+ // Fill in the input! The return value is the new AST of the filled in
280
+ // fromNode.
281
+ nodeContext.ast = filler.filler(fillerAst);
282
+ }
283
+ // log(generate(ast.program));
284
+ });
285
+ // Order matters here! *Prepend* the input nodes to this one, because
286
+ // you have to declare functions in order of use in GLSL
287
+ var sections = mergeShaderSections(continuation_1, isDataNode(node) ||
288
+ node.sourceType === SourceType.EXPRESSION ||
289
+ node.sourceType === SourceType.FN_BODY_FRAGMENT
290
+ ? emptyShaderSections()
291
+ : findShaderSections(ast));
292
+ var filler = isDataNode(node)
293
+ ? makeExpression(toGlsl(node))
294
+ : parser.produceFiller(node, ast);
295
+ return [sections, filler, __assign(__assign({}, compiledIds), (_a = {}, _a[node.id] = node, _a))];
296
+ }
297
+ else {
298
+ // TODO: This duplicates the above branch, and also does this mean we
299
+ // recalculate the shader sections and filler for every edge? Can I move
300
+ // these lines above the loop?
301
+ var sections = isDataNode(node) ||
302
+ node.sourceType === SourceType.EXPRESSION ||
303
+ node.sourceType === SourceType.FN_BODY_FRAGMENT
304
+ ? emptyShaderSections()
305
+ : findShaderSections(ast);
306
+ var filler = isDataNode(node)
307
+ ? makeExpression(toGlsl(node))
308
+ : parser.produceFiller(node, ast);
309
+ return [sections, filler, __assign(__assign({}, compiledIds), (_b = {}, _b[node.id] = node, _b))];
310
+ }
311
+ };
312
+ export var compileGraph = function (engineContext, engine, graph) {
313
+ // computeGraphContext(engineContext, engine, graph);
314
+ var outputFrag = graph.nodes.find(function (node) { return node.type === 'output' && node.stage === 'fragment'; });
315
+ if (!outputFrag) {
316
+ throw new Error('No fragment output in graph');
317
+ }
318
+ var _a = __read(compileNode(engine, graph, graph.edges, engineContext, outputFrag), 3), fragment = _a[0], fragmentIds = _a[2];
319
+ var outputVert = graph.nodes.find(function (node) { return node.type === 'output' && node.stage === 'vertex'; });
320
+ if (!outputVert) {
321
+ throw new Error('No vertex output in graph');
322
+ }
323
+ var vertexIds = collectConnectedNodes(graph, outputVert);
324
+ // Some fragment shaders reference vertex shaders which may not have been
325
+ // given edges in the graph. Build invisible edges from these vertex nodes to
326
+ // the hidden "mainStmts" input on the output node, which inlines the function
327
+ // calls to those vertex main() statements and includes them in the output
328
+ var orphanNodes = graph.nodes.filter(function (node) {
329
+ return isSourceNode(node) &&
330
+ node.stage === 'vertex' &&
331
+ node.nextStageNodeId &&
332
+ fragmentIds[node.nextStageNodeId] &&
333
+ !vertexIds[node.id];
334
+ });
335
+ var orphanEdges = orphanNodes.map(function (node) { return ({
336
+ id: makeId(),
337
+ from: node.id,
338
+ to: outputVert.id,
339
+ output: 'main',
340
+ input: "filler_".concat(MAGIC_OUTPUT_STMTS),
341
+ stage: 'vertex',
342
+ category: 'code',
343
+ }); });
344
+ var _b = __read(compileNode(engine, graph, __spreadArray(__spreadArray([], __read(graph.edges), false), __read(orphanEdges), false), engineContext, outputVert), 2), vertex = _b[0];
345
+ // Every compileNode returns the AST so far, as well as the filler for the
346
+ // next node with inputs. On the final step, we discard the filler
347
+ return {
348
+ fragment: fragment,
349
+ vertex: vertex,
350
+ outputFrag: outputFrag,
351
+ outputVert: outputVert,
352
+ orphanNodes: orphanNodes,
353
+ activeNodeIds: new Set(__spreadArray(__spreadArray(__spreadArray([], __read(Object.keys(vertexIds)), false), __read(Object.keys(fragmentIds)), false), __read(orphanNodes.map(function (node) { return node.id; })), false)),
354
+ };
355
+ };
356
+ /**
357
+ * Find engine nodes to set properties on, like find a Physical node so
358
+ * consumers can set physicalNode.myProperty = 123.
359
+ *
360
+ * Finds all active nodes in the graph that have inputs that are properties,
361
+ * which currently means it will find all active engine nodes.
362
+ */
363
+ export var collectNodeProperties = function (graph) {
364
+ var nodesWithProperties = {
365
+ node: function (node) {
366
+ var _a;
367
+ return 'config' in node &&
368
+ 'properties' in node.config &&
369
+ !!((_a = node.config.properties) === null || _a === void 0 ? void 0 : _a.length);
370
+ },
371
+ input: function (input) { return !!input.property; },
372
+ };
373
+ var outputFrag = graph.nodes.find(function (node) { return node.type === 'output' && node.stage === 'fragment'; });
374
+ var outputVert = graph.nodes.find(function (node) { return node.type === 'output' && node.stage === 'vertex'; });
375
+ var fragProperties = filterGraphFromNode(graph, outputFrag, nodesWithProperties);
376
+ var vertProperties = filterGraphFromNode(graph, outputVert, nodesWithProperties);
377
+ return {
378
+ nodes: __assign(__assign({}, fragProperties.nodes), vertProperties.nodes),
379
+ inputs: __assign(__assign({}, fragProperties.inputs), vertProperties.inputs),
380
+ };
381
+ };