@luma.gl/shadertools 9.3.5 → 9.4.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/dist.dev.js +6555 -2373
  2. package/dist/dist.min.js +2984 -237
  3. package/dist/index.cjs +4356 -268
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +13 -3
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +6 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/preprocessor/preprocessor.d.ts +11 -1
  10. package/dist/lib/preprocessor/preprocessor.d.ts.map +1 -1
  11. package/dist/lib/preprocessor/preprocessor.js +47 -2
  12. package/dist/lib/preprocessor/preprocessor.js.map +1 -1
  13. package/dist/lib/shader-assembler.d.ts +10 -10
  14. package/dist/lib/shader-assembler.d.ts.map +1 -1
  15. package/dist/lib/shader-assembler.js +36 -20
  16. package/dist/lib/shader-assembler.js.map +1 -1
  17. package/dist/lib/shader-assembly/assemble-shaders.d.ts +28 -7
  18. package/dist/lib/shader-assembly/assemble-shaders.d.ts.map +1 -1
  19. package/dist/lib/shader-assembly/assemble-shaders.js +133 -15
  20. package/dist/lib/shader-assembly/assemble-shaders.js.map +1 -1
  21. package/dist/lib/shader-assembly/platform-info.d.ts +2 -0
  22. package/dist/lib/shader-assembly/platform-info.d.ts.map +1 -1
  23. package/dist/lib/shader-assembly/shader-hooks.d.ts +1 -1
  24. package/dist/lib/shader-assembly/shader-hooks.d.ts.map +1 -1
  25. package/dist/lib/shader-assembly/shader-hooks.js +6 -3
  26. package/dist/lib/shader-assembly/shader-hooks.js.map +1 -1
  27. package/dist/lib/shader-assembly/shader-injections.d.ts +4 -1
  28. package/dist/lib/shader-assembly/shader-injections.d.ts.map +1 -1
  29. package/dist/lib/shader-assembly/shader-injections.js +68 -11
  30. package/dist/lib/shader-assembly/shader-injections.js.map +1 -1
  31. package/dist/lib/shader-assembly/shader-plugin-varyings.d.ts +15 -0
  32. package/dist/lib/shader-assembly/shader-plugin-varyings.d.ts.map +1 -0
  33. package/dist/lib/shader-assembly/shader-plugin-varyings.js +412 -0
  34. package/dist/lib/shader-assembly/shader-plugin-varyings.js.map +1 -0
  35. package/dist/lib/shader-assembly/shader-plugin-vertex-inputs.d.ts +12 -0
  36. package/dist/lib/shader-assembly/shader-plugin-vertex-inputs.d.ts.map +1 -0
  37. package/dist/lib/shader-assembly/shader-plugin-vertex-inputs.js +237 -0
  38. package/dist/lib/shader-assembly/shader-plugin-vertex-inputs.js.map +1 -0
  39. package/dist/lib/shader-module/shader-module.d.ts +3 -0
  40. package/dist/lib/shader-module/shader-module.d.ts.map +1 -1
  41. package/dist/lib/shader-module/shader-module.js.map +1 -1
  42. package/dist/lib/shader-module/shader-pass-pipeline.d.ts +14 -0
  43. package/dist/lib/shader-module/shader-pass-pipeline.d.ts.map +1 -0
  44. package/dist/lib/shader-module/shader-pass-pipeline.js +5 -0
  45. package/dist/lib/shader-module/shader-pass-pipeline.js.map +1 -0
  46. package/dist/lib/shader-module/shader-pass.d.ts +21 -4
  47. package/dist/lib/shader-module/shader-pass.d.ts.map +1 -1
  48. package/dist/lib/shader-plugin.d.ts +68 -0
  49. package/dist/lib/shader-plugin.d.ts.map +1 -0
  50. package/dist/lib/shader-plugin.js +98 -0
  51. package/dist/lib/shader-plugin.js.map +1 -0
  52. package/dist/lib/utils/assert.js +1 -1
  53. package/dist/lib/utils/assert.js.map +1 -1
  54. package/dist/modules/color/float-colors.d.ts +111 -5
  55. package/dist/modules/color/float-colors.d.ts.map +1 -1
  56. package/dist/modules/color/float-colors.js +200 -34
  57. package/dist/modules/color/float-colors.js.map +1 -1
  58. package/dist/modules/engine/clip/clip.d.ts +12 -0
  59. package/dist/modules/engine/clip/clip.d.ts.map +1 -0
  60. package/dist/modules/engine/clip/clip.js +129 -0
  61. package/dist/modules/engine/clip/clip.js.map +1 -0
  62. package/dist/modules/engine/filter/filter.d.ts +12 -0
  63. package/dist/modules/engine/filter/filter.d.ts.map +1 -0
  64. package/dist/modules/engine/filter/filter.js +88 -0
  65. package/dist/modules/engine/filter/filter.js.map +1 -0
  66. package/dist/modules/geospatial/dggs/dggs-wgsl.d.ts +3 -0
  67. package/dist/modules/geospatial/dggs/dggs-wgsl.d.ts.map +1 -0
  68. package/dist/modules/geospatial/dggs/dggs-wgsl.js +1892 -0
  69. package/dist/modules/geospatial/dggs/dggs-wgsl.js.map +1 -0
  70. package/dist/modules/geospatial/dggs/dggs.d.ts +6 -0
  71. package/dist/modules/geospatial/dggs/dggs.d.ts.map +1 -0
  72. package/dist/modules/geospatial/dggs/dggs.js +10 -0
  73. package/dist/modules/geospatial/dggs/dggs.js.map +1 -0
  74. package/dist/modules/lighting/water-material/water-material.d.ts +28 -0
  75. package/dist/modules/lighting/water-material/water-material.d.ts.map +1 -0
  76. package/dist/modules/lighting/water-material/water-material.js +179 -0
  77. package/dist/modules/lighting/water-material/water-material.js.map +1 -0
  78. package/dist/modules/lighting/water-material/water-shaders-glsl.d.ts +3 -0
  79. package/dist/modules/lighting/water-material/water-shaders-glsl.d.ts.map +1 -0
  80. package/dist/modules/lighting/water-material/water-shaders-glsl.js +232 -0
  81. package/dist/modules/lighting/water-material/water-shaders-glsl.js.map +1 -0
  82. package/dist/modules/lighting/water-material/water-shaders-wgsl.d.ts +2 -0
  83. package/dist/modules/lighting/water-material/water-shaders-wgsl.d.ts.map +1 -0
  84. package/dist/modules/lighting/water-material/water-shaders-wgsl.js +230 -0
  85. package/dist/modules/lighting/water-material/water-shaders-wgsl.js.map +1 -0
  86. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.d.ts +2 -1
  87. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.d.ts.map +1 -1
  88. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.js +268 -0
  89. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.js.map +1 -1
  90. package/package.json +2 -2
  91. package/src/index.ts +50 -3
  92. package/src/lib/preprocessor/preprocessor.ts +68 -3
  93. package/src/lib/shader-assembler.ts +49 -20
  94. package/src/lib/shader-assembly/assemble-shaders.ts +242 -30
  95. package/src/lib/shader-assembly/platform-info.ts +2 -0
  96. package/src/lib/shader-assembly/shader-hooks.ts +7 -3
  97. package/src/lib/shader-assembly/shader-injections.ts +96 -11
  98. package/src/lib/shader-assembly/shader-plugin-varyings.ts +524 -0
  99. package/src/lib/shader-assembly/shader-plugin-vertex-inputs.ts +279 -0
  100. package/src/lib/shader-module/shader-module.ts +3 -0
  101. package/src/lib/shader-module/shader-pass-pipeline.ts +19 -0
  102. package/src/lib/shader-module/shader-pass.ts +29 -4
  103. package/src/lib/shader-plugin.ts +214 -0
  104. package/src/lib/utils/assert.ts +1 -1
  105. package/src/modules/color/float-colors.ts +272 -37
  106. package/src/modules/engine/clip/clip.ts +149 -0
  107. package/src/modules/engine/filter/filter.ts +107 -0
  108. package/src/modules/geospatial/dggs/dggs-wgsl.ts +1892 -0
  109. package/src/modules/geospatial/dggs/dggs.ts +12 -0
  110. package/src/modules/lighting/water-material/water-material.ts +234 -0
  111. package/src/modules/lighting/water-material/water-shaders-glsl.ts +233 -0
  112. package/src/modules/lighting/water-material/water-shaders-wgsl.ts +230 -0
  113. package/src/modules/math/fp64/fp64-arithmetic-wgsl.ts +268 -0
package/dist/index.cjs CHANGED
@@ -25,15 +25,22 @@ var __publicField = (obj, key, value) => {
25
25
  // dist/index.js
26
26
  var dist_exports = {};
27
27
  __export(dist_exports, {
28
+ STORAGE_COLOR_DEFAULT_BYTE_STRIDES: () => STORAGE_COLOR_DEFAULT_BYTE_STRIDES,
29
+ STORAGE_COLOR_FORMAT: () => STORAGE_COLOR_FORMAT,
30
+ STORAGE_COLOR_FORMAT_BYTE_LENGTHS: () => STORAGE_COLOR_FORMAT_BYTE_LENGTHS,
28
31
  ShaderAssembler: () => ShaderAssembler,
29
32
  _getDependencyGraph: () => getDependencyGraph,
30
33
  _resolveModules: () => resolveModules,
31
34
  assembleGLSLShaderPair: () => assembleGLSLShaderPair,
32
35
  capitalize: () => capitalize,
33
36
  checkShaderModuleDeprecations: () => checkShaderModuleDeprecations,
37
+ clipShaderPlugin: () => clipShaderPlugin,
38
+ colors: () => colors,
34
39
  combineInjects: () => combineInjects,
35
40
  convertToVec4: () => convertToVec4,
41
+ dggs: () => dggs,
36
42
  dirlight: () => dirlight,
43
+ filterShaderPlugin: () => filterShaderPlugin,
37
44
  floatColors: () => floatColors,
38
45
  fp32: () => fp32,
39
46
  fp64: () => fp64,
@@ -59,6 +66,7 @@ __export(dist_exports, {
59
66
  initializeShaderModules: () => initializeShaderModules,
60
67
  lambertMaterial: () => lambertMaterial,
61
68
  lighting: () => lighting,
69
+ mergeShaderPluginModules: () => mergeShaderPluginModules,
62
70
  normalizeByteColor3: () => normalizeByteColor3,
63
71
  normalizeByteColor4: () => normalizeByteColor4,
64
72
  pbrMaterial: () => pbrMaterial,
@@ -67,13 +75,16 @@ __export(dist_exports, {
67
75
  picking: () => picking,
68
76
  preprocess: () => preprocess,
69
77
  random: () => random,
78
+ resolveShaderPlugins: () => resolveShaderPlugins,
70
79
  resolveUseByteColors: () => resolveUseByteColors,
71
80
  skin: () => skin,
81
+ storageColors: () => storageColors,
72
82
  toHalfFloat: () => toHalfFloat,
73
83
  typeToChannelCount: () => typeToChannelCount,
74
84
  typeToChannelSuffix: () => typeToChannelSuffix,
75
85
  validateShaderModuleUniformLayout: () => validateShaderModuleUniformLayout,
76
- warnIfGLSLUniformBlocksAreNotStd140: () => warnIfGLSLUniformBlocksAreNotStd140
86
+ warnIfGLSLUniformBlocksAreNotStd140: () => warnIfGLSLUniformBlocksAreNotStd140,
87
+ waterMaterial: () => waterMaterial
77
88
  });
78
89
  module.exports = __toCommonJS(dist_exports);
79
90
 
@@ -218,7 +229,7 @@ function getHookStage(hook) {
218
229
  throw new Error(type);
219
230
  }
220
231
  }
221
- function injectShader(source4, stage, inject, injectStandardStubs = false) {
232
+ function injectShader(source4, stage, inject, injectStandardStubs = false, language = "glsl", entryPoints = {}) {
222
233
  const isVertex = stage === "vertex";
223
234
  for (const key in inject) {
224
235
  const fragmentData = inject[key];
@@ -231,33 +242,33 @@ function injectShader(source4, stage, inject, injectStandardStubs = false) {
231
242
  `;
232
243
  switch (key) {
233
244
  case "vs:#decl":
234
- if (isVertex) {
245
+ if (language === "wgsl" || isVertex) {
235
246
  source4 = source4.replace(DECLARATION_INJECT_MARKER, fragmentString);
236
247
  }
237
248
  break;
238
249
  case "vs:#main-start":
239
- if (isVertex) {
240
- source4 = source4.replace(REGEX_START_OF_MAIN, (match) => match + fragmentString);
250
+ if (language === "wgsl" || isVertex) {
251
+ source4 = language === "wgsl" ? injectWGSLStageMain(source4, "vertex", fragmentString, "start", entryPoints.vertex) : source4.replace(REGEX_START_OF_MAIN, (match) => match + fragmentString);
241
252
  }
242
253
  break;
243
254
  case "vs:#main-end":
244
- if (isVertex) {
245
- source4 = source4.replace(REGEX_END_OF_MAIN, (match) => fragmentString + match);
255
+ if (language === "wgsl" || isVertex) {
256
+ source4 = language === "wgsl" ? injectWGSLStageMain(source4, "vertex", fragmentString, "end", entryPoints.vertex) : source4.replace(REGEX_END_OF_MAIN, (match) => fragmentString + match);
246
257
  }
247
258
  break;
248
259
  case "fs:#decl":
249
- if (!isVertex) {
260
+ if (language === "wgsl" || !isVertex) {
250
261
  source4 = source4.replace(DECLARATION_INJECT_MARKER, fragmentString);
251
262
  }
252
263
  break;
253
264
  case "fs:#main-start":
254
- if (!isVertex) {
255
- source4 = source4.replace(REGEX_START_OF_MAIN, (match) => match + fragmentString);
265
+ if (language === "wgsl" || !isVertex) {
266
+ source4 = language === "wgsl" ? injectWGSLStageMain(source4, "fragment", fragmentString, "start", entryPoints.fragment) : source4.replace(REGEX_START_OF_MAIN, (match) => match + fragmentString);
256
267
  }
257
268
  break;
258
269
  case "fs:#main-end":
259
- if (!isVertex) {
260
- source4 = source4.replace(REGEX_END_OF_MAIN, (match) => fragmentString + match);
270
+ if (language === "wgsl" || !isVertex) {
271
+ source4 = language === "wgsl" ? injectWGSLStageMain(source4, "fragment", fragmentString, "end", entryPoints.fragment) : source4.replace(REGEX_END_OF_MAIN, (match) => fragmentString + match);
261
272
  }
262
273
  break;
263
274
  default:
@@ -281,6 +292,49 @@ ${inject[key]}` : inject[key];
281
292
  });
282
293
  return result;
283
294
  }
295
+ function injectWGSLStageMain(source4, stage, fragmentString, position, entryPoint) {
296
+ const bodyRange = getWGSLStageFunctionBodyRange(source4, stage, entryPoint);
297
+ if (!bodyRange) {
298
+ return source4;
299
+ }
300
+ if (position === "start") {
301
+ const insertionIndex = bodyRange.openBraceIndex + 1;
302
+ return `${source4.slice(0, insertionIndex)}
303
+ ${fragmentString}${source4.slice(insertionIndex)}`;
304
+ }
305
+ return `${source4.slice(0, bodyRange.closeBraceIndex)}${fragmentString}${source4.slice(bodyRange.closeBraceIndex)}`;
306
+ }
307
+ function getWGSLStageFunctionBodyRange(source4, stage, entryPoint) {
308
+ const stageAttribute = stage === "vertex" ? "@vertex" : "@fragment";
309
+ const stageIndex = source4.indexOf(stageAttribute);
310
+ if (stageIndex < 0) {
311
+ return null;
312
+ }
313
+ const functionIndex = entryPoint ? source4.search(new RegExp(`\\bfn\\s+${escapeRegExp(entryPoint)}\\s*\\(`)) : source4.indexOf("fn", stageIndex);
314
+ if (functionIndex < 0) {
315
+ return null;
316
+ }
317
+ const openBraceIndex = source4.indexOf("{", functionIndex);
318
+ if (openBraceIndex < 0) {
319
+ return null;
320
+ }
321
+ let braceDepth = 0;
322
+ for (let index = openBraceIndex; index < source4.length; index++) {
323
+ const character = source4[index];
324
+ if (character === "{") {
325
+ braceDepth++;
326
+ } else if (character === "}") {
327
+ braceDepth--;
328
+ if (braceDepth === 0) {
329
+ return { openBraceIndex, closeBraceIndex: index };
330
+ }
331
+ }
332
+ }
333
+ return null;
334
+ }
335
+ function escapeRegExp(value) {
336
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
337
+ }
284
338
 
285
339
  // dist/lib/shader-module/shader-module.js
286
340
  function initializeShaderModules(modules) {
@@ -392,6 +446,97 @@ function resolveModules(modules) {
392
446
  return getShaderDependencies(modules);
393
447
  }
394
448
 
449
+ // dist/lib/shader-plugin.js
450
+ var import_core = require("@luma.gl/core");
451
+ var SHADER_PLUGIN_INJECTION_TARGET_REGEX = /^(vs|fs):(?:#(?:decl|main-start|main-end)|[A-Za-z_][\w-]*)$/;
452
+ function resolveShaderPlugins(plugins = [], shaderLanguage) {
453
+ const modules = [];
454
+ const defines = {};
455
+ const injections = {};
456
+ const vertexInputs = {};
457
+ const varyings = {};
458
+ for (const plugin of plugins) {
459
+ appendShaderPluginVariant({ modules, defines, injections, vertexInputs, varyings }, plugin);
460
+ appendShaderPluginVariant({ modules, defines, injections, vertexInputs, varyings }, plugin[shaderLanguage]);
461
+ }
462
+ for (const name of Object.keys(varyings)) {
463
+ if (vertexInputs[name]) {
464
+ throw new Error(`ShaderPlugin name "${name}" cannot be both a vertex input and a varying`);
465
+ }
466
+ }
467
+ return { modules, defines, injections, vertexInputs, varyings };
468
+ }
469
+ function mergeShaderPluginModules(modules = [], pluginModules = []) {
470
+ const mergedModules = [...modules];
471
+ const seenModuleNames = new Set(mergedModules.map((module2) => module2.name));
472
+ for (const pluginModule of pluginModules) {
473
+ if (!seenModuleNames.has(pluginModule.name)) {
474
+ mergedModules.push(pluginModule);
475
+ seenModuleNames.add(pluginModule.name);
476
+ }
477
+ }
478
+ return mergedModules;
479
+ }
480
+ function appendShaderPluginVariant(resolved, variant) {
481
+ var _a;
482
+ if (!variant) {
483
+ return;
484
+ }
485
+ if ((_a = variant.modules) == null ? void 0 : _a.length) {
486
+ resolved.modules.push(...variant.modules);
487
+ }
488
+ if (variant.defines) {
489
+ Object.assign(resolved.defines, variant.defines);
490
+ }
491
+ for (const [name, type] of Object.entries(variant.vertexInputs || {})) {
492
+ assertShaderPluginInterfaceName(name, "vertex input");
493
+ const existingType = resolved.vertexInputs[name];
494
+ if (existingType && existingType !== type) {
495
+ throw new Error(`ShaderPlugin vertex input "${name}" has conflicting types "${existingType}" and "${type}"`);
496
+ }
497
+ resolved.vertexInputs[name] = type;
498
+ }
499
+ for (const [name, varying] of Object.entries(variant.varyings || {})) {
500
+ assertShaderPluginInterfaceName(name, "varying");
501
+ const normalizedVarying = normalizeShaderPluginVarying(name, varying);
502
+ const existingVarying = resolved.varyings[name];
503
+ if (existingVarying && (existingVarying.type !== normalizedVarying.type || existingVarying.interpolation !== normalizedVarying.interpolation)) {
504
+ throw new Error(`ShaderPlugin varying "${name}" has conflicting declarations "${existingVarying.type}/${existingVarying.interpolation}" and "${normalizedVarying.type}/${normalizedVarying.interpolation}"`);
505
+ }
506
+ resolved.varyings[name] = normalizedVarying;
507
+ }
508
+ for (const injection of variant.injections || []) {
509
+ assertNamedShaderPluginInjectionTarget(injection.target);
510
+ if (!resolved.injections[injection.target]) {
511
+ resolved.injections[injection.target] = [];
512
+ }
513
+ const injections = resolved.injections[injection.target];
514
+ injections.push({
515
+ injection: injection.injection,
516
+ order: injection.order ?? 0
517
+ });
518
+ }
519
+ }
520
+ function assertShaderPluginInterfaceName(name, kind) {
521
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || name.startsWith("_luma_")) {
522
+ throw new Error(`ShaderPlugin ${kind} "${name}" must be a valid non-reserved identifier`);
523
+ }
524
+ }
525
+ function normalizeShaderPluginVarying(name, varying) {
526
+ const { primitiveType } = import_core.shaderTypeDecoder.getAttributeShaderTypeInfo(varying.type);
527
+ const integerType = primitiveType === "i32" || primitiveType === "u32";
528
+ const interpolation = varying.interpolation || (integerType ? "flat" : "smooth");
529
+ if (integerType && interpolation === "smooth") {
530
+ throw new Error(`ShaderPlugin integer varying "${name}" must use flat interpolation`);
531
+ }
532
+ return { type: varying.type, interpolation };
533
+ }
534
+ function assertNamedShaderPluginInjectionTarget(target) {
535
+ if (!SHADER_PLUGIN_INJECTION_TARGET_REGEX.test(target)) {
536
+ throw new Error(`ShaderPlugin injection target "${target}" must be a named shader anchor or hook`);
537
+ }
538
+ }
539
+
395
540
  // dist/lib/shader-module/shader-module-uniform-layout.js
396
541
  var GLSL_UNIFORM_BLOCK_FIELD_REGEXP = /^(?:uniform\s+)?(?:(?:lowp|mediump|highp)\s+)?[A-Za-z0-9_]+(?:<[^>]+>)?\s+([A-Za-z0-9_]+)(?:\s*\[[^\]]+\])?\s*;/;
397
542
  var GLSL_UNIFORM_BLOCK_REGEXP = /((?:layout\s*\([^)]*\)\s*)*)uniform\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{([\s\S]*?)\}\s*([A-Za-z_][A-Za-z0-9_]*)?\s*;/g;
@@ -684,11 +829,12 @@ function makeVariableTextRegExp(qualifier) {
684
829
  }
685
830
 
686
831
  // dist/lib/shader-assembly/shader-hooks.js
687
- function getShaderHooks(hookFunctions, hookInjections) {
832
+ function getShaderHooks(hookFunctions, hookInjections, shaderLanguage = "glsl") {
688
833
  let result = "";
689
834
  for (const hookName in hookFunctions) {
690
835
  const hookFunction = hookFunctions[hookName];
691
- result += `void ${hookFunction.signature} {
836
+ const functionPrefix = shaderLanguage === "wgsl" ? "fn" : "void";
837
+ result += `${functionPrefix} ${hookFunction.signature} {
692
838
  `;
693
839
  if (hookFunction.header) {
694
840
  result += ` ${hookFunction.header}`;
@@ -721,7 +867,9 @@ function normalizeShaderHooks(hookFunctions) {
721
867
  hook = hookFunction;
722
868
  }
723
869
  hook = hook.trim();
724
- const [shaderStage, signature] = hook.split(":");
870
+ const stageSeparatorIndex = hook.indexOf(":");
871
+ const shaderStage = hook.slice(0, stageSeparatorIndex);
872
+ const signature = hook.slice(stageSeparatorIndex + 1);
725
873
  const name = hook.replace(/\(.+/, "");
726
874
  const normalizedHook = Object.assign(opts, { signature });
727
875
  switch (shaderStage) {
@@ -1051,140 +1199,856 @@ function getStorageTextureAccess(resourceType) {
1051
1199
  return match == null ? void 0 : match[1];
1052
1200
  }
1053
1201
 
1054
- // dist/lib/shader-assembly/assemble-shaders.js
1055
- var INJECT_SHADER_DECLARATIONS = `
1202
+ // dist/lib/preprocessor/preprocessor.js
1203
+ var DEFINE_NAME_PATTERN = "([a-zA-Z_][a-zA-Z0-9_]*)";
1204
+ var IF_REGEXP = /^\s*\#\s*if\s+(.+?)\s*(?:\/\/.*)?$/;
1205
+ var IFDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*$`);
1206
+ var IFNDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifndef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
1207
+ var ELSE_REGEXP = /^\s*\#\s*else\s*(?:\/\/.*)?$/;
1208
+ var ENDIF_REGEXP = /^\s*\#\s*endif\s*$/;
1209
+ var IFDEF_WITH_COMMENT_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
1210
+ var ENDIF_WITH_COMMENT_REGEXP = /^\s*\#\s*endif\s*(?:\/\/.*)?$/;
1211
+ function preprocess(source4, options) {
1212
+ var _a, _b;
1213
+ const lines = source4.split("\n");
1214
+ const output = [];
1215
+ const conditionalStack = [];
1216
+ let conditional = true;
1217
+ for (const line of lines) {
1218
+ const matchIfExpression = line.match(IF_REGEXP);
1219
+ const matchIf = line.match(IFDEF_WITH_COMMENT_REGEXP) || line.match(IFDEF_REGEXP);
1220
+ const matchIfNot = line.match(IFNDEF_REGEXP);
1221
+ const matchElse = line.match(ELSE_REGEXP);
1222
+ const matchEnd = line.match(ENDIF_WITH_COMMENT_REGEXP) || line.match(ENDIF_REGEXP);
1223
+ if (matchIfExpression) {
1224
+ const branchTaken = evaluateIfExpression(matchIfExpression[1], (options == null ? void 0 : options.defines) || {});
1225
+ const active = conditional && branchTaken;
1226
+ conditionalStack.push({ parentActive: conditional, branchTaken, active });
1227
+ conditional = active;
1228
+ } else if (matchIf || matchIfNot) {
1229
+ const defineName = (_a = matchIf || matchIfNot) == null ? void 0 : _a[1];
1230
+ const defineValue = Boolean((_b = options == null ? void 0 : options.defines) == null ? void 0 : _b[defineName]);
1231
+ const branchTaken = matchIf ? defineValue : !defineValue;
1232
+ const active = conditional && branchTaken;
1233
+ conditionalStack.push({ parentActive: conditional, branchTaken, active });
1234
+ conditional = active;
1235
+ } else if (matchElse) {
1236
+ const currentConditional = conditionalStack[conditionalStack.length - 1];
1237
+ if (!currentConditional) {
1238
+ throw new Error("Encountered #else without matching #if, #ifdef or #ifndef");
1239
+ }
1240
+ currentConditional.active = currentConditional.parentActive && !currentConditional.branchTaken;
1241
+ currentConditional.branchTaken = true;
1242
+ conditional = currentConditional.active;
1243
+ } else if (matchEnd) {
1244
+ conditionalStack.pop();
1245
+ conditional = conditionalStack.length ? conditionalStack[conditionalStack.length - 1].active : true;
1246
+ } else if (conditional) {
1247
+ output.push(line);
1248
+ }
1249
+ }
1250
+ if (conditionalStack.length > 0) {
1251
+ throw new Error("Unterminated conditional block in shader source");
1252
+ }
1253
+ return output.join("\n");
1254
+ }
1255
+ function evaluateIfExpression(expression, defines) {
1256
+ const trimmedExpression = expression.trim();
1257
+ if (/^[+-]?\d+(?:\.\d+)?$/.test(trimmedExpression)) {
1258
+ return Number(trimmedExpression) !== 0;
1259
+ }
1260
+ if (trimmedExpression === "true") {
1261
+ return true;
1262
+ }
1263
+ if (trimmedExpression === "false") {
1264
+ return false;
1265
+ }
1266
+ const negatedDefineMatch = trimmedExpression.match(new RegExp(`^!\\s*${DEFINE_NAME_PATTERN}$`));
1267
+ if (negatedDefineMatch) {
1268
+ return !Boolean(defines[negatedDefineMatch[1]]);
1269
+ }
1270
+ const defineMatch = trimmedExpression.match(new RegExp(`^${DEFINE_NAME_PATTERN}$`));
1271
+ if (defineMatch) {
1272
+ return Boolean(defines[defineMatch[1]]);
1273
+ }
1274
+ const definedMatch = trimmedExpression.match(new RegExp(`^defined\\s*\\(\\s*${DEFINE_NAME_PATTERN}\\s*\\)$`));
1275
+ if (definedMatch) {
1276
+ return defines[definedMatch[1]] !== void 0;
1277
+ }
1278
+ const negatedDefinedMatch = trimmedExpression.match(new RegExp(`^!\\s*defined\\s*\\(\\s*${DEFINE_NAME_PATTERN}\\s*\\)$`));
1279
+ if (negatedDefinedMatch) {
1280
+ return defines[negatedDefinedMatch[1]] === void 0;
1281
+ }
1282
+ throw new Error(`Unsupported #if expression "${expression}"`);
1283
+ }
1056
1284
 
1057
- ${DECLARATION_INJECT_MARKER}
1058
- `;
1059
- var RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
1060
- var FRAGMENT_SHADER_PROLOGUE = (
1061
- /* glsl */
1062
- `precision highp float;
1063
- `
1064
- );
1065
- function assembleWGSLShader(options) {
1066
- const modules = getShaderModuleDependencies(options.modules || []);
1067
- const { source: source4, bindingAssignments } = assembleShaderWGSL(options.platformInfo, {
1068
- ...options,
1069
- source: options.source,
1070
- stage: "vertex",
1071
- modules
1072
- });
1285
+ // dist/lib/shader-assembly/shader-plugin-vertex-inputs.js
1286
+ var import_core2 = require("@luma.gl/core");
1287
+ function getShaderPluginVertexInputDeclarationsGLSL(source4, vertexInputs) {
1288
+ const declarations = [];
1289
+ for (const [name, type] of Object.entries(vertexInputs)) {
1290
+ assertGLSLVertexInputIsNew(source4, name);
1291
+ declarations.push(`in ${getGLSLAttributeType(type)} ${name};`);
1292
+ }
1293
+ return declarations.join("\n");
1294
+ }
1295
+ function assembleShaderPluginVertexInputsWGSL(source4, vertexEntryPoint, vertexInputs) {
1296
+ const entries = Object.entries(vertexInputs);
1297
+ if (entries.length === 0) {
1298
+ return { source: source4, declarations: "", initialization: "" };
1299
+ }
1300
+ const functionRange = getWGSLFunctionParameterRange(source4, vertexEntryPoint);
1301
+ const parameterSource = source4.slice(functionRange.openParenthesis + 1, functionRange.closeParenthesis);
1302
+ const interfaceInfo = getWGSLVertexInterfaceInfo(source4, parameterSource);
1303
+ const usedLocations = new Set(interfaceInfo.locations);
1304
+ const generatedParameters = [];
1305
+ const declarations = [];
1306
+ const initialization = [];
1307
+ for (const [name, type] of entries) {
1308
+ if (interfaceInfo.names.has(name) || hasWGSLModuleVariable(source4, name)) {
1309
+ throw new Error(`ShaderPlugin vertex input "${name}" conflicts with an existing WGSL shader input or variable`);
1310
+ }
1311
+ const location = getFirstUnusedLocation(usedLocations);
1312
+ usedLocations.add(location);
1313
+ const parameterName = `_luma_${name}`;
1314
+ generatedParameters.push(`@location(${location}) ${parameterName}: ${type}`);
1315
+ declarations.push(`var<private> ${name}: ${type};`);
1316
+ initialization.push(`${name} = ${parameterName};`);
1317
+ }
1318
+ const separator = parameterSource.trim() ? ",\n " : "\n ";
1319
+ const suffix = parameterSource.trim() ? "" : "\n";
1320
+ const injectedParameters = `${parameterSource}${separator}${generatedParameters.join(",\n ")}${suffix}`;
1321
+ const transformedSource = source4.slice(0, functionRange.openParenthesis + 1) + injectedParameters + source4.slice(functionRange.closeParenthesis);
1073
1322
  return {
1074
- source: source4,
1075
- getUniforms: assembleGetUniforms(modules),
1076
- bindingAssignments,
1077
- bindingTable: getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments)
1323
+ source: transformedSource,
1324
+ declarations: declarations.join("\n"),
1325
+ initialization: initialization.join("\n")
1078
1326
  };
1079
1327
  }
1080
- function assembleGLSLShaderPair(options) {
1081
- const { vs: vs4, fs: fs5 } = options;
1082
- const modules = getShaderModuleDependencies(options.modules || []);
1083
- return {
1084
- vs: assembleShaderGLSL(options.platformInfo, {
1085
- ...options,
1086
- source: vs4,
1087
- stage: "vertex",
1088
- modules
1089
- }),
1090
- fs: assembleShaderGLSL(options.platformInfo, {
1091
- ...options,
1092
- // @ts-expect-error
1093
- source: fs5,
1094
- stage: "fragment",
1095
- modules
1096
- }),
1097
- getUniforms: assembleGetUniforms(modules)
1098
- };
1328
+ function getGLSLAttributeType(type) {
1329
+ const { primitiveType, components } = import_core2.shaderTypeDecoder.getAttributeShaderTypeInfo(type);
1330
+ const scalarType = primitiveType === "i32" ? "int" : primitiveType === "u32" ? "uint" : "float";
1331
+ if (components === 1) {
1332
+ return scalarType;
1333
+ }
1334
+ const prefix = scalarType === "int" ? "i" : scalarType === "uint" ? "u" : "";
1335
+ return `${prefix}vec${components}`;
1336
+ }
1337
+ function assertGLSLVertexInputIsNew(source4, name) {
1338
+ const escapedName = escapeRegExp2(name);
1339
+ const declaration = new RegExp(`\\b(?:in|attribute)\\s+(?:(?:lowp|mediump|highp)\\s+)?[A-Za-z_][A-Za-z0-9_]*\\s+${escapedName}\\s*(?:\\[|;)`);
1340
+ if (declaration.test(source4)) {
1341
+ throw new Error(`ShaderPlugin vertex input "${name}" conflicts with an existing GLSL input`);
1342
+ }
1343
+ }
1344
+ function getWGSLFunctionParameterRange(source4, entryPoint) {
1345
+ const functionPattern = new RegExp(`\\bfn\\s+${escapeRegExp2(entryPoint)}\\s*\\(`, "g");
1346
+ const match = functionPattern.exec(source4);
1347
+ if (!match) {
1348
+ throw new Error(`ShaderPlugin vertex inputs require WGSL vertex entry point "${entryPoint}"`);
1349
+ }
1350
+ const openParenthesis = source4.indexOf("(", match.index);
1351
+ const closeParenthesis = findMatchingDelimiter(source4, openParenthesis, "(", ")");
1352
+ if (closeParenthesis < 0) {
1353
+ throw new Error(`Unable to parse WGSL vertex entry point "${entryPoint}" parameters`);
1354
+ }
1355
+ return { openParenthesis, closeParenthesis };
1356
+ }
1357
+ function getWGSLVertexInterfaceInfo(source4, parameterSource) {
1358
+ const locations = getWGSLLocations(parameterSource);
1359
+ const names = new Set(getWGSLDeclaredNames(parameterSource));
1360
+ const parameterTypes = getWGSLParameterTypes(parameterSource);
1361
+ for (const parameterType of parameterTypes) {
1362
+ const structBody = getWGSLStructBody(source4, parameterType);
1363
+ if (structBody === null) {
1364
+ continue;
1365
+ }
1366
+ locations.push(...getWGSLLocations(structBody));
1367
+ for (const name of getWGSLDeclaredNames(structBody)) {
1368
+ names.add(name);
1369
+ }
1370
+ }
1371
+ return { locations, names };
1099
1372
  }
1100
- function assembleShaderWGSL(platformInfo, options) {
1101
- var _a;
1102
- const {
1103
- // id,
1104
- source: source4,
1105
- stage,
1106
- modules,
1107
- // defines = {},
1108
- hookFunctions = [],
1109
- inject = {},
1110
- log: log2
1111
- } = options;
1112
- assert(typeof source4 === "string", "shader source must be a string");
1113
- const coreSource = source4;
1114
- let assembledSource = "";
1115
- const hookFunctionMap = normalizeShaderHooks(hookFunctions);
1116
- const hookInjections = {};
1117
- const declInjections = {};
1118
- const mainInjections = {};
1119
- for (const key in inject) {
1120
- const injection = typeof inject[key] === "string" ? { injection: inject[key], order: 0 } : inject[key];
1121
- const match = /^(v|f)s:(#)?([\w-]+)$/.exec(key);
1122
- if (match) {
1123
- const hash = match[2];
1124
- const name = match[3];
1125
- if (hash) {
1126
- if (name === "decl") {
1127
- declInjections[key] = [injection];
1128
- } else {
1129
- mainInjections[key] = [injection];
1130
- }
1131
- } else {
1132
- hookInjections[key] = [injection];
1133
- }
1134
- } else {
1135
- mainInjections[key] = [injection];
1373
+ function getWGSLLocations(source4) {
1374
+ const locations = [];
1375
+ const locationPattern = /@location\s*\(\s*(\d+)\s*\)/g;
1376
+ let match = locationPattern.exec(source4);
1377
+ while (match) {
1378
+ locations.push(Number(match[1]));
1379
+ match = locationPattern.exec(source4);
1380
+ }
1381
+ return locations;
1382
+ }
1383
+ function getWGSLDeclaredNames(source4) {
1384
+ const names = [];
1385
+ const namePattern = /(?:^|,)\s*(?:@[A-Za-z_][\w]*(?:\([^)]*\))?\s*)*([A-Za-z_][\w]*)\s*:/gm;
1386
+ let match = namePattern.exec(source4);
1387
+ while (match) {
1388
+ names.push(match[1]);
1389
+ match = namePattern.exec(source4);
1390
+ }
1391
+ return names;
1392
+ }
1393
+ function getWGSLParameterTypes(source4) {
1394
+ const types = [];
1395
+ const typePattern = /:\s*([A-Za-z_][\w]*)\b/g;
1396
+ let match = typePattern.exec(source4);
1397
+ while (match) {
1398
+ types.push(match[1]);
1399
+ match = typePattern.exec(source4);
1400
+ }
1401
+ return types;
1402
+ }
1403
+ function getWGSLStructBody(source4, structName) {
1404
+ const structPattern = new RegExp(`\\bstruct\\s+${escapeRegExp2(structName)}\\s*\\{`, "g");
1405
+ const match = structPattern.exec(source4);
1406
+ if (!match) {
1407
+ return null;
1408
+ }
1409
+ const openBrace = source4.indexOf("{", match.index);
1410
+ const closeBrace = findMatchingDelimiter(source4, openBrace, "{", "}");
1411
+ return closeBrace < 0 ? null : source4.slice(openBrace + 1, closeBrace);
1412
+ }
1413
+ function hasWGSLModuleVariable(source4, name) {
1414
+ const escapedName = escapeRegExp2(name);
1415
+ const variablePattern = new RegExp(`\\b(?:var(?:<[^>]+>)?|let|const)\\s+${escapedName}\\b`, "g");
1416
+ let match = variablePattern.exec(source4);
1417
+ while (match) {
1418
+ if (getBraceDepthAt(source4, match.index) === 0) {
1419
+ return true;
1136
1420
  }
1421
+ match = variablePattern.exec(source4);
1137
1422
  }
1138
- const modulesToInject = modules;
1139
- const applicationRelocation = relocateWGSLApplicationBindings(coreSource);
1140
- const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(applicationRelocation.source);
1141
- const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(modulesToInject, options._bindingRegistry, usedBindingsByGroup);
1142
- const bindingAssignments = [];
1143
- for (const module2 of modulesToInject) {
1144
- if (log2) {
1145
- checkShaderModuleDeprecations(module2, coreSource, log2);
1423
+ return false;
1424
+ }
1425
+ function getFirstUnusedLocation(usedLocations) {
1426
+ let location = 0;
1427
+ while (usedLocations.has(location)) {
1428
+ location++;
1429
+ }
1430
+ return location;
1431
+ }
1432
+ function findMatchingDelimiter(source4, openIndex, openCharacter, closeCharacter) {
1433
+ let depth = 0;
1434
+ let blockCommentDepth = 0;
1435
+ let lineComment = false;
1436
+ for (let index = openIndex; index < source4.length; index++) {
1437
+ const character = source4[index];
1438
+ const nextCharacter = source4[index + 1];
1439
+ if (lineComment) {
1440
+ if (character === "\n")
1441
+ lineComment = false;
1442
+ continue;
1146
1443
  }
1147
- const relocation = relocateWGSLModuleBindings(getShaderModuleSource(module2, "wgsl", log2), module2, {
1148
- usedBindingsByGroup,
1149
- bindingRegistry: options._bindingRegistry,
1150
- reservedBindingKeysByGroup
1151
- });
1152
- bindingAssignments.push(...relocation.bindingAssignments);
1153
- const moduleSource = relocation.source;
1154
- assembledSource += moduleSource;
1155
- const injections = ((_a = module2.injections) == null ? void 0 : _a[stage]) || {};
1156
- for (const key in injections) {
1157
- const match = /^(v|f)s:#([\w-]+)$/.exec(key);
1158
- if (match) {
1159
- const name = match[2];
1160
- const injectionType = name === "decl" ? declInjections : mainInjections;
1161
- injectionType[key] = injectionType[key] || [];
1162
- injectionType[key].push(injections[key]);
1163
- } else {
1164
- hookInjections[key] = hookInjections[key] || [];
1165
- hookInjections[key].push(injections[key]);
1444
+ if (blockCommentDepth > 0) {
1445
+ if (character === "/" && nextCharacter === "*") {
1446
+ blockCommentDepth++;
1447
+ index++;
1448
+ } else if (character === "*" && nextCharacter === "/") {
1449
+ blockCommentDepth--;
1450
+ index++;
1166
1451
  }
1452
+ continue;
1453
+ }
1454
+ if (character === "/" && nextCharacter === "/") {
1455
+ lineComment = true;
1456
+ index++;
1457
+ continue;
1458
+ }
1459
+ if (character === "/" && nextCharacter === "*") {
1460
+ blockCommentDepth = 1;
1461
+ index++;
1462
+ continue;
1167
1463
  }
1464
+ if (character === openCharacter)
1465
+ depth++;
1466
+ if (character === closeCharacter && --depth === 0)
1467
+ return index;
1168
1468
  }
1169
- assembledSource += INJECT_SHADER_DECLARATIONS;
1170
- assembledSource = injectShader(assembledSource, stage, declInjections);
1171
- assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections);
1172
- assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
1173
- assembledSource += applicationRelocation.source;
1174
- assembledSource = injectShader(assembledSource, stage, mainInjections);
1175
- assertNoUnresolvedAutoBindings(assembledSource);
1176
- return { source: assembledSource, bindingAssignments };
1469
+ return -1;
1177
1470
  }
1178
- function assembleShaderGLSL(platformInfo, options) {
1179
- var _a;
1180
- const { source: source4, stage, language = "glsl", modules, defines = {}, hookFunctions = [], inject = {}, prologue = true, log: log2 } = options;
1181
- assert(typeof source4 === "string", "shader source must be a string");
1182
- const sourceVersion = language === "glsl" ? getShaderInfo(source4).version : -1;
1183
- const targetVersion = platformInfo.shaderLanguageVersion;
1184
- const sourceVersionDirective = sourceVersion === 100 ? "#version 100" : "#version 300 es";
1185
- const sourceLines = source4.split("\n");
1186
- const coreSource = sourceLines.slice(1).join("\n");
1187
- const allDefines = {};
1471
+ function getBraceDepthAt(source4, endIndex) {
1472
+ let depth = 0;
1473
+ let blockCommentDepth = 0;
1474
+ let lineComment = false;
1475
+ for (let index = 0; index < endIndex; index++) {
1476
+ const character = source4[index];
1477
+ const nextCharacter = source4[index + 1];
1478
+ if (lineComment) {
1479
+ if (character === "\n")
1480
+ lineComment = false;
1481
+ continue;
1482
+ }
1483
+ if (blockCommentDepth > 0) {
1484
+ if (character === "/" && nextCharacter === "*") {
1485
+ blockCommentDepth++;
1486
+ index++;
1487
+ } else if (character === "*" && nextCharacter === "/") {
1488
+ blockCommentDepth--;
1489
+ index++;
1490
+ }
1491
+ continue;
1492
+ }
1493
+ if (character === "/" && nextCharacter === "/") {
1494
+ lineComment = true;
1495
+ index++;
1496
+ } else if (character === "/" && nextCharacter === "*") {
1497
+ blockCommentDepth = 1;
1498
+ index++;
1499
+ } else if (character === "{") {
1500
+ depth++;
1501
+ } else if (character === "}") {
1502
+ depth--;
1503
+ }
1504
+ }
1505
+ return depth;
1506
+ }
1507
+ function escapeRegExp2(value) {
1508
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1509
+ }
1510
+
1511
+ // dist/lib/shader-assembly/shader-plugin-varyings.js
1512
+ var import_core3 = require("@luma.gl/core");
1513
+ function assembleShaderPluginVaryingsGLSL(source4, stage, varyings) {
1514
+ const declarations = [];
1515
+ const initialization = [];
1516
+ for (const [name, varying] of Object.entries(varyings)) {
1517
+ assertGLSLVaryingIsNew(source4, name);
1518
+ const qualifier = varying.interpolation === "flat" ? "flat " : "";
1519
+ const direction = stage === "vertex" ? "out" : "in";
1520
+ declarations.push(`${qualifier}${direction} ${getGLSLAttributeType(varying.type)} ${name};`);
1521
+ if (stage === "vertex") {
1522
+ initialization.push(`${name} = ${getGLSLZeroValue(varying.type)};`);
1523
+ }
1524
+ }
1525
+ return { declarations: declarations.join("\n"), initialization: initialization.join("\n") };
1526
+ }
1527
+ function assembleShaderPluginVaryingsWGSL(source4, vertexEntryPoint, fragmentEntryPoint, varyings) {
1528
+ const entries = Object.entries(varyings);
1529
+ if (entries.length === 0) {
1530
+ return {
1531
+ source: source4,
1532
+ declarations: "",
1533
+ vertexInitialization: "",
1534
+ fragmentInitialization: ""
1535
+ };
1536
+ }
1537
+ let transformedSource = source4;
1538
+ let vertexFunction = getWGSLFunctionRange(transformedSource, vertexEntryPoint, "vertex");
1539
+ const vertexOutputType = getWGSLNamedReturnType(transformedSource, vertexFunction);
1540
+ let fragmentFunction = getWGSLFunctionRange(transformedSource, fragmentEntryPoint, "fragment");
1541
+ const fragmentInput = getWGSLFragmentStructInput(transformedSource, fragmentFunction);
1542
+ const vertexOutputStruct = getWGSLStructRange(transformedSource, vertexOutputType);
1543
+ const fragmentInputStruct = getWGSLStructRange(transformedSource, fragmentInput.type);
1544
+ const interfaceNames = /* @__PURE__ */ new Set([
1545
+ ...getWGSLDeclaredNames2(vertexFunction.parameters),
1546
+ ...getWGSLDeclaredNames2(vertexOutputStruct.body),
1547
+ ...getWGSLDeclaredNames2(fragmentFunction.parameters),
1548
+ ...getWGSLDeclaredNames2(fragmentInputStruct.body)
1549
+ ]);
1550
+ const usedLocations = /* @__PURE__ */ new Set([
1551
+ ...getWGSLLocations2(vertexOutputStruct.body),
1552
+ ...getWGSLLocations2(fragmentInputStruct.body)
1553
+ ]);
1554
+ const fields = [];
1555
+ const declarations = [];
1556
+ const vertexInitialization = [];
1557
+ const fragmentInitialization = [];
1558
+ for (const [name, varying] of entries) {
1559
+ if (interfaceNames.has(name) || hasWGSLModuleVariable2(transformedSource, name)) {
1560
+ throw new Error(`ShaderPlugin varying "${name}" conflicts with existing WGSL stage I/O or a module variable`);
1561
+ }
1562
+ const location = getFirstUnusedLocation2(usedLocations);
1563
+ usedLocations.add(location);
1564
+ const interpolation = varying.interpolation === "flat" ? " @interpolate(flat)" : "";
1565
+ fields.push(` @location(${location})${interpolation} ${name}: ${varying.type},`);
1566
+ declarations.push(`var<private> ${name}: ${varying.type};`);
1567
+ vertexInitialization.push(`${name} = ${getWGSLZeroValue(varying.type)};`);
1568
+ fragmentInitialization.push(`${name} = ${fragmentInput.name}.${name};`);
1569
+ }
1570
+ assertWGSLStructConstructorsAreLocal(transformedSource, vertexOutputType, vertexFunction.openBrace, vertexFunction.closeBrace);
1571
+ transformedSource = appendWGSLConstructorArgumentsInFunction(transformedSource, vertexOutputType, vertexFunction, entries.map(([name]) => name));
1572
+ vertexFunction = getWGSLFunctionRange(transformedSource, vertexEntryPoint, "vertex");
1573
+ transformedSource = rewriteWGSLVertexReturns(transformedSource, vertexFunction, entries.map(([name]) => name));
1574
+ const structNames = vertexOutputType === fragmentInput.type ? [vertexOutputType] : [vertexOutputType, fragmentInput.type];
1575
+ const structInsertions = structNames.map((structName) => getWGSLStructRange(transformedSource, structName).closeBrace).sort((left, right) => right - left);
1576
+ for (const insertionIndex of structInsertions) {
1577
+ transformedSource = transformedSource.slice(0, insertionIndex) + `${fields.join("\n")}
1578
+ ` + transformedSource.slice(insertionIndex);
1579
+ }
1580
+ fragmentFunction = getWGSLFunctionRange(transformedSource, fragmentEntryPoint, "fragment");
1581
+ const fragmentInputPattern = new RegExp(`\\b${escapeRegExp3(fragmentInput.name)}\\s*:`);
1582
+ if (!fragmentInputPattern.test(fragmentFunction.parameters)) {
1583
+ throw new Error(`Unable to preserve WGSL fragment input "${fragmentInput.name}"`);
1584
+ }
1585
+ return {
1586
+ source: transformedSource,
1587
+ declarations: declarations.join("\n"),
1588
+ vertexInitialization: vertexInitialization.join("\n"),
1589
+ fragmentInitialization: fragmentInitialization.join("\n")
1590
+ };
1591
+ }
1592
+ function getWGSLFunctionRange(source4, entryPoint, stage) {
1593
+ const functionPattern = new RegExp(`\\bfn\\s+${escapeRegExp3(entryPoint)}\\s*\\(`, "g");
1594
+ const match = functionPattern.exec(source4);
1595
+ if (!match) {
1596
+ throw new Error(`ShaderPlugin varyings require WGSL ${stage} entry point "${entryPoint}"`);
1597
+ }
1598
+ const openParenthesis = source4.indexOf("(", match.index);
1599
+ const closeParenthesis = findMatchingDelimiter2(source4, openParenthesis, "(", ")");
1600
+ const openBrace = source4.indexOf("{", closeParenthesis);
1601
+ const closeBrace = findMatchingDelimiter2(source4, openBrace, "{", "}");
1602
+ if (closeParenthesis < 0 || openBrace < 0 || closeBrace < 0) {
1603
+ throw new Error(`Unable to parse WGSL ${stage} entry point "${entryPoint}"`);
1604
+ }
1605
+ return {
1606
+ openParenthesis,
1607
+ closeParenthesis,
1608
+ openBrace,
1609
+ closeBrace,
1610
+ parameters: source4.slice(openParenthesis + 1, closeParenthesis)
1611
+ };
1612
+ }
1613
+ function getWGSLNamedReturnType(source4, functionRange) {
1614
+ const resultSource = source4.slice(functionRange.closeParenthesis + 1, functionRange.openBrace);
1615
+ const match = /->\s*([A-Za-z_][\w]*)\s*$/.exec(resultSource.trim());
1616
+ if (!match || getWGSLStructRangeOrNull(source4, match[1]) === null) {
1617
+ throw new Error("ShaderPlugin varyings require the WGSL vertex entry point to return a named struct");
1618
+ }
1619
+ return match[1];
1620
+ }
1621
+ function getWGSLFragmentStructInput(source4, functionRange) {
1622
+ const candidates = [];
1623
+ for (const parameter of splitTopLevel(functionRange.parameters, ",")) {
1624
+ const match = /(?:@[A-Za-z_][\w]*(?:\([^)]*\))?\s*)*([A-Za-z_][\w]*)\s*:\s*([A-Za-z_][\w]*)\s*$/.exec(parameter.trim());
1625
+ if (match && getWGSLStructRangeOrNull(source4, match[2])) {
1626
+ candidates.push({ name: match[1], type: match[2] });
1627
+ }
1628
+ }
1629
+ if (candidates.length !== 1) {
1630
+ throw new Error(`ShaderPlugin varyings require exactly one named WGSL fragment input struct; found ${candidates.length}`);
1631
+ }
1632
+ return candidates[0];
1633
+ }
1634
+ function getWGSLStructRange(source4, structName) {
1635
+ const range = getWGSLStructRangeOrNull(source4, structName);
1636
+ if (!range) {
1637
+ throw new Error(`Unable to find WGSL stage I/O struct "${structName}"`);
1638
+ }
1639
+ return range;
1640
+ }
1641
+ function getWGSLStructRangeOrNull(source4, structName) {
1642
+ const structPattern = new RegExp(`\\bstruct\\s+${escapeRegExp3(structName)}\\s*\\{`, "g");
1643
+ const match = structPattern.exec(source4);
1644
+ if (!match) {
1645
+ return null;
1646
+ }
1647
+ const openBrace = source4.indexOf("{", match.index);
1648
+ const closeBrace = findMatchingDelimiter2(source4, openBrace, "{", "}");
1649
+ return closeBrace < 0 ? null : { openBrace, closeBrace, body: source4.slice(openBrace + 1, closeBrace) };
1650
+ }
1651
+ function assertWGSLStructConstructorsAreLocal(source4, structName, functionStart, functionEnd) {
1652
+ const constructorPattern = new RegExp(`\\b${escapeRegExp3(structName)}\\s*\\(`, "g");
1653
+ let match = constructorPattern.exec(source4);
1654
+ while (match) {
1655
+ if (match.index < functionStart || match.index > functionEnd) {
1656
+ throw new Error(`ShaderPlugin varying output struct "${structName}" is constructed outside the selected vertex entry point`);
1657
+ }
1658
+ match = constructorPattern.exec(source4);
1659
+ }
1660
+ }
1661
+ function appendWGSLConstructorArgumentsInFunction(source4, structName, functionRange, varyingNames) {
1662
+ const constructorPattern = new RegExp(`\\b${escapeRegExp3(structName)}\\s*\\(`, "g");
1663
+ const insertions = [];
1664
+ let match = constructorPattern.exec(source4);
1665
+ while (match) {
1666
+ if (match.index > functionRange.openBrace && match.index < functionRange.closeBrace) {
1667
+ const openParenthesis = source4.indexOf("(", match.index);
1668
+ const closeParenthesis = findMatchingDelimiter2(source4, openParenthesis, "(", ")");
1669
+ if (closeParenthesis < 0 || closeParenthesis > functionRange.closeBrace) {
1670
+ throw new Error(`Unable to parse WGSL output constructor "${structName}"`);
1671
+ }
1672
+ insertions.push({ openParenthesis, closeParenthesis });
1673
+ }
1674
+ match = constructorPattern.exec(source4);
1675
+ }
1676
+ for (const insertion of insertions.sort((left, right) => right.closeParenthesis - left.closeParenthesis)) {
1677
+ const existingArguments = source4.slice(insertion.openParenthesis + 1, insertion.closeParenthesis).trim();
1678
+ const separator = existingArguments ? ", " : "";
1679
+ source4 = source4.slice(0, insertion.closeParenthesis) + separator + varyingNames.join(", ") + source4.slice(insertion.closeParenthesis);
1680
+ }
1681
+ return source4;
1682
+ }
1683
+ function rewriteWGSLVertexReturns(source4, functionRange, varyingNames) {
1684
+ const returns = findWGSLReturnStatements(source4, functionRange.openBrace + 1, functionRange.closeBrace);
1685
+ for (let returnIndex = returns.length - 1; returnIndex >= 0; returnIndex--) {
1686
+ const statement = returns[returnIndex];
1687
+ const expression = source4.slice(statement.expressionStart, statement.semicolon).trim();
1688
+ if (!expression) {
1689
+ throw new Error("ShaderPlugin varying vertex entry point cannot use an empty return");
1690
+ }
1691
+ const outputName = `_luma_vertexOutput${returnIndex}`;
1692
+ const assignments = varyingNames.map((name) => `${outputName}.${name} = ${name};`).join("\n");
1693
+ const replacement = `{
1694
+ var ${outputName} = ${expression};
1695
+ ${assignments}
1696
+ return ${outputName};
1697
+ }`;
1698
+ source4 = source4.slice(0, statement.start) + replacement + source4.slice(statement.semicolon + 1);
1699
+ }
1700
+ return source4;
1701
+ }
1702
+ function findWGSLReturnStatements(source4, startIndex, endIndex) {
1703
+ const statements = [];
1704
+ let index = startIndex;
1705
+ while (index < endIndex) {
1706
+ index = skipWGSLTrivia(source4, index, endIndex);
1707
+ if (source4.slice(index, index + 6) === "return" && !/[A-Za-z0-9_]/.test(source4[index + 6] || "")) {
1708
+ const expressionStart = index + 6;
1709
+ const semicolon = findStatementSemicolon(source4, expressionStart, endIndex);
1710
+ if (semicolon < 0) {
1711
+ throw new Error("Unable to parse WGSL return statement in selected vertex entry point");
1712
+ }
1713
+ statements.push({ start: index, expressionStart, semicolon });
1714
+ index = semicolon + 1;
1715
+ } else {
1716
+ index++;
1717
+ }
1718
+ }
1719
+ return statements;
1720
+ }
1721
+ function findStatementSemicolon(source4, startIndex, endIndex) {
1722
+ let parenthesisDepth = 0;
1723
+ let bracketDepth = 0;
1724
+ for (let index = startIndex; index < endIndex; index++) {
1725
+ const skippedIndex = skipWGSLTrivia(source4, index, endIndex);
1726
+ if (skippedIndex !== index) {
1727
+ index = skippedIndex - 1;
1728
+ continue;
1729
+ }
1730
+ const character = source4[index];
1731
+ if (character === "(")
1732
+ parenthesisDepth++;
1733
+ if (character === ")")
1734
+ parenthesisDepth--;
1735
+ if (character === "[")
1736
+ bracketDepth++;
1737
+ if (character === "]")
1738
+ bracketDepth--;
1739
+ if (character === ";" && parenthesisDepth === 0 && bracketDepth === 0)
1740
+ return index;
1741
+ }
1742
+ return -1;
1743
+ }
1744
+ function skipWGSLTrivia(source4, startIndex, endIndex) {
1745
+ let index = startIndex;
1746
+ if (source4[index] === "/" && source4[index + 1] === "/") {
1747
+ const newline = source4.indexOf("\n", index + 2);
1748
+ return newline < 0 || newline > endIndex ? endIndex : newline + 1;
1749
+ }
1750
+ if (source4[index] === "/" && source4[index + 1] === "*") {
1751
+ let depth = 1;
1752
+ index += 2;
1753
+ while (index < endIndex && depth > 0) {
1754
+ if (source4[index] === "/" && source4[index + 1] === "*") {
1755
+ depth++;
1756
+ index += 2;
1757
+ } else if (source4[index] === "*" && source4[index + 1] === "/") {
1758
+ depth--;
1759
+ index += 2;
1760
+ } else {
1761
+ index++;
1762
+ }
1763
+ }
1764
+ }
1765
+ return index;
1766
+ }
1767
+ function splitTopLevel(source4, delimiter) {
1768
+ const parts = [];
1769
+ let startIndex = 0;
1770
+ let parenthesisDepth = 0;
1771
+ let angleDepth = 0;
1772
+ for (let index = 0; index < source4.length; index++) {
1773
+ const character = source4[index];
1774
+ if (character === "(")
1775
+ parenthesisDepth++;
1776
+ if (character === ")")
1777
+ parenthesisDepth--;
1778
+ if (character === "<")
1779
+ angleDepth++;
1780
+ if (character === ">")
1781
+ angleDepth--;
1782
+ if (character === delimiter && parenthesisDepth === 0 && angleDepth === 0) {
1783
+ parts.push(source4.slice(startIndex, index));
1784
+ startIndex = index + 1;
1785
+ }
1786
+ }
1787
+ parts.push(source4.slice(startIndex));
1788
+ return parts;
1789
+ }
1790
+ function getWGSLLocations2(source4) {
1791
+ const locations = [];
1792
+ const locationPattern = /@location\s*\(\s*(\d+)\s*\)/g;
1793
+ let match = locationPattern.exec(source4);
1794
+ while (match) {
1795
+ locations.push(Number(match[1]));
1796
+ match = locationPattern.exec(source4);
1797
+ }
1798
+ return locations;
1799
+ }
1800
+ function getWGSLDeclaredNames2(source4) {
1801
+ const names = [];
1802
+ const namePattern = /(?:^|,)\s*(?:@[A-Za-z_][\w]*(?:\([^)]*\))?\s*)*([A-Za-z_][\w]*)\s*:/gm;
1803
+ let match = namePattern.exec(source4);
1804
+ while (match) {
1805
+ names.push(match[1]);
1806
+ match = namePattern.exec(source4);
1807
+ }
1808
+ return names;
1809
+ }
1810
+ function hasWGSLModuleVariable2(source4, name) {
1811
+ const variablePattern = new RegExp(`\\b(?:var(?:<[^>]+>)?|let|const)\\s+${escapeRegExp3(name)}\\b`, "g");
1812
+ let match = variablePattern.exec(source4);
1813
+ while (match) {
1814
+ if (getBraceDepthAt2(source4, match.index) === 0) {
1815
+ return true;
1816
+ }
1817
+ match = variablePattern.exec(source4);
1818
+ }
1819
+ return false;
1820
+ }
1821
+ function getFirstUnusedLocation2(usedLocations) {
1822
+ let location = 0;
1823
+ while (usedLocations.has(location))
1824
+ location++;
1825
+ return location;
1826
+ }
1827
+ function getGLSLZeroValue(type) {
1828
+ const { primitiveType, components } = import_core3.shaderTypeDecoder.getAttributeShaderTypeInfo(type);
1829
+ const scalar = primitiveType === "u32" ? "0u" : primitiveType === "i32" ? "0" : "0.0";
1830
+ return components === 1 ? scalar : `${getGLSLAttributeType(type)}(${scalar})`;
1831
+ }
1832
+ function getWGSLZeroValue(type) {
1833
+ const { primitiveType, components } = import_core3.shaderTypeDecoder.getAttributeShaderTypeInfo(type);
1834
+ const scalar = `${primitiveType}(0)`;
1835
+ return components === 1 ? scalar : `${type}(${scalar})`;
1836
+ }
1837
+ function assertGLSLVaryingIsNew(source4, name) {
1838
+ const declaration = new RegExp(`\\b(?:flat\\s+|smooth\\s+)?(?:in|out|varying)\\s+(?:(?:lowp|mediump|highp)\\s+)?[A-Za-z_][A-Za-z0-9_]*\\s+${escapeRegExp3(name)}\\s*(?:\\[|;)`);
1839
+ if (declaration.test(source4)) {
1840
+ throw new Error(`ShaderPlugin varying "${name}" conflicts with existing GLSL stage I/O`);
1841
+ }
1842
+ }
1843
+ function findMatchingDelimiter2(source4, openIndex, openCharacter, closeCharacter) {
1844
+ let depth = 0;
1845
+ let blockCommentDepth = 0;
1846
+ let lineComment = false;
1847
+ for (let index = openIndex; index < source4.length; index++) {
1848
+ const character = source4[index];
1849
+ const nextCharacter = source4[index + 1];
1850
+ if (lineComment) {
1851
+ if (character === "\n")
1852
+ lineComment = false;
1853
+ continue;
1854
+ }
1855
+ if (blockCommentDepth > 0) {
1856
+ if (character === "/" && nextCharacter === "*") {
1857
+ blockCommentDepth++;
1858
+ index++;
1859
+ } else if (character === "*" && nextCharacter === "/") {
1860
+ blockCommentDepth--;
1861
+ index++;
1862
+ }
1863
+ continue;
1864
+ }
1865
+ if (character === "/" && nextCharacter === "/") {
1866
+ lineComment = true;
1867
+ index++;
1868
+ continue;
1869
+ }
1870
+ if (character === "/" && nextCharacter === "*") {
1871
+ blockCommentDepth = 1;
1872
+ index++;
1873
+ continue;
1874
+ }
1875
+ if (character === openCharacter)
1876
+ depth++;
1877
+ if (character === closeCharacter && --depth === 0)
1878
+ return index;
1879
+ }
1880
+ return -1;
1881
+ }
1882
+ function getBraceDepthAt2(source4, endIndex) {
1883
+ let depth = 0;
1884
+ for (let index = 0; index < endIndex; index++) {
1885
+ const skippedIndex = skipWGSLTrivia(source4, index, endIndex);
1886
+ if (skippedIndex !== index) {
1887
+ index = skippedIndex - 1;
1888
+ continue;
1889
+ }
1890
+ if (source4[index] === "{")
1891
+ depth++;
1892
+ if (source4[index] === "}")
1893
+ depth--;
1894
+ }
1895
+ return depth;
1896
+ }
1897
+ function escapeRegExp3(value) {
1898
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1899
+ }
1900
+
1901
+ // dist/lib/shader-assembly/assemble-shaders.js
1902
+ var INJECT_SHADER_DECLARATIONS = `
1903
+
1904
+ ${DECLARATION_INJECT_MARKER}
1905
+ `;
1906
+ var RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
1907
+ var FRAGMENT_SHADER_PROLOGUE = (
1908
+ /* glsl */
1909
+ `precision highp float;
1910
+ `
1911
+ );
1912
+ function assembleWGSLShader(options) {
1913
+ const modules = getShaderModuleDependencies(options.modules || []);
1914
+ const { source: source4, bindingAssignments } = assembleShaderWGSL(options.platformInfo, {
1915
+ ...options,
1916
+ source: options.source,
1917
+ stage: "vertex",
1918
+ modules
1919
+ });
1920
+ return {
1921
+ source: source4,
1922
+ getUniforms: assembleGetUniforms(modules),
1923
+ bindingAssignments,
1924
+ bindingTable: getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments)
1925
+ };
1926
+ }
1927
+ function assembleGLSLShaderPair(options) {
1928
+ const { vs: vs4, fs: fs5 } = options;
1929
+ const modules = getShaderModuleDependencies(options.modules || []);
1930
+ return {
1931
+ vs: assembleShaderGLSL(options.platformInfo, {
1932
+ ...options,
1933
+ source: vs4,
1934
+ stage: "vertex",
1935
+ modules
1936
+ }),
1937
+ fs: assembleShaderGLSL(options.platformInfo, {
1938
+ ...options,
1939
+ // @ts-expect-error
1940
+ source: fs5,
1941
+ stage: "fragment",
1942
+ modules
1943
+ }),
1944
+ getUniforms: assembleGetUniforms(modules)
1945
+ };
1946
+ }
1947
+ function assembleShaderWGSL(platformInfo, options) {
1948
+ const {
1949
+ // id,
1950
+ source: source4,
1951
+ stage,
1952
+ modules,
1953
+ // defines = {},
1954
+ defines = {},
1955
+ hookFunctions = [],
1956
+ inject = {},
1957
+ pluginInjections = {},
1958
+ pluginVertexInputs = {},
1959
+ pluginVaryings = {},
1960
+ vertexEntryPoint = "vertexMain",
1961
+ fragmentEntryPoint = "fragmentMain",
1962
+ log: log2
1963
+ } = options;
1964
+ assert(typeof source4 === "string", "shader source must be a string");
1965
+ const preprocessedSource = preprocess(source4, { defines });
1966
+ const pluginVertexInputAssembly = assembleShaderPluginVertexInputsWGSL(preprocessedSource, vertexEntryPoint, pluginVertexInputs);
1967
+ const pluginVaryingAssembly = assembleShaderPluginVaryingsWGSL(pluginVertexInputAssembly.source, vertexEntryPoint, fragmentEntryPoint, pluginVaryings);
1968
+ const coreSource = pluginVaryingAssembly.source;
1969
+ let assembledSource = "";
1970
+ const hookFunctionMap = normalizeShaderHooks(hookFunctions);
1971
+ const hookInjections = {};
1972
+ const declInjections = {};
1973
+ const mainInjections = {};
1974
+ appendInjections(pluginInjections, hookInjections, declInjections, mainInjections);
1975
+ for (const key in inject) {
1976
+ const injection = typeof inject[key] === "string" ? { injection: inject[key], order: 0 } : inject[key];
1977
+ const match = /^(v|f)s:(#)?([\w-]+)$/.exec(key);
1978
+ if (match) {
1979
+ const hash = match[2];
1980
+ const name = match[3];
1981
+ if (hash) {
1982
+ if (name === "decl") {
1983
+ declInjections[key] = [injection];
1984
+ } else {
1985
+ mainInjections[key] = [injection];
1986
+ }
1987
+ } else {
1988
+ hookInjections[key] = [injection];
1989
+ }
1990
+ } else {
1991
+ mainInjections[key] = [injection];
1992
+ }
1993
+ }
1994
+ appendGeneratedVertexInputInjections(pluginVertexInputAssembly.declarations, pluginVertexInputAssembly.initialization, declInjections, mainInjections);
1995
+ appendGeneratedVaryingInjections(pluginVaryingAssembly, declInjections, mainInjections);
1996
+ const modulesToInject = modules;
1997
+ const applicationRelocation = relocateWGSLApplicationBindings(coreSource);
1998
+ const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(applicationRelocation.source);
1999
+ const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(modulesToInject, options._bindingRegistry, usedBindingsByGroup, defines);
2000
+ const bindingAssignments = [];
2001
+ for (const module2 of modulesToInject) {
2002
+ if (log2) {
2003
+ checkShaderModuleDeprecations(module2, coreSource, log2);
2004
+ }
2005
+ const preprocessedModuleSource = preprocess(getShaderModuleSource(module2, "wgsl", log2), {
2006
+ defines
2007
+ });
2008
+ const relocation = relocateWGSLModuleBindings(preprocessedModuleSource, module2, {
2009
+ usedBindingsByGroup,
2010
+ bindingRegistry: options._bindingRegistry,
2011
+ reservedBindingKeysByGroup
2012
+ });
2013
+ bindingAssignments.push(...relocation.bindingAssignments);
2014
+ const relocatedModuleSource = relocation.source;
2015
+ assembledSource += relocatedModuleSource;
2016
+ const injections = getWGSLModuleInjections(module2);
2017
+ for (const key in injections) {
2018
+ const match = /^(v|f)s:#([\w-]+)$/.exec(key);
2019
+ if (match) {
2020
+ const name = match[2];
2021
+ const injectionType = name === "decl" ? declInjections : mainInjections;
2022
+ injectionType[key] = injectionType[key] || [];
2023
+ injectionType[key].push(injections[key]);
2024
+ } else {
2025
+ hookInjections[key] = hookInjections[key] || [];
2026
+ hookInjections[key].push(injections[key]);
2027
+ }
2028
+ }
2029
+ }
2030
+ assembledSource += INJECT_SHADER_DECLARATIONS;
2031
+ assembledSource = injectShader(assembledSource, stage, getWGSLDeclarationInjections(declInjections), false, "wgsl", { vertex: vertexEntryPoint, fragment: fragmentEntryPoint });
2032
+ assembledSource += getWGSLShaderHooks(hookFunctionMap, hookInjections);
2033
+ assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
2034
+ assembledSource += applicationRelocation.source;
2035
+ assembledSource = injectShader(assembledSource, stage, mainInjections, false, "wgsl", {
2036
+ vertex: vertexEntryPoint,
2037
+ fragment: fragmentEntryPoint
2038
+ });
2039
+ assertNoUnresolvedAutoBindings(assembledSource);
2040
+ return { source: assembledSource, bindingAssignments };
2041
+ }
2042
+ function assembleShaderGLSL(platformInfo, options) {
2043
+ var _a;
2044
+ const { source: source4, stage, language = "glsl", modules, defines = {}, hookFunctions = [], inject = {}, pluginInjections = {}, pluginVertexInputs = {}, pluginVaryings = {}, prologue = true, log: log2 } = options;
2045
+ assert(typeof source4 === "string", "shader source must be a string");
2046
+ const sourceVersion = language === "glsl" ? getShaderInfo(source4).version : -1;
2047
+ const targetVersion = platformInfo.shaderLanguageVersion;
2048
+ const sourceVersionDirective = sourceVersion === 100 ? "#version 100" : "#version 300 es";
2049
+ const sourceLines = source4.split("\n");
2050
+ const coreSource = sourceLines.slice(1).join("\n");
2051
+ const allDefines = {};
1188
2052
  modules.forEach((module2) => {
1189
2053
  Object.assign(allDefines, module2.defines);
1190
2054
  });
@@ -1214,6 +2078,7 @@ ${getApplicationDefines(allDefines)}
1214
2078
  const hookInjections = {};
1215
2079
  const declInjections = {};
1216
2080
  const mainInjections = {};
2081
+ appendInjections(pluginInjections, hookInjections, declInjections, mainInjections);
1217
2082
  for (const key in inject) {
1218
2083
  const injection = typeof inject[key] === "string" ? { injection: inject[key], order: 0 } : inject[key];
1219
2084
  const match = /^(v|f)s:(#)?([\w-]+)$/.exec(key);
@@ -1233,6 +2098,32 @@ ${getApplicationDefines(allDefines)}
1233
2098
  mainInjections[key] = [injection];
1234
2099
  }
1235
2100
  }
2101
+ if (stage === "vertex") {
2102
+ const declarations = getShaderPluginVertexInputDeclarationsGLSL(coreSource, pluginVertexInputs);
2103
+ if (declarations) {
2104
+ declInjections["vs:#decl"] = declInjections["vs:#decl"] || [];
2105
+ declInjections["vs:#decl"].push({
2106
+ injection: declarations,
2107
+ order: Number.MIN_SAFE_INTEGER
2108
+ });
2109
+ }
2110
+ }
2111
+ const pluginVaryingAssembly = assembleShaderPluginVaryingsGLSL(coreSource, stage, pluginVaryings);
2112
+ if (pluginVaryingAssembly.declarations) {
2113
+ const declarationTarget = stage === "vertex" ? "vs:#decl" : "fs:#decl";
2114
+ declInjections[declarationTarget] = declInjections[declarationTarget] || [];
2115
+ declInjections[declarationTarget].push({
2116
+ injection: pluginVaryingAssembly.declarations,
2117
+ order: Number.MIN_SAFE_INTEGER
2118
+ });
2119
+ }
2120
+ if (pluginVaryingAssembly.initialization) {
2121
+ mainInjections["vs:#main-start"] = mainInjections["vs:#main-start"] || [];
2122
+ mainInjections["vs:#main-start"].push({
2123
+ injection: pluginVaryingAssembly.initialization,
2124
+ order: Number.MIN_SAFE_INTEGER
2125
+ });
2126
+ }
1236
2127
  for (const module2 of modules) {
1237
2128
  if (log2) {
1238
2129
  checkShaderModuleDeprecations(module2, coreSource, log2);
@@ -1278,23 +2169,94 @@ function assembleGetUniforms(modules) {
1278
2169
  return uniforms;
1279
2170
  };
1280
2171
  }
1281
- function getApplicationDefines(defines = {}) {
1282
- let sourceText = "";
1283
- for (const define in defines) {
1284
- const value = defines[define];
1285
- if (value || Number.isFinite(value)) {
1286
- sourceText += `#define ${define.toUpperCase()} ${defines[define]}
1287
- `;
2172
+ function appendInjections(injections, hookInjections, declInjections, mainInjections) {
2173
+ for (const key in injections) {
2174
+ const match = /^(v|f)s:(#)?([\w-]+)$/.exec(key);
2175
+ if (match) {
2176
+ const hash = match[2];
2177
+ const name = match[3];
2178
+ const injectionType = hash ? name === "decl" ? declInjections : mainInjections : hookInjections;
2179
+ injectionType[key] = injectionType[key] || [];
2180
+ injectionType[key].push(...injections[key]);
2181
+ } else {
2182
+ mainInjections[key] = mainInjections[key] || [];
2183
+ mainInjections[key].push(...injections[key]);
1288
2184
  }
1289
2185
  }
1290
- return sourceText;
1291
2186
  }
1292
- function getShaderModuleSource(module2, stage, log2) {
1293
- let moduleSource;
1294
- switch (stage) {
1295
- case "vertex":
1296
- moduleSource = module2.vs || "";
1297
- break;
2187
+ function appendGeneratedVertexInputInjections(declarations, initialization, declarationInjections, mainInjections) {
2188
+ if (declarations) {
2189
+ declarationInjections["vs:#decl"] = declarationInjections["vs:#decl"] || [];
2190
+ declarationInjections["vs:#decl"].push({
2191
+ injection: declarations,
2192
+ order: Number.MIN_SAFE_INTEGER
2193
+ });
2194
+ }
2195
+ if (initialization) {
2196
+ mainInjections["vs:#main-start"] = mainInjections["vs:#main-start"] || [];
2197
+ mainInjections["vs:#main-start"].push({
2198
+ injection: initialization,
2199
+ order: Number.MIN_SAFE_INTEGER
2200
+ });
2201
+ }
2202
+ }
2203
+ function appendGeneratedVaryingInjections(assembly, declarationInjections, mainInjections) {
2204
+ if (assembly.declarations) {
2205
+ declarationInjections["vs:#decl"] = declarationInjections["vs:#decl"] || [];
2206
+ declarationInjections["vs:#decl"].push({
2207
+ injection: assembly.declarations,
2208
+ order: Number.MIN_SAFE_INTEGER
2209
+ });
2210
+ }
2211
+ if (assembly.vertexInitialization) {
2212
+ mainInjections["vs:#main-start"] = mainInjections["vs:#main-start"] || [];
2213
+ mainInjections["vs:#main-start"].push({
2214
+ injection: assembly.vertexInitialization,
2215
+ order: Number.MIN_SAFE_INTEGER
2216
+ });
2217
+ }
2218
+ if (assembly.fragmentInitialization) {
2219
+ mainInjections["fs:#main-start"] = mainInjections["fs:#main-start"] || [];
2220
+ mainInjections["fs:#main-start"].push({
2221
+ injection: assembly.fragmentInitialization,
2222
+ order: Number.MIN_SAFE_INTEGER
2223
+ });
2224
+ }
2225
+ }
2226
+ function getWGSLModuleInjections(module2) {
2227
+ var _a, _b;
2228
+ return {
2229
+ ...((_a = module2.instance) == null ? void 0 : _a.normalizedInjections.vertex) || {},
2230
+ ...((_b = module2.instance) == null ? void 0 : _b.normalizedInjections.fragment) || {}
2231
+ };
2232
+ }
2233
+ function getWGSLDeclarationInjections(declarationInjections) {
2234
+ const unifiedDeclarations = [
2235
+ ...declarationInjections["vs:#decl"] || [],
2236
+ ...declarationInjections["fs:#decl"] || []
2237
+ ];
2238
+ return unifiedDeclarations.length ? { "vs:#decl": unifiedDeclarations } : {};
2239
+ }
2240
+ function getWGSLShaderHooks(hookFunctionMap, hookInjections) {
2241
+ return getShaderHooks(hookFunctionMap.vertex, hookInjections, "wgsl") + getShaderHooks(hookFunctionMap.fragment, hookInjections, "wgsl");
2242
+ }
2243
+ function getApplicationDefines(defines = {}) {
2244
+ let sourceText = "";
2245
+ for (const define in defines) {
2246
+ const value = defines[define];
2247
+ if (value || Number.isFinite(value)) {
2248
+ sourceText += `#define ${define.toUpperCase()} ${defines[define]}
2249
+ `;
2250
+ }
2251
+ }
2252
+ return sourceText;
2253
+ }
2254
+ function getShaderModuleSource(module2, stage, log2) {
2255
+ let moduleSource;
2256
+ switch (stage) {
2257
+ case "vertex":
2258
+ moduleSource = module2.vs || "";
2259
+ break;
1298
2260
  case "fragment":
1299
2261
  moduleSource = module2.fs || "";
1300
2262
  break;
@@ -1409,13 +2371,13 @@ function relocateWGSLApplicationBindingMatch(declarationMatch, usedBindingsByGro
1409
2371
  relocationState.sawSupportedBindingDeclaration = true;
1410
2372
  return match;
1411
2373
  }
1412
- function reserveRegisteredModuleBindings(modules, bindingRegistry, usedBindingsByGroup) {
2374
+ function reserveRegisteredModuleBindings(modules, bindingRegistry, usedBindingsByGroup, defines) {
1413
2375
  const reservedBindingKeysByGroup = /* @__PURE__ */ new Map();
1414
2376
  if (!bindingRegistry) {
1415
2377
  return reservedBindingKeysByGroup;
1416
2378
  }
1417
2379
  for (const module2 of modules) {
1418
- for (const binding of getModuleWGSLBindingDeclarations(module2)) {
2380
+ for (const binding of getModuleWGSLBindingDeclarations(module2, defines)) {
1419
2381
  const registryKey = getBindingRegistryKey(binding.group, module2.name, binding.name);
1420
2382
  const location = bindingRegistry.get(registryKey);
1421
2383
  if (location !== void 0) {
@@ -1446,9 +2408,9 @@ function claimReservedBindingLocation(reservedBindingKeysByGroup, group, locatio
1446
2408
  }
1447
2409
  return true;
1448
2410
  }
1449
- function getModuleWGSLBindingDeclarations(module2) {
2411
+ function getModuleWGSLBindingDeclarations(module2, defines) {
1450
2412
  const declarations = [];
1451
- const moduleSource = module2.source || "";
2413
+ const moduleSource = preprocess(module2.source || "", { defines });
1452
2414
  for (const match of getWGSLBindingDeclarationMatches(moduleSource, MODULE_WGSL_BINDING_DECLARATION_REGEXES)) {
1453
2415
  declarations.push({
1454
2416
  name: match.name,
@@ -1539,53 +2501,6 @@ function formatWGSLSourceSnippet(source4) {
1539
2501
  return source4.replace(/\s+/g, " ").trim();
1540
2502
  }
1541
2503
 
1542
- // dist/lib/preprocessor/preprocessor.js
1543
- var DEFINE_NAME_PATTERN = "([a-zA-Z_][a-zA-Z0-9_]*)";
1544
- var IFDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*$`);
1545
- var IFNDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifndef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
1546
- var ELSE_REGEXP = /^\s*\#\s*else\s*(?:\/\/.*)?$/;
1547
- var ENDIF_REGEXP = /^\s*\#\s*endif\s*$/;
1548
- var IFDEF_WITH_COMMENT_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
1549
- var ENDIF_WITH_COMMENT_REGEXP = /^\s*\#\s*endif\s*(?:\/\/.*)?$/;
1550
- function preprocess(source4, options) {
1551
- var _a, _b;
1552
- const lines = source4.split("\n");
1553
- const output = [];
1554
- const conditionalStack = [];
1555
- let conditional = true;
1556
- for (const line of lines) {
1557
- const matchIf = line.match(IFDEF_WITH_COMMENT_REGEXP) || line.match(IFDEF_REGEXP);
1558
- const matchIfNot = line.match(IFNDEF_REGEXP);
1559
- const matchElse = line.match(ELSE_REGEXP);
1560
- const matchEnd = line.match(ENDIF_WITH_COMMENT_REGEXP) || line.match(ENDIF_REGEXP);
1561
- if (matchIf || matchIfNot) {
1562
- const defineName = (_a = matchIf || matchIfNot) == null ? void 0 : _a[1];
1563
- const defineValue = Boolean((_b = options == null ? void 0 : options.defines) == null ? void 0 : _b[defineName]);
1564
- const branchTaken = matchIf ? defineValue : !defineValue;
1565
- const active = conditional && branchTaken;
1566
- conditionalStack.push({ parentActive: conditional, branchTaken, active });
1567
- conditional = active;
1568
- } else if (matchElse) {
1569
- const currentConditional = conditionalStack[conditionalStack.length - 1];
1570
- if (!currentConditional) {
1571
- throw new Error("Encountered #else without matching #ifdef or #ifndef");
1572
- }
1573
- currentConditional.active = currentConditional.parentActive && !currentConditional.branchTaken;
1574
- currentConditional.branchTaken = true;
1575
- conditional = currentConditional.active;
1576
- } else if (matchEnd) {
1577
- conditionalStack.pop();
1578
- conditional = conditionalStack.length ? conditionalStack[conditionalStack.length - 1].active : true;
1579
- } else if (conditional) {
1580
- output.push(line);
1581
- }
1582
- }
1583
- if (conditionalStack.length > 0) {
1584
- throw new Error("Unterminated conditional block in shader source");
1585
- }
1586
- return output.join("\n");
1587
- }
1588
-
1589
2504
  // dist/lib/shader-assembler.js
1590
2505
  var _ShaderAssembler = class {
1591
2506
  /** Hook functions */
@@ -1596,14 +2511,15 @@ var _ShaderAssembler = class {
1596
2511
  _wgslBindingRegistry = /* @__PURE__ */ new Map();
1597
2512
  /**
1598
2513
  * A default shader assembler instance - the natural place to register default modules and hooks
1599
- * @returns
2514
+ * @returns Shared default shader assembler.
1600
2515
  */
1601
2516
  static getDefaultShaderAssembler() {
1602
2517
  _ShaderAssembler.defaultShaderAssembler = _ShaderAssembler.defaultShaderAssembler || new _ShaderAssembler();
1603
2518
  return _ShaderAssembler.defaultShaderAssembler;
1604
2519
  }
1605
2520
  /**
1606
- * Add a default module that does not have to be provided with every call to assembleShaders()
2521
+ * Add a default module that does not have to be provided with every assembly call.
2522
+ * @param module Shader module to include in later assembly calls.
1607
2523
  */
1608
2524
  addDefaultModule(module2) {
1609
2525
  if (!this._defaultModules.find((m) => m.name === (typeof module2 === "string" ? module2 : module2.name))) {
@@ -1612,6 +2528,7 @@ var _ShaderAssembler = class {
1612
2528
  }
1613
2529
  /**
1614
2530
  * Remove a default module
2531
+ * @param module Shader module to remove from later assembly calls.
1615
2532
  */
1616
2533
  removeDefaultModule(module2) {
1617
2534
  const moduleName = typeof module2 === "string" ? module2 : module2.name;
@@ -1619,8 +2536,8 @@ var _ShaderAssembler = class {
1619
2536
  }
1620
2537
  /**
1621
2538
  * Register a shader hook
1622
- * @param hook
1623
- * @param opts
2539
+ * @param hook Stage-prefixed hook signature, such as `vs:OFFSET_POSITION(inout vec4 position)`.
2540
+ * @param opts Optional hook metadata such as always-on header and footer source.
1624
2541
  */
1625
2542
  addShaderHook(hook, opts) {
1626
2543
  if (opts) {
@@ -1630,29 +2547,24 @@ var _ShaderAssembler = class {
1630
2547
  }
1631
2548
  /**
1632
2549
  * Assemble a WGSL unified shader
1633
- * @param platformInfo
1634
- * @param props
1635
- * @returns
2550
+ * @param props WGSL source, platform information, shader modules, defines, and injections.
2551
+ * @returns Assembled WGSL source, resolved modules, uniforms, and binding debug metadata.
1636
2552
  */
1637
2553
  assembleWGSLShader(props) {
1638
2554
  const modules = this._getModuleList(props.modules);
1639
2555
  const hookFunctions = this._hookFunctions;
1640
- const { source: source4, getUniforms: getUniforms4, bindingAssignments } = assembleWGSLShader({
2556
+ const defines = getShaderPreprocessorDefines(props, modules);
2557
+ const preprocessedApplicationSource = props.platformInfo.shaderLanguage === "wgsl" && props.source ? preprocess(props.source, { defines }) : props.source;
2558
+ const { source: assembledSource, getUniforms: getUniforms4, bindingAssignments } = assembleWGSLShader({
1641
2559
  ...props,
1642
2560
  // @ts-expect-error
1643
- source: props.source,
2561
+ source: preprocessedApplicationSource,
2562
+ defines,
1644
2563
  _bindingRegistry: this._wgslBindingRegistry,
1645
2564
  modules,
1646
2565
  hookFunctions
1647
2566
  });
1648
- const defines = {
1649
- ...modules.reduce((accumulator, module2) => {
1650
- Object.assign(accumulator, module2.defines);
1651
- return accumulator;
1652
- }, {}),
1653
- ...props.defines
1654
- };
1655
- const preprocessedSource = props.platformInfo.shaderLanguage === "wgsl" ? preprocess(source4, { defines }) : source4;
2567
+ const preprocessedSource = props.platformInfo.shaderLanguage === "wgsl" ? preprocess(assembledSource, { defines }) : assembledSource;
1656
2568
  return {
1657
2569
  source: preprocessedSource,
1658
2570
  getUniforms: getUniforms4,
@@ -1663,9 +2575,8 @@ var _ShaderAssembler = class {
1663
2575
  }
1664
2576
  /**
1665
2577
  * Assemble a pair of shaders into a single shader program
1666
- * @param platformInfo
1667
- * @param props
1668
- * @returns
2578
+ * @param props GLSL vertex and fragment source, platform information, shader modules, defines, and injections.
2579
+ * @returns Assembled GLSL source, resolved modules, and combined uniform getter.
1669
2580
  */
1670
2581
  assembleGLSLShaderPair(props) {
1671
2582
  const modules = this._getModuleList(props.modules);
@@ -1710,6 +2621,22 @@ var _ShaderAssembler = class {
1710
2621
  var ShaderAssembler = _ShaderAssembler;
1711
2622
  /** Default ShaderAssembler instance */
1712
2623
  __publicField(ShaderAssembler, "defaultShaderAssembler");
2624
+ function getShaderPreprocessorDefines(props, modules) {
2625
+ return {
2626
+ ...getPlatformPreprocessorDefines(props.platformInfo),
2627
+ ...modules.reduce((accumulator, module2) => {
2628
+ Object.assign(accumulator, module2.defines);
2629
+ return accumulator;
2630
+ }, {}),
2631
+ ...props.defines
2632
+ };
2633
+ }
2634
+ function getPlatformPreprocessorDefines(platformInfo) {
2635
+ const limits = platformInfo.limits || {};
2636
+ return {
2637
+ LUMA_SUPPORTS_VERTEX_STORAGE_BUFFERS: platformInfo.type === "webgpu" && (limits["maxStorageBuffersInVertexStage"] || 0) > 0
2638
+ };
2639
+ }
1713
2640
 
1714
2641
  // dist/lib/glsl-utils/shader-utils.js
1715
2642
  var FS_GLES = (
@@ -1909,14 +2836,14 @@ function generateShaderForModule(module2, options) {
1909
2836
  }
1910
2837
 
1911
2838
  // dist/modules/math/fp16/fp16-utils.js
1912
- var import_core = require("@math.gl/core");
2839
+ var import_core4 = require("@math.gl/core");
1913
2840
  var float16Tables = null;
1914
2841
  var buffer = new ArrayBuffer(4);
1915
2842
  var floatView = new Float32Array(buffer);
1916
2843
  var uint32View = new Uint32Array(buffer);
1917
2844
  function toHalfFloat(val) {
1918
2845
  float16Tables ||= generateFloat16Tables();
1919
- val = (0, import_core.clamp)(val, -65504, 65504);
2846
+ val = (0, import_core4.clamp)(val, -65504, 65504);
1920
2847
  floatView[0] = val;
1921
2848
  const f = uint32View[0];
1922
2849
  const e = f >> 23 & 511;
@@ -2435,11 +3362,278 @@ var fp64arithmeticWGSL = (
2435
3362
 
2436
3363
  @group(0) @binding(auto) var<uniform> fp64arithmetic : Fp64ArithmeticUniforms;
2437
3364
 
3365
+ struct Fp64Bits {
3366
+ sign: u32,
3367
+ exponent: i32,
3368
+ significand: vec2u,
3369
+ isZero: bool,
3370
+ isInf: bool,
3371
+ isNan: bool,
3372
+ };
3373
+
2438
3374
  fn fp64_nan(seed: f32) -> f32 {
2439
3375
  let nanBits = 0x7fc00000u | select(0u, 1u, seed < 0.0);
2440
3376
  return bitcast<f32>(nanBits);
2441
3377
  }
2442
3378
 
3379
+ fn fp64_u64_is_zero(value: vec2u) -> bool {
3380
+ return value.x == 0u && value.y == 0u;
3381
+ }
3382
+
3383
+ fn fp64_u64_compare(a: vec2u, b: vec2u) -> i32 {
3384
+ if (a.x != b.x) {
3385
+ return select(-1, 1, a.x > b.x);
3386
+ }
3387
+ if (a.y != b.y) {
3388
+ return select(-1, 1, a.y > b.y);
3389
+ }
3390
+ return 0;
3391
+ }
3392
+
3393
+ fn fp64_u64_add(a: vec2u, b: vec2u) -> vec2u {
3394
+ let low = a.y + b.y;
3395
+ let carry = select(0u, 1u, low < a.y);
3396
+ return vec2u(a.x + b.x + carry, low);
3397
+ }
3398
+
3399
+ fn fp64_u64_sub(a: vec2u, b: vec2u) -> vec2u {
3400
+ let borrow = select(0u, 1u, a.y < b.y);
3401
+ return vec2u(a.x - b.x - borrow, a.y - b.y);
3402
+ }
3403
+
3404
+ fn fp64_u64_shift_left(value: vec2u, shift: u32) -> vec2u {
3405
+ if (shift == 0u) {
3406
+ return value;
3407
+ }
3408
+ if (shift < 32u) {
3409
+ return vec2u((value.x << shift) | (value.y >> (32u - shift)), value.y << shift);
3410
+ }
3411
+ if (shift == 32u) {
3412
+ return vec2u(value.y, 0u);
3413
+ }
3414
+ if (shift < 64u) {
3415
+ return vec2u(value.y << (shift - 32u), 0u);
3416
+ }
3417
+ return vec2u(0u);
3418
+ }
3419
+
3420
+ fn fp64_u64_shift_right(value: vec2u, shift: u32) -> vec2u {
3421
+ if (shift == 0u) {
3422
+ return value;
3423
+ }
3424
+ if (shift < 32u) {
3425
+ return vec2u(value.x >> shift, (value.y >> shift) | (value.x << (32u - shift)));
3426
+ }
3427
+ if (shift == 32u) {
3428
+ return vec2u(0u, value.x);
3429
+ }
3430
+ if (shift < 64u) {
3431
+ return vec2u(0u, value.x >> (shift - 32u));
3432
+ }
3433
+ return vec2u(0u);
3434
+ }
3435
+
3436
+ fn fp64_u64_get_bit(value: vec2u, bitIndex: u32) -> bool {
3437
+ if (bitIndex >= 64u) {
3438
+ return false;
3439
+ }
3440
+ if (bitIndex >= 32u) {
3441
+ return ((value.x >> (bitIndex - 32u)) & 1u) != 0u;
3442
+ }
3443
+ return ((value.y >> bitIndex) & 1u) != 0u;
3444
+ }
3445
+
3446
+ fn fp64_u64_has_bits_below(value: vec2u, bitCount: u32) -> bool {
3447
+ if (bitCount == 0u) {
3448
+ return false;
3449
+ }
3450
+ if (bitCount >= 64u) {
3451
+ return !fp64_u64_is_zero(value);
3452
+ }
3453
+ if (bitCount > 32u) {
3454
+ let highBitCount = bitCount - 32u;
3455
+ let highMask = (1u << highBitCount) - 1u;
3456
+ return value.y != 0u || (value.x & highMask) != 0u;
3457
+ }
3458
+ if (bitCount == 32u) {
3459
+ return value.y != 0u;
3460
+ }
3461
+ let lowMask = (1u << bitCount) - 1u;
3462
+ return (value.y & lowMask) != 0u;
3463
+ }
3464
+
3465
+ fn fp64_u64_shift_right_sticky(value: vec2u, shift: u32) -> vec2u {
3466
+ var shifted = fp64_u64_shift_right(value, shift);
3467
+ if (fp64_u64_has_bits_below(value, shift)) {
3468
+ shifted.y = shifted.y | 1u;
3469
+ }
3470
+ return shifted;
3471
+ }
3472
+
3473
+ fn fp64_u64_count_leading_zeros(value: vec2u) -> u32 {
3474
+ if (value.x != 0u) {
3475
+ return countLeadingZeros(value.x);
3476
+ }
3477
+ return 32u + countLeadingZeros(value.y);
3478
+ }
3479
+
3480
+ fn fp64_round_shift_right_to_u32(value: vec2u, shift: u32) -> u32 {
3481
+ if (shift == 0u) {
3482
+ return value.y;
3483
+ }
3484
+
3485
+ let truncated = fp64_u64_shift_right(value, shift);
3486
+ var rounded = truncated.y;
3487
+ let guard = fp64_u64_get_bit(value, shift - 1u);
3488
+ let hasTrailingBits = fp64_u64_has_bits_below(value, shift - 1u);
3489
+ if (guard && (hasTrailingBits || (rounded & 1u) == 1u)) {
3490
+ rounded = rounded + 1u;
3491
+ }
3492
+ return rounded;
3493
+ }
3494
+
3495
+ fn fp64_make_f32_bits_from_u64(sign: u32, significand: vec2u, baseExponent: i32) -> u32 {
3496
+ if (fp64_u64_is_zero(significand)) {
3497
+ return sign << 31u;
3498
+ }
3499
+
3500
+ let leadingZeros = fp64_u64_count_leading_zeros(significand);
3501
+ let mostSignificantBit = 63u - leadingZeros;
3502
+ var exponent = baseExponent + i32(mostSignificantBit);
3503
+
3504
+ if (exponent > 127) {
3505
+ return (sign << 31u) | 0x7f800000u;
3506
+ }
3507
+
3508
+ if (exponent >= -126) {
3509
+ let shift = i32(mostSignificantBit) - 23;
3510
+ var significand24: u32;
3511
+ if (shift > 0) {
3512
+ significand24 = fp64_round_shift_right_to_u32(significand, u32(shift));
3513
+ } else {
3514
+ significand24 = fp64_u64_shift_left(significand, u32(-shift)).y;
3515
+ }
3516
+
3517
+ if (significand24 >= 0x1000000u) {
3518
+ significand24 = significand24 >> 1u;
3519
+ exponent = exponent + 1;
3520
+ if (exponent > 127) {
3521
+ return (sign << 31u) | 0x7f800000u;
3522
+ }
3523
+ }
3524
+
3525
+ return (sign << 31u) | (u32(exponent + 127) << 23u) | (significand24 & 0x7fffffu);
3526
+ }
3527
+
3528
+ let scaleExponent = baseExponent + 149;
3529
+ var mantissa: u32;
3530
+ if (scaleExponent >= 0) {
3531
+ mantissa = fp64_u64_shift_left(significand, u32(scaleExponent)).y;
3532
+ } else {
3533
+ mantissa = fp64_round_shift_right_to_u32(significand, u32(-scaleExponent));
3534
+ }
3535
+
3536
+ if (mantissa >= 0x800000u) {
3537
+ return (sign << 31u) | 0x00800000u;
3538
+ }
3539
+ return (sign << 31u) | mantissa;
3540
+ }
3541
+
3542
+ fn fp64_decode_bits(bits: vec2u) -> Fp64Bits {
3543
+ let sign = bits.x >> 31u;
3544
+ let exponentBits = (bits.x >> 20u) & 0x7ffu;
3545
+ let fractionHigh = bits.x & 0xfffffu;
3546
+ let fractionLow = bits.y;
3547
+ let fraction = vec2u(fractionHigh, fractionLow);
3548
+
3549
+ if (exponentBits == 0x7ffu) {
3550
+ let isInf = fp64_u64_is_zero(fraction);
3551
+ return Fp64Bits(sign, 0, vec2u(0u), false, isInf, !isInf);
3552
+ }
3553
+
3554
+ if (exponentBits == 0u) {
3555
+ let isZero = fp64_u64_is_zero(fraction);
3556
+ return Fp64Bits(sign, -1022, fraction, isZero, false, false);
3557
+ }
3558
+
3559
+ return Fp64Bits(sign, i32(exponentBits) - 1023, vec2u((1u << 20u) | fractionHigh, fractionLow), false, false, false);
3560
+ }
3561
+
3562
+ fn fp64_finite_magnitude_compare(a: Fp64Bits, b: Fp64Bits) -> i32 {
3563
+ if (a.exponent != b.exponent) {
3564
+ return select(-1, 1, a.exponent > b.exponent);
3565
+ }
3566
+ return fp64_u64_compare(a.significand, b.significand);
3567
+ }
3568
+
3569
+ fn fp64_add_aligned_magnitudes_to_f32_bits(sign: u32, larger: Fp64Bits, smaller: Fp64Bits) -> u32 {
3570
+ let largeSignificand = fp64_u64_shift_left(larger.significand, 3u);
3571
+ let smallSignificand = fp64_u64_shift_right_sticky(
3572
+ fp64_u64_shift_left(smaller.significand, 3u),
3573
+ u32(larger.exponent - smaller.exponent)
3574
+ );
3575
+ let resultSignificand = fp64_u64_add(largeSignificand, smallSignificand);
3576
+ return fp64_make_f32_bits_from_u64(sign, resultSignificand, larger.exponent - 55);
3577
+ }
3578
+
3579
+ fn fp64_sub_aligned_magnitudes_to_f32_bits(sign: u32, larger: Fp64Bits, smaller: Fp64Bits) -> u32 {
3580
+ let largeSignificand = fp64_u64_shift_left(larger.significand, 3u);
3581
+ let smallSignificand = fp64_u64_shift_right_sticky(
3582
+ fp64_u64_shift_left(smaller.significand, 3u),
3583
+ u32(larger.exponent - smaller.exponent)
3584
+ );
3585
+ let resultSignificand = fp64_u64_sub(largeSignificand, smallSignificand);
3586
+ return fp64_make_f32_bits_from_u64(sign, resultSignificand, larger.exponent - 55);
3587
+ }
3588
+
3589
+ // Subtract two raw binary64 values and round the exact result once to f32.
3590
+ // The input words are canonical high/low words: .x contains sign/exponent/high
3591
+ // fraction bits, and .y contains the low 32 fraction bits.
3592
+ fn sub_fp64u32_to_f32_bits(aBits: vec2u, bBits: vec2u) -> u32 {
3593
+ let a = fp64_decode_bits(aBits);
3594
+ let b = fp64_decode_bits(bBits);
3595
+ let bSubtractionSign = b.sign ^ 1u;
3596
+
3597
+ if (a.isNan || b.isNan) {
3598
+ return 0x7fc00000u;
3599
+ }
3600
+ if (a.isInf && b.isInf) {
3601
+ if (a.sign == bSubtractionSign) {
3602
+ return (a.sign << 31u) | 0x7f800000u;
3603
+ }
3604
+ return 0x7fc00000u;
3605
+ }
3606
+ if (a.isInf) {
3607
+ return (a.sign << 31u) | 0x7f800000u;
3608
+ }
3609
+ if (b.isInf) {
3610
+ return (bSubtractionSign << 31u) | 0x7f800000u;
3611
+ }
3612
+ if (a.isZero && b.isZero) {
3613
+ return select(0u, 0x80000000u, a.sign == 1u && b.sign == 0u);
3614
+ }
3615
+
3616
+ let magnitudeComparison = fp64_finite_magnitude_compare(a, b);
3617
+ if (a.sign == bSubtractionSign) {
3618
+ if (magnitudeComparison >= 0) {
3619
+ return fp64_add_aligned_magnitudes_to_f32_bits(a.sign, a, b);
3620
+ }
3621
+ return fp64_add_aligned_magnitudes_to_f32_bits(a.sign, b, a);
3622
+ }
3623
+
3624
+ if (magnitudeComparison == 0) {
3625
+ return 0u;
3626
+ }
3627
+ if (magnitudeComparison > 0) {
3628
+ return fp64_sub_aligned_magnitudes_to_f32_bits(a.sign, a, b);
3629
+ }
3630
+ return fp64_sub_aligned_magnitudes_to_f32_bits(bSubtractionSign, b, a);
3631
+ }
3632
+
3633
+ fn sub_fp64u32_to_f32(aBits: vec2u, bBits: vec2u) -> f32 {
3634
+ return bitcast<f32>(sub_fp64u32_to_f32_bits(aBits, bBits));
3635
+ }
3636
+
2443
3637
  fn fp64_runtime_zero() -> f32 {
2444
3638
  return fp64arithmetic.ONE * 0.0;
2445
3639
  }
@@ -3342,59 +4536,2054 @@ var fp64 = {
3342
4536
  fp64ifyMatrix4
3343
4537
  };
3344
4538
 
3345
- // dist/modules/color/float-colors.js
3346
- var GLSL_UNIFORMS = (
3347
- /* glsl */
3348
- `layout(std140) uniform floatColorsUniforms {
4539
+ // dist/modules/geospatial/dggs/dggs-wgsl.js
4540
+ var dggsWGSL = (
4541
+ /* wgsl */
4542
+ `const DGGS_H3_CELL_MODE: u32 = 1u;
4543
+ const DGGS_H3_MAX_RESOLUTION: u32 = 15u;
4544
+ const DGGS_H3_MAX_BASE_CELL: u32 = 121u;
4545
+ const DGGS_H3_UNUSED_DIGIT: u32 = 7u;
4546
+ const DGGS_H3_RES0_U_GNOMONIC: f32 = 0.381966011250105;
4547
+ const DGGS_H3_RSQRT7: f32 = 0.3779644730092272;
4548
+ const DGGS_H3_ONETHIRD: f32 = 0.3333333333333333;
4549
+ const DGGS_H3_AP7_ROT_RADS: f32 = 0.3334731722518321;
4550
+ const DGGS_H3_SQRT3_2: f32 = 0.8660254037844386;
4551
+ const DGGS_S2_MAX_LEVEL: u32 = 30u;
4552
+ const DGGS_GEOHASH_MAX_LENGTH: u32 = 12u;
4553
+ const DGGS_GEOHASH_LENGTH_BIT_OFFSET: u32 = 60u;
4554
+ const DGGS_QUADKEY_MAX_LENGTH: u32 = 29u;
4555
+ const DGGS_QUADKEY_LENGTH_BIT_OFFSET: u32 = 58u;
4556
+ const DGGS_A5_FIRST_HILBERT_RESOLUTION: u32 = 2u;
4557
+ const DGGS_A5_MAX_RESOLUTION: u32 = 30u;
4558
+ const DGGS_A5_HILBERT_START_BIT: u32 = 58u;
4559
+ const DGGS_PI: f32 = 3.141592653589793;
4560
+ const DGGS_TWO_PI: f32 = 6.283185307179586;
4561
+ const DGGS_TWO_PI_OVER_5: f32 = 1.2566370614359172;
4562
+ const DGGS_PI_OVER_5: f32 = 0.6283185307179586;
4563
+ const DGGS_PI_OVER_2: f32 = 1.5707963267948966;
4564
+ const DGGS_A5_DISTANCE_TO_EDGE: f32 = 0.6180339887498949;
4565
+ const DGGS_A5_REFLECTED_TRIANGLE_SCALE: f32 = 3.23606797749979;
4566
+ const DGGS_A5_LONGITUDE_OFFSET: f32 = 93.0;
4567
+ const DGGS_RADIANS_TO_DEGREES: f32 = 57.29577951308232;
4568
+ const DGGS_DEGREES_TO_RADIANS: f32 = 0.017453292519943295;
4569
+ const DGGS_U64_HIGH_WORD_FLOAT_SCALE: f32 = 4294967296.0;
4570
+
4571
+ // DGGS 64-bit helper functions use canonical word order vec2u(high, low).
4572
+ // Arrow BigInt64/BigUint64 buffers are read as little-endian vec2u(low, high),
4573
+ // so storage-buffer reads should pass through dggs_u64_from_little_endian_words().
4574
+ fn dggs_u64_make(high: u32, low: u32) -> vec2u {
4575
+ return vec2u(high, low);
4576
+ }
4577
+
4578
+ fn dggs_u64_from_little_endian_words(words: vec2u) -> vec2u {
4579
+ return words.yx;
4580
+ }
4581
+
4582
+ fn dggs_u64_to_little_endian_words(value: vec2u) -> vec2u {
4583
+ return value.yx;
4584
+ }
4585
+
4586
+ fn dggs_u64_high(value: vec2u) -> u32 {
4587
+ return value.x;
4588
+ }
4589
+
4590
+ fn dggs_u64_low(value: vec2u) -> u32 {
4591
+ return value.y;
4592
+ }
4593
+
4594
+ fn dggs_u64_is_zero(value: vec2u) -> bool {
4595
+ return value.x == 0u && value.y == 0u;
4596
+ }
4597
+
4598
+ fn dggs_u64_equal(a: vec2u, b: vec2u) -> bool {
4599
+ return a.x == b.x && a.y == b.y;
4600
+ }
4601
+
4602
+ fn dggs_u64_compare(a: vec2u, b: vec2u) -> i32 {
4603
+ if (a.x != b.x) {
4604
+ return select(-1, 1, a.x > b.x);
4605
+ }
4606
+ if (a.y != b.y) {
4607
+ return select(-1, 1, a.y > b.y);
4608
+ }
4609
+ return 0;
4610
+ }
4611
+
4612
+ fn dggs_u64_less(a: vec2u, b: vec2u) -> bool {
4613
+ return dggs_u64_compare(a, b) < 0;
4614
+ }
4615
+
4616
+ fn dggs_u64_add(a: vec2u, b: vec2u) -> vec2u {
4617
+ let low = a.y + b.y;
4618
+ let carry = select(0u, 1u, low < a.y);
4619
+ return vec2u(a.x + b.x + carry, low);
4620
+ }
4621
+
4622
+ fn dggs_u64_subtract(a: vec2u, b: vec2u) -> vec2u {
4623
+ let borrow = select(0u, 1u, a.y < b.y);
4624
+ return vec2u(a.x - b.x - borrow, a.y - b.y);
4625
+ }
4626
+
4627
+ fn dggs_u64_to_f32(value: vec2u) -> f32 {
4628
+ return f32(value.x) * DGGS_U64_HIGH_WORD_FLOAT_SCALE + f32(value.y);
4629
+ }
4630
+
4631
+ fn dggs_i64_from_little_endian_words(words: vec2u) -> vec2u {
4632
+ return dggs_u64_from_little_endian_words(words);
4633
+ }
4634
+
4635
+ fn dggs_i64_to_little_endian_words(value: vec2u) -> vec2u {
4636
+ return dggs_u64_to_little_endian_words(value);
4637
+ }
4638
+
4639
+ fn dggs_i64_is_negative(value: vec2u) -> bool {
4640
+ return (value.x & 0x80000000u) != 0u;
4641
+ }
4642
+
4643
+ fn dggs_i64_negate(value: vec2u) -> vec2u {
4644
+ return dggs_u64_add(vec2u(~value.x, ~value.y), vec2u(0u, 1u));
4645
+ }
4646
+
4647
+ fn dggs_i64_compare(a: vec2u, b: vec2u) -> i32 {
4648
+ let highA = bitcast<i32>(a.x);
4649
+ let highB = bitcast<i32>(b.x);
4650
+ if (highA != highB) {
4651
+ return select(-1, 1, highA > highB);
4652
+ }
4653
+ if (a.y != b.y) {
4654
+ return select(-1, 1, a.y > b.y);
4655
+ }
4656
+ return 0;
4657
+ }
4658
+
4659
+ fn dggs_i64_less(a: vec2u, b: vec2u) -> bool {
4660
+ return dggs_i64_compare(a, b) < 0;
4661
+ }
4662
+
4663
+ fn dggs_i64_subtract(a: vec2u, b: vec2u) -> vec2u {
4664
+ return dggs_u64_subtract(a, b);
4665
+ }
4666
+
4667
+ fn dggs_i64_to_f32(value: vec2u) -> f32 {
4668
+ if (dggs_i64_is_negative(value)) {
4669
+ return -dggs_u64_to_f32(dggs_i64_negate(value));
4670
+ }
4671
+ return dggs_u64_to_f32(value);
4672
+ }
4673
+
4674
+ fn dggs_u32_mask_low(bitCount: u32) -> u32 {
4675
+ if (bitCount == 0u) {
4676
+ return 0u;
4677
+ }
4678
+ if (bitCount >= 32u) {
4679
+ return 0xffffffffu;
4680
+ }
4681
+ return (1u << bitCount) - 1u;
4682
+ }
4683
+
4684
+ fn dggs_u64_shift_left(value: vec2u, shift: u32) -> vec2u {
4685
+ if (shift == 0u) {
4686
+ return value;
4687
+ }
4688
+ if (shift < 32u) {
4689
+ return vec2u((value.x << shift) | (value.y >> (32u - shift)), value.y << shift);
4690
+ }
4691
+ if (shift == 32u) {
4692
+ return vec2u(value.y, 0u);
4693
+ }
4694
+ if (shift < 64u) {
4695
+ return vec2u(value.y << (shift - 32u), 0u);
4696
+ }
4697
+ return vec2u(0u);
4698
+ }
4699
+
4700
+ fn dggs_u64_shift_right(value: vec2u, shift: u32) -> vec2u {
4701
+ if (shift == 0u) {
4702
+ return value;
4703
+ }
4704
+ if (shift < 32u) {
4705
+ return vec2u(value.x >> shift, (value.y >> shift) | (value.x << (32u - shift)));
4706
+ }
4707
+ if (shift == 32u) {
4708
+ return vec2u(0u, value.x);
4709
+ }
4710
+ if (shift < 64u) {
4711
+ return vec2u(0u, value.x >> (shift - 32u));
4712
+ }
4713
+ return vec2u(0u);
4714
+ }
4715
+
4716
+ fn dggs_u64_extract_bits(value: vec2u, bitOffset: u32, bitCount: u32) -> u32 {
4717
+ if (bitCount == 0u || bitOffset >= 64u) {
4718
+ return 0u;
4719
+ }
4720
+ let shifted = dggs_u64_shift_right(value, bitOffset);
4721
+ return shifted.y & dggs_u32_mask_low(bitCount);
4722
+ }
4723
+
4724
+ fn dggs_u64_set_bits(value: vec2u, bitOffset: u32, bitCount: u32, bits: u32) -> vec2u {
4725
+ if (bitCount == 0u || bitOffset >= 64u) {
4726
+ return value;
4727
+ }
4728
+ let bitMask = dggs_u32_mask_low(bitCount);
4729
+ let fieldMask = dggs_u64_shift_left(vec2u(0u, bitMask), bitOffset);
4730
+ let fieldBits = dggs_u64_shift_left(vec2u(0u, bits & bitMask), bitOffset);
4731
+ return (value & vec2u(~fieldMask.x, ~fieldMask.y)) | fieldBits;
4732
+ }
4733
+
4734
+ fn dggs_u64_count_trailing_zeros(value: vec2u) -> u32 {
4735
+ if (value.y != 0u) {
4736
+ return countTrailingZeros(value.y);
4737
+ }
4738
+ if (value.x != 0u) {
4739
+ return 32u + countTrailingZeros(value.x);
4740
+ }
4741
+ return 64u;
4742
+ }
4743
+
4744
+ struct DggsA5Cell {
4745
+ origin : u32,
4746
+ segment : u32,
4747
+ resolution : u32,
4748
+ valid : u32,
4749
+ quintantShift : u32,
4750
+ hilbertResolution : u32,
4751
+ };
4752
+
4753
+ struct DggsA5Anchor {
4754
+ q : u32,
4755
+ offset : vec2i,
4756
+ flips : vec2i,
4757
+ orientation : u32,
4758
+ quintant : u32,
4759
+ };
4760
+
4761
+ struct DggsA5FaceTriangle {
4762
+ a : vec2f,
4763
+ b : vec2f,
4764
+ c : vec2f,
4765
+ };
4766
+
4767
+ struct DggsA5SphericalTriangle {
4768
+ a : vec3f,
4769
+ b : vec3f,
4770
+ c : vec3f,
4771
+ };
4772
+
4773
+ struct DggsH3FaceIJK {
4774
+ face : u32,
4775
+ coord : vec3i,
4776
+ valid : u32,
4777
+ };
4778
+
4779
+ struct DggsH3BoundaryVertex {
4780
+ face : u32,
4781
+ coord : vec3i,
4782
+ resolution : u32,
4783
+ valid : u32,
4784
+ };
4785
+
4786
+ fn dggs_boundary_point_to_fp64_split(point : vec2f) -> vec4f {
4787
+ return vec4f(point.x, point.y, 0.0, 0.0);
4788
+ }
4789
+
4790
+ fn dggs_a5_positive_mod_i32(value : i32, modulus : i32) -> i32 {
4791
+ return ((value % modulus) + modulus) % modulus;
4792
+ }
4793
+
4794
+ fn dggs_a5_has_bit(index : vec2u, bitOffset : u32) -> bool {
4795
+ return dggs_u64_extract_bits(index, bitOffset, 1u) != 0u;
4796
+ }
4797
+
4798
+ fn dggs_a5_get_resolution(index : vec2u) -> u32 {
4799
+ if (dggs_u64_is_zero(index)) {
4800
+ return 0u;
4801
+ }
4802
+ if (
4803
+ dggs_a5_has_bit(index, 0u) ||
4804
+ dggs_u64_extract_bits(index, 0u, 3u) == 4u ||
4805
+ dggs_u64_extract_bits(index, 0u, 5u) == 16u
4806
+ ) {
4807
+ return DGGS_A5_MAX_RESOLUTION;
4808
+ }
4809
+
4810
+ var resolution = DGGS_A5_MAX_RESOLUTION - 1u;
4811
+ var shifted = dggs_u64_shift_right(index, 1u);
4812
+ if (dggs_u64_is_zero(shifted)) {
4813
+ return 0u;
4814
+ }
4815
+
4816
+ var remaining = shifted.y;
4817
+ if (remaining == 0u) {
4818
+ shifted = dggs_u64_shift_right(shifted, 32u);
4819
+ resolution -= 16u;
4820
+ remaining = shifted.y;
4821
+ }
4822
+ if ((remaining & 0xffffu) == 0u) {
4823
+ remaining >>= 16u;
4824
+ resolution -= 8u;
4825
+ }
4826
+ if (resolution >= 6u && (remaining & 0xffu) == 0u) {
4827
+ remaining >>= 8u;
4828
+ resolution -= 4u;
4829
+ }
4830
+ if (resolution >= 4u && (remaining & 0xfu) == 0u) {
4831
+ remaining >>= 4u;
4832
+ resolution -= 2u;
4833
+ }
4834
+ loop {
4835
+ if ((remaining & 1u) != 0u) {
4836
+ break;
4837
+ }
4838
+ if (resolution == 0u) {
4839
+ break;
4840
+ }
4841
+ resolution -= 1u;
4842
+ remaining >>= select(2u, 1u, resolution < DGGS_A5_FIRST_HILBERT_RESOLUTION);
4843
+ }
4844
+ return resolution;
4845
+ }
4846
+
4847
+ fn dggs_a5_get_origin_first_quintant(origin : u32) -> u32 {
4848
+ let values = array<u32, 12>(
4849
+ 4u, 2u, 3u, 0u, 2u, 4u, 2u, 2u, 3u, 0u, 3u, 0u
4850
+ );
4851
+ return values[min(origin, 11u)];
4852
+ }
4853
+
4854
+ fn dggs_a5_get_origin_step(origin : u32) -> i32 {
4855
+ let values = array<i32, 12>(
4856
+ -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1
4857
+ );
4858
+ return values[min(origin, 11u)];
4859
+ }
4860
+
4861
+ fn dggs_a5_get_origin_orientation(origin : u32, faceRelativeQuintant : u32) -> u32 {
4862
+ // Orientation codes: uv=0, vu=1, uw=2, wu=3, vw=4, wv=5.
4863
+ let values = array<u32, 60>(
4864
+ 1u, 2u, 4u, 4u, 4u,
4865
+ 1u, 0u, 5u, 3u, 2u,
4866
+ 3u, 0u, 5u, 3u, 2u,
4867
+ 3u, 0u, 5u, 3u, 2u,
4868
+ 3u, 2u, 4u, 1u, 2u,
4869
+ 1u, 0u, 5u, 3u, 2u,
4870
+ 3u, 2u, 4u, 1u, 2u,
4871
+ 3u, 2u, 4u, 1u, 2u,
4872
+ 3u, 0u, 5u, 3u, 2u,
4873
+ 1u, 0u, 5u, 3u, 2u,
4874
+ 1u, 0u, 5u, 3u, 2u,
4875
+ 3u, 2u, 4u, 1u, 2u
4876
+ );
4877
+ return values[min(origin, 11u) * 5u + min(faceRelativeQuintant, 4u)];
4878
+ }
4879
+
4880
+ fn dggs_a5_get_origin_angle(origin : u32) -> f32 {
4881
+ if (origin == 0u || origin == 9u) {
4882
+ return 0.0;
4883
+ }
4884
+ return DGGS_PI_OVER_5;
4885
+ }
4886
+
4887
+ fn dggs_a5_get_origin_quaternion(origin : u32) -> vec4f {
4888
+ let values = array<vec4f, 12>(
4889
+ vec4f(0.0, 0.0, 0.0, 1.0),
4890
+ vec4f(0.0, 0.525731112119, 0.0, 0.850650808352),
4891
+ vec4f(-0.5, 0.688190960236, 0.0, 0.525731112119),
4892
+ vec4f(-0.809016994375, -0.262865556060, 0.0, 0.525731112119),
4893
+ vec4f(-0.5, 0.162459848116, 0.0, 0.850650808352),
4894
+ vec4f(-0.309016994375, -0.425325404176, 0.0, 0.850650808352),
4895
+ vec4f(0.309016994375, -0.425325404176, 0.0, 0.850650808352),
4896
+ vec4f(0.809016994375, -0.262865556060, 0.0, 0.525731112119),
4897
+ vec4f(0.0, -0.850650808352, 0.0, 0.525731112119),
4898
+ vec4f(0.0, -1.0, 0.0, 0.0),
4899
+ vec4f(0.5, 0.688190960236, 0.0, 0.525731112119),
4900
+ vec4f(0.5, 0.162459848116, 0.0, 0.850650808352)
4901
+ );
4902
+ return values[min(origin, 11u)];
4903
+ }
4904
+
4905
+ fn dggs_a5_quaternion_rotate(point : vec3f, quaternion : vec4f) -> vec3f {
4906
+ let q = quaternion.xyz;
4907
+ let t = 2.0 * cross(q, point);
4908
+ return point + quaternion.w * t + cross(q, t);
4909
+ }
4910
+
4911
+ fn dggs_a5_deserialize(index : vec2u) -> DggsA5Cell {
4912
+ if (dggs_u64_is_zero(index)) {
4913
+ return DggsA5Cell(0u, 0u, 0u, 0u, DGGS_A5_HILBERT_START_BIT, 0u);
4914
+ }
4915
+
4916
+ let resolution = dggs_a5_get_resolution(index);
4917
+ var quintantShift = DGGS_A5_HILBERT_START_BIT;
4918
+ var quintantOffset = 0u;
4919
+ if (resolution == DGGS_A5_MAX_RESOLUTION) {
4920
+ var markerBits = 5u;
4921
+ if (dggs_a5_has_bit(index, 0u)) {
4922
+ markerBits = 1u;
4923
+ } else if (dggs_a5_has_bit(index, 2u)) {
4924
+ markerBits = 3u;
4925
+ }
4926
+ quintantShift = DGGS_A5_HILBERT_START_BIT + markerBits;
4927
+ quintantOffset = select(select(40u, 32u, markerBits == 3u), 0u, markerBits == 1u);
4928
+ }
4929
+
4930
+ let topBits = dggs_u64_shift_right(index, quintantShift).y + quintantOffset;
4931
+ if (resolution == 0u) {
4932
+ if (topBits >= 12u) {
4933
+ return DggsA5Cell(0u, 0u, resolution, 0u, quintantShift, 0u);
4934
+ }
4935
+ return DggsA5Cell(topBits, 0u, resolution, 1u, quintantShift, 0u);
4936
+ }
4937
+
4938
+ let origin = topBits / 5u;
4939
+ if (origin >= 12u) {
4940
+ return DggsA5Cell(0u, 0u, resolution, 0u, quintantShift, 0u);
4941
+ }
4942
+ let segment = (topBits + dggs_a5_get_origin_first_quintant(origin)) % 5u;
4943
+ let hilbertResolution = select(0u, resolution - 1u, resolution >= 2u);
4944
+ return DggsA5Cell(origin, segment, resolution, 1u, quintantShift, hilbertResolution);
4945
+ }
4946
+
4947
+ fn dggs_a5_segment_to_anchor_info(cell : DggsA5Cell) -> DggsA5Anchor {
4948
+ let firstQuintant = dggs_a5_get_origin_first_quintant(cell.origin);
4949
+ let faceRelativeQuintant = (cell.segment + 5u - firstQuintant) % 5u;
4950
+ let orientation = dggs_a5_get_origin_orientation(cell.origin, faceRelativeQuintant);
4951
+ let step = dggs_a5_get_origin_step(cell.origin);
4952
+ let quintant = u32(dggs_a5_positive_mod_i32(
4953
+ i32(firstQuintant) + step * i32(faceRelativeQuintant),
4954
+ 5
4955
+ ));
4956
+ return DggsA5Anchor(0u, vec2i(0), vec2i(1), orientation, quintant);
4957
+ }
4958
+
4959
+ fn dggs_a5_is_reverse_orientation(orientation : u32) -> bool {
4960
+ return orientation == 1u || orientation == 3u || orientation == 4u;
4961
+ }
4962
+
4963
+ fn dggs_a5_is_invert_j_orientation(orientation : u32) -> bool {
4964
+ return orientation == 5u || orientation == 4u;
4965
+ }
4966
+
4967
+ fn dggs_a5_is_flip_ij_orientation(orientation : u32) -> bool {
4968
+ return orientation == 3u || orientation == 2u;
4969
+ }
4970
+
4971
+ fn dggs_a5_pattern_value(index : u32, flipIj : bool) -> u32 {
4972
+ let pattern = array<u32, 8>(0u, 1u, 3u, 4u, 5u, 6u, 7u, 2u);
4973
+ let flippedPattern = array<u32, 8>(0u, 1u, 2u, 7u, 3u, 4u, 5u, 6u);
4974
+ return select(pattern[min(index, 7u)], flippedPattern[min(index, 7u)], flipIj);
4975
+ }
4976
+
4977
+ fn dggs_a5_quaternary_to_flips(digit : u32) -> vec2i {
4978
+ if (digit == 1u) {
4979
+ return vec2i(1, -1);
4980
+ }
4981
+ if (digit == 3u) {
4982
+ return vec2i(-1, 1);
4983
+ }
4984
+ return vec2i(1, 1);
4985
+ }
4986
+
4987
+ fn dggs_a5_quaternary_to_kj(digit : u32, flips : vec2i) -> vec2i {
4988
+ var p = vec2i(0);
4989
+ var q = vec2i(0);
4990
+ if (flips.x == 1 && flips.y == 1) {
4991
+ p = vec2i(1, 0);
4992
+ q = vec2i(0, 1);
4993
+ } else if (flips.x == -1 && flips.y == 1) {
4994
+ p = vec2i(0, -1);
4995
+ q = vec2i(-1, 0);
4996
+ } else if (flips.x == 1 && flips.y == -1) {
4997
+ p = vec2i(0, 1);
4998
+ q = vec2i(1, 0);
4999
+ } else {
5000
+ p = vec2i(-1, 0);
5001
+ q = vec2i(0, -1);
5002
+ }
5003
+
5004
+ if (digit == 1u) {
5005
+ return p;
5006
+ }
5007
+ if (digit == 2u) {
5008
+ return p + q;
5009
+ }
5010
+ if (digit == 3u) {
5011
+ return q + 2 * p;
5012
+ }
5013
+ return vec2i(0);
5014
+ }
5015
+
5016
+ fn dggs_a5_kj_to_ij(kj : vec2i) -> vec2i {
5017
+ return vec2i(kj.x - kj.y, kj.y);
5018
+ }
5019
+
5020
+ fn dggs_a5_shift_digits(
5021
+ digits : ptr<function, array<u32, 29>>,
5022
+ digitIndex : u32,
5023
+ flips : vec2i,
5024
+ invertJ : bool,
5025
+ flipIj : bool
5026
+ ) {
5027
+ if (digitIndex == 0u) {
5028
+ return;
5029
+ }
5030
+ let parentK = (*digits)[digitIndex];
5031
+ let childK = (*digits)[digitIndex - 1u];
5032
+ let flipSum = flips.x + flips.y;
5033
+ var needsShift = true;
5034
+ var first = true;
5035
+ if (invertJ != (flipSum == 0)) {
5036
+ needsShift = parentK == 1u || parentK == 2u;
5037
+ first = parentK == 1u;
5038
+ } else {
5039
+ needsShift = parentK < 2u;
5040
+ first = parentK == 0u;
5041
+ }
5042
+ if (!needsShift) {
5043
+ return;
5044
+ }
5045
+ let source = select(childK + 4u, childK, first);
5046
+ let destination = dggs_a5_pattern_value(source, flipIj);
5047
+ (*digits)[digitIndex - 1u] = destination % 4u;
5048
+ (*digits)[digitIndex] = u32(dggs_a5_positive_mod_i32(
5049
+ i32(parentK) + 4 + i32(destination / 4u) - i32(source / 4u),
5050
+ 4
5051
+ ));
5052
+ }
5053
+
5054
+ fn dggs_a5_make_anchor(index : vec2u, cell : DggsA5Cell, anchorInfo : DggsA5Anchor) -> DggsA5Anchor {
5055
+ if (cell.hilbertResolution == 0u) {
5056
+ return anchorInfo;
5057
+ }
5058
+
5059
+ let reverse = dggs_a5_is_reverse_orientation(anchorInfo.orientation);
5060
+ let invertJ = dggs_a5_is_invert_j_orientation(anchorInfo.orientation);
5061
+ let flipIj = dggs_a5_is_flip_ij_orientation(anchorInfo.orientation);
5062
+ let hilbertBits = 2u * cell.hilbertResolution;
5063
+ let sBitOffset = cell.quintantShift - hilbertBits;
5064
+ var digits : array<u32, 29>;
5065
+ var digitIndex = 0u;
5066
+ loop {
5067
+ if (digitIndex >= cell.hilbertResolution) {
5068
+ break;
5069
+ }
5070
+ var digit = dggs_u64_extract_bits(index, sBitOffset + 2u * digitIndex, 2u);
5071
+ if (reverse) {
5072
+ digit = 3u - digit;
5073
+ }
5074
+ digits[digitIndex] = digit;
5075
+ digitIndex += 1u;
5076
+ }
5077
+
5078
+ var flips = vec2i(1, 1);
5079
+ digitIndex = cell.hilbertResolution;
5080
+ loop {
5081
+ if (digitIndex == 0u) {
5082
+ break;
5083
+ }
5084
+ digitIndex -= 1u;
5085
+ dggs_a5_shift_digits(&digits, digitIndex, flips, invertJ, flipIj);
5086
+ flips *= dggs_a5_quaternary_to_flips(digits[digitIndex]);
5087
+ }
5088
+
5089
+ flips = vec2i(1, 1);
5090
+ var offset = vec2i(0);
5091
+ digitIndex = cell.hilbertResolution;
5092
+ loop {
5093
+ if (digitIndex == 0u) {
5094
+ break;
5095
+ }
5096
+ digitIndex -= 1u;
5097
+ offset *= 2;
5098
+ offset += dggs_a5_quaternary_to_kj(digits[digitIndex], flips);
5099
+ flips *= dggs_a5_quaternary_to_flips(digits[digitIndex]);
5100
+ }
5101
+
5102
+ var anchorOffset = dggs_a5_kj_to_ij(offset);
5103
+ if (flipIj) {
5104
+ let originalOffset = anchorOffset;
5105
+ anchorOffset = originalOffset.yx;
5106
+ if (flips.x == -1) {
5107
+ anchorOffset += vec2i(-1, 1);
5108
+ }
5109
+ if (flips.y == -1) {
5110
+ anchorOffset -= vec2i(-1, 1);
5111
+ }
5112
+ }
5113
+ if (invertJ) {
5114
+ let i = anchorOffset.x;
5115
+ let j = i32(1u << cell.hilbertResolution) - (i + anchorOffset.y);
5116
+ flips.x = -flips.x;
5117
+ anchorOffset.y = j;
5118
+ }
5119
+
5120
+ return DggsA5Anchor(
5121
+ digits[0],
5122
+ anchorOffset,
5123
+ flips,
5124
+ anchorInfo.orientation,
5125
+ anchorInfo.quintant
5126
+ );
5127
+ }
5128
+
5129
+ fn dggs_a5_rotate_face_point(point : vec2f, quintant : u32) -> vec2f {
5130
+ let angle = f32(quintant) * DGGS_TWO_PI_OVER_5;
5131
+ let cosine = cos(angle);
5132
+ let sine = sin(angle);
5133
+ return vec2f(point.x * cosine - point.y * sine, point.x * sine + point.y * cosine);
5134
+ }
5135
+
5136
+ fn dggs_a5_get_base_pentagon_vertex(vertexIndex : u32) -> vec2f {
5137
+ let vertices = array<vec2f, 5>(
5138
+ vec2f(0.0, 0.0),
5139
+ vec2f(0.199381847431, 0.375413822391),
5140
+ vec2f(0.618033988750, 0.449027976580),
5141
+ vec2f(0.817415836181, 0.073614154188),
5142
+ vec2f(0.418652141319, -0.073614154188)
5143
+ );
5144
+ return vertices[min(vertexIndex, 4u)];
5145
+ }
5146
+
5147
+ fn dggs_a5_get_base_triangle_vertex(vertexIndex : u32) -> vec2f {
5148
+ let vertices = array<vec2f, 3>(
5149
+ vec2f(0.0, 0.0),
5150
+ vec2f(0.618033988750, 0.449027976580),
5151
+ vec2f(0.618033988750, -0.449027976580)
5152
+ );
5153
+ return vertices[min(vertexIndex, 2u)];
5154
+ }
5155
+
5156
+ fn dggs_a5_get_face_vertex(vertexIndex : u32) -> vec2f {
5157
+ return dggs_a5_rotate_face_point(dggs_a5_get_base_triangle_vertex(1u), 4u - min(vertexIndex, 4u));
5158
+ }
5159
+
5160
+ fn dggs_a5_get_triangle_vertex(quintant : u32, vertexIndex : u32) -> vec2f {
5161
+ return dggs_a5_rotate_face_point(dggs_a5_get_base_triangle_vertex(vertexIndex), quintant);
5162
+ }
5163
+
5164
+ fn dggs_a5_get_pentagon_vertex(
5165
+ anchor : DggsA5Anchor,
5166
+ hilbertResolution : u32,
5167
+ vertexIndex : u32
5168
+ ) -> vec2f {
5169
+ let flipSum = anchor.flips.x + anchor.flips.y;
5170
+ let reflectsY = ((flipSum == -2 || flipSum == 2) && anchor.q > 1u) ||
5171
+ (flipSum == 0 && (anchor.q == 0u || anchor.q == 3u));
5172
+ let sourceIndex = select(vertexIndex, 4u - vertexIndex, reflectsY);
5173
+ var point = dggs_a5_get_base_pentagon_vertex(sourceIndex);
5174
+
5175
+ if (anchor.flips.x == 1 && anchor.flips.y == -1) {
5176
+ point = -point;
5177
+ }
5178
+ if (reflectsY) {
5179
+ point.y = -point.y;
5180
+ }
5181
+ if (anchor.flips.x == -1 && anchor.flips.y == -1) {
5182
+ point = -point;
5183
+ } else if (anchor.flips.x == -1) {
5184
+ point += vec2f(-0.618033988750, 0.449027976580);
5185
+ } else if (anchor.flips.y == -1) {
5186
+ point += vec2f(0.618033988750, -0.449027976580);
5187
+ }
5188
+
5189
+ let translation = vec2f(
5190
+ f32(anchor.offset.x) * 0.618033988750 + f32(anchor.offset.y) * 0.618033988750,
5191
+ f32(anchor.offset.x) * 0.449027976580 + f32(anchor.offset.y) * -0.449027976580
5192
+ );
5193
+ point = (point + translation) / f32(1u << hilbertResolution);
5194
+ return dggs_a5_rotate_face_point(point, anchor.quintant);
5195
+ }
5196
+
5197
+ fn dggs_a5_get_shape_vertex_count(cell : DggsA5Cell) -> u32 {
5198
+ return select(5u, 3u, cell.resolution == 1u);
5199
+ }
5200
+
5201
+ fn dggs_a5_get_reversed_shape_index(vertexIndex : u32, shapeVertexCount : u32) -> u32 {
5202
+ if (vertexIndex == 0u || vertexIndex >= shapeVertexCount) {
5203
+ return 0u;
5204
+ }
5205
+ return shapeVertexCount - vertexIndex;
5206
+ }
5207
+
5208
+ fn dggs_a5_get_cell_shape_face_point(index : vec2u, cell : DggsA5Cell, shapeIndex : u32) -> vec2f {
5209
+ let anchorInfo = dggs_a5_segment_to_anchor_info(cell);
5210
+ if (cell.resolution == 0u) {
5211
+ return dggs_a5_get_face_vertex(shapeIndex);
5212
+ }
5213
+ if (cell.resolution == 1u) {
5214
+ return dggs_a5_get_triangle_vertex(anchorInfo.quintant, shapeIndex);
5215
+ }
5216
+ let anchor = dggs_a5_make_anchor(index, cell, anchorInfo);
5217
+ return dggs_a5_get_pentagon_vertex(anchor, cell.hilbertResolution, shapeIndex);
5218
+ }
5219
+
5220
+ fn dggs_a5_get_cell_center_face_point(index : vec2u, cell : DggsA5Cell) -> vec2f {
5221
+ let shapeVertexCount = dggs_a5_get_shape_vertex_count(cell);
5222
+ var center = vec2f(0.0);
5223
+ var vertexIndex = 0u;
5224
+ loop {
5225
+ if (vertexIndex >= shapeVertexCount) {
5226
+ break;
5227
+ }
5228
+ center += dggs_a5_get_cell_shape_face_point(index, cell, vertexIndex);
5229
+ vertexIndex += 1u;
5230
+ }
5231
+ return center / f32(shapeVertexCount);
5232
+ }
5233
+
5234
+ fn dggs_a5_to_polar(point : vec2f) -> vec2f {
5235
+ return vec2f(length(point), atan2(point.y, point.x));
5236
+ }
5237
+
5238
+ fn dggs_a5_to_cartesian(thetaPhi : vec2f) -> vec3f {
5239
+ let sinPhi = sin(thetaPhi.y);
5240
+ return vec3f(sinPhi * cos(thetaPhi.x), sinPhi * sin(thetaPhi.x), cos(thetaPhi.y));
5241
+ }
5242
+
5243
+ fn dggs_a5_normalize_vec3(point : vec3f) -> vec3f {
5244
+ let magnitude = length(point);
5245
+ if (magnitude < 1.0e-12) {
5246
+ return vec3f(0.0, 0.0, 1.0);
5247
+ }
5248
+ return point / magnitude;
5249
+ }
5250
+
5251
+ fn dggs_a5_get_face_triangle_index(polar : vec2f) -> u32 {
5252
+ return u32(dggs_a5_positive_mod_i32(i32(floor(polar.y / DGGS_PI_OVER_5)), 10));
5253
+ }
5254
+
5255
+ fn dggs_a5_normalize_gamma(gamma : f32) -> f32 {
5256
+ let segment = gamma / DGGS_TWO_PI_OVER_5;
5257
+ return (segment - round(segment)) * DGGS_TWO_PI_OVER_5;
5258
+ }
5259
+
5260
+ fn dggs_a5_should_reflect(polar : vec2f) -> bool {
5261
+ return polar.x * cos(dggs_a5_normalize_gamma(polar.y)) > DGGS_A5_DISTANCE_TO_EDGE;
5262
+ }
5263
+
5264
+ fn dggs_a5_get_base_face_triangle(faceTriangleIndex : u32) -> DggsA5FaceTriangle {
5265
+ let quintant = ((faceTriangleIndex + 1u) / 2u) % 5u;
5266
+ let center = dggs_a5_get_triangle_vertex(quintant, 0u);
5267
+ let corner1 = dggs_a5_get_triangle_vertex(quintant, 1u);
5268
+ let corner2 = dggs_a5_get_triangle_vertex(quintant, 2u);
5269
+ let edgeMidpoint = (corner1 + corner2) * 0.5;
5270
+ if ((faceTriangleIndex & 1u) == 0u) {
5271
+ return DggsA5FaceTriangle(center, edgeMidpoint, corner1);
5272
+ }
5273
+ return DggsA5FaceTriangle(center, corner2, edgeMidpoint);
5274
+ }
5275
+
5276
+ fn dggs_a5_get_face_triangle_vertex(triangle : DggsA5FaceTriangle, vertexIndex : u32) -> vec2f {
5277
+ if (vertexIndex == 0u) {
5278
+ return triangle.a;
5279
+ }
5280
+ if (vertexIndex == 1u) {
5281
+ return triangle.b;
5282
+ }
5283
+ return triangle.c;
5284
+ }
5285
+
5286
+ fn dggs_a5_get_reflected_face_triangle(
5287
+ faceTriangleIndex : u32,
5288
+ squashed : bool
5289
+ ) -> DggsA5FaceTriangle {
5290
+ let baseTriangle = dggs_a5_get_base_face_triangle(faceTriangleIndex);
5291
+ let even = (faceTriangleIndex & 1u) == 0u;
5292
+ let midpoint = select(baseTriangle.c, baseTriangle.b, even);
5293
+ let scale = select(2.0, DGGS_A5_REFLECTED_TRIANGLE_SCALE, squashed);
5294
+ let reflectedA = -baseTriangle.a + midpoint * scale;
5295
+ return DggsA5FaceTriangle(reflectedA, baseTriangle.c, baseTriangle.b);
5296
+ }
5297
+
5298
+ fn dggs_a5_get_face_triangle(
5299
+ faceTriangleIndex : u32,
5300
+ reflected : bool,
5301
+ squashed : bool
5302
+ ) -> DggsA5FaceTriangle {
5303
+ if (reflected) {
5304
+ return dggs_a5_get_reflected_face_triangle(faceTriangleIndex, squashed);
5305
+ }
5306
+ return dggs_a5_get_base_face_triangle(faceTriangleIndex);
5307
+ }
5308
+
5309
+ fn dggs_a5_face_triangle_to_spherical_triangle(
5310
+ faceTriangleIndex : u32,
5311
+ origin : u32,
5312
+ reflected : bool
5313
+ ) -> DggsA5SphericalTriangle {
5314
+ let faceTriangle = dggs_a5_get_face_triangle(faceTriangleIndex, reflected, true);
5315
+ let originAngle = dggs_a5_get_origin_angle(origin);
5316
+ let quaternion = dggs_a5_get_origin_quaternion(origin);
5317
+
5318
+ let polarA = dggs_a5_to_polar(faceTriangle.a);
5319
+ let polarB = dggs_a5_to_polar(faceTriangle.b);
5320
+ let polarC = dggs_a5_to_polar(faceTriangle.c);
5321
+ let sphericalA = dggs_a5_quaternion_rotate(
5322
+ dggs_a5_to_cartesian(vec2f(polarA.y + originAngle, atan(polarA.x))),
5323
+ quaternion
5324
+ );
5325
+ let sphericalB = dggs_a5_quaternion_rotate(
5326
+ dggs_a5_to_cartesian(vec2f(polarB.y + originAngle, atan(polarB.x))),
5327
+ quaternion
5328
+ );
5329
+ let sphericalC = dggs_a5_quaternion_rotate(
5330
+ dggs_a5_to_cartesian(vec2f(polarC.y + originAngle, atan(polarC.x))),
5331
+ quaternion
5332
+ );
5333
+ return DggsA5SphericalTriangle(
5334
+ dggs_a5_normalize_vec3(sphericalA),
5335
+ dggs_a5_normalize_vec3(sphericalB),
5336
+ dggs_a5_normalize_vec3(sphericalC)
5337
+ );
5338
+ }
5339
+
5340
+ fn dggs_a5_face_to_barycentric(point : vec2f, triangle : DggsA5FaceTriangle) -> vec3f {
5341
+ let d31 = triangle.a - triangle.c;
5342
+ let d23 = triangle.c - triangle.b;
5343
+ let d3p = point - triangle.c;
5344
+ let determinant = d23.x * d31.y - d23.y * d31.x;
5345
+ if (abs(determinant) < 1.0e-12) {
5346
+ return vec3f(1.0, 0.0, 0.0);
5347
+ }
5348
+ let b0 = (d23.x * d3p.y - d23.y * d3p.x) / determinant;
5349
+ let b1 = (d31.x * d3p.y - d31.y * d3p.x) / determinant;
5350
+ return vec3f(b0, b1, 1.0 - (b0 + b1));
5351
+ }
5352
+
5353
+ fn dggs_a5_triangle_area(a : vec3f, b : vec3f, c : vec3f) -> f32 {
5354
+ let midA = dggs_a5_normalize_vec3((b + c) * 0.5);
5355
+ let midB = dggs_a5_normalize_vec3((c + a) * 0.5);
5356
+ let midC = dggs_a5_normalize_vec3((a + b) * 0.5);
5357
+ let tripleProduct = dot(midA, cross(midB, midC));
5358
+ let clamped = clamp(tripleProduct, -1.0, 1.0);
5359
+ if (abs(clamped) < 1.0e-8) {
5360
+ return 2.0 * clamped;
5361
+ }
5362
+ return asin(clamped) * 2.0;
5363
+ }
5364
+
5365
+ fn dggs_a5_spherical_triangle_area(triangle : DggsA5SphericalTriangle) -> f32 {
5366
+ return dggs_a5_triangle_area(triangle.a, triangle.b, triangle.c);
5367
+ }
5368
+
5369
+ fn dggs_a5_vector_difference(a : vec3f, b : vec3f) -> f32 {
5370
+ let midpoint = dggs_a5_normalize_vec3((a + b) * 0.5);
5371
+ let crossProduct = cross(a, midpoint);
5372
+ let distance = length(crossProduct);
5373
+ if (distance < 1.0e-8) {
5374
+ return 0.5 * length(a - b);
5375
+ }
5376
+ return distance;
5377
+ }
5378
+
5379
+ fn dggs_a5_slerp(a : vec3f, b : vec3f, t : f32) -> vec3f {
5380
+ let cosineGamma = clamp(dot(a, b), -1.0, 1.0);
5381
+ let gamma = acos(cosineGamma);
5382
+ if (gamma < 1.0e-8) {
5383
+ return dggs_a5_normalize_vec3(mix(a, b, t));
5384
+ }
5385
+ let sinGamma = sin(gamma);
5386
+ if (abs(sinGamma) < 1.0e-8) {
5387
+ return dggs_a5_normalize_vec3(mix(a, b, t));
5388
+ }
5389
+ let weightA = sin((1.0 - t) * gamma) / sinGamma;
5390
+ let weightB = sin(t * gamma) / sinGamma;
5391
+ return dggs_a5_normalize_vec3(weightA * a + weightB * b);
5392
+ }
5393
+
5394
+ fn dggs_a5_safe_acos(value : f32) -> f32 {
5395
+ if (value < 1.0e-3) {
5396
+ return 2.0 * value + value * value * value / 3.0;
5397
+ }
5398
+ return acos(clamp(1.0 - 2.0 * value * value, -1.0, 1.0));
5399
+ }
5400
+
5401
+ fn dggs_a5_polyhedral_inverse_edge(a : vec3f, b : vec3f, edgeHeight : f32) -> vec3f {
5402
+ let difference = dggs_a5_vector_difference(a, b);
5403
+ let denominator = max(dggs_a5_safe_acos(difference), 1.0e-8);
5404
+ let interpolation = dggs_a5_safe_acos(clamp(edgeHeight, 0.0, 1.0) * difference) / denominator;
5405
+ return dggs_a5_slerp(a, b, clamp(interpolation, 0.0, 1.0));
5406
+ }
5407
+
5408
+ fn dggs_a5_polyhedral_inverse(
5409
+ facePoint : vec2f,
5410
+ faceTriangle : DggsA5FaceTriangle,
5411
+ sphericalTriangle : DggsA5SphericalTriangle
5412
+ ) -> vec3f {
5413
+ let barycentric = dggs_a5_face_to_barycentric(facePoint, faceTriangle);
5414
+ if (barycentric.x > 0.9999999) {
5415
+ return sphericalTriangle.a;
5416
+ }
5417
+ if (barycentric.y > 0.9999999) {
5418
+ return sphericalTriangle.b;
5419
+ }
5420
+ if (barycentric.z > 0.9999999) {
5421
+ return sphericalTriangle.c;
5422
+ }
5423
+ if (abs(barycentric.z) < 1.0e-6) {
5424
+ return dggs_a5_polyhedral_inverse_edge(
5425
+ sphericalTriangle.a,
5426
+ sphericalTriangle.b,
5427
+ 1.0 - barycentric.x
5428
+ );
5429
+ }
5430
+ if (abs(barycentric.y) < 1.0e-6) {
5431
+ return dggs_a5_polyhedral_inverse_edge(
5432
+ sphericalTriangle.a,
5433
+ sphericalTriangle.c,
5434
+ 1.0 - barycentric.x
5435
+ );
5436
+ }
5437
+
5438
+ let crossBC = cross(sphericalTriangle.b, sphericalTriangle.c);
5439
+ let areaABC = dggs_a5_spherical_triangle_area(sphericalTriangle);
5440
+ let h = max(1.0 - barycentric.x, 1.0e-8);
5441
+ let areaRatio = barycentric.z / h;
5442
+ let alpha = areaRatio * areaABC;
5443
+ let sineAlpha = sin(alpha);
5444
+ let halfSineAlpha = sin(alpha * 0.5);
5445
+ let chordCoefficient = 2.0 * halfSineAlpha * halfSineAlpha;
5446
+ let dotAB = dot(sphericalTriangle.a, sphericalTriangle.b);
5447
+ let dotBC = dot(sphericalTriangle.b, sphericalTriangle.c);
5448
+ let dotCA = dot(sphericalTriangle.c, sphericalTriangle.a);
5449
+ let lengthBC = length(crossBC);
5450
+ let volume = dot(sphericalTriangle.a, crossBC);
5451
+ let f = sineAlpha * volume + chordCoefficient * (dotAB * dotBC - dotCA);
5452
+ let g = chordCoefficient * lengthBC * (1.0 + dotAB);
5453
+ let edgeAngle = max(acos(clamp(dotBC, -1.0, 1.0)), 1.0e-8);
5454
+ let q = 2.0 / edgeAngle * atan2(g, f);
5455
+ let pointOnBC = dggs_a5_slerp(sphericalTriangle.b, sphericalTriangle.c, q);
5456
+ let k = dggs_a5_vector_difference(sphericalTriangle.a, pointOnBC);
5457
+ let denominator = max(dggs_a5_safe_acos(k), 1.0e-8);
5458
+ let t = dggs_a5_safe_acos(h * k) / denominator;
5459
+ return dggs_a5_slerp(sphericalTriangle.a, pointOnBC, t);
5460
+ }
5461
+
5462
+ fn dggs_a5_authalic_inverse(phi : f32) -> f32 {
5463
+ let sinPhi = sin(phi);
5464
+ let cosPhi = cos(phi);
5465
+ let coefficientX = 2.0 * (cosPhi - sinPhi) * (cosPhi + sinPhi);
5466
+ var u0 = coefficientX * 4.92842354825238055e-17 + 2.19128723067677184e-14;
5467
+ var u1 = coefficientX * u0 + 1.02018123778161003e-11;
5468
+ u0 = coefficientX * u1 - u0 + 5.08622073997266026e-9;
5469
+ u1 = coefficientX * u0 - u1 + 0.00000288319780486075558;
5470
+ u0 = coefficientX * u1 - u0 + 0.00223920899635416575;
5471
+ return phi + 2.0 * sinPhi * cosPhi * u0;
5472
+ }
5473
+
5474
+ fn dggs_a5_normalize_longitude(longitude : f32) -> f32 {
5475
+ var result = longitude;
5476
+ loop {
5477
+ if (result < -180.0) {
5478
+ result += 360.0;
5479
+ } else {
5480
+ break;
5481
+ }
5482
+ }
5483
+ loop {
5484
+ if (result >= 180.0) {
5485
+ result -= 360.0;
5486
+ } else {
5487
+ break;
5488
+ }
5489
+ }
5490
+ return result;
5491
+ }
5492
+
5493
+ fn dggs_a5_normalize_longitude_to_center(longitude : f32, centerLongitude : f32) -> f32 {
5494
+ var result = longitude;
5495
+ loop {
5496
+ if (result - centerLongitude > 180.0) {
5497
+ result -= 360.0;
5498
+ } else {
5499
+ break;
5500
+ }
5501
+ }
5502
+ loop {
5503
+ if (result - centerLongitude < -180.0) {
5504
+ result += 360.0;
5505
+ } else {
5506
+ break;
5507
+ }
5508
+ }
5509
+ return result;
5510
+ }
5511
+
5512
+ fn dggs_a5_sphere_to_lnglat(cartesian : vec3f) -> vec2f {
5513
+ let point = dggs_a5_normalize_vec3(cartesian);
5514
+ let theta = atan2(point.y, point.x);
5515
+ let phi = acos(clamp(point.z, -1.0, 1.0));
5516
+ let longitude = dggs_a5_normalize_longitude(theta * DGGS_RADIANS_TO_DEGREES - DGGS_A5_LONGITUDE_OFFSET);
5517
+ let authalicLatitude = DGGS_PI_OVER_2 - phi;
5518
+ let latitude = dggs_a5_authalic_inverse(authalicLatitude) * DGGS_RADIANS_TO_DEGREES;
5519
+ return vec2f(longitude, latitude);
5520
+ }
5521
+
5522
+ fn dggs_a5_face_to_lnglat(facePoint : vec2f, origin : u32) -> vec2f {
5523
+ let polar = dggs_a5_to_polar(facePoint);
5524
+ let faceTriangleIndex = dggs_a5_get_face_triangle_index(polar);
5525
+ let reflected = dggs_a5_should_reflect(polar);
5526
+ let faceTriangle = dggs_a5_get_face_triangle(faceTriangleIndex, reflected, false);
5527
+ let sphericalTriangle = dggs_a5_face_triangle_to_spherical_triangle(
5528
+ faceTriangleIndex,
5529
+ origin,
5530
+ reflected
5531
+ );
5532
+ return dggs_a5_sphere_to_lnglat(
5533
+ dggs_a5_polyhedral_inverse(facePoint, faceTriangle, sphericalTriangle)
5534
+ );
5535
+ }
5536
+
5537
+ fn dggs_a5_get_boundary_point(index : vec2u, vertexIndex : u32) -> vec2f {
5538
+ let cell = dggs_a5_deserialize(index);
5539
+ if (cell.valid == 0u || dggs_u64_is_zero(index)) {
5540
+ return vec2f(0.0);
5541
+ }
5542
+
5543
+ let shapeVertexCount = dggs_a5_get_shape_vertex_count(cell);
5544
+ let shapeIndex = dggs_a5_get_reversed_shape_index(vertexIndex, shapeVertexCount);
5545
+ let facePoint = dggs_a5_get_cell_shape_face_point(index, cell, shapeIndex);
5546
+ let centerPoint = dggs_a5_get_cell_center_face_point(index, cell);
5547
+ var lngLat = dggs_a5_face_to_lnglat(facePoint, cell.origin);
5548
+ let centerLngLat = dggs_a5_face_to_lnglat(centerPoint, cell.origin);
5549
+ lngLat.x = dggs_a5_normalize_longitude_to_center(lngLat.x, centerLngLat.x);
5550
+ return lngLat;
5551
+ }
5552
+
5553
+ fn dggs_a5_get_boundary_point_fp64_split(index : vec2u, vertexIndex : u32) -> vec4f {
5554
+ return dggs_boundary_point_to_fp64_split(dggs_a5_get_boundary_point(index, vertexIndex));
5555
+ }
5556
+
5557
+ fn dggs_h3_get_mode(index: vec2u) -> u32 {
5558
+ return dggs_u64_extract_bits(index, 59u, 4u);
5559
+ }
5560
+
5561
+ fn dggs_h3_is_cell_mode(index: vec2u) -> bool {
5562
+ return dggs_h3_get_mode(index) == DGGS_H3_CELL_MODE;
5563
+ }
5564
+
5565
+ fn dggs_h3_get_resolution(index: vec2u) -> u32 {
5566
+ return dggs_u64_extract_bits(index, 52u, 4u);
5567
+ }
5568
+
5569
+ fn dggs_h3_get_base_cell(index: vec2u) -> u32 {
5570
+ return dggs_u64_extract_bits(index, 45u, 7u);
5571
+ }
5572
+
5573
+ fn dggs_h3_digit_bit_offset(resolution: u32) -> u32 {
5574
+ return 3u * (DGGS_H3_MAX_RESOLUTION - resolution);
5575
+ }
5576
+
5577
+ fn dggs_h3_get_digit(index: vec2u, resolution: u32) -> u32 {
5578
+ if (resolution == 0u || resolution > DGGS_H3_MAX_RESOLUTION) {
5579
+ return DGGS_H3_UNUSED_DIGIT;
5580
+ }
5581
+ return dggs_u64_extract_bits(index, dggs_h3_digit_bit_offset(resolution), 3u);
5582
+ }
5583
+
5584
+ fn dggs_h3_is_valid_cell_id(index: vec2u) -> bool {
5585
+ let cellResolution = dggs_h3_get_resolution(index);
5586
+ if (
5587
+ !dggs_h3_is_cell_mode(index) ||
5588
+ cellResolution > DGGS_H3_MAX_RESOLUTION ||
5589
+ dggs_h3_get_base_cell(index) > DGGS_H3_MAX_BASE_CELL
5590
+ ) {
5591
+ return false;
5592
+ }
5593
+
5594
+ var digitResolution = 1u;
5595
+ loop {
5596
+ if (digitResolution > DGGS_H3_MAX_RESOLUTION) {
5597
+ break;
5598
+ }
5599
+
5600
+ let digit = dggs_h3_get_digit(index, digitResolution);
5601
+ if (digitResolution <= cellResolution) {
5602
+ if (digit == DGGS_H3_UNUSED_DIGIT) {
5603
+ return false;
5604
+ }
5605
+ } else if (digit != DGGS_H3_UNUSED_DIGIT) {
5606
+ return false;
5607
+ }
5608
+ digitResolution += 1u;
5609
+ }
5610
+ return true;
5611
+ }
5612
+
5613
+ fn dggs_h3_get_parent(index: vec2u, parentResolution: u32) -> vec2u {
5614
+ let currentResolution = dggs_h3_get_resolution(index);
5615
+ let targetResolution = min(parentResolution, currentResolution);
5616
+ var parent = dggs_u64_set_bits(index, 52u, 4u, targetResolution);
5617
+ var resolution = targetResolution + 1u;
5618
+ loop {
5619
+ if (resolution > DGGS_H3_MAX_RESOLUTION) {
5620
+ break;
5621
+ }
5622
+ parent = dggs_u64_set_bits(
5623
+ parent,
5624
+ dggs_h3_digit_bit_offset(resolution),
5625
+ 3u,
5626
+ DGGS_H3_UNUSED_DIGIT
5627
+ );
5628
+ resolution += 1u;
5629
+ }
5630
+ return parent;
5631
+ }
5632
+
5633
+ fn dggs_h3_positive_angle_rads(angle: f32) -> f32 {
5634
+ var result = angle;
5635
+ if (result < 0.0) {
5636
+ result += DGGS_TWO_PI;
5637
+ }
5638
+ if (result >= DGGS_TWO_PI) {
5639
+ result -= DGGS_TWO_PI;
5640
+ }
5641
+ return result;
5642
+ }
5643
+
5644
+ fn dggs_h3_constrain_longitude(longitude: f32) -> f32 {
5645
+ var result = longitude;
5646
+ loop {
5647
+ if (result <= DGGS_PI) {
5648
+ break;
5649
+ }
5650
+ result -= DGGS_TWO_PI;
5651
+ }
5652
+ loop {
5653
+ if (result >= -DGGS_PI) {
5654
+ break;
5655
+ }
5656
+ result += DGGS_TWO_PI;
5657
+ }
5658
+ return result;
5659
+ }
5660
+
5661
+ fn dggs_h3_is_resolution_class_iii(resolution: u32) -> bool {
5662
+ return (resolution & 1u) != 0u;
5663
+ }
5664
+
5665
+ fn dggs_h3_is_base_cell_pentagon(baseCell: u32) -> bool {
5666
+ return baseCell == 4u ||
5667
+ baseCell == 14u ||
5668
+ baseCell == 24u ||
5669
+ baseCell == 38u ||
5670
+ baseCell == 49u ||
5671
+ baseCell == 58u ||
5672
+ baseCell == 63u ||
5673
+ baseCell == 72u ||
5674
+ baseCell == 83u ||
5675
+ baseCell == 97u ||
5676
+ baseCell == 107u ||
5677
+ baseCell == 117u;
5678
+ }
5679
+
5680
+ fn dggs_h3_get_base_cell_home(baseCell: u32) -> DggsH3FaceIJK {
5681
+ let values = array<vec4i, 122>(
5682
+ vec4i(1, 1, 0, 0),
5683
+ vec4i(2, 1, 1, 0),
5684
+ vec4i(1, 0, 0, 0),
5685
+ vec4i(2, 1, 0, 0),
5686
+ vec4i(0, 2, 0, 0),
5687
+ vec4i(1, 1, 1, 0),
5688
+ vec4i(1, 0, 0, 1),
5689
+ vec4i(2, 0, 0, 0),
5690
+ vec4i(0, 1, 0, 0),
5691
+ vec4i(2, 0, 1, 0),
5692
+ vec4i(1, 0, 1, 0),
5693
+ vec4i(1, 0, 1, 1),
5694
+ vec4i(3, 1, 0, 0),
5695
+ vec4i(3, 1, 1, 0),
5696
+ vec4i(11, 2, 0, 0),
5697
+ vec4i(4, 1, 0, 0),
5698
+ vec4i(0, 0, 0, 0),
5699
+ vec4i(6, 0, 1, 0),
5700
+ vec4i(0, 0, 0, 1),
5701
+ vec4i(2, 0, 1, 1),
5702
+ vec4i(7, 0, 0, 1),
5703
+ vec4i(2, 0, 0, 1),
5704
+ vec4i(0, 1, 1, 0),
5705
+ vec4i(6, 0, 0, 1),
5706
+ vec4i(10, 2, 0, 0),
5707
+ vec4i(6, 0, 0, 0),
5708
+ vec4i(3, 0, 0, 0),
5709
+ vec4i(11, 1, 0, 0),
5710
+ vec4i(4, 1, 1, 0),
5711
+ vec4i(3, 0, 1, 0),
5712
+ vec4i(0, 0, 1, 1),
5713
+ vec4i(4, 0, 0, 0),
5714
+ vec4i(5, 0, 1, 0),
5715
+ vec4i(0, 0, 1, 0),
5716
+ vec4i(7, 0, 1, 0),
5717
+ vec4i(11, 1, 1, 0),
5718
+ vec4i(7, 0, 0, 0),
5719
+ vec4i(10, 1, 0, 0),
5720
+ vec4i(12, 2, 0, 0),
5721
+ vec4i(6, 1, 0, 1),
5722
+ vec4i(7, 1, 0, 1),
5723
+ vec4i(4, 0, 0, 1),
5724
+ vec4i(3, 0, 0, 1),
5725
+ vec4i(3, 0, 1, 1),
5726
+ vec4i(4, 0, 1, 0),
5727
+ vec4i(6, 1, 0, 0),
5728
+ vec4i(11, 0, 0, 0),
5729
+ vec4i(8, 0, 0, 1),
5730
+ vec4i(5, 0, 0, 1),
5731
+ vec4i(14, 2, 0, 0),
5732
+ vec4i(5, 0, 0, 0),
5733
+ vec4i(12, 1, 0, 0),
5734
+ vec4i(10, 1, 1, 0),
5735
+ vec4i(4, 0, 1, 1),
5736
+ vec4i(12, 1, 1, 0),
5737
+ vec4i(7, 1, 0, 0),
5738
+ vec4i(11, 0, 1, 0),
5739
+ vec4i(10, 0, 0, 0),
5740
+ vec4i(13, 2, 0, 0),
5741
+ vec4i(10, 0, 0, 1),
5742
+ vec4i(11, 0, 0, 1),
5743
+ vec4i(9, 0, 1, 0),
5744
+ vec4i(8, 0, 1, 0),
5745
+ vec4i(6, 2, 0, 0),
5746
+ vec4i(8, 0, 0, 0),
5747
+ vec4i(9, 0, 0, 1),
5748
+ vec4i(14, 1, 0, 0),
5749
+ vec4i(5, 1, 0, 1),
5750
+ vec4i(16, 0, 1, 1),
5751
+ vec4i(8, 1, 0, 1),
5752
+ vec4i(5, 1, 0, 0),
5753
+ vec4i(12, 0, 0, 0),
5754
+ vec4i(7, 2, 0, 0),
5755
+ vec4i(12, 0, 1, 0),
5756
+ vec4i(10, 0, 1, 0),
5757
+ vec4i(9, 0, 0, 0),
5758
+ vec4i(13, 1, 0, 0),
5759
+ vec4i(16, 0, 0, 1),
5760
+ vec4i(15, 0, 1, 1),
5761
+ vec4i(15, 0, 1, 0),
5762
+ vec4i(16, 0, 1, 0),
5763
+ vec4i(14, 1, 1, 0),
5764
+ vec4i(13, 1, 1, 0),
5765
+ vec4i(5, 2, 0, 0),
5766
+ vec4i(8, 1, 0, 0),
5767
+ vec4i(14, 0, 0, 0),
5768
+ vec4i(9, 1, 0, 1),
5769
+ vec4i(14, 0, 0, 1),
5770
+ vec4i(17, 0, 0, 1),
5771
+ vec4i(12, 0, 0, 1),
5772
+ vec4i(16, 0, 0, 0),
5773
+ vec4i(17, 0, 1, 1),
5774
+ vec4i(15, 0, 0, 1),
5775
+ vec4i(16, 1, 0, 1),
5776
+ vec4i(9, 1, 0, 0),
5777
+ vec4i(15, 0, 0, 0),
5778
+ vec4i(13, 0, 0, 0),
5779
+ vec4i(8, 2, 0, 0),
5780
+ vec4i(13, 0, 1, 0),
5781
+ vec4i(17, 1, 0, 1),
5782
+ vec4i(19, 0, 1, 0),
5783
+ vec4i(14, 0, 1, 0),
5784
+ vec4i(19, 0, 1, 1),
5785
+ vec4i(17, 0, 1, 0),
5786
+ vec4i(13, 0, 0, 1),
5787
+ vec4i(17, 0, 0, 0),
5788
+ vec4i(16, 1, 0, 0),
5789
+ vec4i(9, 2, 0, 0),
5790
+ vec4i(15, 1, 0, 1),
5791
+ vec4i(15, 1, 0, 0),
5792
+ vec4i(18, 0, 1, 1),
5793
+ vec4i(18, 0, 0, 1),
5794
+ vec4i(19, 0, 0, 1),
5795
+ vec4i(17, 1, 0, 0),
5796
+ vec4i(19, 0, 0, 0),
5797
+ vec4i(18, 0, 1, 0),
5798
+ vec4i(18, 1, 0, 1),
5799
+ vec4i(19, 2, 0, 0),
5800
+ vec4i(19, 1, 0, 0),
5801
+ vec4i(18, 0, 0, 0),
5802
+ vec4i(19, 1, 0, 1),
5803
+ vec4i(18, 1, 0, 0)
5804
+ );
5805
+ let value = values[min(baseCell, DGGS_H3_MAX_BASE_CELL)];
5806
+ return DggsH3FaceIJK(
5807
+ u32(value.x),
5808
+ value.yzw,
5809
+ select(1u, 0u, baseCell > DGGS_H3_MAX_BASE_CELL)
5810
+ );
5811
+ }
5812
+
5813
+ fn dggs_h3_get_face_center_geo(face: u32) -> vec2f {
5814
+ let values = array<vec2f, 20>(
5815
+ vec2f(0.803582649718989942, 1.248397419617396099),
5816
+ vec2f(1.307747883455638156, 2.536945009877921159),
5817
+ vec2f(1.054751253523952054, -1.347517358900396623),
5818
+ vec2f(0.600191595538186799, -0.450603909469755746),
5819
+ vec2f(0.491715428198773866, 0.401988202911306943),
5820
+ vec2f(0.172745327415618701, 1.678146885280433686),
5821
+ vec2f(0.605929321571350690, 2.953923329812411617),
5822
+ vec2f(0.427370518328979641, -1.888876200336285401),
5823
+ vec2f(-0.079066118549212831, -0.733429513380867741),
5824
+ vec2f(-0.230961644455383637, 0.506495587332349035),
5825
+ vec2f(0.079066118549212831, 2.408163140208925497),
5826
+ vec2f(0.230961644455383637, -2.635097066257444203),
5827
+ vec2f(-0.172745327415618701, -1.463445768309359553),
5828
+ vec2f(-0.605929321571350690, -0.187669323777381622),
5829
+ vec2f(-0.427370518328979641, 1.252716453253507838),
5830
+ vec2f(-0.600191595538186799, 2.690988744120037492),
5831
+ vec2f(-0.491715428198773866, -2.739604450678486295),
5832
+ vec2f(-0.803582649718989942, -1.893195233972397139),
5833
+ vec2f(-1.307747883455638156, -0.604647643711872080),
5834
+ vec2f(-1.054751253523952054, 1.794075294689396615)
5835
+ );
5836
+ return values[min(face, 19u)];
5837
+ }
5838
+
5839
+ fn dggs_h3_get_face_axis_azimuths(face: u32) -> vec3f {
5840
+ let values = array<vec3f, 20>(
5841
+ vec3f(5.619958268523939882, 3.525563166130744542, 1.431168063737548730),
5842
+ vec3f(5.760339081714187279, 3.665943979320991689, 1.571548876927796127),
5843
+ vec3f(0.780213654393430055, 4.969003859179821079, 2.874608756786625655),
5844
+ vec3f(0.430469363979999913, 4.619259568766391033, 2.524864466373195467),
5845
+ vec3f(6.130269123335111400, 4.035874020941915804, 1.941478918548720291),
5846
+ vec3f(2.692877706530642877, 0.598482604137447119, 4.787272808923838195),
5847
+ vec3f(2.982963003477243874, 0.888567901084048369, 5.077358105870439581),
5848
+ vec3f(3.532912002790141181, 1.438516900396945656, 5.627307105183336758),
5849
+ vec3f(3.494305004259568154, 1.399909901866372864, 5.588700106652763840),
5850
+ vec3f(3.003214169499538391, 0.908819067106342928, 5.097609271892733906),
5851
+ vec3f(5.930472956509811562, 3.836077854116615875, 1.741682751723420374),
5852
+ vec3f(0.138378484090254847, 4.327168688876645809, 2.232773586483450311),
5853
+ vec3f(0.448714947059150361, 4.637505151845541521, 2.543110049452346120),
5854
+ vec3f(0.158629650112549365, 4.347419854898940135, 2.253024752505744869),
5855
+ vec3f(5.891865957979238535, 3.797470855586042958, 1.703075753192847583),
5856
+ vec3f(2.711123289609793325, 0.616728187216597771, 4.805518392002988683),
5857
+ vec3f(3.294508837434268316, 1.200113735041072948, 5.388903939827463911),
5858
+ vec3f(3.804819692245439833, 1.710424589852244509, 5.899214794638635174),
5859
+ vec3f(3.664438879055192436, 1.570043776661997111, 5.758833981448388027),
5860
+ vec3f(2.361378999196363184, 0.266983896803167583, 4.455774101589558636)
5861
+ );
5862
+ return values[min(face, 19u)];
5863
+ }
5864
+
5865
+ fn dggs_h3_get_max_dim_by_cii_resolution(resolution: u32) -> i32 {
5866
+ let values = array<i32, 17>(
5867
+ 2, -1, 14, -1, 98, -1, 686, -1, 4802, -1, 33614, -1, 235298, -1, 1647086, -1, 11529602
5868
+ );
5869
+ return values[min(resolution, 16u)];
5870
+ }
5871
+
5872
+ fn dggs_h3_get_unit_vector(digit: u32) -> vec3i {
5873
+ if (digit == 1u) {
5874
+ return vec3i(0, 0, 1);
5875
+ }
5876
+ if (digit == 2u) {
5877
+ return vec3i(0, 1, 0);
5878
+ }
5879
+ if (digit == 3u) {
5880
+ return vec3i(0, 1, 1);
5881
+ }
5882
+ if (digit == 4u) {
5883
+ return vec3i(1, 0, 0);
5884
+ }
5885
+ if (digit == 5u) {
5886
+ return vec3i(1, 0, 1);
5887
+ }
5888
+ if (digit == 6u) {
5889
+ return vec3i(1, 1, 0);
5890
+ }
5891
+ return vec3i(0);
5892
+ }
5893
+
5894
+ fn dggs_h3_ijk_normalize(coord: vec3i) -> vec3i {
5895
+ var normalized = coord;
5896
+ if (normalized.x < 0) {
5897
+ normalized.y -= normalized.x;
5898
+ normalized.z -= normalized.x;
5899
+ normalized.x = 0;
5900
+ }
5901
+ if (normalized.y < 0) {
5902
+ normalized.x -= normalized.y;
5903
+ normalized.z -= normalized.y;
5904
+ normalized.y = 0;
5905
+ }
5906
+ if (normalized.z < 0) {
5907
+ normalized.x -= normalized.z;
5908
+ normalized.y -= normalized.z;
5909
+ normalized.z = 0;
5910
+ }
5911
+
5912
+ let minimumValue = min(normalized.x, min(normalized.y, normalized.z));
5913
+ if (minimumValue > 0) {
5914
+ normalized -= vec3i(minimumValue);
5915
+ }
5916
+ return normalized;
5917
+ }
5918
+
5919
+ fn dggs_h3_neighbor(coord: vec3i, digit: u32) -> vec3i {
5920
+ if (digit > 0u && digit < DGGS_H3_UNUSED_DIGIT) {
5921
+ return dggs_h3_ijk_normalize(coord + dggs_h3_get_unit_vector(digit));
5922
+ }
5923
+ return coord;
5924
+ }
5925
+
5926
+ fn dggs_h3_down_ap3(coord: vec3i) -> vec3i {
5927
+ let iVector = vec3i(2, 0, 1) * coord.x;
5928
+ let jVector = vec3i(1, 2, 0) * coord.y;
5929
+ let kVector = vec3i(0, 1, 2) * coord.z;
5930
+ return dggs_h3_ijk_normalize(iVector + jVector + kVector);
5931
+ }
5932
+
5933
+ fn dggs_h3_down_ap3r(coord: vec3i) -> vec3i {
5934
+ let iVector = vec3i(2, 1, 0) * coord.x;
5935
+ let jVector = vec3i(0, 2, 1) * coord.y;
5936
+ let kVector = vec3i(1, 0, 2) * coord.z;
5937
+ return dggs_h3_ijk_normalize(iVector + jVector + kVector);
5938
+ }
5939
+
5940
+ fn dggs_h3_down_ap7(coord: vec3i) -> vec3i {
5941
+ let iVector = vec3i(3, 0, 1) * coord.x;
5942
+ let jVector = vec3i(1, 3, 0) * coord.y;
5943
+ let kVector = vec3i(0, 1, 3) * coord.z;
5944
+ return dggs_h3_ijk_normalize(iVector + jVector + kVector);
5945
+ }
5946
+
5947
+ fn dggs_h3_down_ap7r(coord: vec3i) -> vec3i {
5948
+ let iVector = vec3i(3, 1, 0) * coord.x;
5949
+ let jVector = vec3i(0, 3, 1) * coord.y;
5950
+ let kVector = vec3i(1, 0, 3) * coord.z;
5951
+ return dggs_h3_ijk_normalize(iVector + jVector + kVector);
5952
+ }
5953
+
5954
+ fn dggs_h3_get_center_face_ijk(index: vec2u) -> DggsH3FaceIJK {
5955
+ if (!dggs_h3_is_valid_cell_id(index)) {
5956
+ return DggsH3FaceIJK(0u, vec3i(0), 0u);
5957
+ }
5958
+
5959
+ let baseCell = dggs_h3_get_base_cell(index);
5960
+ if (dggs_h3_is_base_cell_pentagon(baseCell)) {
5961
+ return DggsH3FaceIJK(0u, vec3i(0), 0u);
5962
+ }
5963
+
5964
+ var faceIJK = dggs_h3_get_base_cell_home(baseCell);
5965
+ let cellResolution = dggs_h3_get_resolution(index);
5966
+ var resolution = 1u;
5967
+ loop {
5968
+ if (resolution > cellResolution) {
5969
+ break;
5970
+ }
5971
+
5972
+ if (dggs_h3_is_resolution_class_iii(resolution)) {
5973
+ faceIJK.coord = dggs_h3_down_ap7(faceIJK.coord);
5974
+ } else {
5975
+ faceIJK.coord = dggs_h3_down_ap7r(faceIJK.coord);
5976
+ }
5977
+ faceIJK.coord = dggs_h3_neighbor(faceIJK.coord, dggs_h3_get_digit(index, resolution));
5978
+ resolution += 1u;
5979
+ }
5980
+
5981
+ return faceIJK;
5982
+ }
5983
+
5984
+ fn dggs_h3_get_vertex_offset(resolution: u32, vertexIndex: u32) -> vec3i {
5985
+ let pointIndex = vertexIndex % 6u;
5986
+ if (dggs_h3_is_resolution_class_iii(resolution)) {
5987
+ let vertices = array<vec3i, 6>(
5988
+ vec3i(5, 4, 0),
5989
+ vec3i(1, 5, 0),
5990
+ vec3i(0, 5, 4),
5991
+ vec3i(0, 1, 5),
5992
+ vec3i(4, 0, 5),
5993
+ vec3i(5, 0, 1)
5994
+ );
5995
+ return vertices[pointIndex];
5996
+ }
5997
+
5998
+ let vertices = array<vec3i, 6>(
5999
+ vec3i(2, 1, 0),
6000
+ vec3i(1, 2, 0),
6001
+ vec3i(0, 2, 1),
6002
+ vec3i(0, 1, 2),
6003
+ vec3i(1, 0, 2),
6004
+ vec3i(2, 0, 1)
6005
+ );
6006
+ return vertices[pointIndex];
6007
+ }
6008
+
6009
+ fn dggs_h3_get_boundary_vertex(index: vec2u, vertexIndex: u32) -> DggsH3BoundaryVertex {
6010
+ let center = dggs_h3_get_center_face_ijk(index);
6011
+ if (center.valid == 0u) {
6012
+ return DggsH3BoundaryVertex(0u, vec3i(0), 0u, 0u);
6013
+ }
6014
+
6015
+ let cellResolution = dggs_h3_get_resolution(index);
6016
+ var adjustedResolution = cellResolution;
6017
+ var centerCoord = dggs_h3_down_ap3r(dggs_h3_down_ap3(center.coord));
6018
+ if (dggs_h3_is_resolution_class_iii(cellResolution)) {
6019
+ centerCoord = dggs_h3_down_ap7r(centerCoord);
6020
+ adjustedResolution += 1u;
6021
+ }
6022
+
6023
+ let vertexCoord = dggs_h3_ijk_normalize(
6024
+ centerCoord + dggs_h3_get_vertex_offset(cellResolution, vertexIndex)
6025
+ );
6026
+ let maximumDimension = dggs_h3_get_max_dim_by_cii_resolution(adjustedResolution) * 3;
6027
+ let vertexDimension = vertexCoord.x + vertexCoord.y + vertexCoord.z;
6028
+ if (vertexDimension >= maximumDimension) {
6029
+ return DggsH3BoundaryVertex(center.face, vertexCoord, adjustedResolution, 0u);
6030
+ }
6031
+
6032
+ return DggsH3BoundaryVertex(center.face, vertexCoord, adjustedResolution, 1u);
6033
+ }
6034
+
6035
+ fn dggs_h3_is_single_face_boundary(index: vec2u) -> bool {
6036
+ var vertexIndex = 0u;
6037
+ loop {
6038
+ if (vertexIndex >= 6u) {
6039
+ break;
6040
+ }
6041
+ if (dggs_h3_get_boundary_vertex(index, vertexIndex).valid == 0u) {
6042
+ return false;
6043
+ }
6044
+ vertexIndex += 1u;
6045
+ }
6046
+ return true;
6047
+ }
6048
+
6049
+ fn dggs_h3_ijk_to_hex2d(coord: vec3i) -> vec2f {
6050
+ let i = f32(coord.x - coord.z);
6051
+ let j = f32(coord.y - coord.z);
6052
+ return vec2f(i - 0.5 * j, j * DGGS_H3_SQRT3_2);
6053
+ }
6054
+
6055
+ fn dggs_h3_geo_az_distance_rads(center: vec2f, azimuth: f32, distance: f32) -> vec2f {
6056
+ if (distance < 1.0e-7) {
6057
+ return center;
6058
+ }
6059
+
6060
+ let positiveAzimuth = dggs_h3_positive_angle_rads(azimuth);
6061
+ let latitude = asin(clamp(
6062
+ sin(center.x) * cos(distance) + cos(center.x) * sin(distance) * cos(positiveAzimuth),
6063
+ -1.0,
6064
+ 1.0
6065
+ ));
6066
+ var longitude = 0.0;
6067
+ if (abs(latitude - DGGS_PI_OVER_2) < 1.0e-7) {
6068
+ return vec2f(DGGS_PI_OVER_2, 0.0);
6069
+ }
6070
+ if (abs(latitude + DGGS_PI_OVER_2) < 1.0e-7) {
6071
+ return vec2f(-DGGS_PI_OVER_2, 0.0);
6072
+ }
6073
+
6074
+ let inverseCosLatitude = 1.0 / max(cos(latitude), 1.0e-8);
6075
+ let sinLongitude = clamp(
6076
+ sin(positiveAzimuth) * sin(distance) * inverseCosLatitude,
6077
+ -1.0,
6078
+ 1.0
6079
+ );
6080
+ let cosLongitude = clamp(
6081
+ (cos(distance) - sin(center.x) * sin(latitude)) / max(cos(center.x), 1.0e-8) *
6082
+ inverseCosLatitude,
6083
+ -1.0,
6084
+ 1.0
6085
+ );
6086
+ longitude = dggs_h3_constrain_longitude(center.y + atan2(sinLongitude, cosLongitude));
6087
+ return vec2f(latitude, longitude);
6088
+ }
6089
+
6090
+ fn dggs_h3_hex2d_to_lnglat(point: vec2f, face: u32, resolution: u32, substrate: bool) -> vec2f {
6091
+ var radius = length(point);
6092
+ let center = dggs_h3_get_face_center_geo(face);
6093
+ if (radius < 1.0e-7) {
6094
+ return vec2f(center.y * DGGS_RADIANS_TO_DEGREES, center.x * DGGS_RADIANS_TO_DEGREES);
6095
+ }
6096
+
6097
+ var theta = atan2(point.y, point.x);
6098
+ var resolutionIndex = 0u;
6099
+ loop {
6100
+ if (resolutionIndex >= resolution) {
6101
+ break;
6102
+ }
6103
+ radius *= DGGS_H3_RSQRT7;
6104
+ resolutionIndex += 1u;
6105
+ }
6106
+
6107
+ if (substrate) {
6108
+ radius *= DGGS_H3_ONETHIRD;
6109
+ if (dggs_h3_is_resolution_class_iii(resolution)) {
6110
+ radius *= DGGS_H3_RSQRT7;
6111
+ }
6112
+ } else if (dggs_h3_is_resolution_class_iii(resolution)) {
6113
+ theta = dggs_h3_positive_angle_rads(theta + DGGS_H3_AP7_ROT_RADS);
6114
+ }
6115
+
6116
+ radius = atan(radius * DGGS_H3_RES0_U_GNOMONIC);
6117
+ theta = dggs_h3_positive_angle_rads(dggs_h3_get_face_axis_azimuths(face).x - theta);
6118
+ let latitudeLongitude = dggs_h3_geo_az_distance_rads(center, theta, radius);
6119
+ return vec2f(
6120
+ latitudeLongitude.y * DGGS_RADIANS_TO_DEGREES,
6121
+ latitudeLongitude.x * DGGS_RADIANS_TO_DEGREES
6122
+ );
6123
+ }
6124
+
6125
+ fn dggs_h3_get_boundary_point(index: vec2u, vertexIndex: u32) -> vec2f {
6126
+ if (!dggs_h3_is_single_face_boundary(index)) {
6127
+ return vec2f(0.0);
6128
+ }
6129
+
6130
+ let vertex = dggs_h3_get_boundary_vertex(index, vertexIndex % 6u);
6131
+ let hexPoint = dggs_h3_ijk_to_hex2d(vertex.coord);
6132
+ return dggs_h3_hex2d_to_lnglat(hexPoint, vertex.face, vertex.resolution, true);
6133
+ }
6134
+
6135
+ fn dggs_h3_get_boundary_point_fp64_split(index: vec2u, vertexIndex: u32) -> vec4f {
6136
+ return dggs_boundary_point_to_fp64_split(dggs_h3_get_boundary_point(index, vertexIndex));
6137
+ }
6138
+
6139
+ fn dggs_s2_get_face(index: vec2u) -> u32 {
6140
+ return dggs_u64_extract_bits(index, 61u, 3u);
6141
+ }
6142
+
6143
+ fn dggs_s2_get_level(index: vec2u) -> u32 {
6144
+ let trailingZeros = dggs_u64_count_trailing_zeros(index);
6145
+ if (trailingZeros > 60u) {
6146
+ return 0u;
6147
+ }
6148
+ return DGGS_S2_MAX_LEVEL - trailingZeros / 2u;
6149
+ }
6150
+
6151
+ fn dggs_s2_is_valid_cell_id(index: vec2u) -> bool {
6152
+ let trailingZeros = dggs_u64_count_trailing_zeros(index);
6153
+ return !dggs_u64_is_zero(index) &&
6154
+ dggs_s2_get_face(index) <= 5u &&
6155
+ trailingZeros <= 60u &&
6156
+ trailingZeros % 2u == 0u;
6157
+ }
6158
+
6159
+ fn dggs_s2_get_child_position(index: vec2u, level: u32) -> u32 {
6160
+ if (level == 0u || level > DGGS_S2_MAX_LEVEL) {
6161
+ return 0u;
6162
+ }
6163
+ return dggs_u64_extract_bits(index, 2u * (DGGS_S2_MAX_LEVEL - level) + 1u, 2u);
6164
+ }
6165
+
6166
+ // Packed geohash keys store the geohash length in bits 60..63 and right-align
6167
+ // the base32 character codes in the lower 60 bits, first character first.
6168
+ fn dggs_geohash_get_length(index: vec2u) -> u32 {
6169
+ return min(
6170
+ dggs_u64_extract_bits(index, DGGS_GEOHASH_LENGTH_BIT_OFFSET, 4u),
6171
+ DGGS_GEOHASH_MAX_LENGTH
6172
+ );
6173
+ }
6174
+
6175
+ fn dggs_geohash_get_character(index: vec2u, characterIndex: u32) -> u32 {
6176
+ let length = dggs_geohash_get_length(index);
6177
+ if (characterIndex >= length) {
6178
+ return 0u;
6179
+ }
6180
+ return dggs_u64_extract_bits(index, 5u * (length - characterIndex - 1u), 5u);
6181
+ }
6182
+
6183
+ // Returns vec4(west, south, east, north) in longitude/latitude degrees.
6184
+ fn dggs_geohash_get_bounds(index: vec2u) -> vec4f {
6185
+ let length = dggs_geohash_get_length(index);
6186
+ var west = -180.0;
6187
+ var east = 180.0;
6188
+ var south = -90.0;
6189
+ var north = 90.0;
6190
+ var isLongitude = true;
6191
+ var characterIndex = 0u;
6192
+ loop {
6193
+ if (characterIndex >= length) {
6194
+ break;
6195
+ }
6196
+
6197
+ let character = dggs_geohash_get_character(index, characterIndex);
6198
+ var bitIndex = 0u;
6199
+ loop {
6200
+ if (bitIndex >= 5u) {
6201
+ break;
6202
+ }
6203
+
6204
+ let bit = (character >> (4u - bitIndex)) & 1u;
6205
+ if (isLongitude) {
6206
+ let midpoint = (west + east) * 0.5;
6207
+ if (bit == 0u) {
6208
+ east = midpoint;
6209
+ } else {
6210
+ west = midpoint;
6211
+ }
6212
+ } else {
6213
+ let midpoint = (south + north) * 0.5;
6214
+ if (bit == 0u) {
6215
+ north = midpoint;
6216
+ } else {
6217
+ south = midpoint;
6218
+ }
6219
+ }
6220
+ isLongitude = !isLongitude;
6221
+ bitIndex += 1u;
6222
+ }
6223
+ characterIndex += 1u;
6224
+ }
6225
+
6226
+ return vec4f(west, south, east, north);
6227
+ }
6228
+
6229
+ fn dggs_bounds_get_boundary_point(bounds: vec4f, vertexIndex: u32) -> vec2f {
6230
+ let pointIndex = vertexIndex % 4u;
6231
+ if (pointIndex == 0u) {
6232
+ return vec2f(bounds.x, bounds.w);
6233
+ }
6234
+ if (pointIndex == 1u) {
6235
+ return vec2f(bounds.z, bounds.w);
6236
+ }
6237
+ if (pointIndex == 2u) {
6238
+ return vec2f(bounds.z, bounds.y);
6239
+ }
6240
+ return vec2f(bounds.x, bounds.y);
6241
+ }
6242
+
6243
+ fn dggs_geohash_get_boundary_point(index: vec2u, vertexIndex: u32) -> vec2f {
6244
+ return dggs_bounds_get_boundary_point(dggs_geohash_get_bounds(index), vertexIndex);
6245
+ }
6246
+
6247
+ fn dggs_geohash_get_boundary_point_fp64_split(index: vec2u, vertexIndex: u32) -> vec4f {
6248
+ return dggs_boundary_point_to_fp64_split(dggs_geohash_get_boundary_point(index, vertexIndex));
6249
+ }
6250
+
6251
+ // Packed quadkey keys store the quadkey length in bits 58..63 and right-align
6252
+ // the base4 digits in the lower 58 bits, first digit first.
6253
+ fn dggs_quadkey_get_length(index: vec2u) -> u32 {
6254
+ return min(
6255
+ dggs_u64_extract_bits(index, DGGS_QUADKEY_LENGTH_BIT_OFFSET, 6u),
6256
+ DGGS_QUADKEY_MAX_LENGTH
6257
+ );
6258
+ }
6259
+
6260
+ fn dggs_quadkey_get_digit(index: vec2u, digitIndex: u32) -> u32 {
6261
+ let length = dggs_quadkey_get_length(index);
6262
+ if (digitIndex >= length) {
6263
+ return 0u;
6264
+ }
6265
+ return dggs_u64_extract_bits(index, 2u * (length - digitIndex - 1u), 2u);
6266
+ }
6267
+
6268
+ fn dggs_quadkey_get_tile(index: vec2u) -> vec3u {
6269
+ let length = dggs_quadkey_get_length(index);
6270
+ var tileX = 0u;
6271
+ var tileY = 0u;
6272
+ var digitIndex = 0u;
6273
+ loop {
6274
+ if (digitIndex >= length) {
6275
+ break;
6276
+ }
6277
+
6278
+ let digit = dggs_quadkey_get_digit(index, digitIndex);
6279
+ let mask = 1u << (length - digitIndex - 1u);
6280
+ if ((digit & 1u) != 0u) {
6281
+ tileX |= mask;
6282
+ }
6283
+ if ((digit & 2u) != 0u) {
6284
+ tileY |= mask;
6285
+ }
6286
+ digitIndex += 1u;
6287
+ }
6288
+ return vec3u(tileX, tileY, length);
6289
+ }
6290
+
6291
+ fn dggs_web_mercator_tile_y_to_latitude(tileY: f32, tileScale: f32) -> f32 {
6292
+ let mercatorY = DGGS_PI * (1.0 - 2.0 * tileY / tileScale);
6293
+ return (2.0 * atan(exp(mercatorY)) - DGGS_PI * 0.5) * DGGS_RADIANS_TO_DEGREES;
6294
+ }
6295
+
6296
+ // Returns vec4(west, south, east, north) in longitude/latitude degrees.
6297
+ fn dggs_quadkey_get_bounds(index: vec2u) -> vec4f {
6298
+ let tile = dggs_quadkey_get_tile(index);
6299
+ let tileScale = f32(1u << tile.z);
6300
+ let west = f32(tile.x) / tileScale * 360.0 - 180.0;
6301
+ let east = f32(tile.x + 1u) / tileScale * 360.0 - 180.0;
6302
+ let north = dggs_web_mercator_tile_y_to_latitude(f32(tile.y), tileScale);
6303
+ let south = dggs_web_mercator_tile_y_to_latitude(f32(tile.y + 1u), tileScale);
6304
+ return vec4f(west, south, east, north);
6305
+ }
6306
+
6307
+ fn dggs_quadkey_get_boundary_point(index: vec2u, vertexIndex: u32) -> vec2f {
6308
+ return dggs_bounds_get_boundary_point(dggs_quadkey_get_bounds(index), vertexIndex);
6309
+ }
6310
+
6311
+ fn dggs_quadkey_get_boundary_point_fp64_split(index: vec2u, vertexIndex: u32) -> vec4f {
6312
+ return dggs_boundary_point_to_fp64_split(dggs_quadkey_get_boundary_point(index, vertexIndex));
6313
+ }
6314
+
6315
+ fn dggs_s2_digit_to_xy(digit: u32) -> vec2u {
6316
+ if (digit == 1u) {
6317
+ return vec2u(0u, 1u);
6318
+ }
6319
+ if (digit == 2u) {
6320
+ return vec2u(1u, 1u);
6321
+ }
6322
+ if (digit == 3u) {
6323
+ return vec2u(1u, 0u);
6324
+ }
6325
+ return vec2u(0u, 0u);
6326
+ }
6327
+
6328
+ fn dggs_s2_rotate_and_flip_quadrant(size: u32, point: vec2u, digitXY: vec2u) -> vec2u {
6329
+ var result = point;
6330
+ if (digitXY.y == 0u) {
6331
+ if (digitXY.x == 1u) {
6332
+ result = vec2u(size - 1u - result.x, size - 1u - result.y);
6333
+ }
6334
+ result = result.yx;
6335
+ }
6336
+ return result;
6337
+ }
6338
+
6339
+ fn dggs_s2_get_ij(index: vec2u) -> vec2u {
6340
+ let level = dggs_s2_get_level(index);
6341
+ var point = vec2u(0u);
6342
+ var hilbertLevel = 1u;
6343
+ loop {
6344
+ if (hilbertLevel > level) {
6345
+ break;
6346
+ }
6347
+ let digitLevel = level - hilbertLevel + 1u;
6348
+ let digitXY = dggs_s2_digit_to_xy(dggs_s2_get_child_position(index, digitLevel));
6349
+ let size = 1u << (hilbertLevel - 1u);
6350
+ point = dggs_s2_rotate_and_flip_quadrant(size, point, digitXY);
6351
+ point += size * digitXY;
6352
+ hilbertLevel += 1u;
6353
+ }
6354
+ if ((dggs_s2_get_face(index) & 1u) != 0u) {
6355
+ point = point.yx;
6356
+ }
6357
+ return point;
6358
+ }
6359
+
6360
+ fn dggs_s2_single_st_to_uv(st: f32) -> f32 {
6361
+ if (st >= 0.5) {
6362
+ return (4.0 * st * st - 1.0) / 3.0;
6363
+ }
6364
+ return (1.0 - 4.0 * (1.0 - st) * (1.0 - st)) / 3.0;
6365
+ }
6366
+
6367
+ fn dggs_s2_ij_to_st(ij: vec2u, level: u32, offset: vec2f) -> vec2f {
6368
+ let maxSize = f32(1u << level);
6369
+ return (vec2f(f32(ij.x), f32(ij.y)) + offset) / maxSize;
6370
+ }
6371
+
6372
+ fn dggs_s2_st_to_uv(st: vec2f) -> vec2f {
6373
+ return vec2f(dggs_s2_single_st_to_uv(st.x), dggs_s2_single_st_to_uv(st.y));
6374
+ }
6375
+
6376
+ fn dggs_s2_face_uv_to_xyz(face: u32, uv: vec2f) -> vec3f {
6377
+ if (face == 0u) {
6378
+ return vec3f(1.0, uv.x, uv.y);
6379
+ }
6380
+ if (face == 1u) {
6381
+ return vec3f(-uv.x, 1.0, uv.y);
6382
+ }
6383
+ if (face == 2u) {
6384
+ return vec3f(-uv.x, -uv.y, 1.0);
6385
+ }
6386
+ if (face == 3u) {
6387
+ return vec3f(-1.0, -uv.y, -uv.x);
6388
+ }
6389
+ if (face == 4u) {
6390
+ return vec3f(uv.y, -1.0, -uv.x);
6391
+ }
6392
+ return vec3f(uv.y, uv.x, -1.0);
6393
+ }
6394
+
6395
+ fn dggs_s2_xyz_to_lnglat(xyz: vec3f) -> vec2f {
6396
+ let latitude = atan2(xyz.z, length(xyz.xy)) * DGGS_RADIANS_TO_DEGREES;
6397
+ let longitude = atan2(xyz.y, xyz.x) * DGGS_RADIANS_TO_DEGREES;
6398
+ return vec2f(longitude, latitude);
6399
+ }
6400
+
6401
+ fn dggs_s2_get_boundary_offset(vertexIndex: u32) -> vec2f {
6402
+ let pointIndex = vertexIndex % 4u;
6403
+ if (pointIndex == 1u) {
6404
+ return vec2f(0.0, 1.0);
6405
+ }
6406
+ if (pointIndex == 2u) {
6407
+ return vec2f(1.0, 1.0);
6408
+ }
6409
+ if (pointIndex == 3u) {
6410
+ return vec2f(1.0, 0.0);
6411
+ }
6412
+ return vec2f(0.0, 0.0);
6413
+ }
6414
+
6415
+ fn dggs_s2_get_boundary_point(index: vec2u, vertexIndex: u32) -> vec2f {
6416
+ let level = dggs_s2_get_level(index);
6417
+ let ij = dggs_s2_get_ij(index);
6418
+ let st = dggs_s2_ij_to_st(ij, level, dggs_s2_get_boundary_offset(vertexIndex));
6419
+ let uv = dggs_s2_st_to_uv(st);
6420
+ let xyz = dggs_s2_face_uv_to_xyz(dggs_s2_get_face(index), uv);
6421
+ return dggs_s2_xyz_to_lnglat(xyz);
6422
+ }
6423
+
6424
+ fn dggs_s2_get_boundary_point_fp64_split(index: vec2u, vertexIndex: u32) -> vec4f {
6425
+ return dggs_boundary_point_to_fp64_split(dggs_s2_get_boundary_point(index, vertexIndex));
6426
+ }
6427
+ `
6428
+ );
6429
+
6430
+ // dist/modules/geospatial/dggs/dggs.js
6431
+ var dggs = {
6432
+ name: "dggs",
6433
+ source: dggsWGSL
6434
+ };
6435
+
6436
+ // dist/modules/color/float-colors.js
6437
+ var STORAGE_COLOR_FORMAT = {
6438
+ RGBA8UNORM: 0,
6439
+ RGBA16FLOAT: 1,
6440
+ RGBA32FLOAT: 2
6441
+ };
6442
+ var STORAGE_COLOR_FORMAT_BYTE_LENGTHS = {
6443
+ rgba8unorm: 4,
6444
+ rgba16float: 8,
6445
+ rgba32float: 16
6446
+ };
6447
+ var STORAGE_COLOR_DEFAULT_BYTE_STRIDES = {
6448
+ ...STORAGE_COLOR_FORMAT_BYTE_LENGTHS
6449
+ };
6450
+ var STORAGE_COLOR_FORMAT_BY_NAME = {
6451
+ rgba8unorm: STORAGE_COLOR_FORMAT.RGBA8UNORM,
6452
+ rgba16float: STORAGE_COLOR_FORMAT.RGBA16FLOAT,
6453
+ rgba32float: STORAGE_COLOR_FORMAT.RGBA32FLOAT
6454
+ };
6455
+ var STORAGE_COLOR_FORMAT_NAME_BY_VALUE = {
6456
+ [STORAGE_COLOR_FORMAT.RGBA8UNORM]: "rgba8unorm",
6457
+ [STORAGE_COLOR_FORMAT.RGBA16FLOAT]: "rgba16float",
6458
+ [STORAGE_COLOR_FORMAT.RGBA32FLOAT]: "rgba32float"
6459
+ };
6460
+ var COLORS_UNIFORM_TYPES = {
6461
+ useByteColors: "f32"
6462
+ };
6463
+ var COLORS_DEFAULT_UNIFORMS = {
6464
+ useByteColors: true
6465
+ };
6466
+ var STORAGE_COLORS_UNIFORM_TYPES = {
6467
+ format: "u32",
6468
+ wordStride: "u32",
6469
+ wordOffset: "u32",
6470
+ _padding: "u32"
6471
+ };
6472
+ var STORAGE_COLORS_DEFAULT_UNIFORMS = {
6473
+ format: STORAGE_COLOR_FORMAT.RGBA8UNORM,
6474
+ wordStride: STORAGE_COLOR_DEFAULT_BYTE_STRIDES.rgba8unorm / Uint32Array.BYTES_PER_ELEMENT,
6475
+ wordOffset: 0,
6476
+ _padding: 0
6477
+ };
6478
+ var COLORS_GLSL = buildGLSLColorModuleSource("colors");
6479
+ var FLOAT_COLORS_GLSL = buildGLSLColorModuleSource("floatColors");
6480
+ var COLORS_WGSL = buildWGSLColorModuleSource("colors");
6481
+ var FLOAT_COLORS_WGSL = buildWGSLColorModuleSource("floatColors");
6482
+ var STORAGE_COLORS_WGSL = (
6483
+ /* wgsl */
6484
+ `struct storageColorsUniforms {
6485
+ format: u32,
6486
+ wordStride: u32,
6487
+ wordOffset: u32,
6488
+ _padding: u32
6489
+ };
6490
+
6491
+ @group(0) @binding(auto) var<uniform> storageColors : storageColorsUniforms;
6492
+ @group(0) @binding(auto) var<storage, read> storageColorsBuffer : array<u32>;
6493
+
6494
+ const STORAGE_COLOR_FORMAT_RGBA8UNORM : u32 = ${STORAGE_COLOR_FORMAT.RGBA8UNORM}u;
6495
+ const STORAGE_COLOR_FORMAT_RGBA16FLOAT : u32 = ${STORAGE_COLOR_FORMAT.RGBA16FLOAT}u;
6496
+
6497
+ fn storageColors_getWordIndex(rowIndex: u32) -> u32 {
6498
+ return storageColors.wordOffset + rowIndex * storageColors.wordStride;
6499
+ }
6500
+
6501
+ fn storageColors_readRgba8UnormColor(wordIndex: u32) -> vec4<f32> {
6502
+ return unpack4x8unorm(storageColorsBuffer[wordIndex]);
6503
+ }
6504
+
6505
+ fn storageColors_readRgba16FloatColor(wordIndex: u32) -> vec4<f32> {
6506
+ let redGreen = unpack2x16float(storageColorsBuffer[wordIndex]);
6507
+ let blueAlpha = unpack2x16float(storageColorsBuffer[wordIndex + 1u]);
6508
+ return vec4<f32>(redGreen.x, redGreen.y, blueAlpha.x, blueAlpha.y);
6509
+ }
6510
+
6511
+ fn storageColors_readRgba32FloatColor(wordIndex: u32) -> vec4<f32> {
6512
+ return vec4<f32>(
6513
+ bitcast<f32>(storageColorsBuffer[wordIndex]),
6514
+ bitcast<f32>(storageColorsBuffer[wordIndex + 1u]),
6515
+ bitcast<f32>(storageColorsBuffer[wordIndex + 2u]),
6516
+ bitcast<f32>(storageColorsBuffer[wordIndex + 3u])
6517
+ );
6518
+ }
6519
+
6520
+ fn storageColors_readColor(rowIndex: u32) -> vec4<f32> {
6521
+ let wordIndex = storageColors_getWordIndex(rowIndex);
6522
+ if (storageColors.format == STORAGE_COLOR_FORMAT_RGBA8UNORM) {
6523
+ return storageColors_readRgba8UnormColor(wordIndex);
6524
+ }
6525
+ if (storageColors.format == STORAGE_COLOR_FORMAT_RGBA16FLOAT) {
6526
+ return storageColors_readRgba16FloatColor(wordIndex);
6527
+ }
6528
+ return storageColors_readRgba32FloatColor(wordIndex);
6529
+ }
6530
+ `
6531
+ );
6532
+ function buildGLSLColorModuleSource(primaryPrefix) {
6533
+ return (
6534
+ /* glsl */
6535
+ `layout(std140) uniform ${primaryPrefix}Uniforms {
3349
6536
  float useByteColors;
3350
- } floatColors;
6537
+ } ${primaryPrefix};
3351
6538
 
3352
- vec3 floatColors_normalize(vec3 inputColor) {
3353
- return floatColors.useByteColors > 0.5 ? inputColor / 255.0 : inputColor;
6539
+ vec3 ${primaryPrefix}_normalize(vec3 inputColor) {
6540
+ return ${primaryPrefix}.useByteColors > 0.5 ? inputColor / 255.0 : inputColor;
3354
6541
  }
3355
6542
 
3356
- vec4 floatColors_normalize(vec4 inputColor) {
3357
- return floatColors.useByteColors > 0.5 ? inputColor / 255.0 : inputColor;
6543
+ vec4 ${primaryPrefix}_normalize(vec4 inputColor) {
6544
+ return ${primaryPrefix}.useByteColors > 0.5 ? inputColor / 255.0 : inputColor;
3358
6545
  }
3359
6546
 
3360
- vec4 floatColors_premultiplyAlpha(vec4 inputColor) {
6547
+ vec4 ${primaryPrefix}_premultiplyAlpha(vec4 inputColor) {
3361
6548
  return vec4(inputColor.rgb * inputColor.a, inputColor.a);
3362
6549
  }
3363
6550
 
3364
- vec4 floatColors_unpremultiplyAlpha(vec4 inputColor) {
6551
+ vec4 ${primaryPrefix}_unpremultiplyAlpha(vec4 inputColor) {
3365
6552
  return inputColor.a > 0.0 ? vec4(inputColor.rgb / inputColor.a, inputColor.a) : vec4(0.0);
3366
6553
  }
3367
6554
 
3368
- vec4 floatColors_premultiply_alpha(vec4 inputColor) {
3369
- return floatColors_premultiplyAlpha(inputColor);
6555
+ vec4 ${primaryPrefix}_premultiply_alpha(vec4 inputColor) {
6556
+ return ${primaryPrefix}_premultiplyAlpha(inputColor);
3370
6557
  }
3371
6558
 
3372
- vec4 floatColors_unpremultiply_alpha(vec4 inputColor) {
3373
- return floatColors_unpremultiplyAlpha(inputColor);
6559
+ vec4 ${primaryPrefix}_unpremultiply_alpha(vec4 inputColor) {
6560
+ return ${primaryPrefix}_unpremultiplyAlpha(inputColor);
3374
6561
  }
3375
6562
  `
3376
- );
3377
- var WGSL_UNIFORMS = (
3378
- /* wgsl */
3379
- `struct floatColorsUniforms {
6563
+ );
6564
+ }
6565
+ function buildWGSLColorModuleSource(primaryPrefix) {
6566
+ return (
6567
+ /* wgsl */
6568
+ `struct ${primaryPrefix}Uniforms {
3380
6569
  useByteColors: f32
3381
6570
  };
3382
6571
 
3383
- @group(0) @binding(auto) var<uniform> floatColors : floatColorsUniforms;
6572
+ @group(0) @binding(auto) var<uniform> ${primaryPrefix} : ${primaryPrefix}Uniforms;
3384
6573
 
3385
- fn floatColors_normalize(inputColor: vec3<f32>) -> vec3<f32> {
3386
- return select(inputColor, inputColor / 255.0, floatColors.useByteColors > 0.5);
6574
+ fn ${primaryPrefix}_normalize(inputColor: vec3<f32>) -> vec3<f32> {
6575
+ return select(inputColor, inputColor / 255.0, ${primaryPrefix}.useByteColors > 0.5);
3387
6576
  }
3388
6577
 
3389
- fn floatColors_normalize4(inputColor: vec4<f32>) -> vec4<f32> {
3390
- return select(inputColor, inputColor / 255.0, floatColors.useByteColors > 0.5);
6578
+ fn ${primaryPrefix}_normalize4(inputColor: vec4<f32>) -> vec4<f32> {
6579
+ return select(inputColor, inputColor / 255.0, ${primaryPrefix}.useByteColors > 0.5);
3391
6580
  }
3392
6581
 
3393
- fn floatColors_premultiplyAlpha(inputColor: vec4<f32>) -> vec4<f32> {
6582
+ fn ${primaryPrefix}_premultiplyAlpha(inputColor: vec4<f32>) -> vec4<f32> {
3394
6583
  return vec4<f32>(inputColor.rgb * inputColor.a, inputColor.a);
3395
6584
  }
3396
6585
 
3397
- fn floatColors_unpremultiplyAlpha(inputColor: vec4<f32>) -> vec4<f32> {
6586
+ fn ${primaryPrefix}_unpremultiplyAlpha(inputColor: vec4<f32>) -> vec4<f32> {
3398
6587
  return select(
3399
6588
  vec4<f32>(0.0),
3400
6589
  vec4<f32>(inputColor.rgb / inputColor.a, inputColor.a),
@@ -3402,27 +6591,86 @@ fn floatColors_unpremultiplyAlpha(inputColor: vec4<f32>) -> vec4<f32> {
3402
6591
  );
3403
6592
  }
3404
6593
 
3405
- fn floatColors_premultiply_alpha(inputColor: vec4<f32>) -> vec4<f32> {
3406
- return floatColors_premultiplyAlpha(inputColor);
6594
+ fn ${primaryPrefix}_premultiply_alpha(inputColor: vec4<f32>) -> vec4<f32> {
6595
+ return ${primaryPrefix}_premultiplyAlpha(inputColor);
3407
6596
  }
3408
6597
 
3409
- fn floatColors_unpremultiply_alpha(inputColor: vec4<f32>) -> vec4<f32> {
3410
- return floatColors_unpremultiplyAlpha(inputColor);
6598
+ fn ${primaryPrefix}_unpremultiply_alpha(inputColor: vec4<f32>) -> vec4<f32> {
6599
+ return ${primaryPrefix}_unpremultiplyAlpha(inputColor);
3411
6600
  }
3412
6601
  `
3413
- );
6602
+ );
6603
+ }
6604
+ function getStorageColorsUniforms(props = {}) {
6605
+ const format = resolveStorageColorFormat(props.format);
6606
+ const formatName = STORAGE_COLOR_FORMAT_NAME_BY_VALUE[format];
6607
+ const byteStride = props.byteStride ?? STORAGE_COLOR_DEFAULT_BYTE_STRIDES[formatName];
6608
+ const byteOffset = props.byteOffset ?? 0;
6609
+ const minimumByteStride = STORAGE_COLOR_FORMAT_BYTE_LENGTHS[formatName];
6610
+ validateStorageColorByteAlignment("byteStride", byteStride);
6611
+ validateStorageColorByteAlignment("byteOffset", byteOffset);
6612
+ if (byteStride < minimumByteStride) {
6613
+ throw new Error(`storageColors byteStride must be at least ${minimumByteStride} for ${formatName}`);
6614
+ }
6615
+ return {
6616
+ format,
6617
+ wordStride: byteStride / Uint32Array.BYTES_PER_ELEMENT,
6618
+ wordOffset: byteOffset / Uint32Array.BYTES_PER_ELEMENT,
6619
+ _padding: 0
6620
+ };
6621
+ }
6622
+ function resolveStorageColorFormat(format = "rgba8unorm") {
6623
+ if (typeof format === "number") {
6624
+ if (format in STORAGE_COLOR_FORMAT_NAME_BY_VALUE) {
6625
+ return format;
6626
+ }
6627
+ } else if (format in STORAGE_COLOR_FORMAT_BY_NAME) {
6628
+ return STORAGE_COLOR_FORMAT_BY_NAME[format];
6629
+ }
6630
+ throw new Error(`storageColors format must be one of ${Object.keys(STORAGE_COLOR_FORMAT_BY_NAME).join(", ")}`);
6631
+ }
6632
+ function validateStorageColorByteAlignment(name, value) {
6633
+ if (!Number.isInteger(value) || value < 0 || value % Uint32Array.BYTES_PER_ELEMENT !== 0) {
6634
+ throw new Error(`storageColors ${name} must be a non-negative 4-byte aligned integer`);
6635
+ }
6636
+ }
6637
+ var colors = {
6638
+ name: "colors",
6639
+ props: {},
6640
+ uniforms: {},
6641
+ vs: COLORS_GLSL,
6642
+ fs: COLORS_GLSL,
6643
+ source: COLORS_WGSL,
6644
+ uniformTypes: COLORS_UNIFORM_TYPES,
6645
+ defaultUniforms: COLORS_DEFAULT_UNIFORMS
6646
+ };
3414
6647
  var floatColors = {
3415
6648
  name: "floatColors",
3416
6649
  props: {},
3417
6650
  uniforms: {},
3418
- vs: GLSL_UNIFORMS,
3419
- fs: GLSL_UNIFORMS,
3420
- source: WGSL_UNIFORMS,
3421
- uniformTypes: {
3422
- useByteColors: "f32"
3423
- },
3424
- defaultUniforms: {
3425
- useByteColors: true
6651
+ vs: FLOAT_COLORS_GLSL,
6652
+ fs: FLOAT_COLORS_GLSL,
6653
+ source: FLOAT_COLORS_WGSL,
6654
+ uniformTypes: COLORS_UNIFORM_TYPES,
6655
+ defaultUniforms: COLORS_DEFAULT_UNIFORMS
6656
+ };
6657
+ var storageColors = {
6658
+ name: "storageColors",
6659
+ props: {},
6660
+ uniforms: {},
6661
+ bindings: {},
6662
+ source: STORAGE_COLORS_WGSL,
6663
+ uniformTypes: STORAGE_COLORS_UNIFORM_TYPES,
6664
+ defaultUniforms: STORAGE_COLORS_DEFAULT_UNIFORMS,
6665
+ bindingLayout: [
6666
+ { name: "storageColors", group: 0 },
6667
+ { name: "storageColorsBuffer", group: 0 }
6668
+ ],
6669
+ getUniforms(props = {}) {
6670
+ return {
6671
+ ...getStorageColorsUniforms(props),
6672
+ ...props.colorBuffer ? { storageColorsBuffer: props.colorBuffer } : {}
6673
+ };
3426
6674
  }
3427
6675
  };
3428
6676
 
@@ -3611,8 +6859,223 @@ function getUniforms(opts = {}, prevUniforms) {
3611
6859
  return uniforms;
3612
6860
  }
3613
6861
 
6862
+ // dist/modules/engine/filter/filter.js
6863
+ var filterShaderModule = {
6864
+ name: "filter",
6865
+ props: {},
6866
+ uniforms: {},
6867
+ bindingLayout: [{ name: "filter", group: 2 }],
6868
+ uniformTypes: {
6869
+ enabled: "i32",
6870
+ min: "f32",
6871
+ max: "f32"
6872
+ },
6873
+ defaultUniforms: {
6874
+ enabled: 1,
6875
+ min: 0,
6876
+ max: 1
6877
+ },
6878
+ vs: (
6879
+ /* glsl */
6880
+ `layout(std140) uniform filterUniforms {
6881
+ int enabled;
6882
+ float min;
6883
+ float max;
6884
+ } filterState;
6885
+
6886
+ bool filter_isVisible(float value) {
6887
+ return filterState.enabled == 0 || (value >= filterState.min && value <= filterState.max);
6888
+ }
6889
+ `
6890
+ ),
6891
+ source: (
6892
+ /* wgsl */
6893
+ `struct FilterUniforms {
6894
+ enabled: i32,
6895
+ min: f32,
6896
+ max: f32,
6897
+ };
6898
+
6899
+ @group(2) @binding(auto) var<uniform> filterUniforms: FilterUniforms;
6900
+
6901
+ fn filter_isVisible(value: f32) -> bool {
6902
+ return filterUniforms.enabled == 0 ||
6903
+ (value >= filterUniforms.min && value <= filterUniforms.max);
6904
+ }
6905
+ `
6906
+ ),
6907
+ getUniforms(props = {}) {
6908
+ const uniforms = {};
6909
+ if (props.enabled !== void 0) {
6910
+ uniforms.enabled = Number(props.enabled);
6911
+ }
6912
+ if (props.min !== void 0) {
6913
+ uniforms.min = props.min;
6914
+ }
6915
+ if (props.max !== void 0) {
6916
+ uniforms.max = props.max;
6917
+ }
6918
+ return uniforms;
6919
+ }
6920
+ };
6921
+ var filterShaderPlugin = {
6922
+ name: "filter",
6923
+ modules: [filterShaderModule],
6924
+ vertexInputs: {
6925
+ filterValues: "f32"
6926
+ },
6927
+ glsl: {
6928
+ injections: [
6929
+ {
6930
+ target: "vs:FILTER_POSITION",
6931
+ injection: `if (!filter_isVisible(filterValues)) {
6932
+ position = vec4(2.0, 2.0, 2.0, 1.0);
6933
+ }`
6934
+ }
6935
+ ]
6936
+ },
6937
+ wgsl: {
6938
+ injections: [
6939
+ {
6940
+ target: "vs:FILTER_POSITION",
6941
+ injection: `if (!filter_isVisible(filterValues)) {
6942
+ *position = vec4<f32>(2.0, 2.0, 2.0, 1.0);
6943
+ }`
6944
+ }
6945
+ ]
6946
+ }
6947
+ };
6948
+
6949
+ // dist/modules/engine/clip/clip.js
6950
+ var CLIP_MODE_GEOMETRY = 0;
6951
+ var CLIP_MODE_INSTANCE = 1;
6952
+ var clipShaderModule = {
6953
+ name: "clip",
6954
+ props: {},
6955
+ uniforms: {},
6956
+ bindingLayout: [{ name: "clip", group: 2 }],
6957
+ uniformTypes: {
6958
+ enabled: "i32",
6959
+ mode: "i32",
6960
+ bounds: "vec4<f32>"
6961
+ },
6962
+ defaultUniforms: {
6963
+ enabled: 1,
6964
+ mode: CLIP_MODE_GEOMETRY,
6965
+ bounds: [0, 0, 1, 1]
6966
+ },
6967
+ vs: (
6968
+ /* glsl */
6969
+ `layout(std140) uniform clipUniforms {
6970
+ highp int enabled;
6971
+ highp int mode;
6972
+ highp vec4 bounds;
6973
+ } clipState;
6974
+
6975
+ bool clip_isInBounds(vec2 coordinates) {
6976
+ return coordinates.x >= clipState.bounds.x &&
6977
+ coordinates.y >= clipState.bounds.y &&
6978
+ coordinates.x < clipState.bounds.z &&
6979
+ coordinates.y < clipState.bounds.w;
6980
+ }
6981
+ `
6982
+ ),
6983
+ fs: (
6984
+ /* glsl */
6985
+ `layout(std140) uniform clipUniforms {
6986
+ highp int enabled;
6987
+ highp int mode;
6988
+ highp vec4 bounds;
6989
+ } clipState;
6990
+
6991
+ bool clip_isInBounds(vec2 coordinates) {
6992
+ return coordinates.x >= clipState.bounds.x &&
6993
+ coordinates.y >= clipState.bounds.y &&
6994
+ coordinates.x < clipState.bounds.z &&
6995
+ coordinates.y < clipState.bounds.w;
6996
+ }
6997
+ `
6998
+ ),
6999
+ source: (
7000
+ /* wgsl */
7001
+ `struct ClipUniforms {
7002
+ enabled: i32,
7003
+ mode: i32,
7004
+ bounds: vec4<f32>,
7005
+ };
7006
+
7007
+ @group(2) @binding(auto) var<uniform> clipUniforms: ClipUniforms;
7008
+
7009
+ fn clip_isInBounds(coordinates: vec2<f32>) -> bool {
7010
+ return coordinates.x >= clipUniforms.bounds.x &&
7011
+ coordinates.y >= clipUniforms.bounds.y &&
7012
+ coordinates.x < clipUniforms.bounds.z &&
7013
+ coordinates.y < clipUniforms.bounds.w;
7014
+ }
7015
+ `
7016
+ ),
7017
+ getUniforms(props = {}) {
7018
+ const uniforms = {};
7019
+ if (props.enabled !== void 0) {
7020
+ uniforms.enabled = Number(props.enabled);
7021
+ }
7022
+ if (props.mode !== void 0) {
7023
+ uniforms.mode = props.mode === "instance" ? CLIP_MODE_INSTANCE : CLIP_MODE_GEOMETRY;
7024
+ }
7025
+ if (props.bounds !== void 0) {
7026
+ uniforms.bounds = props.bounds;
7027
+ }
7028
+ return uniforms;
7029
+ }
7030
+ };
7031
+ var clipShaderPlugin = {
7032
+ name: "clip",
7033
+ modules: [clipShaderModule],
7034
+ varyings: {
7035
+ clipCoordinates: { type: "vec2<f32>", interpolation: "smooth" }
7036
+ },
7037
+ glsl: {
7038
+ injections: [
7039
+ {
7040
+ target: "vs:CLIP_POSITION",
7041
+ injection: `clipCoordinates = geometryCoordinates;
7042
+ if (clipState.enabled != 0 && clipState.mode == ${CLIP_MODE_INSTANCE} &&
7043
+ !clip_isInBounds(instanceCoordinates)) {
7044
+ position = vec4(2.0, 2.0, 2.0, 1.0);
7045
+ }`
7046
+ },
7047
+ {
7048
+ target: "fs:CLIP_COLOR",
7049
+ injection: `if (clipState.enabled != 0 && clipState.mode == ${CLIP_MODE_GEOMETRY} &&
7050
+ !clip_isInBounds(clipCoordinates)) {
7051
+ discard;
7052
+ }`
7053
+ }
7054
+ ]
7055
+ },
7056
+ wgsl: {
7057
+ injections: [
7058
+ {
7059
+ target: "vs:CLIP_POSITION",
7060
+ injection: `clipCoordinates = geometryCoordinates;
7061
+ if (clipUniforms.enabled != 0 && clipUniforms.mode == ${CLIP_MODE_INSTANCE} &&
7062
+ !clip_isInBounds(instanceCoordinates)) {
7063
+ *position = vec4<f32>(2.0, 2.0, 2.0, 1.0);
7064
+ }`
7065
+ },
7066
+ {
7067
+ target: "fs:CLIP_COLOR",
7068
+ injection: `if (clipUniforms.enabled != 0 && clipUniforms.mode == ${CLIP_MODE_GEOMETRY} &&
7069
+ !clip_isInBounds(clipCoordinates)) {
7070
+ discard;
7071
+ }`
7072
+ }
7073
+ ]
7074
+ }
7075
+ };
7076
+
3614
7077
  // dist/modules/engine/skin/skin.js
3615
- var import_core2 = require("@math.gl/core");
7078
+ var import_core5 = require("@math.gl/core");
3616
7079
  var SKIN_MAX_JOINTS = 20;
3617
7080
  var source2 = (
3618
7081
  /* wgsl */
@@ -3674,7 +7137,7 @@ var skin = {
3674
7137
  const countib = inverseBindMatrices.value.length / 16;
3675
7138
  for (let i = 0; i < countib; i++) {
3676
7139
  const slice = inverseBindMatrices.value.subarray(i * 16, i * 16 + 16);
3677
- matsib.push(new import_core2.Matrix4(Array.from(slice)));
7140
+ matsib.push(new import_core5.Matrix4(Array.from(slice)));
3678
7141
  }
3679
7142
  const top = scenegraphsFromGLTF.gltfNodeIndexToNodeMap.get(skeleton);
3680
7143
  const matrices = {};
@@ -3688,7 +7151,7 @@ var skin = {
3688
7151
  break;
3689
7152
  const worldMat = matrices[scenegraphsFromGLTF.gltfNodeIndexToNodeMap.get(nodeIndex).id];
3690
7153
  const invBindMat = matsib[i];
3691
- const Z = new import_core2.Matrix4().copy(worldMat).multiplyRight(invBindMat);
7154
+ const Z = new import_core5.Matrix4().copy(worldMat).multiplyRight(invBindMat);
3692
7155
  const off = i * 16;
3693
7156
  for (let j = 0; j < 16; j++) {
3694
7157
  mats[off + j] = Z[j];
@@ -3704,7 +7167,7 @@ var skin = {
3704
7167
  };
3705
7168
 
3706
7169
  // dist/modules/lighting/lights/lighting.js
3707
- var import_core3 = require("@luma.gl/core");
7170
+ var import_core6 = require("@luma.gl/core");
3708
7171
 
3709
7172
  // dist/modules/lighting/lights/lighting-glsl.js
3710
7173
  var lightingUniformsGLSL = (
@@ -3987,7 +7450,7 @@ function getLightSourceUniforms({ useByteColors, ambientLight, pointLights = [],
3987
7450
  directionalLightCount++;
3988
7451
  }
3989
7452
  if (pointLights.length + spotLights.length + directionalLights.length > MAX_LIGHTS) {
3990
- import_core3.log.warn(`MAX_LIGHTS exceeded, truncating to ${MAX_LIGHTS}`)();
7453
+ import_core6.log.warn(`MAX_LIGHTS exceeded, truncating to ${MAX_LIGHTS}`)();
3991
7454
  }
3992
7455
  return {
3993
7456
  ambientColor: convertColor(ambientLight, useByteColors),
@@ -4604,6 +8067,631 @@ var phongMaterial = {
4604
8067
  }
4605
8068
  };
4606
8069
 
8070
+ // dist/modules/lighting/water-material/water-shaders-glsl.js
8071
+ var WATER_VS = (
8072
+ /* glsl */
8073
+ `layout(std140) uniform waterMaterialUniforms {
8074
+ uniform float time;
8075
+ uniform vec3 baseColor;
8076
+ uniform float opacity;
8077
+ uniform vec3 fresnelColor;
8078
+ uniform float fresnelPower;
8079
+ uniform float specularIntensity;
8080
+ uniform float normalStrength;
8081
+ uniform int mappingMode;
8082
+ uniform vec2 coordinateScale;
8083
+ uniform vec2 coordinateOffset;
8084
+ uniform vec2 waveADirection;
8085
+ uniform float waveASpeed;
8086
+ uniform float waveAFrequency;
8087
+ uniform float waveAAmplitude;
8088
+ uniform vec2 waveBDirection;
8089
+ uniform float waveBSpeed;
8090
+ uniform float waveBFrequency;
8091
+ uniform float waveBAmplitude;
8092
+ } waterMaterial;
8093
+ `
8094
+ );
8095
+ var WATER_FS = (
8096
+ /* glsl */
8097
+ `layout(std140) uniform waterMaterialUniforms {
8098
+ uniform float time;
8099
+ uniform vec3 baseColor;
8100
+ uniform float opacity;
8101
+ uniform vec3 fresnelColor;
8102
+ uniform float fresnelPower;
8103
+ uniform float specularIntensity;
8104
+ uniform float normalStrength;
8105
+ uniform int mappingMode;
8106
+ uniform vec2 coordinateScale;
8107
+ uniform vec2 coordinateOffset;
8108
+ uniform vec2 waveADirection;
8109
+ uniform float waveASpeed;
8110
+ uniform float waveAFrequency;
8111
+ uniform float waveAAmplitude;
8112
+ uniform vec2 waveBDirection;
8113
+ uniform float waveBSpeed;
8114
+ uniform float waveBFrequency;
8115
+ uniform float waveBAmplitude;
8116
+ } waterMaterial;
8117
+
8118
+ vec2 water_getDirection(vec2 direction) {
8119
+ float directionLength = length(direction);
8120
+ return directionLength > 0.0 ? direction / directionLength : vec2(1.0, 0.0);
8121
+ }
8122
+
8123
+ vec2 water_getCoordinates(vec3 position_worldspace, vec3 position_objectspace, vec2 uv) {
8124
+ vec2 baseCoordinates = uv;
8125
+ if (waterMaterial.mappingMode == 1) {
8126
+ baseCoordinates = position_worldspace.xz;
8127
+ } else if (waterMaterial.mappingMode == 2) {
8128
+ vec3 globeDirection = normalize(position_objectspace);
8129
+ float longitude = atan(globeDirection.x, globeDirection.z);
8130
+ float latitude = asin(clamp(globeDirection.y, -1.0, 1.0));
8131
+ baseCoordinates = vec2(longitude, latitude);
8132
+ }
8133
+ return baseCoordinates * waterMaterial.coordinateScale + waterMaterial.coordinateOffset;
8134
+ }
8135
+
8136
+ vec2 water_getWaveGradient(
8137
+ vec2 coordinates,
8138
+ vec2 direction,
8139
+ float speed,
8140
+ float frequency,
8141
+ float amplitude
8142
+ ) {
8143
+ vec2 normalizedDirection = water_getDirection(direction);
8144
+ float phase = dot(coordinates * frequency, normalizedDirection) + waterMaterial.time * speed;
8145
+ return cos(phase) * normalizedDirection * frequency * amplitude;
8146
+ }
8147
+
8148
+ vec3 water_getTangent(vec3 normal_worldspace) {
8149
+ vec3 referenceAxis = abs(normal_worldspace.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
8150
+ return normalize(cross(referenceAxis, normal_worldspace));
8151
+ }
8152
+
8153
+ vec3 water_getNormal(
8154
+ vec3 position_worldspace,
8155
+ vec3 position_objectspace,
8156
+ vec3 normal_worldspace,
8157
+ vec2 uv
8158
+ ) {
8159
+ vec2 coordinates = water_getCoordinates(position_worldspace, position_objectspace, uv);
8160
+ vec2 gradient =
8161
+ water_getWaveGradient(
8162
+ coordinates,
8163
+ waterMaterial.waveADirection,
8164
+ waterMaterial.waveASpeed,
8165
+ waterMaterial.waveAFrequency,
8166
+ waterMaterial.waveAAmplitude
8167
+ ) +
8168
+ water_getWaveGradient(
8169
+ coordinates,
8170
+ waterMaterial.waveBDirection,
8171
+ waterMaterial.waveBSpeed,
8172
+ waterMaterial.waveBFrequency,
8173
+ waterMaterial.waveBAmplitude
8174
+ );
8175
+
8176
+ vec3 tangent = water_getTangent(normal_worldspace);
8177
+ vec3 bitangent = normalize(cross(normal_worldspace, tangent));
8178
+ vec3 perturbation =
8179
+ waterMaterial.normalStrength * (gradient.x * tangent + gradient.y * bitangent);
8180
+
8181
+ return normalize(normal_worldspace + perturbation);
8182
+ }
8183
+
8184
+ vec3 water_getSpecularContribution(
8185
+ vec3 light_direction,
8186
+ vec3 view_direction,
8187
+ vec3 normal_worldspace,
8188
+ vec3 light_color,
8189
+ float fresnel
8190
+ ) {
8191
+ vec3 halfway_direction = normalize(light_direction + view_direction);
8192
+ float specular =
8193
+ pow(max(dot(normal_worldspace, halfway_direction), 0.0), 72.0) *
8194
+ waterMaterial.specularIntensity *
8195
+ (0.25 + 0.75 * fresnel);
8196
+
8197
+ return waterMaterial.fresnelColor * light_color * specular;
8198
+ }
8199
+
8200
+ vec4 water_getColorMapped(
8201
+ vec3 cameraPosition,
8202
+ vec3 position_worldspace,
8203
+ vec3 position_objectspace,
8204
+ vec3 normal_worldspace,
8205
+ vec2 uv
8206
+ ) {
8207
+ vec3 waterNormal = water_getNormal(
8208
+ position_worldspace,
8209
+ position_objectspace,
8210
+ normalize(normal_worldspace),
8211
+ uv
8212
+ );
8213
+ vec3 viewDirection = normalize(cameraPosition - position_worldspace);
8214
+ float fresnel =
8215
+ pow(
8216
+ 1.0 - max(dot(viewDirection, waterNormal), 0.0),
8217
+ max(waterMaterial.fresnelPower, 0.0001)
8218
+ );
8219
+ vec3 surfaceColor = mix(
8220
+ waterMaterial.baseColor,
8221
+ waterMaterial.fresnelColor,
8222
+ clamp(fresnel * 0.6, 0.0, 1.0)
8223
+ );
8224
+
8225
+ if (lighting.enabled == 0) {
8226
+ return vec4(surfaceColor, waterMaterial.opacity);
8227
+ }
8228
+
8229
+ vec3 lightColor = surfaceColor * (0.15 + 0.85 * lighting.ambientColor);
8230
+
8231
+ for (int i = 0; i < lighting.pointLightCount; i++) {
8232
+ PointLight pointLight = lighting_getPointLight(i);
8233
+ vec3 lightPosition = pointLight.position;
8234
+ vec3 lightDirection = normalize(lightPosition - position_worldspace);
8235
+ float attenuation =
8236
+ getPointLightAttenuation(pointLight, distance(lightPosition, position_worldspace));
8237
+ vec3 incidentLight = pointLight.color / attenuation;
8238
+ float diffuse = max(dot(waterNormal, lightDirection), 0.0);
8239
+
8240
+ lightColor += surfaceColor * incidentLight * diffuse;
8241
+ lightColor += water_getSpecularContribution(
8242
+ lightDirection,
8243
+ viewDirection,
8244
+ waterNormal,
8245
+ incidentLight,
8246
+ fresnel
8247
+ );
8248
+ }
8249
+
8250
+ for (int i = 0; i < lighting.spotLightCount; i++) {
8251
+ SpotLight spotLight = lighting_getSpotLight(i);
8252
+ vec3 lightPosition = spotLight.position;
8253
+ vec3 lightDirection = normalize(lightPosition - position_worldspace);
8254
+ float attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
8255
+ vec3 incidentLight = spotLight.color / attenuation;
8256
+ float diffuse = max(dot(waterNormal, lightDirection), 0.0);
8257
+
8258
+ lightColor += surfaceColor * incidentLight * diffuse;
8259
+ lightColor += water_getSpecularContribution(
8260
+ lightDirection,
8261
+ viewDirection,
8262
+ waterNormal,
8263
+ incidentLight,
8264
+ fresnel
8265
+ );
8266
+ }
8267
+
8268
+ for (int i = 0; i < lighting.directionalLightCount; i++) {
8269
+ DirectionalLight directionalLight = lighting_getDirectionalLight(i);
8270
+ vec3 lightDirection = normalize(-directionalLight.direction);
8271
+ float diffuse = max(dot(waterNormal, lightDirection), 0.0);
8272
+
8273
+ lightColor += surfaceColor * directionalLight.color * diffuse;
8274
+ lightColor += water_getSpecularContribution(
8275
+ lightDirection,
8276
+ viewDirection,
8277
+ waterNormal,
8278
+ directionalLight.color,
8279
+ fresnel
8280
+ );
8281
+ }
8282
+
8283
+ lightColor = mix(lightColor, waterMaterial.fresnelColor, clamp(fresnel, 0.0, 1.0) * 0.35);
8284
+ return vec4(lightColor, waterMaterial.opacity);
8285
+ }
8286
+
8287
+ vec4 water_getColor(
8288
+ vec3 cameraPosition,
8289
+ vec3 position_worldspace,
8290
+ vec3 normal_worldspace,
8291
+ vec2 uv
8292
+ ) {
8293
+ return water_getColorMapped(
8294
+ cameraPosition,
8295
+ position_worldspace,
8296
+ position_worldspace,
8297
+ normal_worldspace,
8298
+ uv
8299
+ );
8300
+ }
8301
+ `
8302
+ );
8303
+
8304
+ // dist/modules/lighting/water-material/water-shaders-wgsl.js
8305
+ var WATER_WGSL = (
8306
+ /* wgsl */
8307
+ `struct waterMaterialUniforms {
8308
+ time: f32,
8309
+ baseColor: vec3<f32>,
8310
+ opacity: f32,
8311
+ fresnelColor: vec3<f32>,
8312
+ fresnelPower: f32,
8313
+ specularIntensity: f32,
8314
+ normalStrength: f32,
8315
+ mappingMode: i32,
8316
+ coordinateScale: vec2<f32>,
8317
+ coordinateOffset: vec2<f32>,
8318
+ waveADirection: vec2<f32>,
8319
+ waveASpeed: f32,
8320
+ waveAFrequency: f32,
8321
+ waveAAmplitude: f32,
8322
+ waveBDirection: vec2<f32>,
8323
+ waveBSpeed: f32,
8324
+ waveBFrequency: f32,
8325
+ waveBAmplitude: f32,
8326
+ };
8327
+
8328
+ @group(3) @binding(auto) var<uniform> waterMaterial : waterMaterialUniforms;
8329
+
8330
+ fn water_getDirection(direction: vec2<f32>) -> vec2<f32> {
8331
+ let directionLength = length(direction);
8332
+ if (directionLength > 0.0) {
8333
+ return direction / directionLength;
8334
+ }
8335
+
8336
+ return vec2<f32>(1.0, 0.0);
8337
+ }
8338
+
8339
+ fn water_getCoordinates(
8340
+ position_worldspace: vec3<f32>,
8341
+ position_objectspace: vec3<f32>,
8342
+ uv: vec2<f32>
8343
+ ) -> vec2<f32> {
8344
+ var baseCoordinates = uv;
8345
+ if (waterMaterial.mappingMode == 1) {
8346
+ baseCoordinates = position_worldspace.xz;
8347
+ } else if (waterMaterial.mappingMode == 2) {
8348
+ let globeDirection = normalize(position_objectspace);
8349
+ let longitude = atan2(globeDirection.x, globeDirection.z);
8350
+ let latitude = asin(clamp(globeDirection.y, -1.0, 1.0));
8351
+ baseCoordinates = vec2<f32>(longitude, latitude);
8352
+ }
8353
+
8354
+ return baseCoordinates * waterMaterial.coordinateScale + waterMaterial.coordinateOffset;
8355
+ }
8356
+
8357
+ fn water_getWaveGradient(
8358
+ coordinates: vec2<f32>,
8359
+ direction: vec2<f32>,
8360
+ speed: f32,
8361
+ frequency: f32,
8362
+ amplitude: f32
8363
+ ) -> vec2<f32> {
8364
+ let normalizedDirection = water_getDirection(direction);
8365
+ let phase = dot(coordinates * frequency, normalizedDirection) + waterMaterial.time * speed;
8366
+ return cos(phase) * normalizedDirection * frequency * amplitude;
8367
+ }
8368
+
8369
+ fn water_getTangent(normal_worldspace: vec3<f32>) -> vec3<f32> {
8370
+ var referenceAxis = vec3<f32>(0.0, 0.0, 1.0);
8371
+ if (abs(normal_worldspace.z) >= 0.999) {
8372
+ referenceAxis = vec3<f32>(0.0, 1.0, 0.0);
8373
+ }
8374
+
8375
+ return normalize(cross(referenceAxis, normal_worldspace));
8376
+ }
8377
+
8378
+ fn water_getNormal(
8379
+ position_worldspace: vec3<f32>,
8380
+ position_objectspace: vec3<f32>,
8381
+ normal_worldspace: vec3<f32>,
8382
+ uv: vec2<f32>
8383
+ ) -> vec3<f32> {
8384
+ let coordinates = water_getCoordinates(position_worldspace, position_objectspace, uv);
8385
+ let gradient =
8386
+ water_getWaveGradient(
8387
+ coordinates,
8388
+ waterMaterial.waveADirection,
8389
+ waterMaterial.waveASpeed,
8390
+ waterMaterial.waveAFrequency,
8391
+ waterMaterial.waveAAmplitude
8392
+ ) +
8393
+ water_getWaveGradient(
8394
+ coordinates,
8395
+ waterMaterial.waveBDirection,
8396
+ waterMaterial.waveBSpeed,
8397
+ waterMaterial.waveBFrequency,
8398
+ waterMaterial.waveBAmplitude
8399
+ );
8400
+ let tangent = water_getTangent(normal_worldspace);
8401
+ let bitangent = normalize(cross(normal_worldspace, tangent));
8402
+ let perturbation =
8403
+ waterMaterial.normalStrength * (gradient.x * tangent + gradient.y * bitangent);
8404
+
8405
+ return normalize(normal_worldspace + perturbation);
8406
+ }
8407
+
8408
+ fn water_getSpecularContribution(
8409
+ light_direction: vec3<f32>,
8410
+ view_direction: vec3<f32>,
8411
+ normal_worldspace: vec3<f32>,
8412
+ light_color: vec3<f32>,
8413
+ fresnel: f32
8414
+ ) -> vec3<f32> {
8415
+ let halfwayDirection = normalize(light_direction + view_direction);
8416
+ let specular =
8417
+ pow(max(dot(normal_worldspace, halfwayDirection), 0.0), 72.0) *
8418
+ waterMaterial.specularIntensity *
8419
+ (0.25 + 0.75 * fresnel);
8420
+
8421
+ return waterMaterial.fresnelColor * light_color * specular;
8422
+ }
8423
+
8424
+ fn water_getColorMapped(
8425
+ cameraPosition: vec3<f32>,
8426
+ position_worldspace: vec3<f32>,
8427
+ position_objectspace: vec3<f32>,
8428
+ normal_worldspace: vec3<f32>,
8429
+ uv: vec2<f32>
8430
+ ) -> vec4<f32> {
8431
+ let waterNormal = water_getNormal(
8432
+ position_worldspace,
8433
+ position_objectspace,
8434
+ normalize(normal_worldspace),
8435
+ uv
8436
+ );
8437
+ let viewDirection = normalize(cameraPosition - position_worldspace);
8438
+ let fresnel =
8439
+ pow(
8440
+ 1.0 - max(dot(viewDirection, waterNormal), 0.0),
8441
+ max(waterMaterial.fresnelPower, 0.0001)
8442
+ );
8443
+ let surfaceColor = mix(
8444
+ waterMaterial.baseColor,
8445
+ waterMaterial.fresnelColor,
8446
+ clamp(fresnel * 0.6, 0.0, 1.0)
8447
+ );
8448
+
8449
+ if (lighting.enabled == 0) {
8450
+ return vec4<f32>(surfaceColor, waterMaterial.opacity);
8451
+ }
8452
+
8453
+ var lightColor = surfaceColor * (0.15 + 0.85 * lighting.ambientColor);
8454
+
8455
+ for (var i: i32 = 0; i < lighting.pointLightCount; i++) {
8456
+ let pointLight = lighting_getPointLight(i);
8457
+ let lightPosition = pointLight.position;
8458
+ let lightDirection = normalize(lightPosition - position_worldspace);
8459
+ let attenuation = getPointLightAttenuation(
8460
+ pointLight,
8461
+ distance(lightPosition, position_worldspace)
8462
+ );
8463
+ let incidentLight = pointLight.color / attenuation;
8464
+ let diffuse = max(dot(waterNormal, lightDirection), 0.0);
8465
+
8466
+ lightColor += surfaceColor * incidentLight * diffuse;
8467
+ lightColor += water_getSpecularContribution(
8468
+ lightDirection,
8469
+ viewDirection,
8470
+ waterNormal,
8471
+ incidentLight,
8472
+ fresnel
8473
+ );
8474
+ }
8475
+
8476
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
8477
+ let spotLight = lighting_getSpotLight(i);
8478
+ let lightPosition = spotLight.position;
8479
+ let lightDirection = normalize(lightPosition - position_worldspace);
8480
+ let attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
8481
+ let incidentLight = spotLight.color / attenuation;
8482
+ let diffuse = max(dot(waterNormal, lightDirection), 0.0);
8483
+
8484
+ lightColor += surfaceColor * incidentLight * diffuse;
8485
+ lightColor += water_getSpecularContribution(
8486
+ lightDirection,
8487
+ viewDirection,
8488
+ waterNormal,
8489
+ incidentLight,
8490
+ fresnel
8491
+ );
8492
+ }
8493
+
8494
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
8495
+ let directionalLight = lighting_getDirectionalLight(i);
8496
+ let lightDirection = normalize(-directionalLight.direction);
8497
+ let diffuse = max(dot(waterNormal, lightDirection), 0.0);
8498
+
8499
+ lightColor += surfaceColor * directionalLight.color * diffuse;
8500
+ lightColor += water_getSpecularContribution(
8501
+ lightDirection,
8502
+ viewDirection,
8503
+ waterNormal,
8504
+ directionalLight.color,
8505
+ fresnel
8506
+ );
8507
+ }
8508
+
8509
+ lightColor = mix(
8510
+ lightColor,
8511
+ waterMaterial.fresnelColor,
8512
+ clamp(fresnel, 0.0, 1.0) * 0.35
8513
+ );
8514
+ return vec4<f32>(lightColor, waterMaterial.opacity);
8515
+ }
8516
+
8517
+ fn water_getColor(
8518
+ cameraPosition: vec3<f32>,
8519
+ position_worldspace: vec3<f32>,
8520
+ normal_worldspace: vec3<f32>,
8521
+ uv: vec2<f32>
8522
+ ) -> vec4<f32> {
8523
+ return water_getColorMapped(
8524
+ cameraPosition,
8525
+ position_worldspace,
8526
+ position_worldspace,
8527
+ normal_worldspace,
8528
+ uv
8529
+ );
8530
+ }
8531
+ `
8532
+ );
8533
+
8534
+ // dist/modules/lighting/water-material/water-material.js
8535
+ var DEFAULT_WATER_MATERIAL_UNIFORMS = {
8536
+ time: 0,
8537
+ baseColor: [0.04, 0.18, 0.31],
8538
+ opacity: 0.82,
8539
+ fresnelColor: [0.86, 0.95, 1],
8540
+ fresnelPower: 5,
8541
+ specularIntensity: 1.4,
8542
+ normalStrength: 0.35,
8543
+ mappingMode: 0,
8544
+ coordinateScale: [1, 1],
8545
+ coordinateOffset: [0, 0],
8546
+ waveADirection: [0.9805806756909201, 0.19611613513818402],
8547
+ waveASpeed: 0.6,
8548
+ waveAFrequency: 4,
8549
+ waveAAmplitude: 0.08,
8550
+ waveBDirection: [0.09950371902099893, 0.9950371902099893],
8551
+ waveBSpeed: -0.45,
8552
+ waveBFrequency: 7,
8553
+ waveBAmplitude: 0.04
8554
+ };
8555
+ var waterMaterial = {
8556
+ name: "waterMaterial",
8557
+ firstBindingSlot: 0,
8558
+ bindingLayout: [{ name: "waterMaterial", group: 3 }],
8559
+ dependencies: [lighting],
8560
+ source: WATER_WGSL,
8561
+ vs: WATER_VS,
8562
+ fs: WATER_FS,
8563
+ defines: {
8564
+ LIGHTING_FRAGMENT: true
8565
+ },
8566
+ uniformTypes: {
8567
+ time: "f32",
8568
+ baseColor: "vec3<f32>",
8569
+ opacity: "f32",
8570
+ fresnelColor: "vec3<f32>",
8571
+ fresnelPower: "f32",
8572
+ specularIntensity: "f32",
8573
+ normalStrength: "f32",
8574
+ mappingMode: "i32",
8575
+ coordinateScale: "vec2<f32>",
8576
+ coordinateOffset: "vec2<f32>",
8577
+ waveADirection: "vec2<f32>",
8578
+ waveASpeed: "f32",
8579
+ waveAFrequency: "f32",
8580
+ waveAAmplitude: "f32",
8581
+ waveBDirection: "vec2<f32>",
8582
+ waveBSpeed: "f32",
8583
+ waveBFrequency: "f32",
8584
+ waveBAmplitude: "f32"
8585
+ },
8586
+ defaultUniforms: DEFAULT_WATER_MATERIAL_UNIFORMS,
8587
+ getUniforms(props, previousUniforms = DEFAULT_WATER_MATERIAL_UNIFORMS) {
8588
+ const { mapping, ...uniformProps } = props || {};
8589
+ const uniforms = mergeWaterMaterialUniforms(previousUniforms);
8590
+ if (uniformProps.time !== void 0) {
8591
+ uniforms.time = uniformProps.time;
8592
+ }
8593
+ if (uniformProps.opacity !== void 0) {
8594
+ uniforms.opacity = uniformProps.opacity;
8595
+ }
8596
+ if (uniformProps.fresnelPower !== void 0) {
8597
+ uniforms.fresnelPower = uniformProps.fresnelPower;
8598
+ }
8599
+ if (uniformProps.specularIntensity !== void 0) {
8600
+ uniforms.specularIntensity = uniformProps.specularIntensity;
8601
+ }
8602
+ if (uniformProps.normalStrength !== void 0) {
8603
+ uniforms.normalStrength = uniformProps.normalStrength;
8604
+ }
8605
+ if (uniformProps.coordinateScale !== void 0) {
8606
+ uniforms.coordinateScale = [
8607
+ Number(uniformProps.coordinateScale[0]),
8608
+ Number(uniformProps.coordinateScale[1])
8609
+ ];
8610
+ }
8611
+ if (uniformProps.coordinateOffset !== void 0) {
8612
+ uniforms.coordinateOffset = [
8613
+ Number(uniformProps.coordinateOffset[0]),
8614
+ Number(uniformProps.coordinateOffset[1])
8615
+ ];
8616
+ }
8617
+ if (uniformProps.waveASpeed !== void 0) {
8618
+ uniforms.waveASpeed = uniformProps.waveASpeed;
8619
+ }
8620
+ if (uniformProps.waveAFrequency !== void 0) {
8621
+ uniforms.waveAFrequency = uniformProps.waveAFrequency;
8622
+ }
8623
+ if (uniformProps.waveAAmplitude !== void 0) {
8624
+ uniforms.waveAAmplitude = uniformProps.waveAAmplitude;
8625
+ }
8626
+ if (uniformProps.waveBSpeed !== void 0) {
8627
+ uniforms.waveBSpeed = uniformProps.waveBSpeed;
8628
+ }
8629
+ if (uniformProps.waveBFrequency !== void 0) {
8630
+ uniforms.waveBFrequency = uniformProps.waveBFrequency;
8631
+ }
8632
+ if (uniformProps.waveBAmplitude !== void 0) {
8633
+ uniforms.waveBAmplitude = uniformProps.waveBAmplitude;
8634
+ }
8635
+ if (uniformProps.baseColor) {
8636
+ uniforms.baseColor = normalizeColor3(uniformProps.baseColor);
8637
+ }
8638
+ if (uniformProps.fresnelColor) {
8639
+ uniforms.fresnelColor = normalizeColor3(uniformProps.fresnelColor);
8640
+ }
8641
+ if (uniformProps.waveADirection) {
8642
+ uniforms.waveADirection = normalizeDirection2(uniformProps.waveADirection);
8643
+ }
8644
+ if (uniformProps.waveBDirection) {
8645
+ uniforms.waveBDirection = normalizeDirection2(uniformProps.waveBDirection);
8646
+ }
8647
+ if (mapping !== void 0) {
8648
+ uniforms.mappingMode = mapping === "world" ? 1 : mapping === "object" ? 2 : 0;
8649
+ }
8650
+ return uniforms;
8651
+ }
8652
+ };
8653
+ function mergeWaterMaterialUniforms(previousUniforms) {
8654
+ return {
8655
+ time: previousUniforms.time ?? DEFAULT_WATER_MATERIAL_UNIFORMS.time,
8656
+ baseColor: previousUniforms.baseColor ? normalizeColor3(previousUniforms.baseColor) : DEFAULT_WATER_MATERIAL_UNIFORMS.baseColor,
8657
+ opacity: previousUniforms.opacity ?? DEFAULT_WATER_MATERIAL_UNIFORMS.opacity,
8658
+ fresnelColor: previousUniforms.fresnelColor ? normalizeColor3(previousUniforms.fresnelColor) : DEFAULT_WATER_MATERIAL_UNIFORMS.fresnelColor,
8659
+ fresnelPower: previousUniforms.fresnelPower ?? DEFAULT_WATER_MATERIAL_UNIFORMS.fresnelPower,
8660
+ specularIntensity: previousUniforms.specularIntensity ?? DEFAULT_WATER_MATERIAL_UNIFORMS.specularIntensity,
8661
+ normalStrength: previousUniforms.normalStrength ?? DEFAULT_WATER_MATERIAL_UNIFORMS.normalStrength,
8662
+ mappingMode: previousUniforms.mappingMode ?? DEFAULT_WATER_MATERIAL_UNIFORMS.mappingMode,
8663
+ coordinateScale: previousUniforms.coordinateScale ? [Number(previousUniforms.coordinateScale[0]), Number(previousUniforms.coordinateScale[1])] : DEFAULT_WATER_MATERIAL_UNIFORMS.coordinateScale,
8664
+ coordinateOffset: previousUniforms.coordinateOffset ? [Number(previousUniforms.coordinateOffset[0]), Number(previousUniforms.coordinateOffset[1])] : DEFAULT_WATER_MATERIAL_UNIFORMS.coordinateOffset,
8665
+ waveADirection: previousUniforms.waveADirection ? normalizeDirection2(previousUniforms.waveADirection) : DEFAULT_WATER_MATERIAL_UNIFORMS.waveADirection,
8666
+ waveASpeed: previousUniforms.waveASpeed ?? DEFAULT_WATER_MATERIAL_UNIFORMS.waveASpeed,
8667
+ waveAFrequency: previousUniforms.waveAFrequency ?? DEFAULT_WATER_MATERIAL_UNIFORMS.waveAFrequency,
8668
+ waveAAmplitude: previousUniforms.waveAAmplitude ?? DEFAULT_WATER_MATERIAL_UNIFORMS.waveAAmplitude,
8669
+ waveBDirection: previousUniforms.waveBDirection ? normalizeDirection2(previousUniforms.waveBDirection) : DEFAULT_WATER_MATERIAL_UNIFORMS.waveBDirection,
8670
+ waveBSpeed: previousUniforms.waveBSpeed ?? DEFAULT_WATER_MATERIAL_UNIFORMS.waveBSpeed,
8671
+ waveBFrequency: previousUniforms.waveBFrequency ?? DEFAULT_WATER_MATERIAL_UNIFORMS.waveBFrequency,
8672
+ waveBAmplitude: previousUniforms.waveBAmplitude ?? DEFAULT_WATER_MATERIAL_UNIFORMS.waveBAmplitude
8673
+ };
8674
+ }
8675
+ function normalizeColor3(color) {
8676
+ const normalizedColor = [Number(color[0]), Number(color[1]), Number(color[2])];
8677
+ const maxChannel = Math.max(...normalizedColor.map((channel) => Math.abs(channel)));
8678
+ if (maxChannel > 1) {
8679
+ normalizedColor[0] /= 255;
8680
+ normalizedColor[1] /= 255;
8681
+ normalizedColor[2] /= 255;
8682
+ }
8683
+ return normalizedColor;
8684
+ }
8685
+ function normalizeDirection2(direction) {
8686
+ const x = Number(direction[0]);
8687
+ const y = Number(direction[1]);
8688
+ const length = Math.hypot(x, y);
8689
+ if (length === 0) {
8690
+ return [1, 0];
8691
+ }
8692
+ return [x / length, y / length];
8693
+ }
8694
+
4607
8695
  // dist/modules/lighting/pbr-material/pbr-material-glsl.js
4608
8696
  var vs3 = (
4609
8697
  /* glsl */