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

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 (86) hide show
  1. package/dist/dist.dev.js +233 -249
  2. package/dist/dist.min.js +124 -124
  3. package/dist/glsl.cjs.map +2 -2
  4. package/dist/index.cjs.map +3 -3
  5. package/dist/index.d.ts +3 -3
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/shader-assembler-base.cjs +3 -4
  9. package/dist/lib/shader-assembler-base.d.ts +4 -5
  10. package/dist/lib/shader-assembler-base.d.ts.map +1 -1
  11. package/dist/lib/shader-assembler-base.js +3 -4
  12. package/dist/lib/shader-assembler-base.js.map +1 -1
  13. package/dist/lib/shader-assembly/assemble-glsl-shaders.cjs +6 -17
  14. package/dist/lib/shader-assembly/assemble-glsl-shaders.d.ts +5 -4
  15. package/dist/lib/shader-assembly/assemble-glsl-shaders.d.ts.map +1 -1
  16. package/dist/lib/shader-assembly/assemble-glsl-shaders.js +14 -19
  17. package/dist/lib/shader-assembly/assemble-glsl-shaders.js.map +1 -1
  18. package/dist/lib/shader-assembly/assemble-wgsl-shaders.cjs +6 -25
  19. package/dist/lib/shader-assembly/assemble-wgsl-shaders.d.ts +5 -6
  20. package/dist/lib/shader-assembly/assemble-wgsl-shaders.d.ts.map +1 -1
  21. package/dist/lib/shader-assembly/assemble-wgsl-shaders.js +14 -27
  22. package/dist/lib/shader-assembly/assemble-wgsl-shaders.js.map +1 -1
  23. package/dist/lib/shader-assembly/shader-assembly-shared.d.ts +2 -2
  24. package/dist/lib/shader-assembly/shader-assembly-shared.d.ts.map +1 -1
  25. package/dist/lib/shader-assembly/shader-assembly-shared.js.map +1 -1
  26. package/dist/lib/shader-assembly/shader-assembly-types.d.ts +11 -7
  27. package/dist/lib/shader-assembly/shader-assembly-types.d.ts.map +1 -1
  28. package/dist/lib/shader-assembly/shader-hooks.cjs +26 -29
  29. package/dist/lib/shader-assembly/shader-hooks.d.ts +34 -32
  30. package/dist/lib/shader-assembly/shader-hooks.d.ts.map +1 -1
  31. package/dist/lib/shader-assembly/shader-hooks.js +29 -33
  32. package/dist/lib/shader-assembly/shader-hooks.js.map +1 -1
  33. package/dist/lib/shader-assembly/shader-injections.cjs +26 -2
  34. package/dist/lib/shader-assembly/shader-injections.d.ts +20 -7
  35. package/dist/lib/shader-assembly/shader-injections.d.ts.map +1 -1
  36. package/dist/lib/shader-assembly/shader-injections.js +24 -0
  37. package/dist/lib/shader-assembly/shader-injections.js.map +1 -1
  38. package/dist/lib/shader-module/shader-module.cjs +3 -9
  39. package/dist/lib/shader-module/shader-module.d.ts +4 -8
  40. package/dist/lib/shader-module/shader-module.d.ts.map +1 -1
  41. package/dist/lib/shader-module/shader-module.js +3 -6
  42. package/dist/lib/shader-module/shader-module.js.map +1 -1
  43. package/dist/lib/shader-plugin.cjs +38 -17
  44. package/dist/lib/shader-plugin.d.ts +9 -17
  45. package/dist/lib/shader-plugin.d.ts.map +1 -1
  46. package/dist/lib/shader-plugin.js +43 -16
  47. package/dist/lib/shader-plugin.js.map +1 -1
  48. package/dist/modules/engine/clip/clip.cjs +4 -2
  49. package/dist/modules/engine/clip/clip.d.ts.map +1 -1
  50. package/dist/modules/engine/clip/clip.js +4 -2
  51. package/dist/modules/engine/clip/clip.js.map +1 -1
  52. package/dist/modules/engine/filter/filter.cjs +2 -1
  53. package/dist/modules/engine/filter/filter.d.ts.map +1 -1
  54. package/dist/modules/engine/filter/filter.js +2 -1
  55. package/dist/modules/engine/filter/filter.js.map +1 -1
  56. package/dist/modules/lighting/lights/lighting.d.ts +1 -1
  57. package/dist/modules/lighting/lights/lighting.d.ts.map +1 -1
  58. package/dist/modules/lighting/pbr-material/pbr-material-glsl.cjs +2 -2
  59. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +1 -1
  60. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  61. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +2 -2
  62. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.cjs +2 -2
  63. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +1 -1
  64. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  65. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +2 -2
  66. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +2 -2
  67. package/dist/types/texture-formats.d.ts +1 -1
  68. package/dist/types/texture-formats.d.ts.map +1 -1
  69. package/dist/wgsl.cjs.map +2 -2
  70. package/package.json +3 -3
  71. package/src/index.ts +5 -4
  72. package/src/lib/shader-assembler-base.ts +5 -6
  73. package/src/lib/shader-assembly/assemble-glsl-shaders.ts +27 -26
  74. package/src/lib/shader-assembly/assemble-wgsl-shaders.ts +28 -43
  75. package/src/lib/shader-assembly/shader-assembly-shared.ts +5 -5
  76. package/src/lib/shader-assembly/shader-assembly-types.ts +11 -7
  77. package/src/lib/shader-assembly/shader-hooks.ts +65 -68
  78. package/src/lib/shader-assembly/shader-injections.ts +52 -8
  79. package/src/lib/shader-module/shader-module.ts +7 -14
  80. package/src/lib/shader-plugin.ts +59 -48
  81. package/src/modules/engine/clip/clip.ts +4 -2
  82. package/src/modules/engine/filter/filter.ts +2 -1
  83. package/src/modules/lighting/lights/lighting.ts +1 -1
  84. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +2 -2
  85. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +2 -2
  86. package/src/types/texture-formats.ts +2 -0
package/dist/dist.dev.js CHANGED
@@ -864,6 +864,129 @@ var __exports__ = (() => {
864
864
  return typeof value;
865
865
  }
866
866
 
867
+ // src/lib/shader-module/shader-module.ts
868
+ function initializeShaderModules(modules) {
869
+ modules.map((module) => initializeShaderModule(module));
870
+ }
871
+ function initializeShaderModule(module) {
872
+ if (module.instance) {
873
+ return;
874
+ }
875
+ initializeShaderModules(module.dependencies || []);
876
+ const { propTypes = {}, deprecations = [] } = module;
877
+ const instance = {
878
+ parsedDeprecations: parseDeprecationDefinitions(deprecations),
879
+ normalizedInjections: { vertex: {}, fragment: {} }
880
+ };
881
+ if (propTypes) {
882
+ instance.propValidators = makePropValidators(propTypes);
883
+ }
884
+ module.instance = instance;
885
+ let defaultProps = {};
886
+ if (propTypes) {
887
+ defaultProps = Object.entries(propTypes).reduce(
888
+ (obj, [key, propType]) => {
889
+ const value = propType?.value;
890
+ if (value) {
891
+ obj[key] = value;
892
+ }
893
+ return obj;
894
+ },
895
+ {}
896
+ );
897
+ }
898
+ module.defaultUniforms = {
899
+ ...module.defaultUniforms,
900
+ ...defaultProps
901
+ };
902
+ }
903
+ function getShaderModuleUniforms(module, props, oldUniforms) {
904
+ initializeShaderModule(module);
905
+ const uniforms2 = oldUniforms || { ...module.defaultUniforms };
906
+ if (props && module.getUniforms) {
907
+ return module.getUniforms(props, uniforms2);
908
+ }
909
+ return getValidatedProperties(props, module.instance?.propValidators, module.name);
910
+ }
911
+ function checkShaderModuleDeprecations(shaderModule, shaderSource, log) {
912
+ shaderModule.deprecations?.forEach((def) => {
913
+ if (def.regex?.test(shaderSource)) {
914
+ if (def.deprecated) {
915
+ log.deprecated(def.old, def.new)();
916
+ } else {
917
+ log.removed(def.old, def.new)();
918
+ }
919
+ }
920
+ });
921
+ }
922
+ function parseDeprecationDefinitions(deprecations) {
923
+ deprecations.forEach((def) => {
924
+ switch (def.type) {
925
+ case "function":
926
+ def.regex = new RegExp(`\\b${def.old}\\(`);
927
+ break;
928
+ default:
929
+ def.regex = new RegExp(`${def.type} ${def.old};`);
930
+ }
931
+ });
932
+ return deprecations;
933
+ }
934
+
935
+ // src/lib/shader-module/shader-module-dependencies.ts
936
+ function getShaderModuleDependencies(modules) {
937
+ const visited = /* @__PURE__ */ new Set();
938
+ const recursionStack = /* @__PURE__ */ new Set();
939
+ const result = [];
940
+ const nameToModule = /* @__PURE__ */ new Map();
941
+ function checkNameCollision(module) {
942
+ const existing = nameToModule.get(module.name);
943
+ if (existing && existing !== module) {
944
+ throw new Error(
945
+ `Shader module name collision: Multiple different module objects share the name "${module.name}". Each module object must have a unique name.`
946
+ );
947
+ }
948
+ nameToModule.set(module.name, module);
949
+ }
950
+ function visit(module, path) {
951
+ if (recursionStack.has(module)) {
952
+ const cyclePath = [...path, module.name].join(" -> ");
953
+ throw new Error(`Shader module dependency cycle detected: ${cyclePath}`);
954
+ }
955
+ if (visited.has(module)) {
956
+ return;
957
+ }
958
+ checkNameCollision(module);
959
+ recursionStack.add(module);
960
+ const newPath = [...path, module.name];
961
+ if (module.dependencies) {
962
+ for (const dep of module.dependencies) {
963
+ visit(dep, newPath);
964
+ }
965
+ }
966
+ recursionStack.delete(module);
967
+ visited.add(module);
968
+ result.push(module);
969
+ }
970
+ for (const module of modules) {
971
+ visit(module, []);
972
+ }
973
+ const interfaceProviders = /* @__PURE__ */ new Set();
974
+ for (const module of result) {
975
+ if (module.implements !== void 0) {
976
+ assert(!interfaceProviders.has(module.implements));
977
+ interfaceProviders.add(module.implements);
978
+ }
979
+ }
980
+ initializeShaderModules(result);
981
+ return result;
982
+ }
983
+ function getShaderDependencies(modules) {
984
+ return getShaderModuleDependencies(modules);
985
+ }
986
+ function resolveModules(modules) {
987
+ return getShaderDependencies(modules);
988
+ }
989
+
867
990
  // src/module-injectors.ts
868
991
  var MODULE_INJECTORS_VS = (
869
992
  /* glsl */
@@ -906,37 +1029,34 @@ var __exports__ = (() => {
906
1029
  var REGEX_END_OF_MAIN = /}\n?[^{}]*$/;
907
1030
  var fragments = [];
908
1031
  var DECLARATION_INJECT_MARKER = "__LUMA_INJECT_DECLARATIONS__";
909
- function normalizeInjections(injections) {
910
- const result = { vertex: {}, fragment: {} };
911
- for (const hook in injections) {
912
- let injection = injections[hook];
913
- const stage = getHookStage(hook);
914
- if (typeof injection === "string") {
915
- injection = {
916
- order: 0,
917
- injection
918
- };
1032
+ function resolveShaderInjections(declarations, language, pluginName) {
1033
+ const resolved = {};
1034
+ for (const declaration of declarations) {
1035
+ const source7 = declaration[language];
1036
+ if (source7 === void 0) continue;
1037
+ if (declaration.stage !== "vertex" && declaration.stage !== "fragment" || declaration.target.includes(":")) {
1038
+ throw new Error("Shader injection targets require an explicit stage and unqualified target");
919
1039
  }
920
- result[stage][hook] = injection;
921
- }
922
- return result;
923
- }
924
- function getHookStage(hook) {
925
- const type = hook.slice(0, 2);
926
- switch (type) {
927
- case "vs":
928
- return "vertex";
929
- case "fs":
930
- return "fragment";
931
- default:
932
- throw new Error(type);
1040
+ const target = `${declaration.stage === "vertex" ? "vs" : "fs"}:${declaration.target}`;
1041
+ if (!/^(vs|fs):(?:#(?:decl|main-start|main-end)|[A-Za-z_][A-Za-z0-9_]*)$/.test(target)) {
1042
+ throw new Error(`Shader injection target "${target}" must be a named shader anchor or hook`);
1043
+ }
1044
+ resolved[target] ||= [];
1045
+ resolved[target].push({
1046
+ injection: source7,
1047
+ order: declaration.order ?? 0,
1048
+ ...pluginName ? { pluginName } : {}
1049
+ });
933
1050
  }
1051
+ return resolved;
934
1052
  }
935
1053
  function injectShader(source7, stage, inject, injectStandardStubs = false, language = "glsl", entryPoints = {}) {
936
1054
  const isVertex = stage === "vertex";
937
1055
  for (const key in inject) {
938
1056
  const fragmentData = inject[key];
939
- fragmentData.sort((a, b) => a.order - b.order);
1057
+ fragmentData.sort(
1058
+ (a, b) => a.order - b.order
1059
+ );
940
1060
  fragments.length = fragmentData.length;
941
1061
  for (let i = 0, len = fragmentData.length; i < len; ++i) {
942
1062
  fragments[i] = fragmentData[i].injection;
@@ -1053,134 +1173,6 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1053
1173
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1054
1174
  }
1055
1175
 
1056
- // src/lib/shader-module/shader-module.ts
1057
- function initializeShaderModules(modules) {
1058
- modules.map((module) => initializeShaderModule(module));
1059
- }
1060
- function initializeShaderModule(module) {
1061
- if (module.instance) {
1062
- return;
1063
- }
1064
- initializeShaderModules(module.dependencies || []);
1065
- const {
1066
- propTypes = {},
1067
- deprecations = [],
1068
- // defines = {},
1069
- inject = {}
1070
- } = module;
1071
- const instance = {
1072
- normalizedInjections: normalizeInjections(inject),
1073
- parsedDeprecations: parseDeprecationDefinitions(deprecations)
1074
- };
1075
- if (propTypes) {
1076
- instance.propValidators = makePropValidators(propTypes);
1077
- }
1078
- module.instance = instance;
1079
- let defaultProps = {};
1080
- if (propTypes) {
1081
- defaultProps = Object.entries(propTypes).reduce(
1082
- (obj, [key, propType]) => {
1083
- const value = propType?.value;
1084
- if (value) {
1085
- obj[key] = value;
1086
- }
1087
- return obj;
1088
- },
1089
- {}
1090
- );
1091
- }
1092
- module.defaultUniforms = {
1093
- ...module.defaultUniforms,
1094
- ...defaultProps
1095
- };
1096
- }
1097
- function getShaderModuleUniforms(module, props, oldUniforms) {
1098
- initializeShaderModule(module);
1099
- const uniforms2 = oldUniforms || { ...module.defaultUniforms };
1100
- if (props && module.getUniforms) {
1101
- return module.getUniforms(props, uniforms2);
1102
- }
1103
- return getValidatedProperties(props, module.instance?.propValidators, module.name);
1104
- }
1105
- function checkShaderModuleDeprecations(shaderModule, shaderSource, log) {
1106
- shaderModule.deprecations?.forEach((def) => {
1107
- if (def.regex?.test(shaderSource)) {
1108
- if (def.deprecated) {
1109
- log.deprecated(def.old, def.new)();
1110
- } else {
1111
- log.removed(def.old, def.new)();
1112
- }
1113
- }
1114
- });
1115
- }
1116
- function parseDeprecationDefinitions(deprecations) {
1117
- deprecations.forEach((def) => {
1118
- switch (def.type) {
1119
- case "function":
1120
- def.regex = new RegExp(`\\b${def.old}\\(`);
1121
- break;
1122
- default:
1123
- def.regex = new RegExp(`${def.type} ${def.old};`);
1124
- }
1125
- });
1126
- return deprecations;
1127
- }
1128
-
1129
- // src/lib/shader-module/shader-module-dependencies.ts
1130
- function getShaderModuleDependencies(modules) {
1131
- const visited = /* @__PURE__ */ new Set();
1132
- const recursionStack = /* @__PURE__ */ new Set();
1133
- const result = [];
1134
- const nameToModule = /* @__PURE__ */ new Map();
1135
- function checkNameCollision(module) {
1136
- const existing = nameToModule.get(module.name);
1137
- if (existing && existing !== module) {
1138
- throw new Error(
1139
- `Shader module name collision: Multiple different module objects share the name "${module.name}". Each module object must have a unique name.`
1140
- );
1141
- }
1142
- nameToModule.set(module.name, module);
1143
- }
1144
- function visit(module, path) {
1145
- if (recursionStack.has(module)) {
1146
- const cyclePath = [...path, module.name].join(" -> ");
1147
- throw new Error(`Shader module dependency cycle detected: ${cyclePath}`);
1148
- }
1149
- if (visited.has(module)) {
1150
- return;
1151
- }
1152
- checkNameCollision(module);
1153
- recursionStack.add(module);
1154
- const newPath = [...path, module.name];
1155
- if (module.dependencies) {
1156
- for (const dep of module.dependencies) {
1157
- visit(dep, newPath);
1158
- }
1159
- }
1160
- recursionStack.delete(module);
1161
- visited.add(module);
1162
- result.push(module);
1163
- }
1164
- for (const module of modules) {
1165
- visit(module, []);
1166
- }
1167
- const interfaceProviders = /* @__PURE__ */ new Set();
1168
- for (const module of result) {
1169
- if (module.implements !== void 0) {
1170
- assert(!interfaceProviders.has(module.implements));
1171
- interfaceProviders.add(module.implements);
1172
- }
1173
- }
1174
- initializeShaderModules(result);
1175
- return result;
1176
- }
1177
- function getShaderDependencies(modules) {
1178
- return getShaderModuleDependencies(modules);
1179
- }
1180
- function resolveModules(modules) {
1181
- return getShaderDependencies(modules);
1182
- }
1183
-
1184
1176
  // src/types/shader-type-decoder.ts
1185
1177
  function getVariableShaderTypeInfo(format) {
1186
1178
  const resolvedFormat = resolveVariableShaderTypeAlias(format);
@@ -1382,10 +1374,10 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1382
1374
  };
1383
1375
 
1384
1376
  // src/lib/shader-plugin.ts
1385
- var SHADER_PLUGIN_INJECTION_TARGET_REGEX = /^(vs|fs):(?:#(?:decl|main-start|main-end)|[A-Za-z_][\w-]*)$/;
1386
1377
  function resolveShaderPlugins(plugins = [], shaderLanguage) {
1387
1378
  const resolved = {
1388
1379
  modules: [],
1380
+ hooks: [],
1389
1381
  defines: {},
1390
1382
  injections: {},
1391
1383
  vertexInputs: {},
@@ -1450,19 +1442,41 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1450
1442
  }
1451
1443
  resolved.interStageVariables[name] = normalizedVarying;
1452
1444
  }
1453
- for (const injection of plugin.injections || []) {
1454
- const source7 = injection[shaderLanguage];
1455
- if (source7 === void 0) continue;
1456
- assertNamedShaderPluginInjectionTarget(injection.target);
1457
- if (!resolved.injections[injection.target]) {
1458
- resolved.injections[injection.target] = [];
1445
+ for (const hook of plugin.hooks || []) {
1446
+ const implementation = hook[shaderLanguage];
1447
+ if (!implementation) continue;
1448
+ const target = `${hook.stage === "vertex" ? "vs" : "fs"}:${hook.name}`;
1449
+ if (!/^(vs|fs):[A-Za-z_][A-Za-z0-9_]*$/.test(target)) {
1450
+ throw new Error(`Invalid ShaderPlugin hook ${target}`);
1459
1451
  }
1460
- const injections = resolved.injections[injection.target];
1461
- injections.push({
1462
- pluginName: plugin.name,
1463
- injection: source7,
1464
- order: injection.order ?? 0
1465
- });
1452
+ const existing = resolved.hooks.find(
1453
+ (value) => value.stage === hook.stage && value.name === hook.name
1454
+ );
1455
+ const previous = existing?.[shaderLanguage];
1456
+ if (previous && (previous.parameters !== implementation.parameters || (previous.returnType || "void") !== (implementation.returnType || "void") || previous.header !== implementation.header || previous.footer !== implementation.footer)) {
1457
+ throw new Error(`Conflicting ShaderPlugin hook ${target}`);
1458
+ }
1459
+ if (!existing)
1460
+ resolved.hooks.push({ name: hook.name, stage: hook.stage, [shaderLanguage]: implementation });
1461
+ }
1462
+ const pluginInjections = Array.isArray(plugin.injections) ? plugin.injections : Object.entries(plugin.injections || {}).flatMap(([key, source7]) => {
1463
+ const target = selectShaderLanguage(key, shaderLanguage);
1464
+ if (target === void 0) return [];
1465
+ const [stage, ...name] = target.split(":");
1466
+ if (stage !== "vs" && stage !== "fs")
1467
+ throw new Error(`Invalid ShaderPlugin injection ${key}`);
1468
+ return [
1469
+ {
1470
+ stage: stage === "vs" ? "vertex" : "fragment",
1471
+ target: name.join(":"),
1472
+ [shaderLanguage]: source7
1473
+ }
1474
+ ];
1475
+ });
1476
+ const selectedInjections = resolveShaderInjections(pluginInjections, shaderLanguage, plugin.name);
1477
+ for (const [target, injections] of Object.entries(selectedInjections)) {
1478
+ resolved.injections[target] ||= [];
1479
+ resolved.injections[target].push(...injections);
1466
1480
  }
1467
1481
  }
1468
1482
  function assertShaderPluginInterfaceName(name, kind) {
@@ -1479,12 +1493,9 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1479
1493
  }
1480
1494
  return { type: varying.type, interpolation };
1481
1495
  }
1482
- function assertNamedShaderPluginInjectionTarget(target) {
1483
- if (!SHADER_PLUGIN_INJECTION_TARGET_REGEX.test(target)) {
1484
- throw new Error(
1485
- `ShaderPlugin injection target "${target}" must be a named shader anchor or hook`
1486
- );
1487
- }
1496
+ function selectShaderLanguage(value, language) {
1497
+ const match = /^(glsl|wgsl):([\s\S]+)$/.exec(value);
1498
+ return match ? match[1] === language ? match[2] : void 0 : value;
1488
1499
  }
1489
1500
 
1490
1501
  // src/lib/shader-module/shader-module-uniform-layout.ts
@@ -2691,8 +2702,10 @@ return ${outputName};
2691
2702
  let result = "";
2692
2703
  for (const hookName in hookFunctions) {
2693
2704
  const hookFunction = hookFunctions[hookName];
2694
- const functionPrefix = shaderLanguage === "wgsl" ? "fn" : "void";
2695
- result += `${functionPrefix} ${hookFunction.signature} {
2705
+ const returnType = hookFunction.returnType || "void";
2706
+ const functionPrefix = shaderLanguage === "wgsl" ? "fn" : returnType;
2707
+ const returnSuffix = shaderLanguage === "wgsl" && returnType !== "void" ? ` -> ${returnType}` : "";
2708
+ result += `${functionPrefix} ${hookFunction.signature}${returnSuffix} {
2696
2709
  `;
2697
2710
  if (hookFunction.header) {
2698
2711
  result += ` ${hookFunction.header}`;
@@ -2712,10 +2725,29 @@ return ${outputName};
2712
2725
  }
2713
2726
  return result;
2714
2727
  }
2715
- function normalizeShaderHooks(hookFunctions) {
2728
+ function normalizeShaderHooks(hookFunctions, pluginHooks = [], language = "glsl") {
2716
2729
  const result = { vertex: {}, fragment: {} };
2717
- for (const hookFunction of hookFunctions) {
2718
- addShaderHookToRegistry(result, hookFunction);
2730
+ for (const hook of hookFunctions) {
2731
+ const implementation = hook[language];
2732
+ if (implementation) {
2733
+ const prefix = hook.stage === "vertex" ? "vs" : "fs";
2734
+ result[hook.stage][`${prefix}:${hook.name}`] = {
2735
+ ...implementation,
2736
+ signature: `${hook.name}(${implementation.parameters})`
2737
+ };
2738
+ }
2739
+ }
2740
+ for (const hook of pluginHooks) {
2741
+ const selected = normalizeShaderHooks([hook], [], language);
2742
+ for (const stage of ["vertex", "fragment"]) {
2743
+ for (const [name, declaration] of Object.entries(selected[stage])) {
2744
+ const existing = result[stage][name];
2745
+ if (existing && (existing.signature !== declaration.signature || (existing.returnType || "void") !== (declaration.returnType || "void"))) {
2746
+ throw new Error(`Conflicting ShaderPlugin hook ${name}`);
2747
+ }
2748
+ result[stage][name] = existing || declaration;
2749
+ }
2750
+ }
2719
2751
  }
2720
2752
  return result;
2721
2753
  }
@@ -2739,30 +2771,6 @@ return ${outputName};
2739
2771
  }
2740
2772
  }
2741
2773
  }
2742
- function addShaderHookToRegistry(registry, hookFunction) {
2743
- const options = typeof hookFunction === "string" ? {} : hookFunction;
2744
- const hook = (typeof hookFunction === "string" ? hookFunction : hookFunction.hook).trim();
2745
- const stageSeparatorIndex = hook.indexOf(":");
2746
- const shaderStage = hook.slice(0, stageSeparatorIndex);
2747
- const signature = hook.slice(stageSeparatorIndex + 1);
2748
- const name = hook.replace(/\(.+/, "");
2749
- const normalizedHook = {
2750
- hook: name,
2751
- signature,
2752
- header: options.header || "",
2753
- footer: options.footer || ""
2754
- };
2755
- switch (shaderStage) {
2756
- case "vs":
2757
- registry.vertex[name] = normalizedHook;
2758
- break;
2759
- case "fs":
2760
- registry.fragment[name] = normalizedHook;
2761
- break;
2762
- default:
2763
- throw new Error(shaderStage);
2764
- }
2765
- }
2766
2774
 
2767
2775
  // src/lib/shader-assembly/assemble-wgsl-shaders.ts
2768
2776
  var RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
@@ -2812,13 +2820,16 @@ return ${outputName};
2812
2820
  );
2813
2821
  const coreSource = pluginVaryingAssembly.source;
2814
2822
  let assembledSource = "";
2815
- const hookFunctionMap = normalizeShaderHooks(hookFunctions);
2823
+ const hookFunctionMap = normalizeShaderHooks(hookFunctions, options.pluginHooks, "wgsl");
2816
2824
  const hookInjections = {};
2817
2825
  const declInjections = {};
2818
2826
  const mainInjections = {};
2819
2827
  appendInjections(pluginInjections, hookInjections, declInjections, mainInjections);
2820
- for (const key in inject) {
2821
- const injection = typeof inject[key] === "string" ? { injection: inject[key], order: 0 } : inject[key];
2828
+ const applicationInjections = Array.isArray(inject) ? resolveShaderInjections(inject, "wgsl") : {};
2829
+ appendInjections(applicationInjections, hookInjections, declInjections, mainInjections);
2830
+ const injectionMap = Array.isArray(inject) ? {} : inject;
2831
+ for (const key in injectionMap) {
2832
+ const injection = typeof injectionMap[key] === "string" ? { injection: injectionMap[key], order: 0 } : injectionMap[key];
2822
2833
  const match = /^(v|f)s:(#)?([\w-]+)$/.exec(key);
2823
2834
  if (match) {
2824
2835
  const hash = match[2];
@@ -2872,19 +2883,6 @@ return ${outputName};
2872
2883
  checkShaderModuleDeprecations(module, coreSource, log);
2873
2884
  }
2874
2885
  assembledSource += moduleSource;
2875
- const injections = getWGSLModuleInjections(module);
2876
- for (const key in injections) {
2877
- const match = /^(v|f)s:#([\w-]+)$/.exec(key);
2878
- if (match) {
2879
- const name = match[2];
2880
- const injectionType = name === "decl" ? declInjections : mainInjections;
2881
- injectionType[key] = injectionType[key] || [];
2882
- injectionType[key].push(injections[key]);
2883
- } else if (isShaderHookRegistered(hookFunctionMap, key)) {
2884
- hookInjections[key] = hookInjections[key] || [];
2885
- hookInjections[key].push(injections[key]);
2886
- }
2887
- }
2888
2886
  }
2889
2887
  assembledSource += INJECT_SHADER_DECLARATIONS;
2890
2888
  assembledSource = injectShader(
@@ -2944,12 +2942,6 @@ return ${outputName};
2944
2942
  });
2945
2943
  }
2946
2944
  }
2947
- function getWGSLModuleInjections(module) {
2948
- return {
2949
- ...module.instance?.normalizedInjections.vertex || {},
2950
- ...module.instance?.normalizedInjections.fragment || {}
2951
- };
2952
- }
2953
2945
  function getWGSLDeclarationInjections(declarationInjections) {
2954
2946
  const unifiedDeclarations = [
2955
2947
  ...declarationInjections["vs:#decl"] || [],
@@ -3470,13 +3462,16 @@ ${getApplicationDefines(allDefines)}
3470
3462
  `;
3471
3463
  break;
3472
3464
  }
3473
- const hookFunctionMap = normalizeShaderHooks(hookFunctions);
3465
+ const hookFunctionMap = normalizeShaderHooks(hookFunctions, options.pluginHooks, "glsl");
3474
3466
  const hookInjections = {};
3475
3467
  const declInjections = {};
3476
3468
  const mainInjections = {};
3477
3469
  appendInjections(pluginInjections, hookInjections, declInjections, mainInjections);
3478
- for (const key in inject) {
3479
- const injection = typeof inject[key] === "string" ? { injection: inject[key], order: 0 } : inject[key];
3470
+ const applicationInjections = Array.isArray(inject) ? resolveShaderInjections(inject, "glsl") : {};
3471
+ appendInjections(applicationInjections, hookInjections, declInjections, mainInjections);
3472
+ const injectionMap = Array.isArray(inject) ? {} : inject;
3473
+ for (const key in injectionMap) {
3474
+ const injection = typeof injectionMap[key] === "string" ? { injection: injectionMap[key], order: 0 } : injectionMap[key];
3480
3475
  const match = /^(v|f)s:(#)?([\w-]+)$/.exec(key);
3481
3476
  if (match) {
3482
3477
  const hash = match[2];
@@ -3521,19 +3516,6 @@ ${getApplicationDefines(allDefines)}
3521
3516
  }
3522
3517
  const moduleSource = getShaderModuleSource(module, stage, log);
3523
3518
  assembledSource += moduleSource;
3524
- const injections = module.instance?.normalizedInjections[stage] || {};
3525
- for (const key in injections) {
3526
- const match = /^(v|f)s:#([\w-]+)$/.exec(key);
3527
- if (match) {
3528
- const name = match[2];
3529
- const injectionType = name === "decl" ? declInjections : mainInjections;
3530
- injectionType[key] = injectionType[key] || [];
3531
- injectionType[key].push(injections[key]);
3532
- } else {
3533
- hookInjections[key] = hookInjections[key] || [];
3534
- hookInjections[key].push(injections[key]);
3535
- }
3536
- }
3537
3519
  }
3538
3520
  validateShaderHookInjections(hookFunctionMap, hookInjections, stage);
3539
3521
  assembledSource += "// ----- MAIN SHADER SOURCE -------------------------";
@@ -3638,11 +3620,10 @@ ${getApplicationDefines(allDefines)}
3638
3620
  }
3639
3621
  /**
3640
3622
  * Register a shader hook
3641
- * @param hook Stage-prefixed hook signature, such as `vs:OFFSET_POSITION(inout vec4 position)`.
3642
- * @param options Optional hook metadata such as always-on header and footer source.
3623
+ * @param hook Shared stage and name with language-specific parameters, return type, and source.
3643
3624
  */
3644
- addShaderHook(hook, options) {
3645
- this._hookFunctions.push(options ? { ...options, hook } : hook);
3625
+ addShaderHook(hook) {
3626
+ this._hookFunctions.push(hook);
3646
3627
  }
3647
3628
  /**
3648
3629
  * Remove all registered shader hooks, for example before re-registering hooks for a new
@@ -11673,7 +11654,8 @@ fn filter_isVisible(value: f32) -> bool {
11673
11654
  },
11674
11655
  injections: [
11675
11656
  {
11676
- target: "vs:FILTER_POSITION",
11657
+ stage: "vertex",
11658
+ target: "FILTER_POSITION",
11677
11659
  glsl: `if (!filter_isVisible(filterValues)) {
11678
11660
  position = vec4(2.0, 2.0, 2.0, 1.0);
11679
11661
  }`,
@@ -11774,7 +11756,8 @@ fn clip_isInBounds(coordinates: vec2<f32>) -> bool {
11774
11756
  },
11775
11757
  injections: [
11776
11758
  {
11777
- target: "vs:CLIP_POSITION",
11759
+ stage: "vertex",
11760
+ target: "CLIP_POSITION",
11778
11761
  glsl: `clipCoordinates = geometryCoordinates;
11779
11762
  if (clipState.enabled != 0 && clipState.mode == ${CLIP_MODE_INSTANCE} &&
11780
11763
  !clip_isInBounds(instanceCoordinates)) {
@@ -11787,7 +11770,8 @@ if (clipUniforms.enabled != 0 && clipUniforms.mode == ${CLIP_MODE_INSTANCE} &&
11787
11770
  }`
11788
11771
  },
11789
11772
  {
11790
- target: "fs:CLIP_COLOR",
11773
+ stage: "fragment",
11774
+ target: "CLIP_COLOR",
11791
11775
  glsl: `if (clipState.enabled != 0 && clipState.mode == ${CLIP_MODE_GEOMETRY} &&
11792
11776
  !clip_isInBounds(clipCoordinates)) {
11793
11777
  discard;
@@ -15589,7 +15573,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15589
15573
 
15590
15574
  for(int i = 0; i < lighting.directionalLightCount; i++) {
15591
15575
  if (i < lighting.directionalLightCount) {
15592
- PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
15576
+ PBRInfo_setDirectionalLight(pbrInfo, -lighting_getDirectionalLight(i).direction);
15593
15577
  color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
15594
15578
  }
15595
15579
  }
@@ -15877,7 +15861,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15877
15861
  // Apply directional light
15878
15862
  for(int i = 0; i < lighting.directionalLightCount; i++) {
15879
15863
  if (i < lighting.directionalLightCount) {
15880
- PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
15864
+ PBRInfo_setDirectionalLight(pbrInfo, -lighting_getDirectionalLight(i).direction);
15881
15865
  color += calculateMaterialLightColor(
15882
15866
  pbrInfo,
15883
15867
  lighting_getDirectionalLight(i).color,
@@ -17406,7 +17390,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17406
17390
 
17407
17391
  for (var i = 0; i < lighting.directionalLightCount; i++) {
17408
17392
  if (i < lighting.directionalLightCount) {
17409
- PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
17393
+ PBRInfo_setDirectionalLight(&pbrInfo, -lighting_getDirectionalLight(i).direction);
17410
17394
  color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
17411
17395
  }
17412
17396
  }
@@ -17766,7 +17750,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17766
17750
  // Apply directional light
17767
17751
  for (var i = 0; i < lighting.directionalLightCount; i++) {
17768
17752
  if (i < lighting.directionalLightCount) {
17769
- PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
17753
+ PBRInfo_setDirectionalLight(&pbrInfo, -lighting_getDirectionalLight(i).direction);
17770
17754
  color += calculateMaterialLightColor(
17771
17755
  pbrInfo,
17772
17756
  lighting_getDirectionalLight(i).color,