@shaderfrog/core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +3 -0
- package/.prettierrc.js +3 -0
- package/README.md +3 -0
- package/babel.config.js +6 -0
- package/package.json +47 -0
- package/src/ast/manipulate.ts +392 -0
- package/src/ast/shader-sections.ts +323 -0
- package/src/core/engine.ts +214 -0
- package/src/core/file.js +53 -0
- package/src/core/graph.ts +1007 -0
- package/src/core/nodes/code-nodes.ts +66 -0
- package/src/core/nodes/core-node.ts +48 -0
- package/src/core/nodes/data-nodes.ts +344 -0
- package/src/core/nodes/edge.ts +23 -0
- package/src/core/nodes/engine-node.ts +266 -0
- package/src/core/strategy.ts +520 -0
- package/src/core.test.ts +312 -0
- package/src/plugins/babylon/bablyengine.ts +670 -0
- package/src/plugins/babylon/examples.ts +512 -0
- package/src/plugins/babylon/importers.ts +69 -0
- package/src/plugins/babylon/index.ts +6 -0
- package/src/plugins/three/examples.ts +680 -0
- package/src/plugins/three/importers.ts +18 -0
- package/src/plugins/three/index.ts +6 -0
- package/src/plugins/three/threngine.tsx +571 -0
- package/src/util/ensure.ts +10 -0
- package/src/util/id.ts +2 -0
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
import { Vector2, Vector3, Vector4, Color } from 'three';
|
|
2
|
+
import { Program } from '@shaderfrog/glsl-parser/ast';
|
|
3
|
+
import {
|
|
4
|
+
Graph,
|
|
5
|
+
NodeParser,
|
|
6
|
+
NodeType,
|
|
7
|
+
ShaderStage,
|
|
8
|
+
prepopulatePropertyInputs,
|
|
9
|
+
mangleMainFn,
|
|
10
|
+
} from '../../core/graph';
|
|
11
|
+
import importers from './importers';
|
|
12
|
+
|
|
13
|
+
import { Engine, EngineContext, EngineNodeType } from '../../core/engine';
|
|
14
|
+
import { GraphNode, doesLinkThruShader, nodeName } from '../../core/graph';
|
|
15
|
+
import {
|
|
16
|
+
returnGlPosition,
|
|
17
|
+
returnGlPositionHardCoded,
|
|
18
|
+
returnGlPositionVec3Right,
|
|
19
|
+
} from '../../ast/manipulate';
|
|
20
|
+
import {
|
|
21
|
+
CodeNode,
|
|
22
|
+
NodeProperty,
|
|
23
|
+
property,
|
|
24
|
+
SourceNode,
|
|
25
|
+
} from '../../core/nodes/code-nodes';
|
|
26
|
+
import { NodeInput, NodePosition } from '../../core/nodes/core-node';
|
|
27
|
+
import { DataNode, UniformDataType } from '../../core/nodes/data-nodes';
|
|
28
|
+
import {
|
|
29
|
+
namedAttributeStrategy,
|
|
30
|
+
texture2DStrategy,
|
|
31
|
+
uniformStrategy,
|
|
32
|
+
} from '../../core/strategy';
|
|
33
|
+
|
|
34
|
+
const log = (...args: any[]) =>
|
|
35
|
+
console.log.call(console, '\x1b[35m(three)\x1b[0m', ...args);
|
|
36
|
+
|
|
37
|
+
export const physicalNode = (
|
|
38
|
+
id: string,
|
|
39
|
+
name: string,
|
|
40
|
+
groupId: string | null | undefined,
|
|
41
|
+
position: NodePosition,
|
|
42
|
+
uniforms: UniformDataType[],
|
|
43
|
+
stage: ShaderStage | undefined,
|
|
44
|
+
nextStageNodeId?: string
|
|
45
|
+
): CodeNode =>
|
|
46
|
+
prepopulatePropertyInputs({
|
|
47
|
+
id,
|
|
48
|
+
name,
|
|
49
|
+
groupId,
|
|
50
|
+
position,
|
|
51
|
+
type: EngineNodeType.physical,
|
|
52
|
+
config: {
|
|
53
|
+
uniforms,
|
|
54
|
+
version: 3,
|
|
55
|
+
mangle: false,
|
|
56
|
+
preprocess: true,
|
|
57
|
+
properties: [
|
|
58
|
+
property('Color', 'color', 'rgb', 'uniform_diffuse'),
|
|
59
|
+
property('Texture', 'map', 'texture', 'filler_map'),
|
|
60
|
+
property('Normal Map', 'normalMap', 'texture', 'filler_normalMap'),
|
|
61
|
+
property('Normal Scale', 'normalScale', 'vector2'),
|
|
62
|
+
property('Metalness', 'metalness', 'number', 'uniform_metalness'),
|
|
63
|
+
property('Roughness', 'roughness', 'number', 'uniform_roughness'),
|
|
64
|
+
property(
|
|
65
|
+
'Roughness Map',
|
|
66
|
+
'roughnessMap',
|
|
67
|
+
'texture',
|
|
68
|
+
'filler_roughnessMap'
|
|
69
|
+
),
|
|
70
|
+
property('Displacement Map', 'displacementMap', 'texture'),
|
|
71
|
+
// MeshPhysicalMaterial gets envMap from the scene. MeshStandardMaterial
|
|
72
|
+
// gets it from the material
|
|
73
|
+
// property('Env Map', 'envMap', 'samplerCube'),
|
|
74
|
+
property('Transmission', 'transmission', 'number'),
|
|
75
|
+
property(
|
|
76
|
+
'Transmission Map',
|
|
77
|
+
'transmissionMap',
|
|
78
|
+
'texture',
|
|
79
|
+
'filler_transmissionMap'
|
|
80
|
+
),
|
|
81
|
+
property('Thickness', 'thickness', 'number'),
|
|
82
|
+
property('Index of Refraction', 'ior', 'number'),
|
|
83
|
+
property('Sheen', 'sheen', 'number'),
|
|
84
|
+
property('Reflectivity', 'reflectivity', 'number'),
|
|
85
|
+
property('Clearcoat', 'clearcoat', 'number'),
|
|
86
|
+
property('Iridescence', 'iridescence', 'number'),
|
|
87
|
+
property('Iridescence IOR', 'iridescenceIOR', 'number'),
|
|
88
|
+
property(
|
|
89
|
+
'Iridescence Thickness Range',
|
|
90
|
+
'iridescenceThicknessRange',
|
|
91
|
+
'array',
|
|
92
|
+
undefined,
|
|
93
|
+
['100', '400']
|
|
94
|
+
),
|
|
95
|
+
],
|
|
96
|
+
hardCodedProperties: {
|
|
97
|
+
isMeshPhysicalMaterial: true,
|
|
98
|
+
isMeshStandardMaterial: true,
|
|
99
|
+
},
|
|
100
|
+
strategies: [
|
|
101
|
+
uniformStrategy(),
|
|
102
|
+
stage === 'fragment'
|
|
103
|
+
? texture2DStrategy()
|
|
104
|
+
: namedAttributeStrategy('position'),
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
inputs: [],
|
|
108
|
+
outputs: [
|
|
109
|
+
{
|
|
110
|
+
name: 'vector4',
|
|
111
|
+
category: 'data',
|
|
112
|
+
id: '1',
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
source: '',
|
|
116
|
+
stage,
|
|
117
|
+
nextStageNodeId,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const cacher = (
|
|
121
|
+
engineContext: EngineContext,
|
|
122
|
+
graph: Graph,
|
|
123
|
+
node: SourceNode,
|
|
124
|
+
sibling: SourceNode,
|
|
125
|
+
newValue: (...args: any[]) => any
|
|
126
|
+
) => {
|
|
127
|
+
const cacheKey = programCacheKey(engineContext, graph, node, sibling);
|
|
128
|
+
|
|
129
|
+
if (engineContext.runtime.cache.data[cacheKey]) {
|
|
130
|
+
log('Cache hit', cacheKey);
|
|
131
|
+
} else {
|
|
132
|
+
log('Cache miss', cacheKey);
|
|
133
|
+
}
|
|
134
|
+
const materialData = engineContext.runtime.cache.data[cacheKey] || newValue();
|
|
135
|
+
|
|
136
|
+
engineContext.runtime.cache.data[cacheKey] = materialData;
|
|
137
|
+
engineContext.runtime.engineMaterial = materialData.material;
|
|
138
|
+
|
|
139
|
+
// TODO: We mutate the nodes here, can we avoid that later?
|
|
140
|
+
node.source =
|
|
141
|
+
node.stage === 'fragment' ? materialData.fragment : materialData.vertex;
|
|
142
|
+
sibling.source =
|
|
143
|
+
sibling.stage === 'fragment' ? materialData.fragment : materialData.vertex;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const onBeforeCompileMegaShader = (
|
|
147
|
+
engineContext: EngineContext,
|
|
148
|
+
newMat: any
|
|
149
|
+
) => {
|
|
150
|
+
log('compiling three megashader!');
|
|
151
|
+
const { renderer, sceneData, scene, camera } = engineContext.runtime;
|
|
152
|
+
const { mesh } = sceneData;
|
|
153
|
+
|
|
154
|
+
// Temporarily swap the mesh material to the new one, since materials can
|
|
155
|
+
// be mesh specific, render, then get its source code
|
|
156
|
+
const originalMaterial = mesh.material;
|
|
157
|
+
mesh.material = newMat;
|
|
158
|
+
renderer.compile(scene, camera);
|
|
159
|
+
|
|
160
|
+
// The references to the compiled shaders in WebGL
|
|
161
|
+
const fragmentRef = renderer.properties
|
|
162
|
+
.get(mesh.material)
|
|
163
|
+
.programs.values()
|
|
164
|
+
.next().value.fragmentShader;
|
|
165
|
+
const vertexRef = renderer.properties
|
|
166
|
+
.get(mesh.material)
|
|
167
|
+
.programs.values()
|
|
168
|
+
.next().value.vertexShader;
|
|
169
|
+
|
|
170
|
+
const gl = renderer.getContext();
|
|
171
|
+
const fragment = gl.getShaderSource(fragmentRef);
|
|
172
|
+
const vertex = gl.getShaderSource(vertexRef);
|
|
173
|
+
|
|
174
|
+
// Reset the material on the mesh, since the shader we're computing context
|
|
175
|
+
// for might not be the one actually want on the mesh - like if a toon node
|
|
176
|
+
// was added to the graph but not connected
|
|
177
|
+
mesh.material = originalMaterial;
|
|
178
|
+
|
|
179
|
+
// Do we even need to do this? This is just for debugging right? Using the
|
|
180
|
+
// source on the node is the important thing.
|
|
181
|
+
return {
|
|
182
|
+
material: newMat,
|
|
183
|
+
fragmentRef,
|
|
184
|
+
vertexRef,
|
|
185
|
+
fragment,
|
|
186
|
+
vertex,
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const megaShaderMainpulateAst: NodeParser['manipulateAst'] = (
|
|
191
|
+
engineContext,
|
|
192
|
+
engine,
|
|
193
|
+
graph,
|
|
194
|
+
node,
|
|
195
|
+
ast,
|
|
196
|
+
inputEdges
|
|
197
|
+
) => {
|
|
198
|
+
const programAst = ast as Program;
|
|
199
|
+
const mainName = 'main' || nodeName(node);
|
|
200
|
+
if (node.stage === 'vertex') {
|
|
201
|
+
if (doesLinkThruShader(graph, node)) {
|
|
202
|
+
returnGlPositionHardCoded(mainName, programAst, 'vec3', 'transformed');
|
|
203
|
+
} else {
|
|
204
|
+
returnGlPosition(mainName, programAst);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// We specify engine nodes are mangle: false, which is the graph step that
|
|
209
|
+
// handles renaming the main fn, so we have to do it ourselves
|
|
210
|
+
mangleMainFn(programAst, node);
|
|
211
|
+
return programAst;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const nodeCacheKey = (graph: Graph, node: SourceNode) => {
|
|
215
|
+
return (
|
|
216
|
+
'[ID:' +
|
|
217
|
+
node.id +
|
|
218
|
+
'Edges:' +
|
|
219
|
+
graph.edges
|
|
220
|
+
.filter((edge) => edge.to === node.id)
|
|
221
|
+
.map((edge) => `(${edge.to}->${edge.input})`)
|
|
222
|
+
.sort()
|
|
223
|
+
.join(',') +
|
|
224
|
+
']'
|
|
225
|
+
// Currently excluding node inputs because these are calculated *after*
|
|
226
|
+
// the onbeforecompile, so the next compile, they'll all change!
|
|
227
|
+
// node.inputs.map((i) => `${i.id}${i.bakeable}`)
|
|
228
|
+
);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const programCacheKey = (
|
|
232
|
+
engineContext: EngineContext,
|
|
233
|
+
graph: Graph,
|
|
234
|
+
node: SourceNode,
|
|
235
|
+
sibling: SourceNode
|
|
236
|
+
) => {
|
|
237
|
+
// The megashader source is dependent on scene information, like the number
|
|
238
|
+
// and type of lights in the scene. This kinda sucks - it's duplicating
|
|
239
|
+
// three's material cache key, and is coupled to how three builds shaders
|
|
240
|
+
const { three, scene } = engineContext.runtime;
|
|
241
|
+
const lights: string[] = [];
|
|
242
|
+
scene.traverse((obj: any) => {
|
|
243
|
+
if (obj instanceof three.Light) {
|
|
244
|
+
lights.push(obj.type as string);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
[node, sibling]
|
|
250
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
251
|
+
.map((n) => nodeCacheKey(graph, n))
|
|
252
|
+
.join('-') +
|
|
253
|
+
'|Lights:' +
|
|
254
|
+
lights.join(',') +
|
|
255
|
+
'|Envtex:' +
|
|
256
|
+
scene.environmentTexture
|
|
257
|
+
);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const threeMaterialProperties = (
|
|
261
|
+
three: any,
|
|
262
|
+
graph: Graph,
|
|
263
|
+
node: SourceNode,
|
|
264
|
+
sibling?: SourceNode
|
|
265
|
+
): Record<string, any> => {
|
|
266
|
+
// Find inputs to this node that are dependent on a property of the material
|
|
267
|
+
const propertyInputs = node.inputs
|
|
268
|
+
.filter((i) => i.property)
|
|
269
|
+
.reduce<Record<string, NodeInput>>(
|
|
270
|
+
(acc, input) => ({ ...acc, [input.id]: input }),
|
|
271
|
+
{}
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
// Then look for any edges into those inputs and set the material property
|
|
275
|
+
return graph.edges
|
|
276
|
+
.filter((edge) => edge.to === node.id || edge.to === sibling?.id)
|
|
277
|
+
.reduce<Record<string, any>>((acc, edge) => {
|
|
278
|
+
// Check if we've plugged into an input for a property
|
|
279
|
+
const propertyInput = propertyInputs[edge.input];
|
|
280
|
+
if (propertyInput) {
|
|
281
|
+
// Find the property itself
|
|
282
|
+
const property = (node.config.properties || []).find(
|
|
283
|
+
(p) => p.property === propertyInput.property
|
|
284
|
+
) as NodeProperty;
|
|
285
|
+
|
|
286
|
+
// Initialize the property on the material
|
|
287
|
+
if (property.type === 'texture') {
|
|
288
|
+
acc[property.property] = new three.Texture();
|
|
289
|
+
} else if (property.type === 'number') {
|
|
290
|
+
acc[property.property] = 0.5;
|
|
291
|
+
} else if (property.type === 'rgb') {
|
|
292
|
+
acc[property.property] = new three.Color(1, 1, 1);
|
|
293
|
+
} else if (property.type === 'rgba') {
|
|
294
|
+
acc[property.property] = new three.Color(1, 1, 1, 1);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return acc;
|
|
298
|
+
}, {});
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
export type ThreeRuntime = {
|
|
302
|
+
scene: any;
|
|
303
|
+
camera: any;
|
|
304
|
+
renderer: any;
|
|
305
|
+
three: any;
|
|
306
|
+
sceneData: any;
|
|
307
|
+
engineMaterial: any;
|
|
308
|
+
index: number;
|
|
309
|
+
cache: {
|
|
310
|
+
data: {
|
|
311
|
+
[key: string]: any;
|
|
312
|
+
};
|
|
313
|
+
nodes: {
|
|
314
|
+
[id: string]: {
|
|
315
|
+
fragmentRef: any;
|
|
316
|
+
vertexRef: any;
|
|
317
|
+
fragment: string;
|
|
318
|
+
vertex: string;
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
};
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const evaluateNode = (node: DataNode) => {
|
|
325
|
+
if (node.type === 'number') {
|
|
326
|
+
return parseFloat(node.value);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (node.type === 'vector2') {
|
|
330
|
+
return new Vector2(parseFloat(node.value[0]), parseFloat(node.value[1]));
|
|
331
|
+
} else if (node.type === 'vector3') {
|
|
332
|
+
return new Vector3(
|
|
333
|
+
parseFloat(node.value[0]),
|
|
334
|
+
parseFloat(node.value[1]),
|
|
335
|
+
parseFloat(node.value[2])
|
|
336
|
+
);
|
|
337
|
+
} else if (node.type === 'vector4') {
|
|
338
|
+
return new Vector4(
|
|
339
|
+
parseFloat(node.value[0]),
|
|
340
|
+
parseFloat(node.value[1]),
|
|
341
|
+
parseFloat(node.value[2]),
|
|
342
|
+
parseFloat(node.value[3])
|
|
343
|
+
);
|
|
344
|
+
} else if (node.type === 'rgb') {
|
|
345
|
+
return new Color(
|
|
346
|
+
parseFloat(node.value[0]),
|
|
347
|
+
parseFloat(node.value[1]),
|
|
348
|
+
parseFloat(node.value[2])
|
|
349
|
+
);
|
|
350
|
+
} else if (node.type === 'rgba') {
|
|
351
|
+
return new Vector4(
|
|
352
|
+
parseFloat(node.value[0]),
|
|
353
|
+
parseFloat(node.value[1]),
|
|
354
|
+
parseFloat(node.value[2]),
|
|
355
|
+
parseFloat(node.value[3])
|
|
356
|
+
);
|
|
357
|
+
} else {
|
|
358
|
+
return node.value;
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
export const toonNode = (
|
|
363
|
+
id: string,
|
|
364
|
+
name: string,
|
|
365
|
+
groupId: string | null | undefined,
|
|
366
|
+
position: NodePosition,
|
|
367
|
+
uniforms: UniformDataType[],
|
|
368
|
+
stage: ShaderStage | undefined,
|
|
369
|
+
nextStageNodeId?: string
|
|
370
|
+
): CodeNode =>
|
|
371
|
+
prepopulatePropertyInputs({
|
|
372
|
+
id,
|
|
373
|
+
name,
|
|
374
|
+
groupId,
|
|
375
|
+
position,
|
|
376
|
+
type: EngineNodeType.toon,
|
|
377
|
+
config: {
|
|
378
|
+
uniforms,
|
|
379
|
+
version: 3,
|
|
380
|
+
preprocess: true,
|
|
381
|
+
mangle: false,
|
|
382
|
+
properties: [
|
|
383
|
+
property('Color', 'color', 'rgb', 'uniform_diffuse'),
|
|
384
|
+
property('Texture', 'map', 'texture', 'filler_map'),
|
|
385
|
+
property(
|
|
386
|
+
'Gradient Map',
|
|
387
|
+
'gradientMap',
|
|
388
|
+
'texture',
|
|
389
|
+
'filler_gradientMap'
|
|
390
|
+
),
|
|
391
|
+
property('Normal Map', 'normalMap', 'texture', 'filler_normalMap'),
|
|
392
|
+
property('Normal Scale', 'normalScale', 'vector2'),
|
|
393
|
+
property('Displacement Map', 'displacementMap', 'texture'),
|
|
394
|
+
property('Env Map', 'envMap', 'samplerCube'),
|
|
395
|
+
],
|
|
396
|
+
strategies: [
|
|
397
|
+
uniformStrategy(),
|
|
398
|
+
stage === 'fragment'
|
|
399
|
+
? texture2DStrategy()
|
|
400
|
+
: namedAttributeStrategy('position'),
|
|
401
|
+
],
|
|
402
|
+
},
|
|
403
|
+
inputs: [],
|
|
404
|
+
outputs: [
|
|
405
|
+
{
|
|
406
|
+
name: 'vector4',
|
|
407
|
+
category: 'data',
|
|
408
|
+
id: '1',
|
|
409
|
+
},
|
|
410
|
+
],
|
|
411
|
+
source: '',
|
|
412
|
+
stage,
|
|
413
|
+
nextStageNodeId,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
export const threngine: Engine = {
|
|
417
|
+
name: 'three',
|
|
418
|
+
importers,
|
|
419
|
+
mergeOptions: {
|
|
420
|
+
includePrecisions: true,
|
|
421
|
+
includeVersion: true,
|
|
422
|
+
},
|
|
423
|
+
evaluateNode,
|
|
424
|
+
constructors: {
|
|
425
|
+
[EngineNodeType.physical]: physicalNode,
|
|
426
|
+
[EngineNodeType.toon]: toonNode,
|
|
427
|
+
},
|
|
428
|
+
// TODO: Get from uniform lib?
|
|
429
|
+
preserve: new Set<string>([
|
|
430
|
+
'viewMatrix',
|
|
431
|
+
'modelMatrix',
|
|
432
|
+
'modelViewMatrix',
|
|
433
|
+
'projectionMatrix',
|
|
434
|
+
'normalMatrix',
|
|
435
|
+
'uvTransform',
|
|
436
|
+
// Attributes
|
|
437
|
+
'position',
|
|
438
|
+
'normal',
|
|
439
|
+
'uv',
|
|
440
|
+
'uv2',
|
|
441
|
+
// Varyings
|
|
442
|
+
'vUv',
|
|
443
|
+
'vUv2',
|
|
444
|
+
'vViewPosition',
|
|
445
|
+
'vNormal',
|
|
446
|
+
'vPosition',
|
|
447
|
+
// Uniforms
|
|
448
|
+
'cameraPosition',
|
|
449
|
+
'isOrthographic',
|
|
450
|
+
'diffuse',
|
|
451
|
+
'emissive',
|
|
452
|
+
'specular',
|
|
453
|
+
'shininess',
|
|
454
|
+
'opacity',
|
|
455
|
+
'map',
|
|
456
|
+
'specularTint',
|
|
457
|
+
'time',
|
|
458
|
+
'normalScale',
|
|
459
|
+
'normalMap',
|
|
460
|
+
'envMap',
|
|
461
|
+
'envMapIntensity',
|
|
462
|
+
'flipEnvMap',
|
|
463
|
+
'maxMipLevel',
|
|
464
|
+
'roughnessMap',
|
|
465
|
+
// Uniforms for lighting
|
|
466
|
+
'receiveShadow',
|
|
467
|
+
'ambientLightColor',
|
|
468
|
+
'lightProbe',
|
|
469
|
+
// Light uniform arrays
|
|
470
|
+
'spotLights',
|
|
471
|
+
'pointLights',
|
|
472
|
+
// This isn't three wtf
|
|
473
|
+
'resolution',
|
|
474
|
+
'color',
|
|
475
|
+
'image',
|
|
476
|
+
'gradientMap',
|
|
477
|
+
// TODO: This isn't specific to threejs as an engine, it's specific to the
|
|
478
|
+
// phong shader. If a *shader* node has brightness, it should be unique, not
|
|
479
|
+
// use the threejs one!
|
|
480
|
+
'brightness',
|
|
481
|
+
// TODO: These depend on the shaderlib, this might need to be a runtime
|
|
482
|
+
// concern
|
|
483
|
+
// Metalness
|
|
484
|
+
'roughness',
|
|
485
|
+
'metalness',
|
|
486
|
+
'ior',
|
|
487
|
+
'specularIntensity',
|
|
488
|
+
'clearcoat',
|
|
489
|
+
'clearcoatRoughness',
|
|
490
|
+
'transmission',
|
|
491
|
+
'thickness',
|
|
492
|
+
'attenuationDistance',
|
|
493
|
+
'attenuationTint',
|
|
494
|
+
'transmissionSamplerMap',
|
|
495
|
+
'transmissionSamplerSize',
|
|
496
|
+
'displacementMap',
|
|
497
|
+
'displacementScale',
|
|
498
|
+
'displacementBias',
|
|
499
|
+
]),
|
|
500
|
+
parsers: {
|
|
501
|
+
[NodeType.SOURCE]: {
|
|
502
|
+
manipulateAst: (engineContext, engine, graph, node, ast, inputEdges) => {
|
|
503
|
+
const programAst = ast as Program;
|
|
504
|
+
const mainName = 'main' || nodeName(node);
|
|
505
|
+
|
|
506
|
+
// This hinges on the vertex shader calling vec3(p)
|
|
507
|
+
if (node.stage === 'vertex') {
|
|
508
|
+
if (doesLinkThruShader(graph, node)) {
|
|
509
|
+
returnGlPositionVec3Right(mainName, programAst);
|
|
510
|
+
} else {
|
|
511
|
+
returnGlPosition(mainName, programAst);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return ast;
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
[EngineNodeType.phong]: {
|
|
518
|
+
onBeforeCompile: async (graph, engineContext, node, sibling) => {
|
|
519
|
+
const { three } = engineContext.runtime;
|
|
520
|
+
cacher(engineContext, graph, node, sibling as SourceNode, () =>
|
|
521
|
+
onBeforeCompileMegaShader(
|
|
522
|
+
engineContext,
|
|
523
|
+
new three.MeshPhongMaterial({
|
|
524
|
+
isMeshPhongMaterial: true,
|
|
525
|
+
...threeMaterialProperties(three, graph, node, sibling),
|
|
526
|
+
})
|
|
527
|
+
)
|
|
528
|
+
);
|
|
529
|
+
},
|
|
530
|
+
manipulateAst: megaShaderMainpulateAst,
|
|
531
|
+
},
|
|
532
|
+
[EngineNodeType.physical]: {
|
|
533
|
+
onBeforeCompile: async (graph, engineContext, node, sibling) => {
|
|
534
|
+
const { three } = engineContext.runtime;
|
|
535
|
+
|
|
536
|
+
cacher(engineContext, graph, node, sibling as SourceNode, () =>
|
|
537
|
+
onBeforeCompileMegaShader(
|
|
538
|
+
engineContext,
|
|
539
|
+
new three.MeshPhysicalMaterial({
|
|
540
|
+
// These properties are copied onto the runtime RawShaderMaterial.
|
|
541
|
+
// These exist on the MeshPhysicalMaterial but only in the
|
|
542
|
+
// prototype. We have to hard code them for Object.keys() to work
|
|
543
|
+
...node.config.hardCodedProperties,
|
|
544
|
+
...threeMaterialProperties(three, graph, node, sibling),
|
|
545
|
+
iridescence: 1.0,
|
|
546
|
+
iridescenceIOR: 2.0,
|
|
547
|
+
})
|
|
548
|
+
)
|
|
549
|
+
);
|
|
550
|
+
},
|
|
551
|
+
manipulateAst: megaShaderMainpulateAst,
|
|
552
|
+
},
|
|
553
|
+
[EngineNodeType.toon]: {
|
|
554
|
+
onBeforeCompile: async (graph, engineContext, node, sibling) => {
|
|
555
|
+
const { three } = engineContext.runtime;
|
|
556
|
+
|
|
557
|
+
cacher(engineContext, graph, node, sibling as SourceNode, () =>
|
|
558
|
+
onBeforeCompileMegaShader(
|
|
559
|
+
engineContext,
|
|
560
|
+
new three.MeshToonMaterial({
|
|
561
|
+
gradientMap: new three.Texture(),
|
|
562
|
+
isMeshToonMaterial: true,
|
|
563
|
+
...threeMaterialProperties(three, graph, node, sibling),
|
|
564
|
+
})
|
|
565
|
+
)
|
|
566
|
+
);
|
|
567
|
+
},
|
|
568
|
+
manipulateAst: megaShaderMainpulateAst,
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
};
|
package/src/util/id.ts
ADDED