@luma.gl/shadertools 9.3.0-alpha.8 → 9.3.0

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 +1020 -191
  2. package/dist/dist.min.js +440 -145
  3. package/dist/index.cjs +915 -173
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +7 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +4 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/color/normalize-byte-colors.d.ts +23 -0
  10. package/dist/lib/color/normalize-byte-colors.d.ts.map +1 -0
  11. package/dist/lib/color/normalize-byte-colors.js +42 -0
  12. package/dist/lib/color/normalize-byte-colors.js.map +1 -0
  13. package/dist/lib/glsl-utils/shader-utils.js +4 -4
  14. package/dist/lib/glsl-utils/shader-utils.js.map +1 -1
  15. package/dist/lib/shader-assembly/assemble-shaders.d.ts.map +1 -1
  16. package/dist/lib/shader-assembly/assemble-shaders.js +102 -49
  17. package/dist/lib/shader-assembly/assemble-shaders.js.map +1 -1
  18. package/dist/lib/shader-assembly/wgsl-binding-debug.d.ts.map +1 -1
  19. package/dist/lib/shader-assembly/wgsl-binding-debug.js +7 -3
  20. package/dist/lib/shader-assembly/wgsl-binding-debug.js.map +1 -1
  21. package/dist/lib/shader-assembly/wgsl-binding-scan.d.ts +19 -0
  22. package/dist/lib/shader-assembly/wgsl-binding-scan.d.ts.map +1 -0
  23. package/dist/lib/shader-assembly/wgsl-binding-scan.js +151 -0
  24. package/dist/lib/shader-assembly/wgsl-binding-scan.js.map +1 -0
  25. package/dist/lib/shader-generator/glsl/generate-glsl.js +4 -4
  26. package/dist/lib/shader-generator/glsl/generate-glsl.js.map +1 -1
  27. package/dist/lib/shader-module/shader-module-uniform-layout.d.ts +69 -0
  28. package/dist/lib/shader-module/shader-module-uniform-layout.d.ts.map +1 -1
  29. package/dist/lib/shader-module/shader-module-uniform-layout.js +145 -4
  30. package/dist/lib/shader-module/shader-module-uniform-layout.js.map +1 -1
  31. package/dist/modules/color/float-colors.d.ts +26 -0
  32. package/dist/modules/color/float-colors.d.ts.map +1 -0
  33. package/dist/modules/color/float-colors.js +82 -0
  34. package/dist/modules/color/float-colors.js.map +1 -0
  35. package/dist/modules/engine/picking/picking.d.ts +8 -8
  36. package/dist/modules/engine/picking/picking.d.ts.map +1 -1
  37. package/dist/modules/engine/picking/picking.js +13 -15
  38. package/dist/modules/engine/picking/picking.js.map +1 -1
  39. package/dist/modules/engine/project/project.d.ts +1 -1
  40. package/dist/modules/engine/project/project.js +1 -1
  41. package/dist/modules/engine/skin/skin.d.ts +2 -2
  42. package/dist/modules/engine/skin/skin.d.ts.map +1 -1
  43. package/dist/modules/engine/skin/skin.js +1 -1
  44. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts +1 -0
  45. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts.map +1 -1
  46. package/dist/modules/lighting/gouraud-material/gouraud-material.js +6 -3
  47. package/dist/modules/lighting/gouraud-material/gouraud-material.js.map +1 -1
  48. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.d.ts +2 -2
  49. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.d.ts.map +1 -1
  50. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.js +2 -2
  51. package/dist/modules/lighting/lights/lighting-glsl.d.ts +1 -1
  52. package/dist/modules/lighting/lights/lighting-glsl.d.ts.map +1 -1
  53. package/dist/modules/lighting/lights/lighting-glsl.js +1 -1
  54. package/dist/modules/lighting/lights/lighting.d.ts +4 -2
  55. package/dist/modules/lighting/lights/lighting.d.ts.map +1 -1
  56. package/dist/modules/lighting/lights/lighting.js +17 -11
  57. package/dist/modules/lighting/lights/lighting.js.map +1 -1
  58. package/dist/modules/lighting/no-material/dirlight.d.ts +3 -3
  59. package/dist/modules/lighting/no-material/dirlight.d.ts.map +1 -1
  60. package/dist/modules/lighting/no-material/dirlight.js +2 -2
  61. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +2 -2
  62. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  63. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +138 -35
  64. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js.map +1 -1
  65. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +1 -1
  66. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  67. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +139 -35
  68. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js.map +1 -1
  69. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +74 -6
  70. package/dist/modules/lighting/pbr-material/pbr-material.d.ts.map +1 -1
  71. package/dist/modules/lighting/pbr-material/pbr-material.js +70 -2
  72. package/dist/modules/lighting/pbr-material/pbr-material.js.map +1 -1
  73. package/dist/modules/lighting/pbr-material/pbr-projection.js +1 -1
  74. package/dist/modules/lighting/pbr-material/pbr-scene.d.ts +40 -0
  75. package/dist/modules/lighting/pbr-material/pbr-scene.d.ts.map +1 -0
  76. package/dist/modules/lighting/pbr-material/pbr-scene.js +67 -0
  77. package/dist/modules/lighting/pbr-material/pbr-scene.js.map +1 -0
  78. package/dist/modules/lighting/phong-material/phong-material.d.ts +1 -0
  79. package/dist/modules/lighting/phong-material/phong-material.d.ts.map +1 -1
  80. package/dist/modules/lighting/phong-material/phong-material.js +6 -3
  81. package/dist/modules/lighting/phong-material/phong-material.js.map +1 -1
  82. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts +2 -2
  83. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts.map +1 -1
  84. package/dist/modules/lighting/phong-material/phong-shaders-glsl.js +2 -2
  85. package/dist/modules/math/fp64/fp64-arithmetic-glsl.d.ts +1 -1
  86. package/dist/modules/math/fp64/fp64-arithmetic-glsl.d.ts.map +1 -1
  87. package/dist/modules/math/fp64/fp64-arithmetic-glsl.js +1 -1
  88. package/package.json +2 -2
  89. package/src/index.ts +17 -1
  90. package/src/lib/color/normalize-byte-colors.ts +57 -0
  91. package/src/lib/glsl-utils/shader-utils.ts +4 -4
  92. package/src/lib/shader-assembly/assemble-shaders.ts +197 -69
  93. package/src/lib/shader-assembly/wgsl-binding-debug.ts +14 -3
  94. package/src/lib/shader-assembly/wgsl-binding-scan.ts +228 -0
  95. package/src/lib/shader-generator/glsl/generate-glsl.ts +4 -4
  96. package/src/lib/shader-module/shader-module-uniform-layout.ts +236 -10
  97. package/src/modules/color/float-colors.ts +99 -0
  98. package/src/modules/engine/picking/picking.ts +17 -19
  99. package/src/modules/engine/project/project.ts +1 -1
  100. package/src/modules/engine/skin/skin.ts +1 -1
  101. package/src/modules/lighting/gouraud-material/gouraud-material.ts +10 -3
  102. package/src/modules/lighting/lambert-material/lambert-shaders-glsl.ts +2 -2
  103. package/src/modules/lighting/lights/lighting-glsl.ts +1 -1
  104. package/src/modules/lighting/lights/lighting.ts +20 -11
  105. package/src/modules/lighting/no-material/dirlight.ts +2 -2
  106. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +138 -35
  107. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +139 -35
  108. package/src/modules/lighting/pbr-material/pbr-material.ts +110 -3
  109. package/src/modules/lighting/pbr-material/pbr-projection.ts +1 -1
  110. package/src/modules/lighting/pbr-material/pbr-scene.ts +91 -0
  111. package/src/modules/lighting/phong-material/phong-material.ts +10 -3
  112. package/src/modules/lighting/phong-material/phong-shaders-glsl.ts +2 -2
  113. package/src/modules/math/fp64/fp64-arithmetic-glsl.ts +1 -1
@@ -5,25 +5,81 @@
5
5
  import type {ShaderModule} from './shader-module';
6
6
  import {assert} from '../utils/assert';
7
7
 
8
+ /**
9
+ * Shader stages supported by shader-module uniform-layout validation helpers.
10
+ */
8
11
  export type ShaderModuleUniformLayoutStage = 'vertex' | 'fragment' | 'wgsl';
9
12
 
13
+ /**
14
+ * Describes the result of comparing declared `uniformTypes` with the fields
15
+ * found in a shader uniform block.
16
+ */
10
17
  export type ShaderModuleUniformLayoutValidationResult = {
18
+ /** Name of the shader module being validated. */
11
19
  moduleName: string;
20
+ /** Expected block name derived from the shader module name. */
12
21
  uniformBlockName: string;
22
+ /** Shader stage that was inspected. */
13
23
  stage: ShaderModuleUniformLayoutStage;
24
+ /** Field names declared by the module metadata. */
14
25
  expectedUniformNames: string[];
26
+ /** Field names parsed from the shader source. */
15
27
  actualUniformNames: string[];
28
+ /** Whether the declared and parsed field lists match exactly. */
16
29
  matches: boolean;
17
30
  };
18
31
 
32
+ /**
33
+ * Parsed information about one GLSL uniform block declaration.
34
+ */
35
+ export type GLSLUniformBlockInfo = {
36
+ /** Declared block type name. */
37
+ blockName: string;
38
+ /** Optional instance name that follows the block declaration. */
39
+ instanceName: string | null;
40
+ /** Raw layout qualifier text, if present. */
41
+ layoutQualifier: string | null;
42
+ /** Whether any explicit layout qualifier was present. */
43
+ hasLayoutQualifier: boolean;
44
+ /** Whether the block explicitly declares `layout(std140)`. */
45
+ isStd140: boolean;
46
+ /** Raw source text inside the block braces. */
47
+ body: string;
48
+ };
49
+
50
+ /**
51
+ * Logging surface used by validation and warning helpers.
52
+ */
19
53
  type Logger = {
54
+ /** Error logger compatible with luma's deferred log API. */
20
55
  error?: (...args: unknown[]) => () => unknown;
56
+ /** Warning logger compatible with luma's deferred log API. */
57
+ warn?: (...args: unknown[]) => () => unknown;
21
58
  };
22
59
 
60
+ /**
61
+ * Matches one field declaration inside a GLSL uniform block body.
62
+ */
63
+ const GLSL_UNIFORM_BLOCK_FIELD_REGEXP =
64
+ /^(?:uniform\s+)?(?:(?:lowp|mediump|highp)\s+)?[A-Za-z0-9_]+(?:<[^>]+>)?\s+([A-Za-z0-9_]+)(?:\s*\[[^\]]+\])?\s*;/;
65
+ /**
66
+ * Matches full GLSL uniform block declarations, including optional layout qualifiers.
67
+ */
68
+ const GLSL_UNIFORM_BLOCK_REGEXP =
69
+ /((?: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;
70
+
71
+ /**
72
+ * Returns the uniform block type name expected for the supplied shader module.
73
+ */
23
74
  export function getShaderModuleUniformBlockName(module: ShaderModule): string {
24
75
  return `${module.name}Uniforms`;
25
76
  }
26
77
 
78
+ /**
79
+ * Returns the ordered field names parsed from a shader module's uniform block.
80
+ *
81
+ * @returns `null` when the stage has no source or the expected block is absent.
82
+ */
27
83
  export function getShaderModuleUniformBlockFields(
28
84
  module: ShaderModule,
29
85
  stage: ShaderModuleUniformLayoutStage
@@ -43,6 +99,12 @@ export function getShaderModuleUniformBlockFields(
43
99
  );
44
100
  }
45
101
 
102
+ /**
103
+ * Computes the validation result for a shader module's declared and parsed
104
+ * uniform-block field lists.
105
+ *
106
+ * @returns `null` when the module has no declared uniform types or no matching block.
107
+ */
46
108
  export function getShaderModuleUniformLayoutValidationResult(
47
109
  module: ShaderModule,
48
110
  stage: ShaderModuleUniformLayoutStage
@@ -67,6 +129,12 @@ export function getShaderModuleUniformLayoutValidationResult(
67
129
  };
68
130
  }
69
131
 
132
+ /**
133
+ * Validates that a shader module's parsed uniform block matches `uniformTypes`.
134
+ *
135
+ * When a mismatch is detected, the helper logs a formatted error and optionally
136
+ * throws via {@link assert}.
137
+ */
70
138
  export function validateShaderModuleUniformLayout(
71
139
  module: ShaderModule,
72
140
  stage: ShaderModuleUniformLayoutStage,
@@ -90,6 +158,67 @@ export function validateShaderModuleUniformLayout(
90
158
  return validationResult;
91
159
  }
92
160
 
161
+ /**
162
+ * Parses all GLSL uniform blocks in a shader source string.
163
+ */
164
+ export function getGLSLUniformBlocks(shaderSource: string): GLSLUniformBlockInfo[] {
165
+ const blocks: GLSLUniformBlockInfo[] = [];
166
+ const uncommentedSource = stripShaderComments(shaderSource);
167
+
168
+ for (const sourceMatch of uncommentedSource.matchAll(GLSL_UNIFORM_BLOCK_REGEXP)) {
169
+ const layoutQualifier = sourceMatch[1]?.trim() || null;
170
+ blocks.push({
171
+ blockName: sourceMatch[2],
172
+ body: sourceMatch[3],
173
+ instanceName: sourceMatch[4] || null,
174
+ layoutQualifier,
175
+ hasLayoutQualifier: Boolean(layoutQualifier),
176
+ isStd140: Boolean(
177
+ layoutQualifier && /\blayout\s*\([^)]*\bstd140\b[^)]*\)/.exec(layoutQualifier)
178
+ )
179
+ });
180
+ }
181
+
182
+ return blocks;
183
+ }
184
+
185
+ /**
186
+ * Emits warnings for GLSL uniform blocks that do not explicitly declare
187
+ * `layout(std140)`.
188
+ *
189
+ * @returns The list of parsed blocks that were considered non-compliant.
190
+ */
191
+ export function warnIfGLSLUniformBlocksAreNotStd140(
192
+ shaderSource: string,
193
+ stage: Exclude<ShaderModuleUniformLayoutStage, 'wgsl'>,
194
+ log?: Logger,
195
+ context?: {label?: string}
196
+ ): GLSLUniformBlockInfo[] {
197
+ const nonStd140Blocks = getGLSLUniformBlocks(shaderSource).filter(block => !block.isStd140);
198
+ const seenBlockNames = new Set<string>();
199
+
200
+ for (const block of nonStd140Blocks) {
201
+ if (seenBlockNames.has(block.blockName)) {
202
+ continue;
203
+ }
204
+ seenBlockNames.add(block.blockName);
205
+
206
+ const shaderLabel = context?.label ? `${context.label} ` : '';
207
+ const actualLayout = block.hasLayoutQualifier
208
+ ? `declares ${normalizeWhitespace(block.layoutQualifier!)} instead of layout(std140)`
209
+ : 'does not declare layout(std140)';
210
+ const message = `${shaderLabel}${stage} shader uniform block ${
211
+ block.blockName
212
+ } ${actualLayout}. luma.gl host-side shader block packing assumes explicit layout(std140) for GLSL uniform blocks. Add \`layout(std140)\` to the block declaration.`;
213
+ log?.warn?.(message, block)();
214
+ }
215
+
216
+ return nonStd140Blocks;
217
+ }
218
+
219
+ /**
220
+ * Extracts field names from the named GLSL or WGSL uniform block/struct.
221
+ */
93
222
  function extractShaderUniformBlockFieldNames(
94
223
  shaderSource: string,
95
224
  language: 'glsl' | 'wgsl',
@@ -115,9 +244,7 @@ function extractShaderUniformBlockFieldNames(
115
244
  const fieldMatch =
116
245
  language === 'wgsl'
117
246
  ? line.match(/^([A-Za-z0-9_]+)\s*:/)
118
- : line.match(
119
- /^(?:uniform\s+)?[A-Za-z0-9_]+(?:<[^>]+>)?\s+([A-Za-z0-9_]+)(?:\s*\[[^\]]+\])?\s*;/
120
- );
247
+ : line.match(GLSL_UNIFORM_BLOCK_FIELD_REGEXP);
121
248
 
122
249
  if (fieldMatch) {
123
250
  fieldNames.push(fieldMatch[1]);
@@ -127,6 +254,9 @@ function extractShaderUniformBlockFieldNames(
127
254
  return fieldNames;
128
255
  }
129
256
 
257
+ /**
258
+ * Extracts the body of a WGSL struct with the supplied name.
259
+ */
130
260
  function extractWGSLStructBody(shaderSource: string, uniformBlockName: string): string | null {
131
261
  const structMatch = new RegExp(`\\bstruct\\s+${uniformBlockName}\\b`, 'm').exec(shaderSource);
132
262
  if (!structMatch) {
@@ -158,17 +288,22 @@ function extractWGSLStructBody(shaderSource: string, uniformBlockName: string):
158
288
  return null;
159
289
  }
160
290
 
291
+ /**
292
+ * Extracts the body of a named GLSL uniform block from shader source.
293
+ */
161
294
  function extractGLSLUniformBlockBody(
162
295
  shaderSource: string,
163
296
  uniformBlockName: string
164
297
  ): string | null {
165
- const sourceMatch = shaderSource.match(
166
- new RegExp(`uniform\\s+${uniformBlockName}\\s*\\{([\\s\\S]*?)\\}\\s*[A-Za-z0-9_]+\\s*;`, 'm')
298
+ const block = getGLSLUniformBlocks(shaderSource).find(
299
+ candidate => candidate.blockName === uniformBlockName
167
300
  );
168
-
169
- return sourceMatch?.[1] || null;
301
+ return block?.body || null;
170
302
  }
171
303
 
304
+ /**
305
+ * Returns `true` when the two arrays contain the same strings in the same order.
306
+ */
172
307
  function areStringArraysEqual(leftValues: string[], rightValues: string[]): boolean {
173
308
  if (leftValues.length !== rightValues.length) {
174
309
  return false;
@@ -183,12 +318,103 @@ function areStringArraysEqual(leftValues: string[], rightValues: string[]): bool
183
318
  return true;
184
319
  }
185
320
 
321
+ /**
322
+ * Formats the standard validation error message for a shader-module layout mismatch.
323
+ */
186
324
  function formatShaderModuleUniformLayoutError(
187
325
  validationResult: ShaderModuleUniformLayoutValidationResult
188
326
  ): string {
327
+ const {expectedUniformNames, actualUniformNames} = validationResult;
328
+ const missingUniformNames = expectedUniformNames.filter(
329
+ uniformName => !actualUniformNames.includes(uniformName)
330
+ );
331
+ const unexpectedUniformNames = actualUniformNames.filter(
332
+ uniformName => !expectedUniformNames.includes(uniformName)
333
+ );
334
+ const mismatchDetails = [
335
+ `Expected ${expectedUniformNames.length} fields, found ${actualUniformNames.length}.`
336
+ ];
337
+ const firstMismatchDescription = getFirstUniformMismatchDescription(
338
+ expectedUniformNames,
339
+ actualUniformNames
340
+ );
341
+ if (firstMismatchDescription) {
342
+ mismatchDetails.push(firstMismatchDescription);
343
+ }
344
+ if (missingUniformNames.length) {
345
+ mismatchDetails.push(
346
+ `Missing from shader block (${missingUniformNames.length}): ${formatUniformNameList(
347
+ missingUniformNames
348
+ )}.`
349
+ );
350
+ }
351
+ if (unexpectedUniformNames.length) {
352
+ mismatchDetails.push(
353
+ `Unexpected in shader block (${unexpectedUniformNames.length}): ${formatUniformNameList(
354
+ unexpectedUniformNames
355
+ )}.`
356
+ );
357
+ }
358
+ if (
359
+ expectedUniformNames.length <= 12 &&
360
+ actualUniformNames.length <= 12 &&
361
+ (missingUniformNames.length || unexpectedUniformNames.length)
362
+ ) {
363
+ mismatchDetails.push(`Expected: ${expectedUniformNames.join(', ')}.`);
364
+ mismatchDetails.push(`Actual: ${actualUniformNames.join(', ')}.`);
365
+ }
366
+
189
367
  return `${validationResult.moduleName}: ${validationResult.stage} shader uniform block ${
190
368
  validationResult.uniformBlockName
191
- } does not match module.uniformTypes.\nExpected: ${validationResult.expectedUniformNames.join(
192
- ', '
193
- )}\nActual: ${validationResult.actualUniformNames.join(', ')}`;
369
+ } does not match module.uniformTypes. ${mismatchDetails.join(' ')}`;
370
+ }
371
+
372
+ /**
373
+ * Removes line and block comments from shader source before lightweight parsing.
374
+ */
375
+ function stripShaderComments(shaderSource: string): string {
376
+ return shaderSource.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*$/gm, '');
377
+ }
378
+
379
+ /**
380
+ * Collapses repeated whitespace in a layout qualifier for log-friendly output.
381
+ */
382
+ function normalizeWhitespace(value: string): string {
383
+ return value.replace(/\s+/g, ' ').trim();
384
+ }
385
+
386
+ function getFirstUniformMismatchDescription(
387
+ expectedUniformNames: string[],
388
+ actualUniformNames: string[]
389
+ ): string | null {
390
+ const minimumLength = Math.min(expectedUniformNames.length, actualUniformNames.length);
391
+ for (let index = 0; index < minimumLength; index++) {
392
+ if (expectedUniformNames[index] !== actualUniformNames[index]) {
393
+ return `First mismatch at field ${index + 1}: expected ${
394
+ expectedUniformNames[index]
395
+ }, found ${actualUniformNames[index]}.`;
396
+ }
397
+ }
398
+
399
+ if (expectedUniformNames.length > actualUniformNames.length) {
400
+ return `Shader block ends after field ${actualUniformNames.length}; expected next field ${
401
+ expectedUniformNames[actualUniformNames.length]
402
+ }.`;
403
+ }
404
+ if (actualUniformNames.length > expectedUniformNames.length) {
405
+ return `Shader block has extra field ${actualUniformNames.length}: ${
406
+ actualUniformNames[expectedUniformNames.length]
407
+ }.`;
408
+ }
409
+
410
+ return null;
411
+ }
412
+
413
+ function formatUniformNameList(uniformNames: string[], maxNames = 8): string {
414
+ if (uniformNames.length <= maxNames) {
415
+ return uniformNames.join(', ');
416
+ }
417
+
418
+ const remainingCount = uniformNames.length - maxNames;
419
+ return `${uniformNames.slice(0, maxNames).join(', ')}, ... (${remainingCount} more)`;
194
420
  }
@@ -0,0 +1,99 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {ShaderModule} from '../../lib/shader-module/shader-module';
6
+
7
+ export type FloatColorsProps = {
8
+ /**
9
+ * When true, semantic colors are interpreted as 0-255 byte-style values and normalized in shader code.
10
+ * When false, semantic colors are interpreted directly as floats.
11
+ */
12
+ useByteColors?: boolean;
13
+ };
14
+
15
+ export type FloatColorsUniforms = {
16
+ /** Controls whether shader helpers normalize semantic colors from byte space. */
17
+ useByteColors: boolean;
18
+ };
19
+
20
+ const GLSL_UNIFORMS = /* glsl */ `\
21
+ layout(std140) uniform floatColorsUniforms {
22
+ float useByteColors;
23
+ } floatColors;
24
+
25
+ vec3 floatColors_normalize(vec3 inputColor) {
26
+ return floatColors.useByteColors > 0.5 ? inputColor / 255.0 : inputColor;
27
+ }
28
+
29
+ vec4 floatColors_normalize(vec4 inputColor) {
30
+ return floatColors.useByteColors > 0.5 ? inputColor / 255.0 : inputColor;
31
+ }
32
+
33
+ vec4 floatColors_premultiplyAlpha(vec4 inputColor) {
34
+ return vec4(inputColor.rgb * inputColor.a, inputColor.a);
35
+ }
36
+
37
+ vec4 floatColors_unpremultiplyAlpha(vec4 inputColor) {
38
+ return inputColor.a > 0.0 ? vec4(inputColor.rgb / inputColor.a, inputColor.a) : vec4(0.0);
39
+ }
40
+
41
+ vec4 floatColors_premultiply_alpha(vec4 inputColor) {
42
+ return floatColors_premultiplyAlpha(inputColor);
43
+ }
44
+
45
+ vec4 floatColors_unpremultiply_alpha(vec4 inputColor) {
46
+ return floatColors_unpremultiplyAlpha(inputColor);
47
+ }
48
+ `;
49
+
50
+ const WGSL_UNIFORMS = /* wgsl */ `\
51
+ struct floatColorsUniforms {
52
+ useByteColors: f32
53
+ };
54
+
55
+ @group(0) @binding(auto) var<uniform> floatColors : floatColorsUniforms;
56
+
57
+ fn floatColors_normalize(inputColor: vec3<f32>) -> vec3<f32> {
58
+ return select(inputColor, inputColor / 255.0, floatColors.useByteColors > 0.5);
59
+ }
60
+
61
+ fn floatColors_normalize4(inputColor: vec4<f32>) -> vec4<f32> {
62
+ return select(inputColor, inputColor / 255.0, floatColors.useByteColors > 0.5);
63
+ }
64
+
65
+ fn floatColors_premultiplyAlpha(inputColor: vec4<f32>) -> vec4<f32> {
66
+ return vec4<f32>(inputColor.rgb * inputColor.a, inputColor.a);
67
+ }
68
+
69
+ fn floatColors_unpremultiplyAlpha(inputColor: vec4<f32>) -> vec4<f32> {
70
+ return select(
71
+ vec4<f32>(0.0),
72
+ vec4<f32>(inputColor.rgb / inputColor.a, inputColor.a),
73
+ inputColor.a > 0.0
74
+ );
75
+ }
76
+
77
+ fn floatColors_premultiply_alpha(inputColor: vec4<f32>) -> vec4<f32> {
78
+ return floatColors_premultiplyAlpha(inputColor);
79
+ }
80
+
81
+ fn floatColors_unpremultiply_alpha(inputColor: vec4<f32>) -> vec4<f32> {
82
+ return floatColors_unpremultiplyAlpha(inputColor);
83
+ }
84
+ `;
85
+
86
+ export const floatColors = {
87
+ name: 'floatColors',
88
+ props: {} as FloatColorsProps,
89
+ uniforms: {} as FloatColorsUniforms,
90
+ vs: GLSL_UNIFORMS,
91
+ fs: GLSL_UNIFORMS,
92
+ source: WGSL_UNIFORMS,
93
+ uniformTypes: {
94
+ useByteColors: 'f32'
95
+ },
96
+ defaultUniforms: {
97
+ useByteColors: true
98
+ }
99
+ } as const satisfies ShaderModule<FloatColorsProps, FloatColorsUniforms>;
@@ -4,6 +4,7 @@
4
4
 
5
5
  import {ShaderModule} from '../../../lib/shader-module/shader-module';
6
6
  import type {NumberArray3, NumberArray4} from '@math.gl/core';
7
+ import {normalizeByteColor4, resolveUseByteColors} from '../../../lib/color/normalize-byte-colors';
7
8
 
8
9
  // cyan color
9
10
  const DEFAULT_HIGHLIGHT_COLOR: NumberArray4 = [0, 1, 1, 1];
@@ -22,8 +23,8 @@ export type PickingProps = {
22
23
  highlightedObjectColor?: NumberArray3 | null;
23
24
  /** Color of visual highlight of "selected" item */
24
25
  highlightColor?: NumberArray3 | NumberArray4;
25
- /** Color range 0-1 or 0-255 */
26
- useFloatColors?: boolean;
26
+ /** Interpret highlight colors as byte-style 0-255 values. */
27
+ useByteColors?: boolean;
27
28
  };
28
29
 
29
30
  /**
@@ -39,8 +40,8 @@ export type PickingUniforms = {
39
40
  isActive?: boolean;
40
41
  /** Set to true when picking an attribute value instead of object index */
41
42
  isAttribute?: boolean;
42
- /** Color range 0-1 or 0-255 */
43
- useFloatColors?: boolean;
43
+ /** Interpret highlight colors as byte-style 0-255 values. */
44
+ useByteColors?: boolean;
44
45
  /** Do we have a highlighted item? */
45
46
  isHighlightActive?: boolean;
46
47
  /** Set to a picking color to visually highlight that item */
@@ -50,11 +51,11 @@ export type PickingUniforms = {
50
51
  };
51
52
 
52
53
  const vs = /* glsl */ `\
53
- uniform pickingUniforms {
54
+ layout(std140) uniform pickingUniforms {
54
55
  float isActive;
55
56
  float isAttribute;
56
57
  float isHighlightActive;
57
- float useFloatColors;
58
+ float useByteColors;
58
59
  vec3 highlightedObjectColor;
59
60
  vec4 highlightColor;
60
61
  } picking;
@@ -63,12 +64,12 @@ out vec4 picking_vRGBcolor_Avalid;
63
64
 
64
65
  // Normalize unsigned byte color to 0-1 range
65
66
  vec3 picking_normalizeColor(vec3 color) {
66
- return picking.useFloatColors > 0.5 ? color : color / 255.0;
67
+ return picking.useByteColors > 0.5 ? color / 255.0 : color;
67
68
  }
68
69
 
69
70
  // Normalize unsigned byte color to 0-1 range
70
71
  vec4 picking_normalizeColor(vec4 color) {
71
- return picking.useFloatColors > 0.5 ? color : color / 255.0;
72
+ return picking.useByteColors > 0.5 ? color / 255.0 : color;
72
73
  }
73
74
 
74
75
  bool picking_isColorZero(vec3 color) {
@@ -124,11 +125,11 @@ void picking_setPickingAttribute(vec3 value) {
124
125
  `;
125
126
 
126
127
  const fs = /* glsl */ `\
127
- uniform pickingUniforms {
128
+ layout(std140) uniform pickingUniforms {
128
129
  float isActive;
129
130
  float isAttribute;
130
131
  float isHighlightActive;
131
- float useFloatColors;
132
+ float useByteColors;
132
133
  vec3 highlightedObjectColor;
133
134
  vec4 highlightColor;
134
135
  } picking;
@@ -202,7 +203,7 @@ export const picking = {
202
203
  isActive: 'f32',
203
204
  isAttribute: 'f32',
204
205
  isHighlightActive: 'f32',
205
- useFloatColors: 'f32',
206
+ useByteColors: 'f32',
206
207
  highlightedObjectColor: 'vec3<f32>',
207
208
  highlightColor: 'vec4<f32>'
208
209
  },
@@ -210,7 +211,7 @@ export const picking = {
210
211
  isActive: false,
211
212
  isAttribute: false,
212
213
  isHighlightActive: false,
213
- useFloatColors: true,
214
+ useByteColors: true,
214
215
  highlightedObjectColor: [0, 0, 0],
215
216
  highlightColor: DEFAULT_HIGHLIGHT_COLOR
216
217
  },
@@ -222,6 +223,7 @@ export const picking = {
222
223
 
223
224
  function getUniforms(opts: PickingProps = {}, prevUniforms?: PickingUniforms): PickingUniforms {
224
225
  const uniforms = {} as PickingUniforms;
226
+ const useByteColors = resolveUseByteColors(opts.useByteColors, true);
225
227
 
226
228
  if (opts.highlightedObjectColor === undefined) {
227
229
  // Unless highlightedObjectColor explicitly null or set, do not update state
@@ -234,11 +236,7 @@ function getUniforms(opts: PickingProps = {}, prevUniforms?: PickingUniforms): P
234
236
  }
235
237
 
236
238
  if (opts.highlightColor) {
237
- const color = Array.from(opts.highlightColor, x => x / 255);
238
- if (!Number.isFinite(color[3])) {
239
- color[3] = 1;
240
- }
241
- uniforms.highlightColor = color as NumberArray4;
239
+ uniforms.highlightColor = normalizeByteColor4(opts.highlightColor, useByteColors);
242
240
  }
243
241
 
244
242
  if (opts.isActive !== undefined) {
@@ -246,8 +244,8 @@ function getUniforms(opts: PickingProps = {}, prevUniforms?: PickingUniforms): P
246
244
  uniforms.isAttribute = Boolean(opts.isAttribute);
247
245
  }
248
246
 
249
- if (opts.useFloatColors !== undefined) {
250
- uniforms.useFloatColors = Boolean(opts.useFloatColors);
247
+ if (opts.useByteColors !== undefined) {
248
+ uniforms.useByteColors = Boolean(opts.useByteColors);
251
249
  }
252
250
 
253
251
  return uniforms;
@@ -78,7 +78,7 @@ varying vec4 project_vPositionWorld;
78
78
  varying vec3 project_vNormalWorld;
79
79
 
80
80
  // Project uniform block
81
- uniform Project {
81
+ layout(std140) uniform Project {
82
82
  mat4 viewMatrix;
83
83
  mat4 projectionMatrix;
84
84
  mat4 viewProjectionMatrix;
@@ -25,7 +25,7 @@ fn getSkinMatrix(weights: vec4f, joints: vec4u) -> mat4x4<f32> {
25
25
 
26
26
  export const vs = /* glsl */ `\
27
27
 
28
- uniform skinUniforms {
28
+ layout(std140) uniform skinUniforms {
29
29
  mat4 jointMatrix[SKIN_MAX_JOINTS];
30
30
  } skin;
31
31
 
@@ -7,6 +7,7 @@ import {ShaderModule} from '../../../lib/shader-module/shader-module';
7
7
  import {lighting} from '../lights/lighting';
8
8
  import {PHONG_VS, PHONG_FS} from '../phong-material/phong-shaders-glsl';
9
9
  import {PHONG_WGSL} from '../phong-material/phong-shaders-wgsl';
10
+ import {normalizeByteColor3, resolveUseByteColors} from '../../../lib/color/normalize-byte-colors';
10
11
 
11
12
  export type GouraudMaterialProps = {
12
13
  unlit?: boolean;
@@ -15,6 +16,7 @@ export type GouraudMaterialProps = {
15
16
  /** Specularity exponent */
16
17
  shininess?: number;
17
18
  specularColor?: [number, number, number];
19
+ useByteColors?: boolean;
18
20
  };
19
21
 
20
22
  /** In Gouraud shading, color is calculated for each triangle vertex normal, and then color is interpolated colors across the triangle */
@@ -36,20 +38,25 @@ export const gouraudMaterial: ShaderModule<GouraudMaterialProps> = {
36
38
  ambient: 'f32',
37
39
  diffuse: 'f32',
38
40
  shininess: 'f32',
39
- specularColor: 'vec3<f32>'
41
+ specularColor: 'vec3<f32>',
42
+ useByteColors: 'i32'
40
43
  },
41
44
  defaultUniforms: {
42
45
  unlit: false,
43
46
  ambient: 0.35,
44
47
  diffuse: 0.6,
45
48
  shininess: 32,
46
- specularColor: [0.15, 0.15, 0.15]
49
+ specularColor: [0.15, 0.15, 0.15],
50
+ useByteColors: true
47
51
  },
48
52
 
49
53
  getUniforms(props: GouraudMaterialProps) {
50
54
  const uniforms = {...props};
51
55
  if (uniforms.specularColor) {
52
- uniforms.specularColor = uniforms.specularColor.map(x => x / 255) as NumberArray3;
56
+ uniforms.specularColor = normalizeByteColor3(
57
+ uniforms.specularColor,
58
+ resolveUseByteColors(uniforms.useByteColors, true)
59
+ ) as NumberArray3;
53
60
  }
54
61
  return {...gouraudMaterial.defaultUniforms, ...uniforms};
55
62
  }
@@ -3,7 +3,7 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  export const LAMBERT_VS = /* glsl */ `\
6
- uniform lambertMaterialUniforms {
6
+ layout(std140) uniform lambertMaterialUniforms {
7
7
  uniform bool unlit;
8
8
  uniform float ambient;
9
9
  uniform float diffuse;
@@ -11,7 +11,7 @@ uniform lambertMaterialUniforms {
11
11
  `;
12
12
 
13
13
  export const LAMBERT_FS = /* glsl */ `\
14
- uniform lambertMaterialUniforms {
14
+ layout(std140) uniform lambertMaterialUniforms {
15
15
  uniform bool unlit;
16
16
  uniform float ambient;
17
17
  uniform float diffuse;
@@ -37,7 +37,7 @@ struct UniformLight {
37
37
  vec2 coneCos;
38
38
  };
39
39
 
40
- uniform lightingUniforms {
40
+ layout(std140) uniform lightingUniforms {
41
41
  int enabled;
42
42
  int directionalLightCount;
43
43
  int pointLightCount;