@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.
- package/README.md +184 -1
- package/dist/ast/manipulate.js +328 -0
- package/dist/ast/shader-sections.js +256 -0
- package/dist/context.js +230 -0
- package/dist/engine.js +209 -0
- package/dist/evaluate.js +27 -0
- package/dist/graph-types.js +7 -0
- package/dist/graph.js +381 -0
- package/dist/graph.test.js +168 -0
- package/dist/nodes/code-nodes.js +18 -0
- package/dist/nodes/core-node.js +9 -0
- package/dist/nodes/data-nodes.js +123 -0
- package/dist/nodes/edge.js +1 -0
- package/dist/nodes/engine-node.js +189 -0
- package/dist/parsers.js +213 -0
- package/dist/plugins/babylon/bablyengine.js +582 -0
- package/dist/plugins/babylon/importers.js +64 -0
- package/{src/plugins/babylon/index.ts → dist/plugins/babylon/index.js} +0 -1
- package/dist/plugins/playcanvas/importers.js +28 -0
- package/dist/plugins/playcanvas/index.js +2 -0
- package/dist/plugins/playcanvas/playengine.js +510 -0
- package/dist/plugins/three/importers.js +15 -0
- package/{src/plugins/three/index.ts → dist/plugins/three/index.js} +0 -1
- package/dist/plugins/three/threngine.js +495 -0
- package/dist/strategy/assignemntTo.js +26 -0
- package/dist/strategy/declarationOf.js +23 -0
- package/dist/strategy/hardCode.js +23 -0
- package/dist/strategy/index.js +38 -0
- package/dist/strategy/inject.js +122 -0
- package/dist/strategy/namedAttribute.js +48 -0
- package/dist/strategy/texture2D.js +83 -0
- package/dist/strategy/uniform.js +190 -0
- package/dist/strategy/variable.js +80 -0
- package/dist/stratgies.test.js +164 -0
- package/dist/util/ast.js +9 -0
- package/dist/util/ensure.js +7 -0
- package/dist/util/id.js +2 -0
- package/package.json +12 -4
- package/src/ast/manipulate.ts +0 -392
- package/src/ast/shader-sections.ts +0 -323
- package/src/core/engine.ts +0 -214
- package/src/core/file.js +0 -53
- package/src/core/graph.ts +0 -1007
- package/src/core/nodes/code-nodes.ts +0 -66
- package/src/core/nodes/core-node.ts +0 -48
- package/src/core/nodes/data-nodes.ts +0 -344
- package/src/core/nodes/edge.ts +0 -23
- package/src/core/nodes/engine-node.ts +0 -266
- package/src/core/strategy.ts +0 -520
- package/src/core.test.ts +0 -312
- package/src/plugins/babylon/bablyengine.ts +0 -670
- package/src/plugins/babylon/importers.ts +0 -69
- package/src/plugins/three/importers.ts +0 -18
- package/src/plugins/three/threngine.tsx +0 -571
- package/src/util/ensure.ts +0 -10
- package/src/util/id.ts +0 -2
package/README.md
CHANGED
|
@@ -1,3 +1,186 @@
|
|
|
1
1
|
# Shaderfrog Core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
🚨 This library is experimental! 🚨
|
|
4
|
+
🚨 The API can change at any time! 🚨
|
|
5
|
+
|
|
6
|
+
The core graph API that powers Shaderfrog. This API, built on top of the
|
|
7
|
+
[@Shaderfrog/glsl-parser](https://github.com/ShaderFrog/glsl-parser), compiles
|
|
8
|
+
Shaderfrog graphs into an intermediate result, which you then pass off to an
|
|
9
|
+
_engine_ (aka a plugin), to create a running GLSL shader.
|
|
10
|
+
|
|
11
|
+
### Graph
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
interface Graph {
|
|
15
|
+
nodes: GraphNode[];
|
|
16
|
+
edges: Edge[];
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The Shaderfrog _graph_ is a list of nodes and edges. It represents all of the
|
|
21
|
+
GLSL code and configurations in your material. Conceptually, a graph is similar
|
|
22
|
+
to a dependency graph for source code, where edges represent relationships
|
|
23
|
+
(including dependencies) between nodes.
|
|
24
|
+
|
|
25
|
+
Each _node_ in the graph is some type of GLSL (raw source code) and configuration.
|
|
26
|
+
Some graph node GLSL is hard coded, as in written by you, like in a
|
|
27
|
+
`SourceNode`. Some source code is generated at runtime by an engine, and
|
|
28
|
+
injected into a node right before the graph is compiled.
|
|
29
|
+
|
|
30
|
+
Each _edge_ in the graph represents a dependency between two nodes. Edges have
|
|
31
|
+
different types and meanings, based on which inputs and outputs they're
|
|
32
|
+
connected to.
|
|
33
|
+
|
|
34
|
+
The main API function for working with graphs are `compileGraph` and
|
|
35
|
+
`computeGraphContext`:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
type compileGraph = (
|
|
39
|
+
engineContext: EngineContext,
|
|
40
|
+
engine: Engine,
|
|
41
|
+
graph: Graph
|
|
42
|
+
): CompileGraphResult
|
|
43
|
+
|
|
44
|
+
type computeGraphContext = async (
|
|
45
|
+
engineContext: EngineContext,
|
|
46
|
+
engine: Engine,
|
|
47
|
+
graph: Graph
|
|
48
|
+
): void
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
A graph's _context_, more specifically a node's context, is the runtime /
|
|
52
|
+
in-memory computed data associated with a graph node. It includes the parsed AST
|
|
53
|
+
representation of the node, as well as the inputs found in that AST.
|
|
54
|
+
|
|
55
|
+
### Parsers
|
|
56
|
+
|
|
57
|
+
A graph is a vanilla Javscript object. To convert it to context, there's one
|
|
58
|
+
"parser" per node type in the graph, defined in the engine configuration. A
|
|
59
|
+
parser is an object with this interface:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
type NodeParser = {
|
|
63
|
+
// cacheKey?: (graph: Graph, node: GraphNode, sibling?: GraphNode) => string;
|
|
64
|
+
// Callback hook to manipulate the node right before it's compiled by the
|
|
65
|
+
// graph. Engines use this to dynamically generate node source code.
|
|
66
|
+
onBeforeCompile?: OnBeforeCompile;
|
|
67
|
+
// Callback hook to manipulate the parsed AST. Example use is to convert
|
|
68
|
+
// standalone GLSL programs into code that can be used in the graph, like
|
|
69
|
+
// turning `void main() { out = color; }` into `vec4 main() { return color; }`
|
|
70
|
+
manipulateAst?: ManipulateAst;
|
|
71
|
+
// Find the inputs for this node type. Done dynamically because it's based on
|
|
72
|
+
// the source code of the node.
|
|
73
|
+
findInputs?: FindInputs;
|
|
74
|
+
// Create the filler AST offered up to nodes that import this node.
|
|
75
|
+
produceFiller?: ProduceNodeFiller;
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Engine
|
|
80
|
+
|
|
81
|
+
Shaderfrog is a GLSL editor. It's not a Three.js editor, nor a Babylon.js
|
|
82
|
+
editor, etc. The output of Shaderfrog is raw GLSL and metadata.
|
|
83
|
+
|
|
84
|
+
To use shaders in your _engine_, like Three.js, or even your own home grown
|
|
85
|
+
engine, you implement your engine as a _plugin_ to Shaderfrog. An engine
|
|
86
|
+
definition is verbose and likely to change:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
export interface Engine {
|
|
90
|
+
// The name of your engine, like "three"
|
|
91
|
+
name: string;
|
|
92
|
+
// Which GLSL variables are defined in your engine's materials
|
|
93
|
+
preserve: Set<string>;
|
|
94
|
+
// Rules for how to merge source code from different nodes together
|
|
95
|
+
mergeOptions: MergeOptions;
|
|
96
|
+
// Parsers for your engine node types. These are combined with the
|
|
97
|
+
// core engine parsers
|
|
98
|
+
parsers: Record<string, NodeParser>;
|
|
99
|
+
// Functions to import graphs/code from other engines into your own
|
|
100
|
+
importers: EngineImporters;
|
|
101
|
+
// How to evaluate a node, like turning a node of { type: 'vec3' } into a
|
|
102
|
+
// THREE.Vector3
|
|
103
|
+
evaluateNode: (node: DataNode) => any;
|
|
104
|
+
// How to create specific nodes in your engine
|
|
105
|
+
constructors: {
|
|
106
|
+
[EngineNodeType]: NodeConstructor;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Inputs, Holes and Fillers
|
|
112
|
+
|
|
113
|
+
Shaderfrog works by searching each node's AST for certain patterns, like
|
|
114
|
+
`uniform` variables, and creating an interface where you can replace each
|
|
115
|
+
`uniform` variable with the output of another node.
|
|
116
|
+
|
|
117
|
+
Each fillable part of the AST is called a __hole__. Holes are found by executing
|
|
118
|
+
user defined _strategies_ against an AST. With a program such as:
|
|
119
|
+
|
|
120
|
+
```glsl
|
|
121
|
+
uniform vec2 uv;
|
|
122
|
+
void main() {
|
|
123
|
+
vec2 someVar = uv * 2.0;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
If you apply the `uniform` strategy to this code, it will mark the AST nodes
|
|
128
|
+
relevant to the uniform as _holes_:
|
|
129
|
+
|
|
130
|
+
```glsl
|
|
131
|
+
uniform vec2 [uv];
|
|
132
|
+
void main() {
|
|
133
|
+
vec2 someVar = [uv] * 2.0;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
And it adds a new _input_ to your node, named `uv` in this case.
|
|
138
|
+
|
|
139
|
+
When you plug in the output of another node into this input, it _"fills in"_ the
|
|
140
|
+
hole with the _filler_ output of another node. A _filler_ is an AST node. For
|
|
141
|
+
example, if you have another node like:
|
|
142
|
+
|
|
143
|
+
```glsl
|
|
144
|
+
vec2 myFn() {
|
|
145
|
+
return vec2(1.0, 1.0);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
And you plug in the `myFn` output into the `uv` input, the hole is _filled_,
|
|
150
|
+
resulting in:
|
|
151
|
+
|
|
152
|
+
```glsl
|
|
153
|
+
vec2 myFn() {
|
|
154
|
+
return vec2(1.0, 1.0);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
void main() {
|
|
158
|
+
vec2 someVar = myFn() * 2.0;
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Note that this is not a simple find and replace. Not only was the `uv` variable
|
|
163
|
+
replaced, but the declaration line `uniform vec2 uv;` was removed, and `myFn`
|
|
164
|
+
was inlined into the final program.
|
|
165
|
+
|
|
166
|
+
Hole filling always produces a new AST, or more accurately, a new
|
|
167
|
+
`ShaderSections`, which is the intermediary representation of the compilation
|
|
168
|
+
process.
|
|
169
|
+
|
|
170
|
+
### Static Monkeypatching
|
|
171
|
+
|
|
172
|
+
This whole process allows Shaderfrog to monkeypatch engine shaders. When
|
|
173
|
+
modifying an engine shader, the process is:
|
|
174
|
+
|
|
175
|
+
- Shaderfrog creates a `BABYLON.PBRMaterial` or `Three.MeshPhysicalMaterial` (or
|
|
176
|
+
whatever built in material type you want)
|
|
177
|
+
- Shaderfrog reads the engine material's generated GLSL, and then modifies it to
|
|
178
|
+
add new effects by injecting new GLSL
|
|
179
|
+
- Shaderfrog dumps the new compiled GLSL back into the `BABYLON.PBRMaterial` or
|
|
180
|
+
`Three.MeshPhysicalMaterial`, and updates the material to add a new uniforms.
|
|
181
|
+
|
|
182
|
+
Injecting new GLSL into an engine shader is essentially _monkeypatching_ it:
|
|
183
|
+
your code is modifying an external library's code. I call this _static
|
|
184
|
+
monkeypatching_ because compiles new source code. This is opposed to traditional
|
|
185
|
+
monkeypatching in languages like Ruby, where you modify external modules by
|
|
186
|
+
changing them at runtime.
|
|
@@ -0,0 +1,328 @@
|
|
|
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
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
18
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
19
|
+
if (ar || !(i in from)) {
|
|
20
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
21
|
+
ar[i] = from[i];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Utility functions to work with ASTs
|
|
28
|
+
*/
|
|
29
|
+
import { parser, generate } from '@shaderfrog/glsl-parser';
|
|
30
|
+
import { visit, } from '@shaderfrog/glsl-parser/ast';
|
|
31
|
+
var log = function () {
|
|
32
|
+
var _a;
|
|
33
|
+
var args = [];
|
|
34
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
35
|
+
args[_i] = arguments[_i];
|
|
36
|
+
}
|
|
37
|
+
return (_a = console.log).call.apply(_a, __spreadArray([console, '\x1b[31m(core.manipulate)\x1b[0m'], __read(args), false));
|
|
38
|
+
};
|
|
39
|
+
export var findVec4Constructor = function (ast) {
|
|
40
|
+
var parent;
|
|
41
|
+
var visitors = {
|
|
42
|
+
function_call: {
|
|
43
|
+
enter: function (path) {
|
|
44
|
+
var _a, _b;
|
|
45
|
+
if (((_a = path.node.identifier) === null || _a === void 0 ? void 0 : _a.specifier).token === 'vec4') {
|
|
46
|
+
parent = (_b = path.findParent(function (p) { return 'right' in p.node; })) === null || _b === void 0 ? void 0 : _b.node;
|
|
47
|
+
path.skip();
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
visit(ast, visitors);
|
|
53
|
+
return parent;
|
|
54
|
+
};
|
|
55
|
+
export var findAssignmentTo = function (ast, assignTo) {
|
|
56
|
+
var assign;
|
|
57
|
+
var visitors = {
|
|
58
|
+
expression_statement: {
|
|
59
|
+
enter: function (path) {
|
|
60
|
+
var _a, _b;
|
|
61
|
+
if (((_b = (_a = path.node.expression) === null || _a === void 0 ? void 0 : _a.left) === null || _b === void 0 ? void 0 : _b.identifier) === assignTo) {
|
|
62
|
+
assign = path.node;
|
|
63
|
+
}
|
|
64
|
+
path.skip();
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
visit(ast, visitors);
|
|
69
|
+
return assign;
|
|
70
|
+
};
|
|
71
|
+
export var findDeclarationOf = function (ast, declarationOf) {
|
|
72
|
+
var declaration;
|
|
73
|
+
var visitors = {
|
|
74
|
+
declaration_statement: {
|
|
75
|
+
enter: function (path) {
|
|
76
|
+
var _a, _b;
|
|
77
|
+
var foundDecl = (_b = (_a = path.node.declaration) === null || _a === void 0 ? void 0 : _a.declarations) === null || _b === void 0 ? void 0 : _b.find(function (decl) { var _a; return ((_a = decl === null || decl === void 0 ? void 0 : decl.identifier) === null || _a === void 0 ? void 0 : _a.identifier) === declarationOf; });
|
|
78
|
+
if (foundDecl) {
|
|
79
|
+
declaration = foundDecl;
|
|
80
|
+
}
|
|
81
|
+
path.skip();
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
visit(ast, visitors);
|
|
86
|
+
return declaration;
|
|
87
|
+
};
|
|
88
|
+
export var from2To3 = function (ast, stage) {
|
|
89
|
+
var glOut = 'fragmentColor';
|
|
90
|
+
// TODO: add this back in when there's only one after the merge
|
|
91
|
+
// ast.program.unshift({
|
|
92
|
+
// type: 'preprocessor',
|
|
93
|
+
// line: '#version 300 es',
|
|
94
|
+
// _: '\n',
|
|
95
|
+
// });
|
|
96
|
+
if (stage === 'fragment') {
|
|
97
|
+
ast.program.unshift(makeStatement("out vec4 ".concat(glOut)));
|
|
98
|
+
}
|
|
99
|
+
visit(ast, {
|
|
100
|
+
function_call: {
|
|
101
|
+
enter: function (path) {
|
|
102
|
+
var identifier = path.node.identifier;
|
|
103
|
+
if (identifier.type === 'identifier' &&
|
|
104
|
+
identifier.identifier === 'texture2D') {
|
|
105
|
+
identifier.identifier = 'texture';
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
identifier: {
|
|
110
|
+
enter: function (path) {
|
|
111
|
+
if (path.node.identifier === 'gl_FragColor') {
|
|
112
|
+
path.node.identifier = glOut;
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
keyword: {
|
|
117
|
+
enter: function (path) {
|
|
118
|
+
if ((path.node.token === 'attribute' || path.node.token === 'varying') &&
|
|
119
|
+
path.findParent(function (path) { return path.node.type === 'declaration_statement'; })) {
|
|
120
|
+
path.node.token =
|
|
121
|
+
stage === 'vertex' && path.node.token === 'varying' ? 'out' : 'in';
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
export var outDeclaration = function (name) { return ({
|
|
128
|
+
type: 'declaration_statement',
|
|
129
|
+
declaration: {
|
|
130
|
+
type: 'declarator_list',
|
|
131
|
+
specified_type: {
|
|
132
|
+
type: 'fully_specified_type',
|
|
133
|
+
qualifiers: [{ type: 'keyword', token: 'out', whitespace: ' ' }],
|
|
134
|
+
specifier: {
|
|
135
|
+
type: 'type_specifier',
|
|
136
|
+
specifier: { type: 'keyword', token: 'vec4', whitespace: ' ' },
|
|
137
|
+
quantifier: null,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
declarations: [
|
|
141
|
+
{
|
|
142
|
+
type: 'declaration',
|
|
143
|
+
identifier: {
|
|
144
|
+
type: 'identifier',
|
|
145
|
+
identifier: name,
|
|
146
|
+
whitespace: undefined,
|
|
147
|
+
},
|
|
148
|
+
quantifier: null,
|
|
149
|
+
operator: undefined,
|
|
150
|
+
initializer: undefined,
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
commas: [],
|
|
154
|
+
},
|
|
155
|
+
semi: { type: 'literal', literal: ';', whitespace: '\n ' },
|
|
156
|
+
}); };
|
|
157
|
+
export var makeStatement = function (stmt) {
|
|
158
|
+
// log(`Parsing "${stmt}"`);
|
|
159
|
+
var ast;
|
|
160
|
+
try {
|
|
161
|
+
ast = parser.parse("".concat(stmt, ";\n"), { quiet: true });
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
console.error({ stmt: stmt, error: error });
|
|
165
|
+
throw new Error("Error parsing stmt \"".concat(stmt, "\": ").concat(error === null || error === void 0 ? void 0 : error.message));
|
|
166
|
+
}
|
|
167
|
+
// log(util.inspect(ast, false, null, true));
|
|
168
|
+
return ast.program[0];
|
|
169
|
+
};
|
|
170
|
+
export var makeFnStatement = function (fnStmt) {
|
|
171
|
+
var ast;
|
|
172
|
+
try {
|
|
173
|
+
ast = parser.parse("\n void main() {\n ".concat(fnStmt, ";\n }"), { quiet: true });
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.error({ fnStmt: fnStmt, error: error });
|
|
177
|
+
throw new Error("Error parsing fnStmt \"".concat(fnStmt, "\": ").concat(error === null || error === void 0 ? void 0 : error.message));
|
|
178
|
+
}
|
|
179
|
+
// log(util.inspect(ast, false, null, true));
|
|
180
|
+
return ast.program[0].body.statements[0];
|
|
181
|
+
};
|
|
182
|
+
export var makeExpression = function (expr) {
|
|
183
|
+
var ast;
|
|
184
|
+
try {
|
|
185
|
+
ast = parser.parse("void main() {\n a = ".concat(expr, ";\n }"), { quiet: true });
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error({ expr: expr, error: error });
|
|
189
|
+
throw new Error("Error parsing expr \"".concat(expr, "\": ").concat(error === null || error === void 0 ? void 0 : error.message));
|
|
190
|
+
}
|
|
191
|
+
return ast.program[0].body
|
|
192
|
+
.statements[0].expression.right;
|
|
193
|
+
};
|
|
194
|
+
export var makeExpressionWithScopes = function (expr) {
|
|
195
|
+
var ast;
|
|
196
|
+
try {
|
|
197
|
+
ast = parser.parse("void main() {\n ".concat(expr, ";\n }"), { quiet: true });
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.error({ expr: expr, error: error });
|
|
201
|
+
throw new Error("Error parsing expr \"".concat(expr, "\": ").concat(error === null || error === void 0 ? void 0 : error.message));
|
|
202
|
+
}
|
|
203
|
+
// log(util.inspect(ast, false, null, true));
|
|
204
|
+
return {
|
|
205
|
+
scope: ast.scopes[1],
|
|
206
|
+
expression: ast.program[0].body
|
|
207
|
+
.statements[0].expression,
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
export var makeFnBodyStatementWithScopes = function (body) {
|
|
211
|
+
var ast;
|
|
212
|
+
try {
|
|
213
|
+
ast = parser.parse("void main() {\n".concat(body, "\n }"), { quiet: true });
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
console.error({ body: body, error: error });
|
|
217
|
+
throw new Error("Error parsing body \"".concat(body, "\": ").concat(error === null || error === void 0 ? void 0 : error.message));
|
|
218
|
+
}
|
|
219
|
+
// log(util.inspect(ast, false, null, true));
|
|
220
|
+
return {
|
|
221
|
+
scope: ast.scopes[1],
|
|
222
|
+
statements: ast.program[0].body.statements,
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
export var findFn = function (ast, name) {
|
|
226
|
+
return ast.program.find(function (stmt) {
|
|
227
|
+
return stmt.type === 'function' && stmt.prototype.header.name.identifier === name;
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
export var returnGlPosition = function (fnName, ast) {
|
|
231
|
+
return convertVertexMain(fnName, ast, 'vec4', function (assign) { return assign.expression.right; });
|
|
232
|
+
};
|
|
233
|
+
export var returnGlPositionHardCoded = function (fnName, ast, returnType, hardCodedReturn) {
|
|
234
|
+
return convertVertexMain(fnName, ast, returnType, function () {
|
|
235
|
+
return makeExpression(hardCodedReturn);
|
|
236
|
+
});
|
|
237
|
+
};
|
|
238
|
+
export var returnGlPositionVec3Right = function (fnName, ast) {
|
|
239
|
+
return convertVertexMain(fnName, ast, 'vec3', function (assign) {
|
|
240
|
+
var found;
|
|
241
|
+
visit(assign, {
|
|
242
|
+
function_call: {
|
|
243
|
+
enter: function (path) {
|
|
244
|
+
var _a, _b, _c, _d, _e;
|
|
245
|
+
log('returnGlPositionVec3Right', path.node);
|
|
246
|
+
var node = path.node;
|
|
247
|
+
if (((_b = (_a = node === null || node === void 0 ? void 0 : node.identifier) === null || _a === void 0 ? void 0 : _a.specifier) === null || _b === void 0 ? void 0 : _b.token) === 'vec4' &&
|
|
248
|
+
((_e = (_d = (_c = node === null || node === void 0 ? void 0 : node.args) === null || _c === void 0 ? void 0 : _c[2]) === null || _d === void 0 ? void 0 : _d.token) === null || _e === void 0 ? void 0 : _e.includes('1.'))) {
|
|
249
|
+
found = node.args[0];
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
if (!found) {
|
|
255
|
+
console.error(generate(ast));
|
|
256
|
+
throw new Error('Could not find position assignment to convert to return!');
|
|
257
|
+
}
|
|
258
|
+
return found;
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
var convertVertexMain = function (fnName, ast, returnType, generateRight) {
|
|
262
|
+
var mainReturnVar = "frogOut";
|
|
263
|
+
var main = findFn(ast, fnName);
|
|
264
|
+
if (!main) {
|
|
265
|
+
throw new Error("No ".concat(fnName, " fn found!"));
|
|
266
|
+
}
|
|
267
|
+
// Convert the main function to one that returns
|
|
268
|
+
main.prototype.header.returnType.specifier.specifier.token =
|
|
269
|
+
returnType;
|
|
270
|
+
// Find the gl_position assignment line
|
|
271
|
+
var assign = main.body.statements.find(function (stmt) {
|
|
272
|
+
var _a;
|
|
273
|
+
return stmt.type === 'expression_statement' &&
|
|
274
|
+
((_a = stmt.expression.left) === null || _a === void 0 ? void 0 : _a.identifier) === 'gl_Position';
|
|
275
|
+
});
|
|
276
|
+
if (!assign) {
|
|
277
|
+
throw new Error("No gl position assign found in main fn!");
|
|
278
|
+
}
|
|
279
|
+
var rtnStmt = makeFnStatement("".concat(returnType, " ").concat(mainReturnVar, " = 1.0"));
|
|
280
|
+
rtnStmt.declaration.declarations[0].initializer =
|
|
281
|
+
generateRight(assign);
|
|
282
|
+
main.body.statements.splice(main.body.statements.indexOf(assign), 1, rtnStmt);
|
|
283
|
+
main.body.statements.push(makeFnStatement("return ".concat(mainReturnVar)));
|
|
284
|
+
};
|
|
285
|
+
export var convert300MainToReturn = function (suffix, ast) {
|
|
286
|
+
var mainReturnVar = "frogOut_".concat(suffix);
|
|
287
|
+
// Find the output variable, as in "pc_fragColor" from "out highp vec4 pc_fragColor;"
|
|
288
|
+
var outName;
|
|
289
|
+
ast.program.find(function (line, index) {
|
|
290
|
+
var _a, _b;
|
|
291
|
+
var declaration = line === null || line === void 0 ? void 0 : line.declaration;
|
|
292
|
+
if (
|
|
293
|
+
// line.type === 'declaration_statement' &&
|
|
294
|
+
((_b = (_a = declaration === null || declaration === void 0 ? void 0 : declaration.specified_type) === null || _a === void 0 ? void 0 : _a.qualifiers) === null || _b === void 0 ? void 0 : _b.find(function (n) { return n.token === 'out'; })) &&
|
|
295
|
+
declaration.specified_type.specifier.specifier.token ===
|
|
296
|
+
'vec4') {
|
|
297
|
+
// Remove the out declaration
|
|
298
|
+
ast.program.splice(index, 1);
|
|
299
|
+
outName = declaration.declarations[0].identifier.identifier;
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
if (!outName) {
|
|
304
|
+
console.error(generate(ast));
|
|
305
|
+
throw new Error('No "out vec4" line found in the fragment shader');
|
|
306
|
+
}
|
|
307
|
+
ast.program.unshift(makeStatement("vec4 ".concat(mainReturnVar)));
|
|
308
|
+
visit(ast, {
|
|
309
|
+
identifier: {
|
|
310
|
+
enter: function (path) {
|
|
311
|
+
if (path.node.identifier === outName) {
|
|
312
|
+
path.node.identifier = mainReturnVar;
|
|
313
|
+
// @ts-ignore
|
|
314
|
+
path.node.doNotDescope = true; // hack because this var is in the scope which gets renamed later
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
function: {
|
|
319
|
+
enter: function (path) {
|
|
320
|
+
if (path.node.prototype.header.name.identifier === 'main') {
|
|
321
|
+
path.node.prototype.header.returnType.specifier
|
|
322
|
+
.specifier.token = 'vec4';
|
|
323
|
+
path.node.body.statements.push(makeFnStatement("return ".concat(mainReturnVar)));
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
};
|