@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,670 @@
|
|
|
1
|
+
import * as BABYLON from 'babylonjs';
|
|
2
|
+
import { Engine, EngineNodeType, EngineContext } from '../../core/engine';
|
|
3
|
+
import {
|
|
4
|
+
nodeName,
|
|
5
|
+
doesLinkThruShader,
|
|
6
|
+
NodeParser,
|
|
7
|
+
ShaderStage,
|
|
8
|
+
prepopulatePropertyInputs,
|
|
9
|
+
Graph,
|
|
10
|
+
mangleMainFn,
|
|
11
|
+
NodeType,
|
|
12
|
+
} from '../../core/graph';
|
|
13
|
+
import importers from './importers';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
returnGlPositionHardCoded,
|
|
17
|
+
returnGlPosition,
|
|
18
|
+
makeFnStatement,
|
|
19
|
+
returnGlPositionVec3Right,
|
|
20
|
+
} from '../../ast/manipulate';
|
|
21
|
+
|
|
22
|
+
import { Program } from '@shaderfrog/glsl-parser/ast';
|
|
23
|
+
import {
|
|
24
|
+
CodeNode,
|
|
25
|
+
NodeProperty,
|
|
26
|
+
property,
|
|
27
|
+
SourceNode,
|
|
28
|
+
} from '../../core/nodes/code-nodes';
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
namedAttributeStrategy,
|
|
32
|
+
texture2DStrategy,
|
|
33
|
+
uniformStrategy,
|
|
34
|
+
} from '../../core/strategy';
|
|
35
|
+
import { NodeInput, NodePosition } from '../../core/nodes/core-node';
|
|
36
|
+
import { DataNode, UniformDataType } from '../../core/nodes/data-nodes';
|
|
37
|
+
|
|
38
|
+
// Setting these properties on the material have side effects, not just for the
|
|
39
|
+
// GLSL, but for the material itself in JS memory apparently, maybe the bound
|
|
40
|
+
// uniforms?. The material we create in babylengine must have the same initial
|
|
41
|
+
// properties as those in BabylonComponent or else there will be errors with
|
|
42
|
+
// uniforms
|
|
43
|
+
export const physicalDefaultProperties: Partial<
|
|
44
|
+
Record<keyof BABYLON.PBRMaterial, any>
|
|
45
|
+
> = {
|
|
46
|
+
forceIrradianceInFragment: true,
|
|
47
|
+
albedoColor: new BABYLON.Color3(1.0, 1.0, 1.0),
|
|
48
|
+
metallic: 0.0,
|
|
49
|
+
roughness: 1.0,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const log = (...args: any[]) =>
|
|
53
|
+
console.log.call(console, '\x1b[33m(babylengine)\x1b[0m', ...args);
|
|
54
|
+
|
|
55
|
+
const babylonHackCache: Record<string, { fragment: string; vertex: string }> =
|
|
56
|
+
{};
|
|
57
|
+
|
|
58
|
+
export const physicalNode = (
|
|
59
|
+
id: string,
|
|
60
|
+
name: string,
|
|
61
|
+
groupId: string | null | undefined,
|
|
62
|
+
position: NodePosition,
|
|
63
|
+
uniforms: UniformDataType[],
|
|
64
|
+
stage: ShaderStage | undefined,
|
|
65
|
+
nextStageNodeId?: string
|
|
66
|
+
): CodeNode =>
|
|
67
|
+
prepopulatePropertyInputs({
|
|
68
|
+
id,
|
|
69
|
+
name,
|
|
70
|
+
groupId,
|
|
71
|
+
position,
|
|
72
|
+
type: EngineNodeType.physical,
|
|
73
|
+
config: {
|
|
74
|
+
uniforms,
|
|
75
|
+
version: 3,
|
|
76
|
+
mangle: false,
|
|
77
|
+
preprocess: true,
|
|
78
|
+
properties: [
|
|
79
|
+
property('Base Color', 'baseColor', 'rgb', '?????'),
|
|
80
|
+
property('Color', 'albedoColor', 'rgb', 'uniform_vAlbedoColor'),
|
|
81
|
+
property('Texture', 'albedoTexture', 'texture', 'filler_albedoSampler'),
|
|
82
|
+
property('Bump Map', 'bumpTexture', 'texture', 'filler_bumpSampler'),
|
|
83
|
+
property('Metalness', 'metallic', 'number'),
|
|
84
|
+
property('Roughness', 'roughness', 'number'),
|
|
85
|
+
property('Env Map', 'environmentTexture', 'samplerCube'),
|
|
86
|
+
property('Reflection Texture', 'reflectionTexture', 'samplerCube'),
|
|
87
|
+
property('Refraction Texture', 'refractionTexture', 'samplerCube'),
|
|
88
|
+
property('Index Of Refraction', 'indexOfRefraction', 'number'),
|
|
89
|
+
property('Alpha', 'alpha', 'number'),
|
|
90
|
+
property('Direct Intensity', 'directIntensity', 'number'),
|
|
91
|
+
property('Environment Intensity', 'environmentIntensity', 'number'),
|
|
92
|
+
property('Camera Exposure', 'cameraExposure', 'number'),
|
|
93
|
+
property('Camera Contrast', 'cameraContrast', 'number'),
|
|
94
|
+
property('Micro Surface', 'microSurface', 'number'),
|
|
95
|
+
property('Reflectivity Color', 'reflectivityColor', 'rgb'),
|
|
96
|
+
],
|
|
97
|
+
hardCodedProperties: physicalDefaultProperties,
|
|
98
|
+
strategies: [
|
|
99
|
+
uniformStrategy(),
|
|
100
|
+
stage === 'fragment'
|
|
101
|
+
? texture2DStrategy()
|
|
102
|
+
: namedAttributeStrategy('position'),
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
inputs: [],
|
|
106
|
+
outputs: [
|
|
107
|
+
{
|
|
108
|
+
name: 'vector4',
|
|
109
|
+
category: 'data',
|
|
110
|
+
id: '1',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
source: '',
|
|
114
|
+
stage,
|
|
115
|
+
nextStageNodeId,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
export type RuntimeContext = {
|
|
119
|
+
scene: BABYLON.Scene;
|
|
120
|
+
camera: BABYLON.Camera;
|
|
121
|
+
BABYLON: any;
|
|
122
|
+
sceneData: any;
|
|
123
|
+
// material: any;
|
|
124
|
+
// index: number;
|
|
125
|
+
// threeTone: any;
|
|
126
|
+
cache: {
|
|
127
|
+
data: {
|
|
128
|
+
[key: string]: any;
|
|
129
|
+
};
|
|
130
|
+
nodes: {
|
|
131
|
+
[id: string]: {
|
|
132
|
+
// fragmentRef: any;
|
|
133
|
+
// vertexRef: any;
|
|
134
|
+
fragment: string;
|
|
135
|
+
vertex: string;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const toonNode = (
|
|
142
|
+
id: string,
|
|
143
|
+
name: string,
|
|
144
|
+
groupId: string | null | undefined,
|
|
145
|
+
position: NodePosition,
|
|
146
|
+
uniforms: UniformDataType[],
|
|
147
|
+
stage: ShaderStage | undefined,
|
|
148
|
+
nextStageNodeId?: string
|
|
149
|
+
): CodeNode =>
|
|
150
|
+
prepopulatePropertyInputs({
|
|
151
|
+
id,
|
|
152
|
+
name,
|
|
153
|
+
groupId,
|
|
154
|
+
position,
|
|
155
|
+
type: EngineNodeType.toon,
|
|
156
|
+
config: {
|
|
157
|
+
uniforms,
|
|
158
|
+
version: 3,
|
|
159
|
+
preprocess: true,
|
|
160
|
+
mangle: false,
|
|
161
|
+
properties: [
|
|
162
|
+
property('Color', 'color', 'rgb', 'uniform_diffuse'),
|
|
163
|
+
property('Texture', 'map', 'texture', 'filler_map'),
|
|
164
|
+
property(
|
|
165
|
+
'Gradient Map',
|
|
166
|
+
'gradientMap',
|
|
167
|
+
'texture',
|
|
168
|
+
'filler_gradientMap'
|
|
169
|
+
),
|
|
170
|
+
property('Normal Map', 'normalMap', 'texture', 'filler_normalMap'),
|
|
171
|
+
property('Normal Scale', 'normalScale', 'vector2'),
|
|
172
|
+
property('Displacement Map', 'displacementMap', 'texture'),
|
|
173
|
+
property('Env Map', 'envMap', 'samplerCube'),
|
|
174
|
+
],
|
|
175
|
+
strategies: [
|
|
176
|
+
uniformStrategy(),
|
|
177
|
+
stage === 'fragment'
|
|
178
|
+
? texture2DStrategy()
|
|
179
|
+
: namedAttributeStrategy('position'),
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
inputs: [],
|
|
183
|
+
outputs: [
|
|
184
|
+
{
|
|
185
|
+
name: 'vector4',
|
|
186
|
+
category: 'data',
|
|
187
|
+
id: '1',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
source: '',
|
|
191
|
+
stage,
|
|
192
|
+
nextStageNodeId,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const babylonMaterialProperties = (
|
|
196
|
+
scene: BABYLON.Scene,
|
|
197
|
+
graph: Graph,
|
|
198
|
+
node: SourceNode,
|
|
199
|
+
sibling?: SourceNode
|
|
200
|
+
): Record<string, any> => {
|
|
201
|
+
// Find inputs to this node that are dependent on a property of the material
|
|
202
|
+
const propertyInputs = node.inputs
|
|
203
|
+
.filter((i) => i.property)
|
|
204
|
+
.reduce<Record<string, NodeInput>>(
|
|
205
|
+
(acc, input) => ({ ...acc, [input.id]: input }),
|
|
206
|
+
{}
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// Then look for any edges into those inputs and set the material property
|
|
210
|
+
const props = graph.edges
|
|
211
|
+
.filter((edge) => edge.to === node.id || edge.to === sibling?.id)
|
|
212
|
+
.reduce<Record<string, any>>((acc, edge) => {
|
|
213
|
+
// Check if we've plugged into an input for a property
|
|
214
|
+
const propertyInput = propertyInputs[edge.input];
|
|
215
|
+
if (propertyInput) {
|
|
216
|
+
// Find the property itself
|
|
217
|
+
const property = (node.config.properties || []).find(
|
|
218
|
+
(p) => p.property === propertyInput.property
|
|
219
|
+
) as NodeProperty;
|
|
220
|
+
|
|
221
|
+
// Initialize the property on the material
|
|
222
|
+
if (property.type === 'texture') {
|
|
223
|
+
acc[property.property] = new BABYLON.Texture('', scene);
|
|
224
|
+
} else if (property.type === 'number') {
|
|
225
|
+
acc[property.property] = 0.5;
|
|
226
|
+
} else if (property.type === 'rgb') {
|
|
227
|
+
acc[property.property] = new BABYLON.Color3(1, 1, 1);
|
|
228
|
+
} else if (property.type === 'rgba') {
|
|
229
|
+
acc[property.property] = new BABYLON.Color4(1, 1, 1, 1);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return acc;
|
|
233
|
+
}, {});
|
|
234
|
+
return props;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export let mIdx = 0;
|
|
238
|
+
let id = () => mIdx++;
|
|
239
|
+
|
|
240
|
+
const nodeCacheKey = (graph: Graph, node: SourceNode) => {
|
|
241
|
+
return (
|
|
242
|
+
'[ID:' +
|
|
243
|
+
node.id +
|
|
244
|
+
'Edges:' +
|
|
245
|
+
graph.edges
|
|
246
|
+
.filter((edge) => edge.to === node.id)
|
|
247
|
+
.map((edge) => `(${edge.to}->${edge.input})`)
|
|
248
|
+
.sort()
|
|
249
|
+
.join(',') +
|
|
250
|
+
']'
|
|
251
|
+
// Currently excluding node inputs because these are calculated *after*
|
|
252
|
+
// the onbeforecompile, so the next compile, they'll all change!
|
|
253
|
+
// node.inputs.map((i) => `${i.id}${i.bakeable}`)
|
|
254
|
+
);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const programCacheKey = (
|
|
258
|
+
engineContext: EngineContext,
|
|
259
|
+
graph: Graph,
|
|
260
|
+
node: SourceNode,
|
|
261
|
+
sibling: SourceNode
|
|
262
|
+
) => {
|
|
263
|
+
// The megashader source is dependent on scene information, like the number
|
|
264
|
+
// and type of lights in the scene. This kinda sucks - it's duplicating
|
|
265
|
+
// three's material cache key, and is coupled to how three builds shaders
|
|
266
|
+
const scene = engineContext.runtime.scene as BABYLON.Scene;
|
|
267
|
+
const lights = scene.getNodes().filter((n) => n instanceof BABYLON.Light);
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
[node, sibling]
|
|
271
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
272
|
+
.map((n) => nodeCacheKey(graph, n))
|
|
273
|
+
.join('-') +
|
|
274
|
+
'|Lights:' +
|
|
275
|
+
lights.join(',') +
|
|
276
|
+
'|Envtex:' +
|
|
277
|
+
scene.environmentTexture
|
|
278
|
+
);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const cacher = async (
|
|
282
|
+
engineContext: EngineContext,
|
|
283
|
+
graph: Graph,
|
|
284
|
+
node: SourceNode,
|
|
285
|
+
sibling: SourceNode,
|
|
286
|
+
newValue: (...args: any[]) => Promise<any>
|
|
287
|
+
) => {
|
|
288
|
+
const cacheKey = programCacheKey(engineContext, graph, node, sibling);
|
|
289
|
+
|
|
290
|
+
if (engineContext.runtime.cache.data[cacheKey]) {
|
|
291
|
+
log(`Cache hit "${cacheKey}"`);
|
|
292
|
+
} else {
|
|
293
|
+
log(`Cache miss "${cacheKey}"`);
|
|
294
|
+
}
|
|
295
|
+
const materialData = await (engineContext.runtime.cache.data[cacheKey] ||
|
|
296
|
+
newValue());
|
|
297
|
+
log(`Material cache "${cacheKey}" is now`, materialData);
|
|
298
|
+
|
|
299
|
+
engineContext.runtime.cache.data[cacheKey] = materialData;
|
|
300
|
+
engineContext.runtime.engineMaterial = materialData.material;
|
|
301
|
+
|
|
302
|
+
// TODO: We mutate the nodes here, can we avoid that later?
|
|
303
|
+
node.source =
|
|
304
|
+
node.stage === 'fragment' ? materialData.fragment : materialData.vertex;
|
|
305
|
+
sibling.source =
|
|
306
|
+
sibling.stage === 'fragment' ? materialData.fragment : materialData.vertex;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const onBeforeCompileMegaShader = async (
|
|
310
|
+
engineContext: EngineContext,
|
|
311
|
+
graph: Graph,
|
|
312
|
+
node: SourceNode,
|
|
313
|
+
sibling: SourceNode
|
|
314
|
+
): Promise<{
|
|
315
|
+
material: BABYLON.Material;
|
|
316
|
+
fragment: string;
|
|
317
|
+
vertex: string;
|
|
318
|
+
}> => {
|
|
319
|
+
const { scene, sceneData } = engineContext.runtime;
|
|
320
|
+
|
|
321
|
+
const pbrName = `engine_pbr${id()}`;
|
|
322
|
+
const shaderMaterial = new BABYLON.PBRMaterial(pbrName, scene);
|
|
323
|
+
|
|
324
|
+
shaderMaterial.linkRefractionWithTransparency = true;
|
|
325
|
+
shaderMaterial.subSurface.isRefractionEnabled = true;
|
|
326
|
+
const newProperties = {
|
|
327
|
+
...(node.config.hardCodedProperties ||
|
|
328
|
+
sibling.config.hardCodedProperties ||
|
|
329
|
+
{}),
|
|
330
|
+
...babylonMaterialProperties(scene, graph, node, sibling),
|
|
331
|
+
};
|
|
332
|
+
Object.assign(shaderMaterial, newProperties);
|
|
333
|
+
log('Engine megashader initial properties', { newProperties });
|
|
334
|
+
|
|
335
|
+
let vertexSource: string;
|
|
336
|
+
let fragmentSource: string;
|
|
337
|
+
|
|
338
|
+
// This was a previous attempt to do what's done in submeshes below
|
|
339
|
+
// const nodeCache = engineContext.runtime.cache.nodes;
|
|
340
|
+
// fragmentSource =
|
|
341
|
+
// nodeCache[node.id]?.fragment ||
|
|
342
|
+
// nodeCache[node.nextStageNodeId || 'unknown']?.fragment;
|
|
343
|
+
// vertexSource =
|
|
344
|
+
// nodeCache[node.id]?.vertex ||
|
|
345
|
+
// nodeCache[node.nextStageNodeId || 'unknown']?.vertex;
|
|
346
|
+
|
|
347
|
+
const genHackCacheKey = (unknown: BABYLON.MaterialDefines | string[]) =>
|
|
348
|
+
unknown.toString();
|
|
349
|
+
let hackKey: string;
|
|
350
|
+
|
|
351
|
+
return new Promise((resolve) => {
|
|
352
|
+
shaderMaterial.customShaderNameResolve = (
|
|
353
|
+
shaderName,
|
|
354
|
+
uniforms,
|
|
355
|
+
uniformBuffers,
|
|
356
|
+
samplers,
|
|
357
|
+
defines,
|
|
358
|
+
attributes,
|
|
359
|
+
options
|
|
360
|
+
) => {
|
|
361
|
+
hackKey = genHackCacheKey(defines);
|
|
362
|
+
log('Babylengine creating new shader', {
|
|
363
|
+
uniforms,
|
|
364
|
+
uniformBuffers,
|
|
365
|
+
samplers,
|
|
366
|
+
defines,
|
|
367
|
+
attributes,
|
|
368
|
+
options,
|
|
369
|
+
});
|
|
370
|
+
if (options) {
|
|
371
|
+
options.processFinalCode = (type, code) => {
|
|
372
|
+
// If babylon thinks it has cached code for a new shader,
|
|
373
|
+
// processFinalCode doesn't get called. This hack attempt is to try to
|
|
374
|
+
// recreate Babylon's internal cache, based on my understanding that
|
|
375
|
+
// babylon looks to the defines to determine shader uniqueness
|
|
376
|
+
babylonHackCache[hackKey] = babylonHackCache[hackKey] || {
|
|
377
|
+
fragment: '',
|
|
378
|
+
vertex: '',
|
|
379
|
+
};
|
|
380
|
+
if (type === 'vertex') {
|
|
381
|
+
log('captured vertex code', { code });
|
|
382
|
+
vertexSource = code;
|
|
383
|
+
babylonHackCache[hackKey].vertex = code;
|
|
384
|
+
return code;
|
|
385
|
+
} else if (type === 'fragment') {
|
|
386
|
+
log('captured fragment code', { code });
|
|
387
|
+
fragmentSource = code;
|
|
388
|
+
babylonHackCache[hackKey].fragment = code;
|
|
389
|
+
return code;
|
|
390
|
+
}
|
|
391
|
+
throw new Error(`Unknown type ${type}`);
|
|
392
|
+
};
|
|
393
|
+
} else {
|
|
394
|
+
console.warn('No options for', pbrName);
|
|
395
|
+
}
|
|
396
|
+
return shaderName;
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
if (!sceneData.mesh) {
|
|
400
|
+
log('🍃 EFF, no MESHREF RENDER()....');
|
|
401
|
+
}
|
|
402
|
+
shaderMaterial.forceCompilation(sceneData.mesh, (compiledMaterial) => {
|
|
403
|
+
log('Babylon shader compilation done!');
|
|
404
|
+
if (!fragmentSource || !vertexSource) {
|
|
405
|
+
log('Reusing previous mesh render...');
|
|
406
|
+
vertexSource = babylonHackCache[hackKey]?.vertex;
|
|
407
|
+
fragmentSource = babylonHackCache[hackKey]?.fragment;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (!fragmentSource || !vertexSource) {
|
|
411
|
+
debugger;
|
|
412
|
+
}
|
|
413
|
+
log('captured', { fragmentSource, vertexSource });
|
|
414
|
+
|
|
415
|
+
if (node.stage === 'fragment') {
|
|
416
|
+
node.source = fragmentSource;
|
|
417
|
+
}
|
|
418
|
+
if (sibling.stage === 'fragment') {
|
|
419
|
+
sibling.source = fragmentSource;
|
|
420
|
+
}
|
|
421
|
+
if (node.stage === 'vertex') {
|
|
422
|
+
node.source = vertexSource;
|
|
423
|
+
}
|
|
424
|
+
if (sibling.stage === 'vertex') {
|
|
425
|
+
sibling.source = vertexSource;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
engineContext.runtime.cache.nodes[node.id] = {
|
|
429
|
+
// fragmentRef,
|
|
430
|
+
// vertexRef,
|
|
431
|
+
fragment: fragmentSource,
|
|
432
|
+
vertex: vertexSource,
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// This doesn't appear to do anything (see comment above submeshes)
|
|
436
|
+
compiledMaterial.dispose(true);
|
|
437
|
+
|
|
438
|
+
resolve({
|
|
439
|
+
material: compiledMaterial,
|
|
440
|
+
fragment: fragmentSource,
|
|
441
|
+
vertex: vertexSource,
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// TODO: NEED TO DO SAME THREE MANGLIGN STEP HERE
|
|
448
|
+
const megaShaderMainpulateAst: NodeParser['manipulateAst'] = (
|
|
449
|
+
engineContext,
|
|
450
|
+
engine,
|
|
451
|
+
graph,
|
|
452
|
+
node,
|
|
453
|
+
ast,
|
|
454
|
+
inputEdges
|
|
455
|
+
) => {
|
|
456
|
+
const programAst = ast as Program;
|
|
457
|
+
const mainName = 'main' || nodeName(node);
|
|
458
|
+
|
|
459
|
+
if (node.stage === 'vertex') {
|
|
460
|
+
if (doesLinkThruShader(graph, node)) {
|
|
461
|
+
returnGlPositionHardCoded(mainName, programAst, 'vec3', 'transformed');
|
|
462
|
+
} else {
|
|
463
|
+
returnGlPosition(mainName, programAst);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// We specify engine nodes are mangle: false, which is the graph step that
|
|
468
|
+
// handles renaming the main fn, so we have to do it ourselves
|
|
469
|
+
mangleMainFn(programAst, node);
|
|
470
|
+
return programAst;
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const evaluateNode = (node: DataNode) => {
|
|
474
|
+
if (node.type === 'number') {
|
|
475
|
+
return parseFloat(node.value);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (node.type === 'vector2') {
|
|
479
|
+
return new BABYLON.Vector2(
|
|
480
|
+
parseFloat(node.value[0]),
|
|
481
|
+
parseFloat(node.value[1])
|
|
482
|
+
);
|
|
483
|
+
} else if (node.type === 'vector3') {
|
|
484
|
+
return new BABYLON.Vector3(
|
|
485
|
+
parseFloat(node.value[0]),
|
|
486
|
+
parseFloat(node.value[1]),
|
|
487
|
+
parseFloat(node.value[2])
|
|
488
|
+
);
|
|
489
|
+
} else if (node.type === 'vector4') {
|
|
490
|
+
return new BABYLON.Vector4(
|
|
491
|
+
parseFloat(node.value[0]),
|
|
492
|
+
parseFloat(node.value[1]),
|
|
493
|
+
parseFloat(node.value[2]),
|
|
494
|
+
parseFloat(node.value[3])
|
|
495
|
+
);
|
|
496
|
+
} else if (node.type === 'rgb') {
|
|
497
|
+
return new BABYLON.Color3(
|
|
498
|
+
parseFloat(node.value[0]),
|
|
499
|
+
parseFloat(node.value[1]),
|
|
500
|
+
parseFloat(node.value[2])
|
|
501
|
+
);
|
|
502
|
+
} else if (node.type === 'rgba') {
|
|
503
|
+
return new BABYLON.Color4(
|
|
504
|
+
parseFloat(node.value[0]),
|
|
505
|
+
parseFloat(node.value[1]),
|
|
506
|
+
parseFloat(node.value[2]),
|
|
507
|
+
parseFloat(node.value[3])
|
|
508
|
+
);
|
|
509
|
+
} else {
|
|
510
|
+
return node.value;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
export const babylengine: Engine = {
|
|
515
|
+
name: 'babylon',
|
|
516
|
+
importers,
|
|
517
|
+
mergeOptions: {
|
|
518
|
+
includePrecisions: true,
|
|
519
|
+
includeVersion: false,
|
|
520
|
+
},
|
|
521
|
+
evaluateNode,
|
|
522
|
+
constructors: {
|
|
523
|
+
[EngineNodeType.physical]: physicalNode,
|
|
524
|
+
[EngineNodeType.toon]: toonNode,
|
|
525
|
+
},
|
|
526
|
+
// TODO: Get from uniform lib?
|
|
527
|
+
preserve: new Set<string>([
|
|
528
|
+
'viewProjection',
|
|
529
|
+
'normalMatrix',
|
|
530
|
+
'vAmbientInfos',
|
|
531
|
+
'vOpacityInfos',
|
|
532
|
+
'vEmissiveInfos',
|
|
533
|
+
'vLightmapInfos',
|
|
534
|
+
'vReflectivityInfos',
|
|
535
|
+
'vMicroSurfaceSamplerInfos',
|
|
536
|
+
'vReflectionInfos',
|
|
537
|
+
'vReflectionFilteringInfo',
|
|
538
|
+
'vReflectionPosition',
|
|
539
|
+
'vReflectionSize',
|
|
540
|
+
'vBumpInfos',
|
|
541
|
+
'albedoMatrix',
|
|
542
|
+
'ambientMatrix',
|
|
543
|
+
'opacityMatrix',
|
|
544
|
+
'emissiveMatrix',
|
|
545
|
+
'lightmapMatrix',
|
|
546
|
+
'reflectivityMatrix',
|
|
547
|
+
'microSurfaceSamplerMatrix',
|
|
548
|
+
'bumpMatrix',
|
|
549
|
+
'bumpSampler',
|
|
550
|
+
'vTangentSpaceParams',
|
|
551
|
+
'reflectionMatrix',
|
|
552
|
+
'vReflectionColor',
|
|
553
|
+
'vAlbedoColor',
|
|
554
|
+
'vLightingIntensity',
|
|
555
|
+
'vReflectionMicrosurfaceInfos',
|
|
556
|
+
'pointSize',
|
|
557
|
+
'vReflectivityColor',
|
|
558
|
+
'vEmissiveColor',
|
|
559
|
+
'visibility',
|
|
560
|
+
'vMetallicReflectanceFactors',
|
|
561
|
+
'vMetallicReflectanceInfos',
|
|
562
|
+
'metallicReflectanceMatrix',
|
|
563
|
+
'vClearCoatParams',
|
|
564
|
+
'vClearCoatRefractionParams',
|
|
565
|
+
'vClearCoatInfos',
|
|
566
|
+
'clearCoatMatrix',
|
|
567
|
+
'clearCoatRoughnessMatrix',
|
|
568
|
+
'vClearCoatBumpInfos',
|
|
569
|
+
'vClearCoatTangentSpaceParams',
|
|
570
|
+
'clearCoatBumpMatrix',
|
|
571
|
+
'vClearCoatTintParams',
|
|
572
|
+
'clearCoatColorAtDistance',
|
|
573
|
+
'vClearCoatTintInfos',
|
|
574
|
+
'clearCoatTintMatrix',
|
|
575
|
+
'vAnisotropy',
|
|
576
|
+
'vAnisotropyInfos',
|
|
577
|
+
'anisotropyMatrix',
|
|
578
|
+
'vSheenColor',
|
|
579
|
+
'vSheenRoughness',
|
|
580
|
+
'vSheenInfos',
|
|
581
|
+
'sheenMatrix',
|
|
582
|
+
'sheenRoughnessMatrix',
|
|
583
|
+
'vRefractionMicrosurfaceInfos',
|
|
584
|
+
'vRefractionFilteringInfo',
|
|
585
|
+
'vRefractionInfos',
|
|
586
|
+
'refractionMatrix',
|
|
587
|
+
'vThicknessInfos',
|
|
588
|
+
'thicknessMatrix',
|
|
589
|
+
'vThicknessParam',
|
|
590
|
+
'vDiffusionDistance',
|
|
591
|
+
'vTintColor',
|
|
592
|
+
'vSubSurfaceIntensity',
|
|
593
|
+
'scatteringDiffusionProfile',
|
|
594
|
+
'vDetailInfos',
|
|
595
|
+
'detailMatrix',
|
|
596
|
+
'Scene',
|
|
597
|
+
'vEyePosition',
|
|
598
|
+
'vAmbientColor',
|
|
599
|
+
'vCameraInfos',
|
|
600
|
+
'vPositionW',
|
|
601
|
+
'vMainUV1',
|
|
602
|
+
'vNormalW',
|
|
603
|
+
'Light0',
|
|
604
|
+
'albedoSampler',
|
|
605
|
+
'environmentBrdfSampler',
|
|
606
|
+
'position',
|
|
607
|
+
'normal',
|
|
608
|
+
'uv',
|
|
609
|
+
'world',
|
|
610
|
+
'time',
|
|
611
|
+
'Light0',
|
|
612
|
+
'Light1',
|
|
613
|
+
'Light2',
|
|
614
|
+
'Light3',
|
|
615
|
+
'light0',
|
|
616
|
+
'light1',
|
|
617
|
+
'light2',
|
|
618
|
+
'light3',
|
|
619
|
+
'vLightData0',
|
|
620
|
+
'vLightDiffuse0',
|
|
621
|
+
'vLightSpecular0',
|
|
622
|
+
'vLightFalloff0',
|
|
623
|
+
'vSphericalL00',
|
|
624
|
+
'vSphericalL1_1',
|
|
625
|
+
'vSphericalL10',
|
|
626
|
+
'vSphericalL11',
|
|
627
|
+
'vSphericalL2_2',
|
|
628
|
+
'vSphericalL2_1',
|
|
629
|
+
'vSphericalL20',
|
|
630
|
+
'vSphericalL21',
|
|
631
|
+
'vSphericalL22',
|
|
632
|
+
'vAlbedoInfos',
|
|
633
|
+
'reflectionSampler',
|
|
634
|
+
]),
|
|
635
|
+
parsers: {
|
|
636
|
+
[NodeType.SOURCE]: {
|
|
637
|
+
manipulateAst: (engineContext, engine, graph, node, ast, inputEdges) => {
|
|
638
|
+
const programAst = ast as Program;
|
|
639
|
+
const mainName = 'main' || nodeName(node);
|
|
640
|
+
|
|
641
|
+
// This hinges on the vertex shader calling vec3(p)
|
|
642
|
+
if (node.stage === 'vertex') {
|
|
643
|
+
if (doesLinkThruShader(graph, node)) {
|
|
644
|
+
returnGlPositionVec3Right(mainName, programAst);
|
|
645
|
+
} else {
|
|
646
|
+
returnGlPosition(mainName, programAst);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return ast;
|
|
650
|
+
},
|
|
651
|
+
},
|
|
652
|
+
[EngineNodeType.physical]: {
|
|
653
|
+
onBeforeCompile: (graph, engineContext, node, sibling) =>
|
|
654
|
+
cacher(engineContext, graph, node, sibling as SourceNode, () =>
|
|
655
|
+
onBeforeCompileMegaShader(
|
|
656
|
+
engineContext,
|
|
657
|
+
graph,
|
|
658
|
+
node,
|
|
659
|
+
sibling as SourceNode
|
|
660
|
+
)
|
|
661
|
+
),
|
|
662
|
+
manipulateAst: megaShaderMainpulateAst,
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
babylengine.parsers[EngineNodeType.toon] =
|
|
668
|
+
babylengine.parsers[EngineNodeType.physical];
|
|
669
|
+
babylengine.parsers[EngineNodeType.phong] =
|
|
670
|
+
babylengine.parsers[EngineNodeType.physical];
|