@luma.gl/shaders 10.0.0-alpha.6 → 10.0.0-alpha.8

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 (109) hide show
  1. package/dist/dist.dev.js +236 -141
  2. package/dist/dist.min.js +130 -125
  3. package/dist/glsl.cjs.map +1 -1
  4. package/dist/index.cjs.map +2 -2
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/glsl-shader-assembler.cjs +1 -1
  9. package/dist/lib/glsl-shader-assembler.js +1 -1
  10. package/dist/lib/glsl-shader-assembler.js.map +1 -1
  11. package/dist/lib/preprocessor/preprocessor.cjs +21 -0
  12. package/dist/lib/preprocessor/preprocessor.js +21 -0
  13. package/dist/lib/preprocessor/preprocessor.js.map +1 -1
  14. package/dist/lib/shader-assembly/assemble-glsl-shaders.cjs +12 -20
  15. package/dist/lib/shader-assembly/assemble-glsl-shaders.d.ts +3 -2
  16. package/dist/lib/shader-assembly/assemble-glsl-shaders.d.ts.map +1 -1
  17. package/dist/lib/shader-assembly/assemble-glsl-shaders.js +13 -21
  18. package/dist/lib/shader-assembly/assemble-glsl-shaders.js.map +1 -1
  19. package/dist/lib/shader-assembly/assemble-wgsl-shaders.cjs +7 -7
  20. package/dist/lib/shader-assembly/assemble-wgsl-shaders.js +8 -8
  21. package/dist/lib/shader-assembly/assemble-wgsl-shaders.js.map +1 -1
  22. package/dist/lib/shader-assembly/shader-assembly-types.d.ts +11 -7
  23. package/dist/lib/shader-assembly/shader-assembly-types.d.ts.map +1 -1
  24. package/dist/lib/shader-assembly/shader-hooks.cjs +23 -4
  25. package/dist/lib/shader-assembly/shader-hooks.d.ts +1 -1
  26. package/dist/lib/shader-assembly/shader-hooks.d.ts.map +1 -1
  27. package/dist/lib/shader-assembly/shader-hooks.js +27 -4
  28. package/dist/lib/shader-assembly/shader-hooks.js.map +1 -1
  29. package/dist/lib/shader-assembly/shader-injections.d.ts +2 -0
  30. package/dist/lib/shader-assembly/shader-injections.d.ts.map +1 -1
  31. package/dist/lib/shader-assembly/shader-injections.js.map +1 -1
  32. package/dist/lib/shader-assembly/shader-plugin-varyings.cjs +39 -22
  33. package/dist/lib/shader-assembly/shader-plugin-varyings.d.ts +5 -5
  34. package/dist/lib/shader-assembly/shader-plugin-varyings.d.ts.map +1 -1
  35. package/dist/lib/shader-assembly/shader-plugin-varyings.js +45 -19
  36. package/dist/lib/shader-assembly/shader-plugin-varyings.js.map +1 -1
  37. package/dist/lib/shader-generator/glsl/generate-glsl.d.ts +1 -1
  38. package/dist/lib/shader-generator/glsl/generate-glsl.d.ts.map +1 -1
  39. package/dist/lib/shader-generator/wgsl/generate-wgsl.d.ts +1 -1
  40. package/dist/lib/shader-generator/wgsl/generate-wgsl.d.ts.map +1 -1
  41. package/dist/lib/shader-plugin.cjs +94 -34
  42. package/dist/lib/shader-plugin.d.ts +55 -37
  43. package/dist/lib/shader-plugin.d.ts.map +1 -1
  44. package/dist/lib/shader-plugin.js +103 -35
  45. package/dist/lib/shader-plugin.js.map +1 -1
  46. package/dist/lib/wgsl-shader-assembler.cjs +1 -1
  47. package/dist/lib/wgsl-shader-assembler.js +1 -1
  48. package/dist/lib/wgsl-shader-assembler.js.map +1 -1
  49. package/dist/modules/engine/clip/clip.cjs +20 -30
  50. package/dist/modules/engine/clip/clip.d.ts.map +1 -1
  51. package/dist/modules/engine/clip/clip.js +21 -31
  52. package/dist/modules/engine/clip/clip.js.map +1 -1
  53. package/dist/modules/engine/filter/filter.cjs +10 -18
  54. package/dist/modules/engine/filter/filter.d.ts.map +1 -1
  55. package/dist/modules/engine/filter/filter.js +10 -18
  56. package/dist/modules/engine/filter/filter.js.map +1 -1
  57. package/dist/modules/lighting/lights/lighting.d.ts +1 -1
  58. package/dist/modules/lighting/lights/lighting.d.ts.map +1 -1
  59. package/dist/modules/lighting/pbr-material/pbr-material-glsl.cjs +2 -2
  60. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +1 -1
  61. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  62. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +2 -2
  63. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.cjs +2 -2
  64. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +1 -1
  65. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  66. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +2 -2
  67. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +2 -2
  68. package/dist/modules/math/fp64/fp64.cjs +1 -1
  69. package/dist/modules/math/fp64/fp64.d.ts +1 -1
  70. package/dist/modules/math/fp64/fp64.d.ts.map +1 -1
  71. package/dist/modules/math/fp64/fp64.js +1 -1
  72. package/dist/modules/math/fp64/fp64.js.map +1 -1
  73. package/dist/types/texture-formats.d.ts +1 -1
  74. package/dist/types/texture-formats.d.ts.map +1 -1
  75. package/dist/types.cjs +27 -16
  76. package/dist/types.d.ts +7 -7
  77. package/dist/types.d.ts.map +1 -1
  78. package/dist/types.js +2 -7
  79. package/dist/types.js.map +1 -1
  80. package/dist/wgsl.cjs +20 -4
  81. package/dist/wgsl.cjs.map +1 -1
  82. package/dist/wgsl.d.ts +1 -1
  83. package/dist/wgsl.d.ts.map +1 -1
  84. package/dist/wgsl.js +1 -1
  85. package/dist/wgsl.js.map +1 -1
  86. package/package.json +3 -3
  87. package/src/index.ts +5 -4
  88. package/src/lib/glsl-shader-assembler.ts +1 -1
  89. package/src/lib/preprocessor/preprocessor.ts +25 -0
  90. package/src/lib/shader-assembly/assemble-glsl-shaders.ts +19 -23
  91. package/src/lib/shader-assembly/assemble-wgsl-shaders.ts +9 -9
  92. package/src/lib/shader-assembly/shader-assembly-types.ts +11 -7
  93. package/src/lib/shader-assembly/shader-hooks.ts +33 -4
  94. package/src/lib/shader-assembly/shader-injections.ts +2 -0
  95. package/src/lib/shader-assembly/shader-plugin-varyings.ts +58 -27
  96. package/src/lib/shader-generator/glsl/generate-glsl.ts +1 -1
  97. package/src/lib/shader-generator/wgsl/generate-wgsl.ts +1 -1
  98. package/src/lib/shader-module/shader-module-dependencies.ts +1 -1
  99. package/src/lib/shader-plugin.ts +154 -77
  100. package/src/lib/wgsl-shader-assembler.ts +1 -1
  101. package/src/modules/engine/clip/clip.ts +21 -31
  102. package/src/modules/engine/filter/filter.ts +10 -18
  103. package/src/modules/lighting/lights/lighting.ts +1 -1
  104. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +2 -2
  105. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +2 -2
  106. package/src/modules/math/fp64/fp64.ts +1 -1
  107. package/src/types/texture-formats.ts +2 -0
  108. package/src/types.ts +75 -7
  109. package/src/wgsl.ts +12 -1
package/dist/dist.dev.js CHANGED
@@ -1384,47 +1384,53 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1384
1384
  // src/lib/shader-plugin.ts
1385
1385
  var SHADER_PLUGIN_INJECTION_TARGET_REGEX = /^(vs|fs):(?:#(?:decl|main-start|main-end)|[A-Za-z_][\w-]*)$/;
1386
1386
  function resolveShaderPlugins(plugins = [], shaderLanguage) {
1387
- const modules = [];
1388
- const defines = {};
1389
- const injections = {};
1390
- const vertexInputs = {};
1391
- const varyings = {};
1387
+ const resolved = {
1388
+ modules: [],
1389
+ hooks: [],
1390
+ defines: {},
1391
+ injections: {},
1392
+ vertexInputs: {},
1393
+ interStageVariables: {}
1394
+ };
1392
1395
  for (const plugin of plugins) {
1393
- appendShaderPluginVariant({ modules, defines, injections, vertexInputs, varyings }, plugin);
1394
- appendShaderPluginVariant(
1395
- { modules, defines, injections, vertexInputs, varyings },
1396
- plugin[shaderLanguage]
1397
- );
1396
+ appendShaderPlugin(resolved, plugin, shaderLanguage);
1398
1397
  }
1399
- for (const name of Object.keys(varyings)) {
1398
+ const { vertexInputs, interStageVariables } = resolved;
1399
+ for (const name of Object.keys(interStageVariables)) {
1400
1400
  if (vertexInputs[name]) {
1401
1401
  throw new Error(`ShaderPlugin name "${name}" cannot be both a vertex input and a varying`);
1402
1402
  }
1403
1403
  }
1404
- return { modules, defines, injections, vertexInputs, varyings };
1404
+ return resolved;
1405
1405
  }
1406
- function mergeShaderPluginModules(modules = [], pluginModules = []) {
1406
+ function mergeShaderPluginModules(modules = [], pluginModules = [], options = {}) {
1407
1407
  const mergedModules = [...modules];
1408
- const seenModuleNames = new Set(mergedModules.map((module) => module.name));
1408
+ const selectedModules = /* @__PURE__ */ new Map();
1409
+ for (const module of mergedModules) {
1410
+ const selected = selectedModules.get(module.name);
1411
+ if (selected && selected !== module) options.onConflict?.(module.name, selected, module);
1412
+ if (!selected) selectedModules.set(module.name, module);
1413
+ }
1409
1414
  for (const pluginModule of pluginModules) {
1410
- if (!seenModuleNames.has(pluginModule.name)) {
1415
+ const selected = selectedModules.get(pluginModule.name);
1416
+ if (selected && selected !== pluginModule) {
1417
+ options.onConflict?.(pluginModule.name, selected, pluginModule);
1418
+ }
1419
+ if (!selected) {
1411
1420
  mergedModules.push(pluginModule);
1412
- seenModuleNames.add(pluginModule.name);
1421
+ selectedModules.set(pluginModule.name, pluginModule);
1413
1422
  }
1414
1423
  }
1415
1424
  return mergedModules;
1416
1425
  }
1417
- function appendShaderPluginVariant(resolved, variant) {
1418
- if (!variant) {
1419
- return;
1426
+ function appendShaderPlugin(resolved, plugin, shaderLanguage) {
1427
+ if (plugin.module) {
1428
+ resolved.modules.push(plugin.module);
1420
1429
  }
1421
- if (variant.modules?.length) {
1422
- resolved.modules.push(...variant.modules);
1430
+ if (plugin.defines) {
1431
+ Object.assign(resolved.defines, plugin.defines);
1423
1432
  }
1424
- if (variant.defines) {
1425
- Object.assign(resolved.defines, variant.defines);
1426
- }
1427
- for (const [name, type] of Object.entries(variant.vertexInputs || {})) {
1433
+ for (const [name, type] of Object.entries(plugin.vertexInputs || {})) {
1428
1434
  assertShaderPluginInterfaceName(name, "vertex input");
1429
1435
  const existingType = resolved.vertexInputs[name];
1430
1436
  if (existingType && existingType !== type) {
@@ -1434,25 +1440,59 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1434
1440
  }
1435
1441
  resolved.vertexInputs[name] = type;
1436
1442
  }
1437
- for (const [name, varying] of Object.entries(variant.varyings || {})) {
1443
+ for (const [name, varying] of Object.entries(plugin.interStageVariables || {})) {
1438
1444
  assertShaderPluginInterfaceName(name, "varying");
1439
- const normalizedVarying = normalizeShaderPluginVarying(name, varying);
1440
- const existingVarying = resolved.varyings[name];
1445
+ const normalizedVarying = normalizeShaderPluginInterStageVariable(name, varying);
1446
+ const existingVarying = resolved.interStageVariables[name];
1441
1447
  if (existingVarying && (existingVarying.type !== normalizedVarying.type || existingVarying.interpolation !== normalizedVarying.interpolation)) {
1442
1448
  throw new Error(
1443
1449
  `ShaderPlugin varying "${name}" has conflicting declarations "${existingVarying.type}/${existingVarying.interpolation}" and "${normalizedVarying.type}/${normalizedVarying.interpolation}"`
1444
1450
  );
1445
1451
  }
1446
- resolved.varyings[name] = normalizedVarying;
1447
- }
1448
- for (const injection of variant.injections || []) {
1449
- assertNamedShaderPluginInjectionTarget(injection.target);
1450
- if (!resolved.injections[injection.target]) {
1451
- resolved.injections[injection.target] = [];
1452
+ resolved.interStageVariables[name] = normalizedVarying;
1453
+ }
1454
+ for (const hook of plugin.hooks || []) {
1455
+ const declarationSource = typeof hook === "string" ? selectShaderLanguage(hook, shaderLanguage) : void 0;
1456
+ if (typeof hook === "string" && declarationSource === void 0) continue;
1457
+ const signature = typeof hook === "string" ? declarationSource.slice(declarationSource.indexOf("(")) : hook[shaderLanguage];
1458
+ if (signature === void 0) continue;
1459
+ const target = typeof hook === "string" ? declarationSource.split("(")[0] : getShaderPluginTarget(hook);
1460
+ if (!/^(vs|fs):[A-Za-z_][A-Za-z0-9_]*$/.test(target) || !/^\([^]*\)$/.test(signature.trim())) {
1461
+ throw new Error(`Invalid ShaderPlugin hook ${target}`);
1462
+ }
1463
+ const declaration = { hook: `${target}${signature.trim()}` };
1464
+ const existing = resolved.hooks.find((value) => value.hook.split("(")[0] === target);
1465
+ if (existing && existing.hook !== declaration.hook) {
1466
+ throw new Error(`Conflicting ShaderPlugin hook ${target}`);
1467
+ }
1468
+ if (!existing) resolved.hooks.push(declaration);
1469
+ }
1470
+ const pluginInjections = Array.isArray(plugin.injections) ? plugin.injections : Object.entries(plugin.injections || {}).flatMap(([key, source7]) => {
1471
+ const target = selectShaderLanguage(key, shaderLanguage);
1472
+ if (target === void 0) return [];
1473
+ const [stage, ...name] = target.split(":");
1474
+ if (stage !== "vs" && stage !== "fs")
1475
+ throw new Error(`Invalid ShaderPlugin injection ${key}`);
1476
+ return [
1477
+ {
1478
+ stage: stage === "vs" ? "vertex" : "fragment",
1479
+ target: name.join(":"),
1480
+ [shaderLanguage]: source7
1481
+ }
1482
+ ];
1483
+ });
1484
+ for (const injection of pluginInjections) {
1485
+ const source7 = injection[shaderLanguage];
1486
+ if (source7 === void 0) continue;
1487
+ const target = getShaderPluginTarget(injection);
1488
+ assertNamedShaderPluginInjectionTarget(target);
1489
+ if (!resolved.injections[target]) {
1490
+ resolved.injections[target] = [];
1452
1491
  }
1453
- const injections = resolved.injections[injection.target];
1492
+ const injections = resolved.injections[target];
1454
1493
  injections.push({
1455
- injection: injection.injection,
1494
+ pluginName: plugin.name,
1495
+ injection: source7,
1456
1496
  order: injection.order ?? 0
1457
1497
  });
1458
1498
  }
@@ -1462,7 +1502,7 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1462
1502
  throw new Error(`ShaderPlugin ${kind} "${name}" must be a valid non-reserved identifier`);
1463
1503
  }
1464
1504
  }
1465
- function normalizeShaderPluginVarying(name, varying) {
1505
+ function normalizeShaderPluginInterStageVariable(name, varying) {
1466
1506
  const { primitiveType } = shaderTypeDecoder.getAttributeShaderTypeInfo(varying.type);
1467
1507
  const integerType = primitiveType === "i32" || primitiveType === "u32";
1468
1508
  const interpolation = varying.interpolation || (integerType ? "flat" : "smooth");
@@ -1478,6 +1518,16 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1478
1518
  );
1479
1519
  }
1480
1520
  }
1521
+ function getShaderPluginTarget(value) {
1522
+ if (value.stage !== "vertex" && value.stage !== "fragment" || value.target.includes(":")) {
1523
+ throw new Error("ShaderPlugin object targets require an explicit stage and unqualified target");
1524
+ }
1525
+ return `${value.stage === "vertex" ? "vs" : "fs"}:${value.target}`;
1526
+ }
1527
+ function selectShaderLanguage(value, language) {
1528
+ const match = /^(glsl|wgsl):([\s\S]+)$/.exec(value);
1529
+ return match ? match[1] === language ? match[2] : void 0 : value;
1530
+ }
1481
1531
 
1482
1532
  // src/lib/shader-module/shader-module-uniform-layout.ts
1483
1533
  var GLSL_UNIFORM_BLOCK_FIELD_REGEXP = /^(?:uniform\s+)?(?:(?:lowp|mediump|highp)\s+)?[A-Za-z0-9_]+(?:<[^>]+>)?\s+([A-Za-z0-9_]+)(?:\s*\[[^\]]+\])?\s*;/;
@@ -1973,6 +2023,29 @@ ${DECLARATION_INJECT_MARKER}
1973
2023
  if (trimmedExpression === "false") {
1974
2024
  return false;
1975
2025
  }
2026
+ const operandPattern = "(?:[A-Za-z_][A-Za-z0-9_]*|[+-]?\\d+(?:\\.\\d+)?)";
2027
+ const comparison = trimmedExpression.match(
2028
+ new RegExp(`^(${operandPattern})\\s*(==|!=|<=|>=|<|>)\\s*(${operandPattern})$`)
2029
+ );
2030
+ if (comparison) {
2031
+ const resolveOperand = (operand) => /^[A-Za-z_]/.test(operand) ? Number(defines[operand] ?? 0) : Number(operand);
2032
+ const left = resolveOperand(comparison[1]);
2033
+ const right = resolveOperand(comparison[3]);
2034
+ switch (comparison[2]) {
2035
+ case "==":
2036
+ return left === right;
2037
+ case "!=":
2038
+ return left !== right;
2039
+ case "<=":
2040
+ return left <= right;
2041
+ case ">=":
2042
+ return left >= right;
2043
+ case "<":
2044
+ return left < right;
2045
+ case ">":
2046
+ return left > right;
2047
+ }
2048
+ }
1976
2049
  const negatedDefineMatch = trimmedExpression.match(new RegExp(`^!\\s*${DEFINE_NAME_PATTERN}$`));
1977
2050
  if (negatedDefineMatch) {
1978
2051
  return !Boolean(defines[negatedDefineMatch[1]]);
@@ -2225,10 +2298,10 @@ ${DECLARATION_INJECT_MARKER}
2225
2298
  }
2226
2299
 
2227
2300
  // src/lib/shader-assembly/shader-plugin-varyings.ts
2228
- function assembleShaderPluginVaryingsGLSL(source7, stage, varyings) {
2301
+ function assembleShaderPluginInterStageVariablesGLSL(source7, stage, interStageVariables) {
2229
2302
  const declarations = [];
2230
2303
  const initialization = [];
2231
- for (const [name, varying] of Object.entries(varyings)) {
2304
+ for (const [name, varying] of Object.entries(interStageVariables)) {
2232
2305
  assertGLSLVaryingIsNew(source7, name);
2233
2306
  const qualifier = varying.interpolation === "flat" ? "flat " : "";
2234
2307
  const direction = stage === "vertex" ? "out" : "in";
@@ -2239,8 +2312,8 @@ ${DECLARATION_INJECT_MARKER}
2239
2312
  }
2240
2313
  return { declarations: declarations.join("\n"), initialization: initialization.join("\n") };
2241
2314
  }
2242
- function assembleShaderPluginVaryingsWGSL(source7, vertexEntryPoint, fragmentEntryPoint, varyings) {
2243
- const entries = Object.entries(varyings);
2315
+ function assembleShaderPluginInterStageVariablesWGSL(source7, vertexEntryPoint, fragmentEntryPoint, interStageVariables) {
2316
+ const entries = Object.entries(interStageVariables);
2244
2317
  if (entries.length === 0) {
2245
2318
  return {
2246
2319
  source: source7,
@@ -2255,18 +2328,20 @@ ${DECLARATION_INJECT_MARKER}
2255
2328
  let fragmentFunction = getWGSLFunctionRange(transformedSource, fragmentEntryPoint, "fragment");
2256
2329
  const fragmentInput = getWGSLFragmentStructInput(transformedSource, fragmentFunction);
2257
2330
  const vertexOutputStruct = getWGSLStructRange(transformedSource, vertexOutputType);
2258
- const fragmentInputStruct = getWGSLStructRange(transformedSource, fragmentInput.type);
2331
+ const fragmentInputStruct = fragmentInput ? getWGSLStructRange(transformedSource, fragmentInput.type) : null;
2259
2332
  const interfaceNames = /* @__PURE__ */ new Set([
2260
2333
  ...getWGSLDeclaredNames2(vertexFunction.parameters),
2261
2334
  ...getWGSLDeclaredNames2(vertexOutputStruct.body),
2262
2335
  ...getWGSLDeclaredNames2(fragmentFunction.parameters),
2263
- ...getWGSLDeclaredNames2(fragmentInputStruct.body)
2336
+ ...getWGSLDeclaredNames2(fragmentInputStruct?.body || "")
2264
2337
  ]);
2265
2338
  const usedLocations = /* @__PURE__ */ new Set([
2266
2339
  ...getWGSLLocations2(vertexOutputStruct.body),
2267
- ...getWGSLLocations2(fragmentInputStruct.body)
2340
+ ...getWGSLLocations2(fragmentInputStruct?.body || ""),
2341
+ ...getWGSLLocations2(fragmentFunction.parameters)
2268
2342
  ]);
2269
2343
  const fields = [];
2344
+ const fragmentParameters = [];
2270
2345
  const declarations = [];
2271
2346
  const vertexInitialization = [];
2272
2347
  const fragmentInitialization = [];
@@ -2282,7 +2357,20 @@ ${DECLARATION_INJECT_MARKER}
2282
2357
  fields.push(` @location(${location})${interpolation} ${name}: ${varying.type},`);
2283
2358
  declarations.push(`var<private> ${name}: ${varying.type};`);
2284
2359
  vertexInitialization.push(`${name} = ${getWGSLZeroValue(varying.type)};`);
2285
- fragmentInitialization.push(`${name} = ${fragmentInput.name}.${name};`);
2360
+ if (fragmentInput) {
2361
+ fragmentInitialization.push(`${name} = ${fragmentInput.name}.${name};`);
2362
+ } else {
2363
+ const parameterName = `_luma_${name}`;
2364
+ if (interfaceNames.has(parameterName)) {
2365
+ throw new Error(
2366
+ `ShaderPlugin varying "${name}" conflicts with a generated fragment parameter`
2367
+ );
2368
+ }
2369
+ fragmentParameters.push(
2370
+ `@location(${location})${interpolation} ${parameterName}: ${varying.type}`
2371
+ );
2372
+ fragmentInitialization.push(`${name} = ${parameterName};`);
2373
+ }
2286
2374
  }
2287
2375
  assertWGSLStructConstructorsAreLocal(
2288
2376
  transformedSource,
@@ -2302,16 +2390,21 @@ ${DECLARATION_INJECT_MARKER}
2302
2390
  vertexFunction,
2303
2391
  entries.map(([name]) => name)
2304
2392
  );
2305
- const structNames = vertexOutputType === fragmentInput.type ? [vertexOutputType] : [vertexOutputType, fragmentInput.type];
2393
+ const structNames = !fragmentInput || vertexOutputType === fragmentInput.type ? [vertexOutputType] : [vertexOutputType, fragmentInput.type];
2306
2394
  const structInsertions = structNames.map((structName) => getWGSLStructRange(transformedSource, structName).closeBrace).sort((left, right) => right - left);
2307
2395
  for (const insertionIndex of structInsertions) {
2308
2396
  transformedSource = transformedSource.slice(0, insertionIndex) + `${fields.join("\n")}
2309
2397
  ` + transformedSource.slice(insertionIndex);
2310
2398
  }
2311
2399
  fragmentFunction = getWGSLFunctionRange(transformedSource, fragmentEntryPoint, "fragment");
2312
- const fragmentInputPattern = new RegExp(`\\b${escapeRegExp3(fragmentInput.name)}\\s*:`);
2313
- if (!fragmentInputPattern.test(fragmentFunction.parameters)) {
2314
- throw new Error(`Unable to preserve WGSL fragment input "${fragmentInput.name}"`);
2400
+ if (fragmentParameters.length) {
2401
+ let parameters = fragmentFunction.parameters;
2402
+ const parameterTokens = maskWGSLComments(parameters).trimEnd();
2403
+ if (parameterTokens.trim() && !parameterTokens.endsWith(",")) {
2404
+ const insertionIndex = parameterTokens.length;
2405
+ parameters = `${parameters.slice(0, insertionIndex)},${parameters.slice(insertionIndex)}`;
2406
+ }
2407
+ transformedSource = transformedSource.slice(0, fragmentFunction.openParenthesis + 1) + parameters + "\n " + fragmentParameters.join(",\n ") + "\n" + transformedSource.slice(fragmentFunction.closeParenthesis);
2315
2408
  }
2316
2409
  return {
2317
2410
  source: transformedSource,
@@ -2324,7 +2417,9 @@ ${DECLARATION_INJECT_MARKER}
2324
2417
  const functionPattern = new RegExp(`\\bfn\\s+${escapeRegExp3(entryPoint)}\\s*\\(`, "g");
2325
2418
  const match = functionPattern.exec(source7);
2326
2419
  if (!match) {
2327
- throw new Error(`ShaderPlugin varyings require WGSL ${stage} entry point "${entryPoint}"`);
2420
+ throw new Error(
2421
+ `ShaderPlugin interStageVariables require WGSL ${stage} entry point "${entryPoint}"`
2422
+ );
2328
2423
  }
2329
2424
  const openParenthesis = source7.indexOf("(", match.index);
2330
2425
  const closeParenthesis = findMatchingDelimiter2(source7, openParenthesis, "(", ")");
@@ -2346,14 +2441,14 @@ ${DECLARATION_INJECT_MARKER}
2346
2441
  const match = /->\s*([A-Za-z_][\w]*)\s*$/.exec(resultSource.trim());
2347
2442
  if (!match || getWGSLStructRangeOrNull(source7, match[1]) === null) {
2348
2443
  throw new Error(
2349
- "ShaderPlugin varyings require the WGSL vertex entry point to return a named struct"
2444
+ "ShaderPlugin interStageVariables require the WGSL vertex entry point to return a named struct"
2350
2445
  );
2351
2446
  }
2352
2447
  return match[1];
2353
2448
  }
2354
2449
  function getWGSLFragmentStructInput(source7, functionRange) {
2355
2450
  const candidates = [];
2356
- for (const parameter of splitTopLevel(functionRange.parameters, ",")) {
2451
+ for (const parameter of splitTopLevel(maskWGSLComments(functionRange.parameters), ",")) {
2357
2452
  const match = /(?:@[A-Za-z_][\w]*(?:\([^)]*\))?\s*)*([A-Za-z_][\w]*)\s*:\s*([A-Za-z_][\w]*)\s*$/.exec(
2358
2453
  parameter.trim()
2359
2454
  );
@@ -2361,12 +2456,12 @@ ${DECLARATION_INJECT_MARKER}
2361
2456
  candidates.push({ name: match[1], type: match[2] });
2362
2457
  }
2363
2458
  }
2364
- if (candidates.length !== 1) {
2459
+ if (candidates.length > 1) {
2365
2460
  throw new Error(
2366
- `ShaderPlugin varyings require exactly one named WGSL fragment input struct; found ${candidates.length}`
2461
+ `ShaderPlugin interStageVariables require exactly one named WGSL fragment input struct; found ${candidates.length}`
2367
2462
  );
2368
2463
  }
2369
- return candidates[0];
2464
+ return candidates[0] || null;
2370
2465
  }
2371
2466
  function getWGSLStructRange(source7, structName) {
2372
2467
  const range = getWGSLStructRangeOrNull(source7, structName);
@@ -2659,10 +2754,28 @@ return ${outputName};
2659
2754
  }
2660
2755
  return result;
2661
2756
  }
2662
- function normalizeShaderHooks(hookFunctions) {
2757
+ function normalizeShaderHooks(hookFunctions, pluginHooks = [], language) {
2663
2758
  const result = { vertex: {}, fragment: {} };
2664
2759
  for (const hookFunction of hookFunctions) {
2665
- addShaderHookToRegistry(result, hookFunction);
2760
+ const text = typeof hookFunction === "string" ? hookFunction : hookFunction.hook;
2761
+ const qualified = /^(glsl|wgsl):([\s\S]+)$/.exec(text);
2762
+ if (qualified && qualified[1] !== language) continue;
2763
+ addShaderHookToRegistry(
2764
+ result,
2765
+ qualified ? typeof hookFunction === "string" ? qualified[2] : { ...hookFunction, hook: qualified[2] } : hookFunction
2766
+ );
2767
+ }
2768
+ for (const hook of pluginHooks) {
2769
+ const normalized = normalizeShaderHooks([hook]);
2770
+ for (const stage of ["vertex", "fragment"]) {
2771
+ for (const [name, declaration] of Object.entries(normalized[stage])) {
2772
+ const existing = result[stage][name];
2773
+ if (existing && existing.signature !== declaration.signature) {
2774
+ throw new Error(`Conflicting ShaderPlugin hook ${name}`);
2775
+ }
2776
+ result[stage][name] = existing || declaration;
2777
+ }
2778
+ }
2666
2779
  }
2667
2780
  return result;
2668
2781
  }
@@ -2677,7 +2790,12 @@ return ${outputName};
2677
2790
  continue;
2678
2791
  }
2679
2792
  if (!isShaderHookRegistered(registry, hookName)) {
2680
- throw new Error(`Unknown shader hook ${hookName}`);
2793
+ const pluginNames = [
2794
+ ...new Set(hookInjections[hookName].map((injection) => injection.pluginName).filter(Boolean))
2795
+ ];
2796
+ throw new Error(
2797
+ `Unknown shader hook ${hookName}${pluginNames.length ? ` (plugins: ${pluginNames.join(", ")})` : ""}`
2798
+ );
2681
2799
  }
2682
2800
  }
2683
2801
  }
@@ -2687,7 +2805,7 @@ return ${outputName};
2687
2805
  const stageSeparatorIndex = hook.indexOf(":");
2688
2806
  const shaderStage = hook.slice(0, stageSeparatorIndex);
2689
2807
  const signature = hook.slice(stageSeparatorIndex + 1);
2690
- const name = hook.replace(/\(.+/, "");
2808
+ const name = hook.split("(")[0].trim();
2691
2809
  const normalizedHook = {
2692
2810
  hook: name,
2693
2811
  signature,
@@ -2734,7 +2852,7 @@ return ${outputName};
2734
2852
  inject = {},
2735
2853
  pluginInjections = {},
2736
2854
  pluginVertexInputs = {},
2737
- pluginVaryings = {},
2855
+ pluginInterStageVariables = {},
2738
2856
  vertexEntryPoint = "vertexMain",
2739
2857
  fragmentEntryPoint = "fragmentMain",
2740
2858
  log
@@ -2746,15 +2864,15 @@ return ${outputName};
2746
2864
  vertexEntryPoint,
2747
2865
  pluginVertexInputs
2748
2866
  );
2749
- const pluginVaryingAssembly = assembleShaderPluginVaryingsWGSL(
2867
+ const pluginVaryingAssembly = assembleShaderPluginInterStageVariablesWGSL(
2750
2868
  pluginVertexInputAssembly.source,
2751
2869
  vertexEntryPoint,
2752
2870
  fragmentEntryPoint,
2753
- pluginVaryings
2871
+ pluginInterStageVariables
2754
2872
  );
2755
2873
  const coreSource = pluginVaryingAssembly.source;
2756
2874
  let assembledSource = "";
2757
- const hookFunctionMap = normalizeShaderHooks(hookFunctions);
2875
+ const hookFunctionMap = normalizeShaderHooks(hookFunctions, options.pluginHooks, "wgsl");
2758
2876
  const hookInjections = {};
2759
2877
  const declInjections = {};
2760
2878
  const mainInjections = {};
@@ -2767,15 +2885,15 @@ return ${outputName};
2767
2885
  const name = match[3];
2768
2886
  if (hash) {
2769
2887
  if (name === "decl") {
2770
- declInjections[key] = [injection];
2888
+ declInjections[key] = [...declInjections[key] || [], injection];
2771
2889
  } else {
2772
- mainInjections[key] = [injection];
2890
+ mainInjections[key] = [...mainInjections[key] || [], injection];
2773
2891
  }
2774
2892
  } else {
2775
- hookInjections[key] = [injection];
2893
+ hookInjections[key] = [...hookInjections[key] || [], injection];
2776
2894
  }
2777
2895
  } else {
2778
- mainInjections[key] = [injection];
2896
+ mainInjections[key] = [...mainInjections[key] || [], injection];
2779
2897
  }
2780
2898
  }
2781
2899
  appendGeneratedVertexInputInjections(
@@ -3376,7 +3494,7 @@ return ${outputName};
3376
3494
  inject = {},
3377
3495
  pluginInjections = {},
3378
3496
  pluginVertexInputs = {},
3379
- pluginVaryings = {},
3497
+ pluginInterStageVariables = {},
3380
3498
  prologue = true,
3381
3499
  log
3382
3500
  } = options;
@@ -3412,7 +3530,7 @@ ${getApplicationDefines(allDefines)}
3412
3530
  `;
3413
3531
  break;
3414
3532
  }
3415
- const hookFunctionMap = normalizeShaderHooks(hookFunctions);
3533
+ const hookFunctionMap = normalizeShaderHooks(hookFunctions, options.pluginHooks, "glsl");
3416
3534
  const hookInjections = {};
3417
3535
  const declInjections = {};
3418
3536
  const mainInjections = {};
@@ -3425,35 +3543,30 @@ ${getApplicationDefines(allDefines)}
3425
3543
  const name = match[3];
3426
3544
  if (hash) {
3427
3545
  if (name === "decl") {
3428
- declInjections[key] = [injection];
3546
+ declInjections[key] = [...declInjections[key] || [], injection];
3429
3547
  } else {
3430
- mainInjections[key] = [injection];
3548
+ mainInjections[key] = [...mainInjections[key] || [], injection];
3431
3549
  }
3432
3550
  } else {
3433
- hookInjections[key] = [injection];
3551
+ hookInjections[key] = [...hookInjections[key] || [], injection];
3434
3552
  }
3435
3553
  } else {
3436
- mainInjections[key] = [injection];
3554
+ mainInjections[key] = [...mainInjections[key] || [], injection];
3437
3555
  }
3438
3556
  }
3439
3557
  if (stage === "vertex") {
3440
3558
  const declarations = getShaderPluginVertexInputDeclarationsGLSL(coreSource, pluginVertexInputs);
3441
- if (declarations) {
3442
- declInjections["vs:#decl"] = declInjections["vs:#decl"] || [];
3443
- declInjections["vs:#decl"].push({
3444
- injection: declarations,
3445
- order: Number.MIN_SAFE_INTEGER
3446
- });
3447
- }
3559
+ if (declarations) assembledSource += `${declarations}
3560
+ `;
3448
3561
  }
3449
- const pluginVaryingAssembly = assembleShaderPluginVaryingsGLSL(coreSource, stage, pluginVaryings);
3562
+ const pluginVaryingAssembly = assembleShaderPluginInterStageVariablesGLSL(
3563
+ coreSource,
3564
+ stage,
3565
+ pluginInterStageVariables
3566
+ );
3450
3567
  if (pluginVaryingAssembly.declarations) {
3451
- const declarationTarget = stage === "vertex" ? "vs:#decl" : "fs:#decl";
3452
- declInjections[declarationTarget] = declInjections[declarationTarget] || [];
3453
- declInjections[declarationTarget].push({
3454
- injection: pluginVaryingAssembly.declarations,
3455
- order: Number.MIN_SAFE_INTEGER
3456
- });
3568
+ assembledSource += `${pluginVaryingAssembly.declarations}
3569
+ `;
3457
3570
  }
3458
3571
  if (pluginVaryingAssembly.initialization) {
3459
3572
  mainInjections["vs:#main-start"] = mainInjections["vs:#main-start"] || [];
@@ -3635,7 +3748,7 @@ ${getApplicationDefines(allDefines)}
3635
3748
  */
3636
3749
  assembleGLSLShaderPair(props) {
3637
3750
  const modules = this._getModuleList(props.modules);
3638
- const hookFunctions = this._hookFunctions;
3751
+ const hookFunctions = [...this._hookFunctions, ...props.hookFunctions || []];
3639
3752
  const vertex = this._transpileShader(props, modules, props.vs, "vertex");
3640
3753
  const fragment = this._transpileShader(props, modules, props.fs, "fragment");
3641
3754
  const assembled = assembleGLSLShaderPair({
@@ -3661,7 +3774,7 @@ ${getApplicationDefines(allDefines)}
3661
3774
  */
3662
3775
  assembleWGSLShader(props) {
3663
3776
  const modules = this._getModuleList(props.modules);
3664
- const hookFunctions = this._hookFunctions;
3777
+ const hookFunctions = [...this._hookFunctions, ...props.hookFunctions || []];
3665
3778
  const translated = this._transpileShader(
3666
3779
  props,
3667
3780
  modules,
@@ -11614,30 +11727,22 @@ fn filter_isVisible(value: f32) -> bool {
11614
11727
  };
11615
11728
  var filterShaderPlugin = {
11616
11729
  name: "filter",
11617
- modules: [filterShaderModule],
11730
+ module: filterShaderModule,
11618
11731
  vertexInputs: {
11619
11732
  filterValues: "f32"
11620
11733
  },
11621
- glsl: {
11622
- injections: [
11623
- {
11624
- target: "vs:FILTER_POSITION",
11625
- injection: `if (!filter_isVisible(filterValues)) {
11734
+ injections: [
11735
+ {
11736
+ stage: "vertex",
11737
+ target: "FILTER_POSITION",
11738
+ glsl: `if (!filter_isVisible(filterValues)) {
11626
11739
  position = vec4(2.0, 2.0, 2.0, 1.0);
11627
- }`
11628
- }
11629
- ]
11630
- },
11631
- wgsl: {
11632
- injections: [
11633
- {
11634
- target: "vs:FILTER_POSITION",
11635
- injection: `if (!filter_isVisible(filterValues)) {
11740
+ }`,
11741
+ wgsl: `if (!filter_isVisible(filterValues)) {
11636
11742
  *position = vec4<f32>(2.0, 2.0, 2.0, 1.0);
11637
11743
  }`
11638
- }
11639
- ]
11640
- }
11744
+ }
11745
+ ]
11641
11746
  };
11642
11747
 
11643
11748
  // src/modules/engine/clip/clip.ts
@@ -11724,48 +11829,38 @@ fn clip_isInBounds(coordinates: vec2<f32>) -> bool {
11724
11829
  };
11725
11830
  var clipShaderPlugin = {
11726
11831
  name: "clip",
11727
- modules: [clipShaderModule],
11728
- varyings: {
11832
+ module: clipShaderModule,
11833
+ interStageVariables: {
11729
11834
  clipCoordinates: { type: "vec2<f32>", interpolation: "smooth" }
11730
11835
  },
11731
- glsl: {
11732
- injections: [
11733
- {
11734
- target: "vs:CLIP_POSITION",
11735
- injection: `clipCoordinates = geometryCoordinates;
11836
+ injections: [
11837
+ {
11838
+ stage: "vertex",
11839
+ target: "CLIP_POSITION",
11840
+ glsl: `clipCoordinates = geometryCoordinates;
11736
11841
  if (clipState.enabled != 0 && clipState.mode == ${CLIP_MODE_INSTANCE} &&
11737
11842
  !clip_isInBounds(instanceCoordinates)) {
11738
11843
  position = vec4(2.0, 2.0, 2.0, 1.0);
11739
- }`
11740
- },
11741
- {
11742
- target: "fs:CLIP_COLOR",
11743
- injection: `if (clipState.enabled != 0 && clipState.mode == ${CLIP_MODE_GEOMETRY} &&
11744
- !clip_isInBounds(clipCoordinates)) {
11745
- discard;
11746
- }`
11747
- }
11748
- ]
11749
- },
11750
- wgsl: {
11751
- injections: [
11752
- {
11753
- target: "vs:CLIP_POSITION",
11754
- injection: `clipCoordinates = geometryCoordinates;
11844
+ }`,
11845
+ wgsl: `clipCoordinates = geometryCoordinates;
11755
11846
  if (clipUniforms.enabled != 0 && clipUniforms.mode == ${CLIP_MODE_INSTANCE} &&
11756
11847
  !clip_isInBounds(instanceCoordinates)) {
11757
11848
  *position = vec4<f32>(2.0, 2.0, 2.0, 1.0);
11758
11849
  }`
11759
- },
11760
- {
11761
- target: "fs:CLIP_COLOR",
11762
- injection: `if (clipUniforms.enabled != 0 && clipUniforms.mode == ${CLIP_MODE_GEOMETRY} &&
11850
+ },
11851
+ {
11852
+ stage: "fragment",
11853
+ target: "CLIP_COLOR",
11854
+ glsl: `if (clipState.enabled != 0 && clipState.mode == ${CLIP_MODE_GEOMETRY} &&
11855
+ !clip_isInBounds(clipCoordinates)) {
11856
+ discard;
11857
+ }`,
11858
+ wgsl: `if (clipUniforms.enabled != 0 && clipUniforms.mode == ${CLIP_MODE_GEOMETRY} &&
11763
11859
  !clip_isInBounds(clipCoordinates)) {
11764
11860
  discard;
11765
11861
  }`
11766
- }
11767
- ]
11768
- }
11862
+ }
11863
+ ]
11769
11864
  };
11770
11865
 
11771
11866
  // src/modules/engine/skin/skin.ts
@@ -15557,7 +15652,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15557
15652
 
15558
15653
  for(int i = 0; i < lighting.directionalLightCount; i++) {
15559
15654
  if (i < lighting.directionalLightCount) {
15560
- PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
15655
+ PBRInfo_setDirectionalLight(pbrInfo, -lighting_getDirectionalLight(i).direction);
15561
15656
  color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
15562
15657
  }
15563
15658
  }
@@ -15845,7 +15940,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15845
15940
  // Apply directional light
15846
15941
  for(int i = 0; i < lighting.directionalLightCount; i++) {
15847
15942
  if (i < lighting.directionalLightCount) {
15848
- PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
15943
+ PBRInfo_setDirectionalLight(pbrInfo, -lighting_getDirectionalLight(i).direction);
15849
15944
  color += calculateMaterialLightColor(
15850
15945
  pbrInfo,
15851
15946
  lighting_getDirectionalLight(i).color,
@@ -17374,7 +17469,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17374
17469
 
17375
17470
  for (var i = 0; i < lighting.directionalLightCount; i++) {
17376
17471
  if (i < lighting.directionalLightCount) {
17377
- PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
17472
+ PBRInfo_setDirectionalLight(&pbrInfo, -lighting_getDirectionalLight(i).direction);
17378
17473
  color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
17379
17474
  }
17380
17475
  }
@@ -17734,7 +17829,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17734
17829
  // Apply directional light
17735
17830
  for (var i = 0; i < lighting.directionalLightCount; i++) {
17736
17831
  if (i < lighting.directionalLightCount) {
17737
- PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
17832
+ PBRInfo_setDirectionalLight(&pbrInfo, -lighting_getDirectionalLight(i).direction);
17738
17833
  color += calculateMaterialLightColor(
17739
17834
  pbrInfo,
17740
17835
  lighting_getDirectionalLight(i).color,