@shaderfrog/core 2.0.0-beta.3 → 3.0.0

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 (49) hide show
  1. package/README.md +2 -2
  2. package/engine.d.ts +39 -3
  3. package/engine.js +5 -0
  4. package/graph/code-nodes.d.ts +8 -2
  5. package/graph/code-nodes.js +5 -1
  6. package/graph/context.d.ts +8 -6
  7. package/graph/context.js +53 -50
  8. package/graph/data-nodes.d.ts +15 -6
  9. package/graph/data-nodes.js +1 -1
  10. package/graph/evaluate.js +7 -0
  11. package/graph/graph-types.d.ts +26 -6
  12. package/graph/graph-types.js +69 -0
  13. package/graph/graph.d.ts +35 -4
  14. package/graph/graph.js +187 -143
  15. package/graph/graph.test.js +244 -54
  16. package/graph/parsers.d.ts +4 -12
  17. package/graph/parsers.js +32 -18
  18. package/graph/shader-sections.d.ts +31 -14
  19. package/graph/shader-sections.js +93 -19
  20. package/package.json +4 -4
  21. package/plugins/babylon/bablyengine.js +9 -10
  22. package/plugins/playcanvas/playengine.js +9 -10
  23. package/plugins/three/importers.d.ts +1 -0
  24. package/plugins/three/importers.js +49 -1
  25. package/plugins/three/threngine.d.ts +6 -4
  26. package/plugins/three/threngine.js +59 -39
  27. package/plugins/three/threngine.test.js +51 -4
  28. package/strategy/assignmentTo.d.ts +10 -0
  29. package/strategy/assignmentTo.js +35 -0
  30. package/strategy/declarationOf.js +2 -2
  31. package/strategy/hardCode.js +1 -4
  32. package/strategy/index.d.ts +1 -1
  33. package/strategy/index.js +1 -1
  34. package/strategy/inject.js +3 -4
  35. package/strategy/namedAttribute.js +2 -2
  36. package/strategy/strategy.d.ts +30 -5
  37. package/strategy/strategy.js +1 -1
  38. package/strategy/stratgies.test.js +59 -31
  39. package/strategy/texture2D.js +20 -22
  40. package/strategy/uniform.js +54 -56
  41. package/strategy/variable.js +5 -5
  42. package/util/ast.d.ts +9 -4
  43. package/util/ast.js +37 -8
  44. package/util/ast.test.d.ts +1 -0
  45. package/util/ast.test.js +14 -0
  46. package/util/indexByid.d.ts +4 -0
  47. package/util/indexByid.js +18 -0
  48. package/util/whitespace.d.ts +2 -0
  49. package/util/whitespace.js +22 -3
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
@@ -1,10 +1,10 @@
1
1
  import { Program } from '@shaderfrog/glsl-parser/ast';
2
2
  import { MergeOptions } from './graph/shader-sections';
3
3
  import { Graph, ShaderStage } from './graph/graph-types';
4
- import { NodePosition } from './graph/base-node';
4
+ import { NodeInput, NodePosition } from './graph/base-node';
5
5
  import { DataNode, UniformDataType } from './graph/data-nodes';
6
6
  import { CodeNode, NodeProperty, SourceNode } from './graph/code-nodes';
7
- import { NodeContext } from './graph/context';
7
+ import { NodeContext, NodeContexts } from './graph/context';
8
8
  import { NodeParser } from './graph/parsers';
9
9
  export declare enum EngineNodeType {
10
10
  toon = "toon",
@@ -30,9 +30,44 @@ export interface Engine {
30
30
  [EngineNodeType.toon]?: ToonNodeConstructor;
31
31
  };
32
32
  }
33
- export type EngineContext = {
33
+ export type EngineContext<T = any> = {
34
34
  engine: string;
35
35
  nodes: Record<string, NodeContext>;
36
+ runtime: T;
37
+ debuggingNonsense: {
38
+ vertexSource?: string;
39
+ vertexPreprocessed?: string;
40
+ fragmentPreprocessed?: string;
41
+ fragmentSource?: string;
42
+ };
43
+ };
44
+ export declare const extendNodeContext: (context: EngineContext, nodeId: string, nodeContext: Partial<NodeContext>) => {
45
+ nodes: {
46
+ [x: string]: NodeContext | {
47
+ id?: string;
48
+ name?: string;
49
+ computedSource?: string;
50
+ ast: import("@shaderfrog/glsl-parser/ast").AstNode | Program;
51
+ inputs?: NodeInput[];
52
+ inputFillers: import("./strategy").InputFillers;
53
+ errors?: import("./graph/context").NodeErrors;
54
+ mainFn?: import("@shaderfrog/glsl-parser/ast").FunctionNode;
55
+ };
56
+ };
57
+ engine: string;
58
+ runtime: any;
59
+ debuggingNonsense: {
60
+ vertexSource?: string;
61
+ vertexPreprocessed?: string;
62
+ fragmentPreprocessed?: string;
63
+ fragmentSource?: string;
64
+ };
65
+ };
66
+ export declare const extendNodesContext: (context: EngineContext, nodesContext: NodeContexts) => {
67
+ nodes: {
68
+ [x: string]: NodeContext;
69
+ };
70
+ engine: string;
36
71
  runtime: any;
37
72
  debuggingNonsense: {
38
73
  vertexSource?: string;
@@ -47,6 +82,7 @@ export type EngineImporter = {
47
82
  edgeMap: {
48
83
  [oldInput: string]: string;
49
84
  };
85
+ code?: Record<string, string>;
50
86
  };
51
87
  export type EngineImporters = {
52
88
  [engine: string]: EngineImporter;
package/engine.js CHANGED
@@ -56,6 +56,11 @@ export var EngineNodeType;
56
56
  EngineNodeType["shader"] = "shader";
57
57
  EngineNodeType["binary"] = "binary";
58
58
  })(EngineNodeType || (EngineNodeType = {}));
59
+ export var extendNodeContext = function (context, nodeId, nodeContext) {
60
+ var _a;
61
+ return (__assign(__assign({}, context), { nodes: __assign(__assign({}, context.nodes), (_a = {}, _a[nodeId] = __assign(__assign({}, (context.nodes[nodeId] || {})), nodeContext), _a)) }));
62
+ };
63
+ export var extendNodesContext = function (context, nodesContext) { return (__assign(__assign({}, context), { nodes: __assign(__assign({}, context.nodes), nodesContext) })); };
59
64
  // type EdgeUpdates = { [edgeId: string]: { oldInput: string; newInput: string } };
60
65
  export var convertNode = function (node, converter) {
61
66
  log("Converting ".concat(node.name, " (").concat(node.id, ")"));
@@ -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
@@ -12,26 +12,28 @@ import { InputFillers } from './parsers';
12
12
  export type NodeContext = {
13
13
  id?: string;
14
14
  name?: string;
15
- source?: string;
15
+ computedSource?: string;
16
16
  ast: AstNode | Program;
17
17
  inputs?: NodeInput[];
18
18
  inputFillers: InputFillers;
19
19
  errors?: NodeErrors;
20
+ mainFn?: FunctionNode;
20
21
  };
22
+ export type NodeContexts = Record<string, NodeContext>;
21
23
  export type NodeErrors = {
22
24
  type: 'errors';
23
25
  nodeId: string;
24
26
  errors: (GlslSyntaxError | string)[];
25
27
  };
26
28
  export declare const isError: (test: any) => test is NodeErrors;
27
- export declare const computeContextForNodes: (engineContext: EngineContext, engine: Engine, graph: Graph, nodes: GraphNode[]) => Promise<NodeErrors | Record<string, NodeContext>>;
29
+ export declare const computeContextForNodes: (engineContext: EngineContext, engine: Engine, graph: Graph, nodes: GraphNode[]) => Promise<NodeErrors | NodeContexts>;
28
30
  /**
29
31
  * Compute the context for every node in the graph, done on initial graph load
30
32
  * to compute the inputs/outputs for every node
31
33
  */
32
- export declare const computeAllContexts: (engineContext: EngineContext, engine: Engine, graph: Graph) => Promise<NodeErrors | undefined>;
34
+ export declare const computeAllContexts: (engineContext: EngineContext, engine: Engine, graph: Graph) => Promise<NodeErrors | NodeContexts>;
33
35
  /**
34
36
  * Compute the contexts for nodes starting from the outputs, working backwards.
35
37
  * Used to only (re)-compute context for any actively used nodes
36
38
  */
37
- export declare const computeGraphContext: (engineContext: EngineContext, engine: Engine, graph: Graph) => Promise<NodeErrors | undefined>;
39
+ export declare const computeGraphContext: (engineContext: EngineContext, engine: Engine, graph: Graph) => Promise<NodeErrors | NodeContexts>;
package/graph/context.js CHANGED
@@ -71,10 +71,12 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
71
71
  return to.concat(ar || Array.prototype.slice.call(from));
72
72
  };
73
73
  import groupBy from 'lodash.groupby';
74
+ import { extendNodeContext, extendNodesContext, } from '../engine';
74
75
  import { mapInputName, SourceType } from './code-nodes';
75
76
  import { NodeType } from './graph-types';
76
- import { collectConnectedNodes, filterGraphFromNode, findLinkedNode, findLinkedVertexNodes, isSourceNode, mangleEntireProgram, } from './graph';
77
+ import { collectConnectedNodes, filterGraphFromNode, findLinkedNode, findLinkedVertexNodes, isSourceNode, mangleEntireProgram, shouldNodeHaveMainFn, } from './graph';
77
78
  import { coreParsers } from './parsers';
79
+ import { findMain } from '../util/ast';
78
80
  var makeError = function (nodeId) {
79
81
  var errors = [];
80
82
  for (var _i = 1; _i < arguments.length; _i++) {
@@ -97,24 +99,32 @@ var collapseNodeInputs = function (node, updatedInputs) {
97
99
  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
100
  };
99
101
  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;
102
+ var parser, sibling, onBeforeCompile, manipulateAst, updatedNodeContext, updatedContext, inputEdges, mainFn, ast, dataInputs, computedInputs, nodeContext;
101
103
  return __generator(this, function (_a) {
102
104
  switch (_a.label) {
103
105
  case 0:
104
106
  parser = __assign(__assign({}, (coreParsers[node.type] || coreParsers[NodeType.SOURCE])), (engine.parsers[node.type] || {}));
105
107
  sibling = findLinkedNode(graph, node.id);
106
108
  onBeforeCompile = parser.onBeforeCompile, manipulateAst = parser.manipulateAst;
109
+ updatedContext = engineContext;
107
110
  if (!onBeforeCompile) return [3 /*break*/, 2];
108
111
  return [4 /*yield*/, onBeforeCompile(graph, engineContext, node, sibling)];
109
112
  case 1:
110
- _a.sent();
113
+ updatedNodeContext = _a.sent();
114
+ if (updatedNodeContext) {
115
+ updatedContext = extendNodeContext(engineContext, node.id, updatedNodeContext);
116
+ }
111
117
  _a.label = 2;
112
118
  case 2:
113
119
  inputEdges = graph.edges.filter(function (edge) { return edge.to === node.id; });
114
120
  try {
115
- ast = parser.produceAst(engineContext, engine, graph, node, inputEdges);
121
+ ast = parser.produceAst(updatedContext, engine, graph, node, inputEdges);
122
+ // Find the main function before mangling
123
+ if (shouldNodeHaveMainFn(node)) {
124
+ mainFn = findMain(ast);
125
+ }
116
126
  if (manipulateAst) {
117
- ast = manipulateAst(engineContext, engine, graph, ast, inputEdges, node, sibling);
127
+ ast = manipulateAst(updatedContext, engine, graph, ast, inputEdges, node, sibling);
118
128
  }
119
129
  }
120
130
  catch (error) {
@@ -126,23 +136,11 @@ var computeNodeContext = function (engineContext, engine, graph, node) { return
126
136
  return input.bakeable && (fromNode === null || fromNode === void 0 ? void 0 : fromNode.type) === 'source';
127
137
  },
128
138
  }, 1).inputs[node.id] || [], 'id');
129
- computedInputs = parser.findInputs(engineContext, ast, inputEdges, node, sibling);
139
+ computedInputs = parser.findInputs(updatedContext, ast, inputEdges, node, sibling);
130
140
  node.inputs = collapseNodeInputs(node, computedInputs.map(function (_a) {
131
141
  var _b = __read(_a, 1), i = _b[0];
132
142
  return (__assign(__assign({}, i), { displayName: mapInputName(node, i) }));
133
143
  })).map(function (input) { return (__assign(__assign({}, input), (input.id in dataInputs ? { baked: true } : {}))); });
134
- nodeContext = {
135
- ast: ast,
136
- id: node.id,
137
- inputFillers: computedInputs.reduce(function (acc, _a) {
138
- 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] = {
141
- filler: filler,
142
- args: args,
143
- }, _b)));
144
- }, {}),
145
- };
146
144
  // Skip mangling if the node tells us to, which probably means it's an engine
147
145
  // node where we don't care about renaming all the variables, or if it's
148
146
  // an expression, where we want to be in the context of other variables
@@ -152,6 +150,20 @@ var computeNodeContext = function (engineContext, engine, graph, node) { return
152
150
  node.sourceType !== SourceType.FN_BODY_FRAGMENT) {
153
151
  mangleEntireProgram(engine, ast, node, findLinkedNode(graph, node.id));
154
152
  }
153
+ nodeContext = __assign(__assign({}, (updatedNodeContext || {})), { ast: ast, id: node.id, mainFn: mainFn, inputFillers: computedInputs.reduce(function (acc, _a) {
154
+ var _b;
155
+ var _c = __read(_a, 4), input = _c[0], filler = _c[1], fillerArgs = _c[2], fillerStmt = _c[3];
156
+ // This is intentionally broken out into an explicit return to force
157
+ // this type declaration. Inlining the object in [input.id]: {...}
158
+ // doesn't force it to be an InputFillerGroup, and it can contain extra
159
+ // arguments by accident
160
+ var fillerGroup = {
161
+ filler: filler,
162
+ fillerArgs: fillerArgs,
163
+ fillerStmt: fillerStmt,
164
+ };
165
+ return __assign(__assign({}, acc), (_b = {}, _b[input.id] = fillerGroup, _b));
166
+ }, {}) });
155
167
  return [2 /*return*/, nodeContext];
156
168
  }
157
169
  });
@@ -162,22 +174,22 @@ export var computeContextForNodes = function (engineContext, engine, graph, node
162
174
  .filter(isSourceNode)
163
175
  .reduce(function (ctx, node) { return __awaiter(void 0, void 0, void 0, function () {
164
176
  var context, nodeContextOrError;
165
- return __generator(this, function (_a) {
166
- switch (_a.label) {
177
+ var _a;
178
+ return __generator(this, function (_b) {
179
+ switch (_b.label) {
167
180
  case 0: return [4 /*yield*/, ctx];
168
181
  case 1:
169
- context = _a.sent();
182
+ context = _b.sent();
170
183
  if (isError(context)) {
171
184
  return [2 /*return*/, context];
172
185
  }
173
186
  return [4 /*yield*/, computeNodeContext(engineContext, engine, graph, node)];
174
187
  case 2:
175
- nodeContextOrError = _a.sent();
188
+ nodeContextOrError = _b.sent();
176
189
  if (isError(nodeContextOrError)) {
177
190
  return [2 /*return*/, nodeContextOrError];
178
191
  }
179
- context[node.id] = __assign(__assign({}, (context[node.id] || {})), nodeContextOrError);
180
- return [2 /*return*/, context];
192
+ return [2 /*return*/, __assign(__assign({}, context), (_a = {}, _a[node.id] = __assign(__assign({}, (context[node.id] || {})), nodeContextOrError), _a))];
181
193
  }
182
194
  });
183
195
  }); }, Promise.resolve(engineContext.nodes))];
@@ -187,26 +199,18 @@ export var computeContextForNodes = function (engineContext, engine, graph, node
187
199
  * Compute the context for every node in the graph, done on initial graph load
188
200
  * to compute the inputs/outputs for every node
189
201
  */
190
- export var computeAllContexts = function (engineContext, engine, graph) { return __awaiter(void 0, void 0, void 0, function () {
191
- var result;
192
- return __generator(this, function (_a) {
193
- switch (_a.label) {
194
- case 0: return [4 /*yield*/, computeContextForNodes(engineContext, engine, graph, graph.nodes)];
195
- case 1:
196
- result = _a.sent();
197
- if (isError(result)) {
198
- return [2 /*return*/, result];
199
- }
200
- return [2 /*return*/];
201
- }
202
- });
203
- }); };
202
+ export var computeAllContexts = function (engineContext, engine, graph) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
203
+ switch (_a.label) {
204
+ case 0: return [4 /*yield*/, computeContextForNodes(engineContext, engine, graph, graph.nodes)];
205
+ case 1: return [2 /*return*/, _a.sent()];
206
+ }
207
+ }); }); };
204
208
  /**
205
209
  * Compute the contexts for nodes starting from the outputs, working backwards.
206
210
  * Used to only (re)-compute context for any actively used nodes
207
211
  */
208
212
  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;
213
+ var outputFrag, outputVert, vertexes, fragments, vertexIds, unlinkedNodes, vertNodesOrError, updatedContext, finalNodeContextOrError;
210
214
  return __generator(this, function (_a) {
211
215
  switch (_a.label) {
212
216
  case 0:
@@ -218,26 +222,25 @@ export var computeGraphContext = function (engineContext, engine, graph) { retur
218
222
  if (!outputVert) {
219
223
  throw new Error('No vertex output in graph');
220
224
  }
221
- vertexIds = collectConnectedNodes(graph, outputVert);
222
- fragmentIds = collectConnectedNodes(graph, outputFrag);
223
- unlinkedNodes = findLinkedVertexNodes(graph, vertexIds);
225
+ vertexes = collectConnectedNodes(graph, outputVert);
226
+ fragments = collectConnectedNodes(graph, outputFrag);
227
+ vertexIds = new Set(Object.keys(vertexes));
228
+ unlinkedNodes = findLinkedVertexNodes(graph, vertexes);
224
229
  return [4 /*yield*/, computeContextForNodes(engineContext, engine, graph, __spreadArray(__spreadArray([
225
230
  outputVert
226
- ], __read(Object.values(vertexIds).filter(function (node) { return node.id !== outputVert.id; })), false), __read(unlinkedNodes), false))];
231
+ ], __read(Object.values(vertexes).filter(function (node) { return node.id !== outputVert.id; })), false), __read(unlinkedNodes), false))];
227
232
  case 1:
228
233
  vertNodesOrError = _a.sent();
229
234
  if (isError(vertNodesOrError)) {
230
235
  return [2 /*return*/, vertNodesOrError];
231
236
  }
232
- return [4 /*yield*/, computeContextForNodes(engineContext, engine, graph, __spreadArray([
237
+ updatedContext = extendNodesContext(engineContext, vertNodesOrError);
238
+ return [4 /*yield*/, computeContextForNodes(updatedContext, engine, graph, __spreadArray([
233
239
  outputFrag
234
- ], __read(Object.values(fragmentIds).filter(function (node) { return node.id !== outputFrag.id; })), false))];
240
+ ], __read(Object.values(fragments).filter(function (node) { return node.id !== outputFrag.id && !vertexIds.has(node.id); })), false))];
235
241
  case 2:
236
- fragNodesOrError = _a.sent();
237
- if (isError(fragNodesOrError)) {
238
- return [2 /*return*/, fragNodesOrError];
239
- }
240
- return [2 /*return*/];
242
+ finalNodeContextOrError = _a.sent();
243
+ return [2 /*return*/, finalNodeContextOrError];
241
244
  }
242
245
  });
243
246
  }); };
@@ -20,17 +20,26 @@ export declare const numberNode: (id: string, name: string, position: NodePositi
20
20
  }) => NumberNode;
21
21
  export type NumberDataUniform = Pick<NumberNode, 'type' | 'value' | 'name' | 'range' | 'stepper'>;
22
22
  export declare const numberUniformData: (name: string, value: string, range?: [string | number, string | number], stepper?: string | number) => NumberDataUniform;
23
- export type AssetVersionNodeData = {
24
- assetId: number;
25
- versionId: number;
23
+ export type TextureNodeValueData = {
24
+ assetId?: number;
25
+ versionId?: number;
26
+ properties?: {
27
+ repeatTexure: boolean;
28
+ repeat?: {
29
+ x: number;
30
+ y: number;
31
+ };
32
+ anisotropy?: number;
33
+ encoding?: 'srgb';
34
+ };
26
35
  };
27
36
  export interface TextureNode extends BaseNode {
28
37
  type: 'texture';
29
- value?: AssetVersionNodeData;
38
+ value?: TextureNodeValueData;
30
39
  }
31
- export declare const textureNode: (id: string, name: string, position: NodePosition, value: AssetVersionNodeData) => TextureNode;
40
+ export declare const textureNode: (id: string, name: string, position: NodePosition, value?: TextureNodeValueData) => TextureNode;
32
41
  export type TextureDataUniform = Pick<TextureNode, 'type' | 'value' | 'name'>;
33
- export declare const textureUniformData: (name: string, value: AssetVersionNodeData) => TextureDataUniform;
42
+ export declare const textureUniformData: (name: string, value: TextureNodeValueData) => TextureDataUniform;
34
43
  export interface SamplerCubeNode extends BaseNode {
35
44
  type: 'samplerCube';
36
45
  value: string;
@@ -40,7 +40,7 @@ export var textureNode = function (id, name, position, value) { return ({
40
40
  id: id,
41
41
  name: name,
42
42
  position: position,
43
- value: value,
43
+ value: __assign(__assign({}, (value || { assetId: undefined, versionId: undefined })), { properties: (value === null || value === void 0 ? void 0 : value.properties) || { repeatTexure: true } }),
44
44
  inputs: [],
45
45
  outputs: [
46
46
  {
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) {
@@ -4,12 +4,11 @@ import { SourceNode } from './code-nodes';
4
4
  /**
5
5
  * Core graph types.
6
6
  *
7
- * Originally abstracted out of graph.ts to avoid a circular
8
- * dependency between graph.ts and parsers.ts. Both files need these types at
9
- * module initialization time, and without this third file, the types will be
10
- * undefined in either graph/parsers.ts at init time. If the types were only
11
- * used at runtime it would be fine, because the circular depenency is resolved
12
- * by then.
7
+ * Originally abstracted out of graph.ts to avoid a circular dependency between
8
+ * graph.ts and parsers.ts. Both files need these types at module initialization
9
+ * time, and without this third file, the types will be undefined in either
10
+ * graph/parsers.ts at init time. If the types were only used at runtime it
11
+ * would be fine, because the circular depenency is resolved by then.
13
12
  */
14
13
  export type ShaderStage = 'fragment' | 'vertex';
15
14
  /**
@@ -35,4 +34,25 @@ export interface Graph {
35
34
  nodes: GraphNode[];
36
35
  edges: Edge[];
37
36
  }
37
+ export type EdgesByNode = {
38
+ [nodeId: string]: {
39
+ from: Edge[];
40
+ to: {
41
+ edges: Edge[];
42
+ edgesByInput: {
43
+ [inputId: string]: Edge;
44
+ };
45
+ };
46
+ };
47
+ };
48
+ export type Grindex = {
49
+ nodes: {
50
+ [nodeId: string]: GraphNode;
51
+ };
52
+ edges: {
53
+ [edgeId: string]: Edge;
54
+ };
55
+ edgesByNode: EdgesByNode;
56
+ };
57
+ export declare const computeGrindex: (graph: Graph) => Grindex;
38
58
  export declare const MAGIC_OUTPUT_STMTS = "mainStmts";
@@ -1,3 +1,40 @@
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 indexById from '../util/indexByid';
1
38
  /**
2
39
  * The type applied to the edge representing a link between node stages
3
40
  */
@@ -19,4 +56,36 @@ export var NodeType;
19
56
  NodeType["BINARY"] = "binary";
20
57
  NodeType["SOURCE"] = "source";
21
58
  })(NodeType || (NodeType = {}));
59
+ var lastGraph;
60
+ var lastGrindex;
61
+ export var computeGrindex = function (graph) {
62
+ // Poor programmer's memoization
63
+ if (graph === lastGraph && lastGrindex) {
64
+ return lastGrindex;
65
+ }
66
+ lastGraph = graph;
67
+ lastGrindex = {
68
+ nodes: indexById(graph.nodes),
69
+ edges: indexById(graph.edges),
70
+ edgesByNode: graph.edges.reduce(function (acc, edge) {
71
+ var _a, _b, _c;
72
+ var _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
73
+ var to = edge.to, from = edge.from;
74
+ return __assign(__assign({}, acc), (_a = {}, _a[to] = {
75
+ to: {
76
+ edges: __spreadArray(__spreadArray([], __read((((_e = (_d = acc[to]) === null || _d === void 0 ? void 0 : _d.to) === null || _e === void 0 ? void 0 : _e.edges) || [])), false), [edge], false),
77
+ edgesByInput: __assign(__assign({}, (_g = (_f = acc[to]) === null || _f === void 0 ? void 0 : _f.to) === null || _g === void 0 ? void 0 : _g.edgesByInput), (_b = {}, _b[edge.input] = edge, _b)),
78
+ },
79
+ from: ((_h = acc[to]) === null || _h === void 0 ? void 0 : _h.from) || [],
80
+ }, _a[from] = {
81
+ to: {
82
+ edges: ((_k = (_j = acc[from]) === null || _j === void 0 ? void 0 : _j.to) === null || _k === void 0 ? void 0 : _k.edges) || [],
83
+ edgesByInput: __assign(__assign({}, (_m = (_l = acc[from]) === null || _l === void 0 ? void 0 : _l.to) === null || _m === void 0 ? void 0 : _m.edgesByInput), (_c = {}, _c[edge.input] = edge, _c)),
84
+ },
85
+ from: __spreadArray(__spreadArray([], __read((((_o = acc[from]) === null || _o === void 0 ? void 0 : _o.from) || [])), false), [edge], false),
86
+ }, _a));
87
+ }, {}),
88
+ };
89
+ return lastGrindex;
90
+ };
22
91
  export var MAGIC_OUTPUT_STMTS = 'mainStmts';
package/graph/graph.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Program } from '@shaderfrog/glsl-parser/ast';
2
2
  import { Engine, EngineContext } from '../engine';
3
- import { NodeErrors } from './context';
3
+ import { NodeContexts, NodeErrors } from './context';
4
4
  import { ShaderSections } from './shader-sections';
5
5
  import { FrogProgram } from '../util/ast';
6
6
  import { DataNode } from './data-nodes';
@@ -11,16 +11,45 @@ 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
+ /**
30
+ * A binary node automatically adds/removes inputs based on how many edges
31
+ * connect to it. If a binary node has edges to "a" and "b", removing the edge
32
+ * to "a" means the edge to "b" needs to be moved down to the "a" one. This
33
+ * function essentially groups edges by target node id, and resets the edge
34
+ * target to its index. This doesn't feel good to do here but I don't have a
35
+ * better idea at the moment. One reason the inputs to binary nodes are
36
+ * automatically updated after compile, but the edges are updated here
37
+ * at the editor layer, before compile. This also hard codes assumptions about
38
+ * (binary) node inputs into the graph, namely they can't have blank inputs.
39
+ */
40
+ export declare const collapseBinaryGraphEdges: (graph: Graph) => Graph;
41
+ /**
42
+ * Restrict edges so that an input handle can't have multiple edges going to it
43
+ */
44
+ export declare const addEdgeAndPruneRestrictions: (edges: Edge[], newEdge: Edge) => Edge[];
45
+ /**
46
+ * Adds an edge to the graph and enforces graph edge business logic rules:
47
+ * - Makes sure "binary" (add/multiply) nodes edges are collapsed
48
+ * - Makes sure two edges can't flow into the same input.
49
+ * See also editor/flow-helpers.ts
50
+ */
51
+ export declare const addGraphEdge: (graph: Graph, newEdge: Edge) => Graph;
52
+ export declare const findLinkedNode: (graph: Graph, nodeId: string) => SourceNode | undefined;
24
53
  /**
25
54
  * Find any unconnected vertex nodes linked to collected fragment nodes
26
55
  */
@@ -33,7 +62,7 @@ export type Predicates = {
33
62
  export type SearchResult = {
34
63
  nodes: Record<string, GraphNode>;
35
64
  inputs: Record<string, NodeInput[]>;
36
- edges: Edge[];
65
+ edges: Record<string, Edge>;
37
66
  };
38
67
  export declare const consSearchResult: () => SearchResult;
39
68
  export declare const mergeSearchResults: (a: SearchResult, b: SearchResult) => SearchResult;
@@ -80,11 +109,13 @@ export declare const compileGraph: (engineContext: EngineContext, engine: Engine
80
109
  export declare const collectNodeProperties: (graph: Graph) => SearchResult;
81
110
  export type IndexedDataInputs = Record<string, NodeInput[]>;
82
111
  export type CompileResult = {
112
+ updatedNodeContext: NodeContexts;
83
113
  fragmentResult: string;
84
114
  vertexResult: string;
85
115
  compileResult: CompileGraphResult;
86
116
  dataNodes: Record<string, GraphNode>;
87
117
  dataInputs: IndexedDataInputs;
118
+ compileMs: string;
88
119
  };
89
120
  export declare const compileSource: (graph: Graph, engine: Engine, ctx: EngineContext) => Promise<CompileResult | NodeErrors>;
90
121
  export {};