@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
@@ -0,0 +1,164 @@
1
+ var __read = (this && this.__read) || function (o, n) {
2
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
3
+ if (!m) return o;
4
+ var i = m.call(o), r, ar = [], e;
5
+ try {
6
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
7
+ }
8
+ catch (error) { e = { error: error }; }
9
+ finally {
10
+ try {
11
+ if (r && !r.done && (m = i["return"])) m.call(i);
12
+ }
13
+ finally { if (e) throw e.error; }
14
+ }
15
+ return ar;
16
+ };
17
+ import { parser } from '@shaderfrog/glsl-parser';
18
+ import { generate } from '@shaderfrog/glsl-parser';
19
+ import { applyStrategy, StrategyType } from './strategy';
20
+ import * as graphModule from './graph';
21
+ import { makeExpression } from './ast/manipulate';
22
+ import preprocess from '@shaderfrog/glsl-parser/preprocessor';
23
+ var orig;
24
+ beforeEach(function () {
25
+ orig = graphModule.mangleName;
26
+ // Terrible hack. in the real world, strategies are applied after mangling
27
+ // @ts-ignore
28
+ graphModule.mangleName = function (name) { return name; };
29
+ });
30
+ afterEach(function () {
31
+ // @ts-ignore
32
+ graphModule.mangleName = orig;
33
+ });
34
+ it('named attribute strategy`', function () {
35
+ var source = "\nin vec3 replaceThisAtrribute;\nvoid main() {\n vec2 y = replaceThisAtrribute;\n}\n";
36
+ var ast = parser.parse(source, { quiet: true });
37
+ var fillers = applyStrategy({
38
+ type: StrategyType.NAMED_ATTRIBUTE,
39
+ config: {
40
+ attributeName: 'replaceThisAtrribute',
41
+ },
42
+ }, { source: source }, ast);
43
+ expect(fillers.length).toBe(1);
44
+ fillers[0][1]({
45
+ type: 'literal',
46
+ literal: "myFiller()",
47
+ whitespace: '',
48
+ });
49
+ var result = generate(ast);
50
+ // Should replace the use of the filler, but not the declaration
51
+ expect(result).toBe("\nin vec3 replaceThisAtrribute;\nvoid main() {\n vec2 y = myFiller();\n}\n");
52
+ });
53
+ it('inject strategy after', function () {
54
+ var source = "\nuniform float x;\n// Some comment\nvoid main() {\n/* some comment */\nre(x, y, z);\n// Middle comment\nre(x, y, z);\n// Final comment\n}";
55
+ var ast = parser.parse(source, { quiet: true });
56
+ var fillers = applyStrategy({
57
+ type: StrategyType.INJECT,
58
+ config: {
59
+ find: 're(x, y, z);',
60
+ insert: 'after',
61
+ count: Infinity,
62
+ },
63
+ }, { source: source }, ast);
64
+ expect(fillers.length).toBe(1);
65
+ fillers[0][1]({
66
+ type: 'literal',
67
+ literal: "someOtherCall(x, y, z);\nsomeOtherCall(x, y, z);",
68
+ whitespace: '',
69
+ });
70
+ var result = generate(ast);
71
+ // Should fill references
72
+ expect(result).toBe("\nuniform float x;\n// Some comment\nvoid main() {\n/* some comment */\nre(x, y, z);\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\n// Middle comment\nre(x, y, z);\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\n// Final comment\n}");
73
+ });
74
+ it('inject strategy before', function () {
75
+ var source = "\nuniform float x;\n// Some comment\nvoid main() {\n/* some comment */\nre(x, y, z);\n// Middle comment\nre(x, y, z);\n// Final comment\n}";
76
+ var ast = parser.parse(source, { quiet: true });
77
+ var fillers = applyStrategy({
78
+ type: StrategyType.INJECT,
79
+ config: {
80
+ find: 're(x, y, z);',
81
+ insert: 'before',
82
+ count: Infinity,
83
+ },
84
+ }, { source: source }, ast);
85
+ expect(fillers.length).toBe(1);
86
+ fillers[0][1]({
87
+ type: 'literal',
88
+ literal: "someOtherCall(x, y, z);\nsomeOtherCall(x, y, z);",
89
+ whitespace: '\n',
90
+ });
91
+ var result = generate(ast);
92
+ // Should fill references
93
+ expect(result).toBe("\nuniform float x;\n// Some comment\nvoid main() {\n/* some comment */\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\nre(x, y, z);\n// Middle comment\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\nre(x, y, z);\n// Final comment\n}");
94
+ });
95
+ it('correctly fills with uniform strategy', function () {
96
+ var _a, _b, _c;
97
+ var ast = parser.parse("\nlayout(std140,column_major) uniform;\nuniform sampler2D image;\nuniform vec4 input, output, other;\nuniform vec4 zenput;\nuniform Light0 { vec4 y; } x;\nvec3 topLevel = vec3(0.0);\nvoid other(in vec3 param) {}\nvoid main() {\n vec4 computed = texture2D(image, uvPow * 1.0);\n vec4 x = input;\n vec4 y = output;\n vec4 z = zenput;\n}", { quiet: true });
98
+ var fillers = applyStrategy({ type: StrategyType.UNIFORM, config: {} }, {}, ast);
99
+ // It should find uniforms with simple types, excluding sampler2D
100
+ expect(fillers.map(function (_a) {
101
+ var _b = __read(_a, 1), name = _b[0].displayName;
102
+ return name;
103
+ })).toEqual([
104
+ 'image',
105
+ 'input',
106
+ 'output',
107
+ 'other',
108
+ 'zenput',
109
+ ]);
110
+ (_a = fillers.find(function (_a) {
111
+ var _b = __read(_a, 1), name = _b[0].displayName;
112
+ return name === 'input';
113
+ })) === null || _a === void 0 ? void 0 : _a[1](makeExpression('a'));
114
+ (_b = fillers.find(function (_a) {
115
+ var _b = __read(_a, 1), name = _b[0].displayName;
116
+ return name === 'output';
117
+ })) === null || _b === void 0 ? void 0 : _b[1](makeExpression('b'));
118
+ (_c = fillers.find(function (_a) {
119
+ var _b = __read(_a, 1), name = _b[0].displayName;
120
+ return name === 'zenput';
121
+ })) === null || _c === void 0 ? void 0 : _c[1](makeExpression('c'));
122
+ var result = generate(ast);
123
+ // Should fill references
124
+ expect(result).toContain('vec4 x = a;');
125
+ expect(result).toContain('vec4 y = b;');
126
+ expect(result).toContain('vec4 z = c;');
127
+ // Should preserve things it shouldn't touch
128
+ expect(result).toContain('layout(std140,column_major) uniform;');
129
+ expect(result).toContain('uniform sampler2D image;');
130
+ expect(result).toContain('uniform Light0 { vec4 y; } x;');
131
+ // Should remove uniforms from declarator list
132
+ expect(result).toContain('uniform vec4 other;');
133
+ // Should remove uniform lines
134
+ expect(result).not.toContain('uniform vec4 zenput');
135
+ });
136
+ it('uses name without suffix for single call', function () {
137
+ var ast = parser.parse("\nvoid main() {\n vec4 computed = texture2D(noiseImage, uvPow * 1.0);\n}", { quiet: true });
138
+ expect(applyStrategy({ type: StrategyType.TEXTURE_2D, config: {} }, {}, ast).map(function (_a) {
139
+ var _b = __read(_a, 1), name = _b[0].displayName;
140
+ return name;
141
+ })).toEqual(['noiseImage']);
142
+ });
143
+ it('finds multiple texture2D inputs for one uniform', function () {
144
+ var ast = parser.parse("\nvoid main() {\n vec4 computed = texture2D(noiseImage, uvPow * 1.0);\n computed += texture2D(noiseImage, uvPow * 2.0);\n}", { quiet: true });
145
+ expect(applyStrategy({ type: StrategyType.TEXTURE_2D, config: {} }, {}, ast).map(function (_a) {
146
+ var _b = __read(_a, 1), name = _b[0].displayName;
147
+ return name;
148
+ })).toEqual(['noiseImage_0', 'noiseImage_1']);
149
+ });
150
+ it('Make sure texture2D finds preprocessed texture() call', function () {
151
+ // I thought this was a regression, but it wasn't a real bug, but tests seems
152
+ // benign to keep anyway
153
+ var program = "\n#define texture2DBias texture\n\nuniform sampler2D normalMap;\n\nvoid getNormal() {\n vec3 normalMap = unpackNormal(texture2DBias(normalMap, vUv0, textureBias));\n}";
154
+ var pp = preprocess(program, {
155
+ preserve: {
156
+ version: function () { return true; },
157
+ },
158
+ });
159
+ var ast = parser.parse(pp, { quiet: true });
160
+ expect(applyStrategy({ type: StrategyType.TEXTURE_2D, config: {} }, {}, ast).map(function (_a) {
161
+ var _b = __read(_a, 1), name = _b[0].displayName;
162
+ return name;
163
+ })).toEqual(['normalMapx']);
164
+ });
@@ -0,0 +1,9 @@
1
+ import { generate } from '@shaderfrog/glsl-parser';
2
+ export var generateFiller = function (filler) {
3
+ if (!filler) {
4
+ throw new Error('Cannot generate void filler!');
5
+ }
6
+ return Array.isArray(filler)
7
+ ? filler.map(generate).join('')
8
+ : generate(filler);
9
+ };
@@ -0,0 +1,7 @@
1
+ export var ensure = function (argument, message) {
2
+ if (message === void 0) { message = 'This value was promised to be there.'; }
3
+ if (argument === undefined || argument === null) {
4
+ throw new TypeError(message);
5
+ }
6
+ return argument;
7
+ };
@@ -0,0 +1,2 @@
1
+ var counter = 0;
2
+ export var makeId = function () { return '' + counter++; };
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@shaderfrog/core",
3
- "version": "0.0.2",
3
+ "version": "0.1.1",
4
4
  "description": "Shaderfrog core",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
7
+ "prepare": "npm run build",
8
+ "build": "./build.sh",
9
+ "watch-test": "jest --watch",
10
+ "test": "jest --colors"
8
11
  },
9
12
  "repository": {
10
13
  "type": "git",
@@ -13,7 +16,7 @@
13
16
  "author": "Andrew Ray",
14
17
  "license": "ISC",
15
18
  "files": [
16
- "src"
19
+ "dist"
17
20
  ],
18
21
  "bugs": {
19
22
  "url": "https://github.com/ShaderFrog/core/issues"
@@ -37,8 +40,10 @@
37
40
  "lodash.groupby": "^4.6.0"
38
41
  },
39
42
  "peerDependencies": {
43
+ "@shaderfrog/glsl-parser": "^2.0.0-beta.5",
40
44
  "babylonjs": ">=4",
41
- "three": ">=0.50"
45
+ "three": ">=0.50",
46
+ "playcanvas": "^1.65.3"
42
47
  },
43
48
  "peerDependenciesMeta": {
44
49
  "babylonjs": {
@@ -46,6 +51,9 @@
46
51
  },
47
52
  "three": {
48
53
  "optional": true
54
+ },
55
+ "playcanvas": {
56
+ "optional": true
49
57
  }
50
58
  }
51
59
  }
@@ -1,392 +0,0 @@
1
- /**
2
- * Utility functions to work with ASTs
3
- */
4
- import { parser, generate } from '@shaderfrog/glsl-parser';
5
- import {
6
- visit,
7
- AstNode,
8
- NodeVisitors,
9
- ExpressionStatementNode,
10
- FunctionNode,
11
- AssignmentNode,
12
- DeclarationStatementNode,
13
- KeywordNode,
14
- DeclarationNode,
15
- } from '@shaderfrog/glsl-parser/ast';
16
- import { Program } from '@shaderfrog/glsl-parser/ast';
17
- import { ShaderStage } from '../core/graph';
18
-
19
- export const findVec4Constructor = (ast: AstNode): AstNode | undefined => {
20
- let parent: AstNode | undefined;
21
- const visitors: NodeVisitors = {
22
- function_call: {
23
- enter: (path) => {
24
- if (
25
- 'specifier' in path.node.identifier &&
26
- path.node.identifier?.specifier?.token === 'vec4'
27
- ) {
28
- parent = path.findParent((p) => 'right' in p.node)?.node;
29
- path.skip();
30
- }
31
- },
32
- },
33
- };
34
- visit(ast, visitors);
35
- return parent;
36
- };
37
-
38
- export const findAssignmentTo = (
39
- ast: AstNode | Program,
40
- assignTo: string
41
- ): ExpressionStatementNode | undefined => {
42
- let assign: ExpressionStatementNode | undefined;
43
- const visitors: NodeVisitors = {
44
- expression_statement: {
45
- enter: (path) => {
46
- if (path.node.expression?.left?.identifier === assignTo) {
47
- assign = path.node;
48
- }
49
- path.skip();
50
- },
51
- },
52
- };
53
- visit(ast, visitors);
54
- return assign;
55
- };
56
-
57
- export const findDeclarationOf = (
58
- ast: AstNode | Program,
59
- declarationOf: string
60
- ): DeclarationNode | undefined => {
61
- let declaration: DeclarationNode | undefined;
62
- const visitors: NodeVisitors = {
63
- declaration_statement: {
64
- enter: (path) => {
65
- const foundDecl = path.node.declaration?.declarations?.find(
66
- (decl: any) => decl?.identifier?.identifier === declarationOf
67
- );
68
- if (foundDecl) {
69
- declaration = foundDecl;
70
- }
71
- path.skip();
72
- },
73
- },
74
- };
75
- visit(ast, visitors);
76
- return declaration;
77
- };
78
-
79
- export const from2To3 = (ast: Program, stage: ShaderStage) => {
80
- const glOut = 'fragmentColor';
81
- // TODO: add this back in when there's only one after the merge
82
- // ast.program.unshift({
83
- // type: 'preprocessor',
84
- // line: '#version 300 es',
85
- // _: '\n',
86
- // });
87
- if (stage === 'fragment') {
88
- ast.program.unshift({
89
- type: 'declaration_statement',
90
- declaration: {
91
- type: 'declarator_list',
92
- specified_type: {
93
- type: 'fully_specified_type',
94
- qualifiers: [{ type: 'keyword', token: 'out', whitespace: ' ' }],
95
- specifier: {
96
- type: 'type_specifier',
97
- specifier: { type: 'keyword', token: 'vec4', whitespace: ' ' },
98
- quantifier: null,
99
- },
100
- },
101
- declarations: [
102
- {
103
- type: 'declaration',
104
- identifier: {
105
- type: 'identifier',
106
- identifier: glOut,
107
- whitespace: undefined,
108
- },
109
- quantifier: null,
110
- operator: undefined,
111
- initializer: undefined,
112
- },
113
- ],
114
- commas: [],
115
- },
116
- semi: { type: 'literal', literal: ';', whitespace: '\n ' },
117
- });
118
- }
119
- visit(ast, {
120
- function_call: {
121
- enter: (path) => {
122
- const identifier = path.node.identifier;
123
- if (
124
- 'specifier' in identifier &&
125
- identifier.specifier?.identifier === 'texture2D'
126
- ) {
127
- identifier.specifier.identifier = 'texture';
128
- }
129
- },
130
- },
131
- identifier: {
132
- enter: (path) => {
133
- if (path.node.identifier === 'gl_FragColor') {
134
- path.node.identifier = glOut;
135
- }
136
- },
137
- },
138
- keyword: {
139
- enter: (path) => {
140
- if (
141
- (path.node.token === 'attribute' || path.node.token === 'varying') &&
142
- path.findParent((path) => path.node.type === 'declaration_statement')
143
- ) {
144
- path.node.token =
145
- stage === 'vertex' && path.node.token === 'varying' ? 'out' : 'in';
146
- }
147
- },
148
- },
149
- });
150
- };
151
-
152
- export const outDeclaration = (name: string): Object => ({
153
- type: 'declaration_statement',
154
- declaration: {
155
- type: 'declarator_list',
156
- specified_type: {
157
- type: 'fully_specified_type',
158
- qualifiers: [{ type: 'keyword', token: 'out', whitespace: ' ' }],
159
- specifier: {
160
- type: 'type_specifier',
161
- specifier: { type: 'keyword', token: 'vec4', whitespace: ' ' },
162
- quantifier: null,
163
- },
164
- },
165
- declarations: [
166
- {
167
- type: 'declaration',
168
- identifier: {
169
- type: 'identifier',
170
- identifier: name,
171
- whitespace: undefined,
172
- },
173
- quantifier: null,
174
- operator: undefined,
175
- initializer: undefined,
176
- },
177
- ],
178
- commas: [],
179
- },
180
- semi: { type: 'literal', literal: ';', whitespace: '\n ' },
181
- });
182
-
183
- export const makeStatement = (stmt: string): AstNode => {
184
- // console.log(stmt);
185
- let ast;
186
- try {
187
- ast = parser.parse(
188
- `${stmt};
189
- `,
190
- { quiet: true }
191
- );
192
- } catch (error: any) {
193
- console.error({ stmt, error });
194
- throw new Error(`Error parsing stmt "${stmt}": ${error?.message}`);
195
- }
196
- // console.log(util.inspect(ast, false, null, true));
197
- return ast.program[0];
198
- };
199
-
200
- export const makeFnStatement = (fnStmt: string): AstNode => {
201
- let ast;
202
- try {
203
- ast = parser.parse(
204
- `
205
- void main() {
206
- ${fnStmt};
207
- }`,
208
- { quiet: true }
209
- );
210
- } catch (error: any) {
211
- console.error({ fnStmt, error });
212
- throw new Error(`Error parsing fnStmt "${fnStmt}": ${error?.message}`);
213
- }
214
-
215
- // console.log(util.inspect(ast, false, null, true));
216
- return (ast.program[0] as FunctionNode).body.statements[0];
217
- };
218
-
219
- export const makeExpression = (expr: string): AstNode => {
220
- let ast;
221
- try {
222
- ast = parser.parse(
223
- `void main() {
224
- a = ${expr};
225
- }`,
226
- { quiet: true }
227
- );
228
- } catch (error: any) {
229
- console.error({ expr, error });
230
- throw new Error(`Error parsing expr "${expr}": ${error?.message}`);
231
- }
232
-
233
- // console.log(util.inspect(ast, false, null, true));
234
- return (ast.program[0] as FunctionNode).body.statements[0].expression.right;
235
- };
236
-
237
- export const makeExpressionWithScopes = (expr: string): Program => {
238
- let ast: Program;
239
- try {
240
- ast = parser.parse(
241
- `void main() {
242
- ${expr};
243
- }`,
244
- { quiet: true }
245
- );
246
- } catch (error: any) {
247
- console.error({ expr, error });
248
- throw new Error(`Error parsing expr "${expr}": ${error?.message}`);
249
- }
250
-
251
- // console.log(util.inspect(ast, false, null, true));
252
- return {
253
- type: 'program',
254
- // Set the main() fn body scope as the global one
255
- scopes: [ast.scopes[1]],
256
- program: [(ast.program[0] as FunctionNode).body.statements[0].expression],
257
- };
258
- };
259
-
260
- export const findFn = (ast: Program, name: string): FunctionNode | undefined =>
261
- ast.program.find(
262
- (stmt): stmt is FunctionNode =>
263
- stmt.type === 'function' && stmt.prototype.header.name.identifier === name
264
- );
265
-
266
- export const returnGlPosition = (fnName: string, ast: Program): void =>
267
- convertVertexMain(fnName, ast, 'vec4', (assign) => assign.expression.right);
268
-
269
- export const returnGlPositionHardCoded = (
270
- fnName: string,
271
- ast: Program,
272
- returnType: string,
273
- hardCodedReturn: string
274
- ): void =>
275
- convertVertexMain(fnName, ast, returnType, () =>
276
- makeExpression(hardCodedReturn)
277
- );
278
-
279
- export const returnGlPositionVec3Right = (fnName: string, ast: Program): void =>
280
- convertVertexMain(fnName, ast, 'vec3', (assign) => {
281
- let found: AstNode | undefined;
282
- visit(assign, {
283
- function_call: {
284
- enter: (path) => {
285
- const { node } = path;
286
- if (
287
- // @ts-ignore
288
- node?.identifier?.specifier?.token === 'vec4' &&
289
- node?.args?.[2]?.token?.includes('1.')
290
- ) {
291
- found = node.args[0];
292
- }
293
- },
294
- },
295
- });
296
- if (!found) {
297
- console.error(generate(ast));
298
- throw new Error(
299
- 'Could not find position assignment to convert to return!'
300
- );
301
- }
302
- return found;
303
- });
304
-
305
- const convertVertexMain = (
306
- fnName: string,
307
- ast: Program,
308
- returnType: string,
309
- generateRight: (positionAssign: ExpressionStatementNode) => AstNode
310
- ) => {
311
- const mainReturnVar = `frogOut`;
312
-
313
- const main = findFn(ast, fnName);
314
- if (!main) {
315
- throw new Error(`No ${fnName} fn found!`);
316
- }
317
-
318
- // Convert the main function to one that returns
319
- (main.prototype.header.returnType.specifier.specifier as KeywordNode).token =
320
- returnType;
321
-
322
- // Find the gl_position assignment line
323
- const assign = main.body.statements.find(
324
- (stmt: AstNode) =>
325
- stmt.type === 'expression_statement' &&
326
- stmt.expression.left?.identifier === 'gl_Position'
327
- );
328
- if (!assign) {
329
- throw new Error(`No gl position assign found in main fn!`);
330
- }
331
-
332
- const rtnStmt = makeFnStatement(
333
- `${returnType} ${mainReturnVar} = 1.0`
334
- ) as DeclarationStatementNode;
335
- rtnStmt.declaration.declarations[0].initializer = generateRight(assign);
336
-
337
- main.body.statements.splice(main.body.statements.indexOf(assign), 1, rtnStmt);
338
- main.body.statements.push(makeFnStatement(`return ${mainReturnVar}`));
339
- };
340
-
341
- export const convert300MainToReturn = (fnName: string, ast: Program): void => {
342
- const mainReturnVar = `frogOut`;
343
-
344
- // Find the output variable, as in "pc_fragColor" from "out highp vec4 pc_fragColor;"
345
- let outName: string | undefined;
346
- ast.program.find((line, index) => {
347
- if (
348
- line.type === 'declaration_statement' &&
349
- line.declaration?.specified_type?.qualifiers?.find(
350
- (n: KeywordNode) => n.token === 'out'
351
- ) &&
352
- line.declaration.specified_type.specifier.specifier.token === 'vec4'
353
- ) {
354
- // Remove the out declaration
355
- ast.program.splice(index, 1);
356
- outName = line.declaration.declarations[0].identifier.identifier;
357
- return true;
358
- }
359
- });
360
- if (!outName) {
361
- console.error(generate(ast));
362
- throw new Error('No "out vec4" line found in the fragment shader');
363
- }
364
-
365
- visit(ast, {
366
- identifier: {
367
- enter: (path) => {
368
- if (path.node.identifier === outName) {
369
- path.node.identifier = mainReturnVar;
370
- // @ts-ignore
371
- path.node.doNotDescope = true; // hack because this var is in the scope which gets renamed later
372
- }
373
- },
374
- },
375
- function: {
376
- enter: (path) => {
377
- if (path.node.prototype.header.name.identifier === fnName) {
378
- (
379
- path.node.prototype.header.returnType.specifier
380
- .specifier as KeywordNode
381
- ).token = 'vec4';
382
- path.node.body.statements.unshift(
383
- makeFnStatement(`vec4 ${mainReturnVar}`)
384
- );
385
- path.node.body.statements.push(
386
- makeFnStatement(`return ${mainReturnVar}`)
387
- );
388
- }
389
- },
390
- },
391
- });
392
- };