@luma.gl/shaders 10.0.0-alpha.5 → 10.0.0-alpha.7

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 (92) hide show
  1. package/LICENSE +38 -0
  2. package/dist/dist.dev.js +159 -127
  3. package/dist/dist.min.js +110 -105
  4. package/dist/glsl.cjs.map +1 -1
  5. package/dist/index.cjs.map +2 -2
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/glsl-shader-assembler.cjs +1 -1
  10. package/dist/lib/glsl-shader-assembler.js +1 -1
  11. package/dist/lib/glsl-shader-assembler.js.map +1 -1
  12. package/dist/lib/preprocessor/preprocessor.cjs +21 -0
  13. package/dist/lib/preprocessor/preprocessor.js +21 -0
  14. package/dist/lib/preprocessor/preprocessor.js.map +1 -1
  15. package/dist/lib/shader-assembly/assemble-glsl-shaders.cjs +11 -19
  16. package/dist/lib/shader-assembly/assemble-glsl-shaders.d.ts +2 -2
  17. package/dist/lib/shader-assembly/assemble-glsl-shaders.d.ts.map +1 -1
  18. package/dist/lib/shader-assembly/assemble-glsl-shaders.js +12 -20
  19. package/dist/lib/shader-assembly/assemble-glsl-shaders.js.map +1 -1
  20. package/dist/lib/shader-assembly/assemble-wgsl-shaders.cjs +6 -6
  21. package/dist/lib/shader-assembly/assemble-wgsl-shaders.js +7 -7
  22. package/dist/lib/shader-assembly/assemble-wgsl-shaders.js.map +1 -1
  23. package/dist/lib/shader-assembly/shader-assembly-types.d.ts +7 -7
  24. package/dist/lib/shader-assembly/shader-assembly-types.d.ts.map +1 -1
  25. package/dist/lib/shader-assembly/shader-hooks.cjs +4 -1
  26. package/dist/lib/shader-assembly/shader-hooks.d.ts.map +1 -1
  27. package/dist/lib/shader-assembly/shader-hooks.js +4 -1
  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 +44 -30
  42. package/dist/lib/shader-plugin.d.ts +33 -32
  43. package/dist/lib/shader-plugin.d.ts.map +1 -1
  44. package/dist/lib/shader-plugin.js +49 -31
  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 +18 -30
  50. package/dist/modules/engine/clip/clip.d.ts.map +1 -1
  51. package/dist/modules/engine/clip/clip.js +19 -31
  52. package/dist/modules/engine/clip/clip.js.map +1 -1
  53. package/dist/modules/engine/filter/filter.cjs +9 -18
  54. package/dist/modules/engine/filter/filter.d.ts.map +1 -1
  55. package/dist/modules/engine/filter/filter.js +9 -18
  56. package/dist/modules/engine/filter/filter.js.map +1 -1
  57. package/dist/modules/math/fp64/fp64.cjs +1 -1
  58. package/dist/modules/math/fp64/fp64.d.ts +1 -1
  59. package/dist/modules/math/fp64/fp64.d.ts.map +1 -1
  60. package/dist/modules/math/fp64/fp64.js +1 -1
  61. package/dist/modules/math/fp64/fp64.js.map +1 -1
  62. package/dist/types.cjs +27 -16
  63. package/dist/types.d.ts +7 -7
  64. package/dist/types.d.ts.map +1 -1
  65. package/dist/types.js +2 -7
  66. package/dist/types.js.map +1 -1
  67. package/dist/wgsl.cjs +20 -4
  68. package/dist/wgsl.cjs.map +1 -1
  69. package/dist/wgsl.d.ts +1 -1
  70. package/dist/wgsl.d.ts.map +1 -1
  71. package/dist/wgsl.js +1 -1
  72. package/dist/wgsl.js.map +1 -1
  73. package/package.json +3 -3
  74. package/src/index.ts +3 -4
  75. package/src/lib/glsl-shader-assembler.ts +1 -1
  76. package/src/lib/preprocessor/preprocessor.ts +25 -0
  77. package/src/lib/shader-assembly/assemble-glsl-shaders.ts +17 -22
  78. package/src/lib/shader-assembly/assemble-wgsl-shaders.ts +8 -8
  79. package/src/lib/shader-assembly/shader-assembly-types.ts +7 -7
  80. package/src/lib/shader-assembly/shader-hooks.ts +6 -1
  81. package/src/lib/shader-assembly/shader-injections.ts +2 -0
  82. package/src/lib/shader-assembly/shader-plugin-varyings.ts +58 -27
  83. package/src/lib/shader-generator/glsl/generate-glsl.ts +1 -1
  84. package/src/lib/shader-generator/wgsl/generate-wgsl.ts +1 -1
  85. package/src/lib/shader-module/shader-module-dependencies.ts +1 -1
  86. package/src/lib/shader-plugin.ts +75 -68
  87. package/src/lib/wgsl-shader-assembler.ts +1 -1
  88. package/src/modules/engine/clip/clip.ts +19 -31
  89. package/src/modules/engine/filter/filter.ts +9 -18
  90. package/src/modules/math/fp64/fp64.ts +1 -1
  91. package/src/types.ts +75 -7
  92. package/src/wgsl.ts +12 -1
package/LICENSE ADDED
@@ -0,0 +1,38 @@
1
+ luma.gl is provided under the MIT license
2
+
3
+ Copyright (c) 2020 vis.gl contributors
4
+
5
+ This software includes parts initially developed by Uber and open sourced under MIT license.
6
+ Copyright (c) 2015 Uber Technologies, Inc.
7
+
8
+ This software includes parts of PhiloGL (https://github.com/philogb/philogl)
9
+ under MIT license. PhiloGL parts Copyright © 2013 Sencha Labs.
10
+
11
+ This software includes adaptations of some postprocessing code from
12
+ THREE.js (https://github.com/mrdoob/three.js/) under MIT license.
13
+ THREE.js parts Copyright © 2010-2018 three.js authors.
14
+
15
+ This software includes adaptations of Gaussian-splat rendering and
16
+ level-of-detail behavior from Spark (https://github.com/sparkjsdev/spark)
17
+ under MIT license.
18
+ Spark parts Copyright © 2025 WORLD LABS TECHNOLOGIES, INC.
19
+
20
+ Additional attribution given in specific source files.
21
+
22
+ Permission is hereby granted, free of charge, to any person obtaining a copy
23
+ of this software and associated documentation files (the "Software"), to deal
24
+ in the Software without restriction, including without limitation the rights
25
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26
+ copies of the Software, and to permit persons to whom the Software is
27
+ furnished to do so, subject to the following conditions:
28
+
29
+ The above copyright notice and this permission notice shall be included in
30
+ all copies or substantial portions of the Software.
31
+
32
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
38
+ THE SOFTWARE.
package/dist/dist.dev.js CHANGED
@@ -1384,47 +1384,52 @@ ${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
+ defines: {},
1390
+ injections: {},
1391
+ vertexInputs: {},
1392
+ interStageVariables: {}
1393
+ };
1392
1394
  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
- );
1395
+ appendShaderPlugin(resolved, plugin, shaderLanguage);
1398
1396
  }
1399
- for (const name of Object.keys(varyings)) {
1397
+ const { vertexInputs, interStageVariables } = resolved;
1398
+ for (const name of Object.keys(interStageVariables)) {
1400
1399
  if (vertexInputs[name]) {
1401
1400
  throw new Error(`ShaderPlugin name "${name}" cannot be both a vertex input and a varying`);
1402
1401
  }
1403
1402
  }
1404
- return { modules, defines, injections, vertexInputs, varyings };
1403
+ return resolved;
1405
1404
  }
1406
- function mergeShaderPluginModules(modules = [], pluginModules = []) {
1405
+ function mergeShaderPluginModules(modules = [], pluginModules = [], options = {}) {
1407
1406
  const mergedModules = [...modules];
1408
- const seenModuleNames = new Set(mergedModules.map((module) => module.name));
1407
+ const selectedModules = /* @__PURE__ */ new Map();
1408
+ for (const module of mergedModules) {
1409
+ const selected = selectedModules.get(module.name);
1410
+ if (selected && selected !== module) options.onConflict?.(module.name, selected, module);
1411
+ if (!selected) selectedModules.set(module.name, module);
1412
+ }
1409
1413
  for (const pluginModule of pluginModules) {
1410
- if (!seenModuleNames.has(pluginModule.name)) {
1414
+ const selected = selectedModules.get(pluginModule.name);
1415
+ if (selected && selected !== pluginModule) {
1416
+ options.onConflict?.(pluginModule.name, selected, pluginModule);
1417
+ }
1418
+ if (!selected) {
1411
1419
  mergedModules.push(pluginModule);
1412
- seenModuleNames.add(pluginModule.name);
1420
+ selectedModules.set(pluginModule.name, pluginModule);
1413
1421
  }
1414
1422
  }
1415
1423
  return mergedModules;
1416
1424
  }
1417
- function appendShaderPluginVariant(resolved, variant) {
1418
- if (!variant) {
1419
- return;
1420
- }
1421
- if (variant.modules?.length) {
1422
- resolved.modules.push(...variant.modules);
1425
+ function appendShaderPlugin(resolved, plugin, shaderLanguage) {
1426
+ if (plugin.module) {
1427
+ resolved.modules.push(plugin.module);
1423
1428
  }
1424
- if (variant.defines) {
1425
- Object.assign(resolved.defines, variant.defines);
1429
+ if (plugin.defines) {
1430
+ Object.assign(resolved.defines, plugin.defines);
1426
1431
  }
1427
- for (const [name, type] of Object.entries(variant.vertexInputs || {})) {
1432
+ for (const [name, type] of Object.entries(plugin.vertexInputs || {})) {
1428
1433
  assertShaderPluginInterfaceName(name, "vertex input");
1429
1434
  const existingType = resolved.vertexInputs[name];
1430
1435
  if (existingType && existingType !== type) {
@@ -1434,25 +1439,28 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1434
1439
  }
1435
1440
  resolved.vertexInputs[name] = type;
1436
1441
  }
1437
- for (const [name, varying] of Object.entries(variant.varyings || {})) {
1442
+ for (const [name, varying] of Object.entries(plugin.interStageVariables || {})) {
1438
1443
  assertShaderPluginInterfaceName(name, "varying");
1439
- const normalizedVarying = normalizeShaderPluginVarying(name, varying);
1440
- const existingVarying = resolved.varyings[name];
1444
+ const normalizedVarying = normalizeShaderPluginInterStageVariable(name, varying);
1445
+ const existingVarying = resolved.interStageVariables[name];
1441
1446
  if (existingVarying && (existingVarying.type !== normalizedVarying.type || existingVarying.interpolation !== normalizedVarying.interpolation)) {
1442
1447
  throw new Error(
1443
1448
  `ShaderPlugin varying "${name}" has conflicting declarations "${existingVarying.type}/${existingVarying.interpolation}" and "${normalizedVarying.type}/${normalizedVarying.interpolation}"`
1444
1449
  );
1445
1450
  }
1446
- resolved.varyings[name] = normalizedVarying;
1451
+ resolved.interStageVariables[name] = normalizedVarying;
1447
1452
  }
1448
- for (const injection of variant.injections || []) {
1453
+ for (const injection of plugin.injections || []) {
1454
+ const source7 = injection[shaderLanguage];
1455
+ if (source7 === void 0) continue;
1449
1456
  assertNamedShaderPluginInjectionTarget(injection.target);
1450
1457
  if (!resolved.injections[injection.target]) {
1451
1458
  resolved.injections[injection.target] = [];
1452
1459
  }
1453
1460
  const injections = resolved.injections[injection.target];
1454
1461
  injections.push({
1455
- injection: injection.injection,
1462
+ pluginName: plugin.name,
1463
+ injection: source7,
1456
1464
  order: injection.order ?? 0
1457
1465
  });
1458
1466
  }
@@ -1462,7 +1470,7 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1462
1470
  throw new Error(`ShaderPlugin ${kind} "${name}" must be a valid non-reserved identifier`);
1463
1471
  }
1464
1472
  }
1465
- function normalizeShaderPluginVarying(name, varying) {
1473
+ function normalizeShaderPluginInterStageVariable(name, varying) {
1466
1474
  const { primitiveType } = shaderTypeDecoder.getAttributeShaderTypeInfo(varying.type);
1467
1475
  const integerType = primitiveType === "i32" || primitiveType === "u32";
1468
1476
  const interpolation = varying.interpolation || (integerType ? "flat" : "smooth");
@@ -1973,6 +1981,29 @@ ${DECLARATION_INJECT_MARKER}
1973
1981
  if (trimmedExpression === "false") {
1974
1982
  return false;
1975
1983
  }
1984
+ const operandPattern = "(?:[A-Za-z_][A-Za-z0-9_]*|[+-]?\\d+(?:\\.\\d+)?)";
1985
+ const comparison = trimmedExpression.match(
1986
+ new RegExp(`^(${operandPattern})\\s*(==|!=|<=|>=|<|>)\\s*(${operandPattern})$`)
1987
+ );
1988
+ if (comparison) {
1989
+ const resolveOperand = (operand) => /^[A-Za-z_]/.test(operand) ? Number(defines[operand] ?? 0) : Number(operand);
1990
+ const left = resolveOperand(comparison[1]);
1991
+ const right = resolveOperand(comparison[3]);
1992
+ switch (comparison[2]) {
1993
+ case "==":
1994
+ return left === right;
1995
+ case "!=":
1996
+ return left !== right;
1997
+ case "<=":
1998
+ return left <= right;
1999
+ case ">=":
2000
+ return left >= right;
2001
+ case "<":
2002
+ return left < right;
2003
+ case ">":
2004
+ return left > right;
2005
+ }
2006
+ }
1976
2007
  const negatedDefineMatch = trimmedExpression.match(new RegExp(`^!\\s*${DEFINE_NAME_PATTERN}$`));
1977
2008
  if (negatedDefineMatch) {
1978
2009
  return !Boolean(defines[negatedDefineMatch[1]]);
@@ -2225,10 +2256,10 @@ ${DECLARATION_INJECT_MARKER}
2225
2256
  }
2226
2257
 
2227
2258
  // src/lib/shader-assembly/shader-plugin-varyings.ts
2228
- function assembleShaderPluginVaryingsGLSL(source7, stage, varyings) {
2259
+ function assembleShaderPluginInterStageVariablesGLSL(source7, stage, interStageVariables) {
2229
2260
  const declarations = [];
2230
2261
  const initialization = [];
2231
- for (const [name, varying] of Object.entries(varyings)) {
2262
+ for (const [name, varying] of Object.entries(interStageVariables)) {
2232
2263
  assertGLSLVaryingIsNew(source7, name);
2233
2264
  const qualifier = varying.interpolation === "flat" ? "flat " : "";
2234
2265
  const direction = stage === "vertex" ? "out" : "in";
@@ -2239,8 +2270,8 @@ ${DECLARATION_INJECT_MARKER}
2239
2270
  }
2240
2271
  return { declarations: declarations.join("\n"), initialization: initialization.join("\n") };
2241
2272
  }
2242
- function assembleShaderPluginVaryingsWGSL(source7, vertexEntryPoint, fragmentEntryPoint, varyings) {
2243
- const entries = Object.entries(varyings);
2273
+ function assembleShaderPluginInterStageVariablesWGSL(source7, vertexEntryPoint, fragmentEntryPoint, interStageVariables) {
2274
+ const entries = Object.entries(interStageVariables);
2244
2275
  if (entries.length === 0) {
2245
2276
  return {
2246
2277
  source: source7,
@@ -2255,18 +2286,20 @@ ${DECLARATION_INJECT_MARKER}
2255
2286
  let fragmentFunction = getWGSLFunctionRange(transformedSource, fragmentEntryPoint, "fragment");
2256
2287
  const fragmentInput = getWGSLFragmentStructInput(transformedSource, fragmentFunction);
2257
2288
  const vertexOutputStruct = getWGSLStructRange(transformedSource, vertexOutputType);
2258
- const fragmentInputStruct = getWGSLStructRange(transformedSource, fragmentInput.type);
2289
+ const fragmentInputStruct = fragmentInput ? getWGSLStructRange(transformedSource, fragmentInput.type) : null;
2259
2290
  const interfaceNames = /* @__PURE__ */ new Set([
2260
2291
  ...getWGSLDeclaredNames2(vertexFunction.parameters),
2261
2292
  ...getWGSLDeclaredNames2(vertexOutputStruct.body),
2262
2293
  ...getWGSLDeclaredNames2(fragmentFunction.parameters),
2263
- ...getWGSLDeclaredNames2(fragmentInputStruct.body)
2294
+ ...getWGSLDeclaredNames2(fragmentInputStruct?.body || "")
2264
2295
  ]);
2265
2296
  const usedLocations = /* @__PURE__ */ new Set([
2266
2297
  ...getWGSLLocations2(vertexOutputStruct.body),
2267
- ...getWGSLLocations2(fragmentInputStruct.body)
2298
+ ...getWGSLLocations2(fragmentInputStruct?.body || ""),
2299
+ ...getWGSLLocations2(fragmentFunction.parameters)
2268
2300
  ]);
2269
2301
  const fields = [];
2302
+ const fragmentParameters = [];
2270
2303
  const declarations = [];
2271
2304
  const vertexInitialization = [];
2272
2305
  const fragmentInitialization = [];
@@ -2282,7 +2315,20 @@ ${DECLARATION_INJECT_MARKER}
2282
2315
  fields.push(` @location(${location})${interpolation} ${name}: ${varying.type},`);
2283
2316
  declarations.push(`var<private> ${name}: ${varying.type};`);
2284
2317
  vertexInitialization.push(`${name} = ${getWGSLZeroValue(varying.type)};`);
2285
- fragmentInitialization.push(`${name} = ${fragmentInput.name}.${name};`);
2318
+ if (fragmentInput) {
2319
+ fragmentInitialization.push(`${name} = ${fragmentInput.name}.${name};`);
2320
+ } else {
2321
+ const parameterName = `_luma_${name}`;
2322
+ if (interfaceNames.has(parameterName)) {
2323
+ throw new Error(
2324
+ `ShaderPlugin varying "${name}" conflicts with a generated fragment parameter`
2325
+ );
2326
+ }
2327
+ fragmentParameters.push(
2328
+ `@location(${location})${interpolation} ${parameterName}: ${varying.type}`
2329
+ );
2330
+ fragmentInitialization.push(`${name} = ${parameterName};`);
2331
+ }
2286
2332
  }
2287
2333
  assertWGSLStructConstructorsAreLocal(
2288
2334
  transformedSource,
@@ -2302,16 +2348,21 @@ ${DECLARATION_INJECT_MARKER}
2302
2348
  vertexFunction,
2303
2349
  entries.map(([name]) => name)
2304
2350
  );
2305
- const structNames = vertexOutputType === fragmentInput.type ? [vertexOutputType] : [vertexOutputType, fragmentInput.type];
2351
+ const structNames = !fragmentInput || vertexOutputType === fragmentInput.type ? [vertexOutputType] : [vertexOutputType, fragmentInput.type];
2306
2352
  const structInsertions = structNames.map((structName) => getWGSLStructRange(transformedSource, structName).closeBrace).sort((left, right) => right - left);
2307
2353
  for (const insertionIndex of structInsertions) {
2308
2354
  transformedSource = transformedSource.slice(0, insertionIndex) + `${fields.join("\n")}
2309
2355
  ` + transformedSource.slice(insertionIndex);
2310
2356
  }
2311
2357
  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}"`);
2358
+ if (fragmentParameters.length) {
2359
+ let parameters = fragmentFunction.parameters;
2360
+ const parameterTokens = maskWGSLComments(parameters).trimEnd();
2361
+ if (parameterTokens.trim() && !parameterTokens.endsWith(",")) {
2362
+ const insertionIndex = parameterTokens.length;
2363
+ parameters = `${parameters.slice(0, insertionIndex)},${parameters.slice(insertionIndex)}`;
2364
+ }
2365
+ transformedSource = transformedSource.slice(0, fragmentFunction.openParenthesis + 1) + parameters + "\n " + fragmentParameters.join(",\n ") + "\n" + transformedSource.slice(fragmentFunction.closeParenthesis);
2315
2366
  }
2316
2367
  return {
2317
2368
  source: transformedSource,
@@ -2324,7 +2375,9 @@ ${DECLARATION_INJECT_MARKER}
2324
2375
  const functionPattern = new RegExp(`\\bfn\\s+${escapeRegExp3(entryPoint)}\\s*\\(`, "g");
2325
2376
  const match = functionPattern.exec(source7);
2326
2377
  if (!match) {
2327
- throw new Error(`ShaderPlugin varyings require WGSL ${stage} entry point "${entryPoint}"`);
2378
+ throw new Error(
2379
+ `ShaderPlugin interStageVariables require WGSL ${stage} entry point "${entryPoint}"`
2380
+ );
2328
2381
  }
2329
2382
  const openParenthesis = source7.indexOf("(", match.index);
2330
2383
  const closeParenthesis = findMatchingDelimiter2(source7, openParenthesis, "(", ")");
@@ -2346,14 +2399,14 @@ ${DECLARATION_INJECT_MARKER}
2346
2399
  const match = /->\s*([A-Za-z_][\w]*)\s*$/.exec(resultSource.trim());
2347
2400
  if (!match || getWGSLStructRangeOrNull(source7, match[1]) === null) {
2348
2401
  throw new Error(
2349
- "ShaderPlugin varyings require the WGSL vertex entry point to return a named struct"
2402
+ "ShaderPlugin interStageVariables require the WGSL vertex entry point to return a named struct"
2350
2403
  );
2351
2404
  }
2352
2405
  return match[1];
2353
2406
  }
2354
2407
  function getWGSLFragmentStructInput(source7, functionRange) {
2355
2408
  const candidates = [];
2356
- for (const parameter of splitTopLevel(functionRange.parameters, ",")) {
2409
+ for (const parameter of splitTopLevel(maskWGSLComments(functionRange.parameters), ",")) {
2357
2410
  const match = /(?:@[A-Za-z_][\w]*(?:\([^)]*\))?\s*)*([A-Za-z_][\w]*)\s*:\s*([A-Za-z_][\w]*)\s*$/.exec(
2358
2411
  parameter.trim()
2359
2412
  );
@@ -2361,12 +2414,12 @@ ${DECLARATION_INJECT_MARKER}
2361
2414
  candidates.push({ name: match[1], type: match[2] });
2362
2415
  }
2363
2416
  }
2364
- if (candidates.length !== 1) {
2417
+ if (candidates.length > 1) {
2365
2418
  throw new Error(
2366
- `ShaderPlugin varyings require exactly one named WGSL fragment input struct; found ${candidates.length}`
2419
+ `ShaderPlugin interStageVariables require exactly one named WGSL fragment input struct; found ${candidates.length}`
2367
2420
  );
2368
2421
  }
2369
- return candidates[0];
2422
+ return candidates[0] || null;
2370
2423
  }
2371
2424
  function getWGSLStructRange(source7, structName) {
2372
2425
  const range = getWGSLStructRangeOrNull(source7, structName);
@@ -2677,7 +2730,12 @@ return ${outputName};
2677
2730
  continue;
2678
2731
  }
2679
2732
  if (!isShaderHookRegistered(registry, hookName)) {
2680
- throw new Error(`Unknown shader hook ${hookName}`);
2733
+ const pluginNames = [
2734
+ ...new Set(hookInjections[hookName].map((injection) => injection.pluginName).filter(Boolean))
2735
+ ];
2736
+ throw new Error(
2737
+ `Unknown shader hook ${hookName}${pluginNames.length ? ` (plugins: ${pluginNames.join(", ")})` : ""}`
2738
+ );
2681
2739
  }
2682
2740
  }
2683
2741
  }
@@ -2734,7 +2792,7 @@ return ${outputName};
2734
2792
  inject = {},
2735
2793
  pluginInjections = {},
2736
2794
  pluginVertexInputs = {},
2737
- pluginVaryings = {},
2795
+ pluginInterStageVariables = {},
2738
2796
  vertexEntryPoint = "vertexMain",
2739
2797
  fragmentEntryPoint = "fragmentMain",
2740
2798
  log
@@ -2746,11 +2804,11 @@ return ${outputName};
2746
2804
  vertexEntryPoint,
2747
2805
  pluginVertexInputs
2748
2806
  );
2749
- const pluginVaryingAssembly = assembleShaderPluginVaryingsWGSL(
2807
+ const pluginVaryingAssembly = assembleShaderPluginInterStageVariablesWGSL(
2750
2808
  pluginVertexInputAssembly.source,
2751
2809
  vertexEntryPoint,
2752
2810
  fragmentEntryPoint,
2753
- pluginVaryings
2811
+ pluginInterStageVariables
2754
2812
  );
2755
2813
  const coreSource = pluginVaryingAssembly.source;
2756
2814
  let assembledSource = "";
@@ -2767,15 +2825,15 @@ return ${outputName};
2767
2825
  const name = match[3];
2768
2826
  if (hash) {
2769
2827
  if (name === "decl") {
2770
- declInjections[key] = [injection];
2828
+ declInjections[key] = [...declInjections[key] || [], injection];
2771
2829
  } else {
2772
- mainInjections[key] = [injection];
2830
+ mainInjections[key] = [...mainInjections[key] || [], injection];
2773
2831
  }
2774
2832
  } else {
2775
- hookInjections[key] = [injection];
2833
+ hookInjections[key] = [...hookInjections[key] || [], injection];
2776
2834
  }
2777
2835
  } else {
2778
- mainInjections[key] = [injection];
2836
+ mainInjections[key] = [...mainInjections[key] || [], injection];
2779
2837
  }
2780
2838
  }
2781
2839
  appendGeneratedVertexInputInjections(
@@ -3376,7 +3434,7 @@ return ${outputName};
3376
3434
  inject = {},
3377
3435
  pluginInjections = {},
3378
3436
  pluginVertexInputs = {},
3379
- pluginVaryings = {},
3437
+ pluginInterStageVariables = {},
3380
3438
  prologue = true,
3381
3439
  log
3382
3440
  } = options;
@@ -3425,35 +3483,30 @@ ${getApplicationDefines(allDefines)}
3425
3483
  const name = match[3];
3426
3484
  if (hash) {
3427
3485
  if (name === "decl") {
3428
- declInjections[key] = [injection];
3486
+ declInjections[key] = [...declInjections[key] || [], injection];
3429
3487
  } else {
3430
- mainInjections[key] = [injection];
3488
+ mainInjections[key] = [...mainInjections[key] || [], injection];
3431
3489
  }
3432
3490
  } else {
3433
- hookInjections[key] = [injection];
3491
+ hookInjections[key] = [...hookInjections[key] || [], injection];
3434
3492
  }
3435
3493
  } else {
3436
- mainInjections[key] = [injection];
3494
+ mainInjections[key] = [...mainInjections[key] || [], injection];
3437
3495
  }
3438
3496
  }
3439
3497
  if (stage === "vertex") {
3440
3498
  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
- }
3499
+ if (declarations) assembledSource += `${declarations}
3500
+ `;
3448
3501
  }
3449
- const pluginVaryingAssembly = assembleShaderPluginVaryingsGLSL(coreSource, stage, pluginVaryings);
3502
+ const pluginVaryingAssembly = assembleShaderPluginInterStageVariablesGLSL(
3503
+ coreSource,
3504
+ stage,
3505
+ pluginInterStageVariables
3506
+ );
3450
3507
  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
- });
3508
+ assembledSource += `${pluginVaryingAssembly.declarations}
3509
+ `;
3457
3510
  }
3458
3511
  if (pluginVaryingAssembly.initialization) {
3459
3512
  mainInjections["vs:#main-start"] = mainInjections["vs:#main-start"] || [];
@@ -3635,7 +3688,7 @@ ${getApplicationDefines(allDefines)}
3635
3688
  */
3636
3689
  assembleGLSLShaderPair(props) {
3637
3690
  const modules = this._getModuleList(props.modules);
3638
- const hookFunctions = this._hookFunctions;
3691
+ const hookFunctions = [...this._hookFunctions, ...props.hookFunctions || []];
3639
3692
  const vertex = this._transpileShader(props, modules, props.vs, "vertex");
3640
3693
  const fragment = this._transpileShader(props, modules, props.fs, "fragment");
3641
3694
  const assembled = assembleGLSLShaderPair({
@@ -3661,7 +3714,7 @@ ${getApplicationDefines(allDefines)}
3661
3714
  */
3662
3715
  assembleWGSLShader(props) {
3663
3716
  const modules = this._getModuleList(props.modules);
3664
- const hookFunctions = this._hookFunctions;
3717
+ const hookFunctions = [...this._hookFunctions, ...props.hookFunctions || []];
3665
3718
  const translated = this._transpileShader(
3666
3719
  props,
3667
3720
  modules,
@@ -11614,30 +11667,21 @@ fn filter_isVisible(value: f32) -> bool {
11614
11667
  };
11615
11668
  var filterShaderPlugin = {
11616
11669
  name: "filter",
11617
- modules: [filterShaderModule],
11670
+ module: filterShaderModule,
11618
11671
  vertexInputs: {
11619
11672
  filterValues: "f32"
11620
11673
  },
11621
- glsl: {
11622
- injections: [
11623
- {
11624
- target: "vs:FILTER_POSITION",
11625
- injection: `if (!filter_isVisible(filterValues)) {
11674
+ injections: [
11675
+ {
11676
+ target: "vs:FILTER_POSITION",
11677
+ glsl: `if (!filter_isVisible(filterValues)) {
11626
11678
  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)) {
11679
+ }`,
11680
+ wgsl: `if (!filter_isVisible(filterValues)) {
11636
11681
  *position = vec4<f32>(2.0, 2.0, 2.0, 1.0);
11637
11682
  }`
11638
- }
11639
- ]
11640
- }
11683
+ }
11684
+ ]
11641
11685
  };
11642
11686
 
11643
11687
  // src/modules/engine/clip/clip.ts
@@ -11724,48 +11768,36 @@ fn clip_isInBounds(coordinates: vec2<f32>) -> bool {
11724
11768
  };
11725
11769
  var clipShaderPlugin = {
11726
11770
  name: "clip",
11727
- modules: [clipShaderModule],
11728
- varyings: {
11771
+ module: clipShaderModule,
11772
+ interStageVariables: {
11729
11773
  clipCoordinates: { type: "vec2<f32>", interpolation: "smooth" }
11730
11774
  },
11731
- glsl: {
11732
- injections: [
11733
- {
11734
- target: "vs:CLIP_POSITION",
11735
- injection: `clipCoordinates = geometryCoordinates;
11775
+ injections: [
11776
+ {
11777
+ target: "vs:CLIP_POSITION",
11778
+ glsl: `clipCoordinates = geometryCoordinates;
11736
11779
  if (clipState.enabled != 0 && clipState.mode == ${CLIP_MODE_INSTANCE} &&
11737
11780
  !clip_isInBounds(instanceCoordinates)) {
11738
11781
  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;
11782
+ }`,
11783
+ wgsl: `clipCoordinates = geometryCoordinates;
11755
11784
  if (clipUniforms.enabled != 0 && clipUniforms.mode == ${CLIP_MODE_INSTANCE} &&
11756
11785
  !clip_isInBounds(instanceCoordinates)) {
11757
11786
  *position = vec4<f32>(2.0, 2.0, 2.0, 1.0);
11758
11787
  }`
11759
- },
11760
- {
11761
- target: "fs:CLIP_COLOR",
11762
- injection: `if (clipUniforms.enabled != 0 && clipUniforms.mode == ${CLIP_MODE_GEOMETRY} &&
11788
+ },
11789
+ {
11790
+ target: "fs:CLIP_COLOR",
11791
+ glsl: `if (clipState.enabled != 0 && clipState.mode == ${CLIP_MODE_GEOMETRY} &&
11792
+ !clip_isInBounds(clipCoordinates)) {
11793
+ discard;
11794
+ }`,
11795
+ wgsl: `if (clipUniforms.enabled != 0 && clipUniforms.mode == ${CLIP_MODE_GEOMETRY} &&
11763
11796
  !clip_isInBounds(clipCoordinates)) {
11764
11797
  discard;
11765
11798
  }`
11766
- }
11767
- ]
11768
- }
11799
+ }
11800
+ ]
11769
11801
  };
11770
11802
 
11771
11803
  // src/modules/engine/skin/skin.ts