p5 2.2.3 → 2.3.0-rc.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 (137) hide show
  1. package/dist/accessibility/color_namer.js +9 -11
  2. package/dist/accessibility/describe.js +0 -1
  3. package/dist/accessibility/gridOutput.js +0 -1
  4. package/dist/accessibility/index.js +9 -10
  5. package/dist/accessibility/outputs.js +0 -1
  6. package/dist/accessibility/textOutput.js +0 -1
  7. package/dist/app.js +11 -10
  8. package/dist/app.node.js +122 -0
  9. package/dist/color/color_conversion.js +9 -11
  10. package/dist/color/creating_reading.js +1 -1
  11. package/dist/color/index.js +2 -2
  12. package/dist/color/p5.Color.js +1 -1
  13. package/dist/color/setting.js +25 -12
  14. package/dist/{constants-BdTiYOQI.js → constants-CYF6mp5_.js} +2 -2
  15. package/dist/core/States.js +1 -1
  16. package/dist/core/constants.js +1 -1
  17. package/dist/core/environment.js +28 -29
  18. package/dist/core/filterShaders.js +1 -1
  19. package/dist/core/friendly_errors/fes_core.js +9 -8
  20. package/dist/core/friendly_errors/file_errors.js +1 -2
  21. package/dist/core/friendly_errors/index.js +1 -1
  22. package/dist/core/friendly_errors/param_validator.js +737 -640
  23. package/dist/core/friendly_errors/sketch_verifier.js +1 -1
  24. package/dist/core/friendly_errors/stacktrace.js +0 -1
  25. package/dist/core/helpers.js +3 -4
  26. package/dist/core/init.js +24 -21
  27. package/dist/core/internationalization.js +1 -1
  28. package/dist/core/legacy.js +9 -11
  29. package/dist/core/main.js +9 -10
  30. package/dist/core/p5.Graphics.js +5 -5
  31. package/dist/core/p5.Renderer.js +3 -3
  32. package/dist/core/p5.Renderer2D.js +9 -10
  33. package/dist/core/p5.Renderer3D.js +5 -5
  34. package/dist/core/rendering.js +5 -5
  35. package/dist/core/structure.js +0 -1
  36. package/dist/core/transform.js +7 -16
  37. package/dist/{creating_reading-C7hu6sg1.js → creating_reading-DLkHH80h.js} +11 -8
  38. package/dist/data/local_storage.js +0 -1
  39. package/dist/dom/dom.js +2 -3
  40. package/dist/dom/index.js +2 -2
  41. package/dist/dom/p5.Element.js +2 -2
  42. package/dist/dom/p5.MediaElement.js +2 -2
  43. package/dist/events/acceleration.js +5 -3
  44. package/dist/events/keyboard.js +0 -1
  45. package/dist/events/pointer.js +0 -2
  46. package/dist/image/const.js +1 -1
  47. package/dist/image/filterRenderer2D.js +19 -12
  48. package/dist/image/image.js +5 -5
  49. package/dist/image/index.js +5 -5
  50. package/dist/image/loading_displaying.js +5 -5
  51. package/dist/image/p5.Image.js +3 -3
  52. package/dist/image/pixels.js +0 -1
  53. package/dist/io/files.js +5 -5
  54. package/dist/io/index.js +5 -5
  55. package/dist/io/p5.Table.js +0 -1
  56. package/dist/io/p5.TableRow.js +0 -1
  57. package/dist/io/p5.XML.js +0 -1
  58. package/dist/{ir_builders-Cd6rU9Vm.js → ir_builders-C2ebb6Lu.js} +234 -1
  59. package/dist/{main-H_nu4eDs.js → main-D2MtO721.js} +107 -136
  60. package/dist/math/Matrices/Matrix.js +1 -1
  61. package/dist/math/Matrices/MatrixNumjs.js +1 -1
  62. package/dist/math/calculation.js +0 -1
  63. package/dist/math/index.js +3 -1
  64. package/dist/math/math.js +3 -17
  65. package/dist/math/noise.js +0 -1
  66. package/dist/math/p5.Matrix.js +1 -2
  67. package/dist/math/p5.Vector.js +233 -279
  68. package/dist/math/patch-vector.js +75 -0
  69. package/dist/math/random.js +0 -1
  70. package/dist/math/trigonometry.js +3 -4
  71. package/dist/{p5.Renderer-BmD2P6Wv.js → p5.Renderer-C0Kzy71d.js} +31 -24
  72. package/dist/{rendering-CC8JNTwG.js → rendering-CvNr0bB8.js} +732 -44
  73. package/dist/shape/2d_primitives.js +1 -4
  74. package/dist/shape/attributes.js +43 -8
  75. package/dist/shape/curves.js +0 -1
  76. package/dist/shape/custom_shapes.js +260 -5
  77. package/dist/shape/index.js +2 -2
  78. package/dist/shape/vertex.js +0 -2
  79. package/dist/strands/ir_builders.js +1 -1
  80. package/dist/strands/ir_types.js +5 -1
  81. package/dist/strands/p5.strands.js +286 -31
  82. package/dist/strands/strands_api.js +179 -8
  83. package/dist/strands/strands_codegen.js +26 -8
  84. package/dist/strands/strands_conditionals.js +1 -1
  85. package/dist/strands/strands_for.js +1 -1
  86. package/dist/strands/strands_node.js +1 -1
  87. package/dist/strands/strands_ternary.js +56 -0
  88. package/dist/strands/strands_transpiler.js +416 -251
  89. package/dist/strands_glslBackend-i-ReKgZo.js +423 -0
  90. package/dist/type/index.js +3 -3
  91. package/dist/type/lib/Typr.js +1 -1
  92. package/dist/type/p5.Font.js +3 -3
  93. package/dist/type/textCore.js +31 -24
  94. package/dist/utilities/conversion.js +0 -1
  95. package/dist/utilities/time_date.js +0 -1
  96. package/dist/utilities/utility_functions.js +0 -1
  97. package/dist/webgl/3d_primitives.js +5 -5
  98. package/dist/webgl/GeometryBuilder.js +1 -1
  99. package/dist/webgl/ShapeBuilder.js +26 -1
  100. package/dist/webgl/enums.js +1 -1
  101. package/dist/webgl/index.js +8 -9
  102. package/dist/webgl/interaction.js +8 -4
  103. package/dist/webgl/light.js +5 -5
  104. package/dist/webgl/loading.js +60 -21
  105. package/dist/webgl/material.js +5 -5
  106. package/dist/webgl/p5.Camera.js +5 -5
  107. package/dist/webgl/p5.Framebuffer.js +5 -5
  108. package/dist/webgl/p5.Geometry.js +3 -5
  109. package/dist/webgl/p5.Quat.js +1 -1
  110. package/dist/webgl/p5.RendererGL.js +17 -21
  111. package/dist/webgl/p5.Shader.js +129 -36
  112. package/dist/webgl/p5.Texture.js +5 -5
  113. package/dist/webgl/strands_glslBackend.js +5 -386
  114. package/dist/webgl/text.js +5 -5
  115. package/dist/webgl/utils.js +5 -5
  116. package/dist/webgl2Compatibility-DA7DLMuq.js +7 -0
  117. package/dist/webgpu/index.js +7 -3
  118. package/dist/webgpu/p5.RendererWebGPU.js +1036 -180
  119. package/dist/webgpu/shaders/color.js +1 -1
  120. package/dist/webgpu/shaders/compute.js +32 -0
  121. package/dist/webgpu/shaders/functions/randomComputeWGSL.js +31 -0
  122. package/dist/webgpu/shaders/functions/randomVertWGSL.js +30 -0
  123. package/dist/webgpu/shaders/functions/randomWGSL.js +30 -0
  124. package/dist/webgpu/shaders/line.js +1 -1
  125. package/dist/webgpu/shaders/material.js +3 -3
  126. package/dist/webgpu/strands_wgslBackend.js +137 -15
  127. package/lib/p5.esm.js +4088 -1950
  128. package/lib/p5.esm.min.js +1 -1
  129. package/lib/p5.js +4088 -1950
  130. package/lib/p5.min.js +1 -1
  131. package/lib/p5.webgpu.esm.js +1638 -306
  132. package/lib/p5.webgpu.js +1637 -305
  133. package/lib/p5.webgpu.min.js +1 -1
  134. package/package.json +6 -1
  135. package/types/global.d.ts +4137 -2396
  136. package/types/p5.d.ts +2702 -1658
  137. package/dist/noise3DGLSL-Bwrdi4gi.js +0 -9
@@ -119,7 +119,7 @@ ${uniforms}
119
119
  @fragment
120
120
  fn main(input: FragmentInput) -> @location(0) vec4<f32> {
121
121
  HOOK_beforeFragment();
122
- var outColor = HOOK_getFinalColor(input.vColor);
122
+ var outColor = HOOK_getFinalColor(input.vColor, input.vVertTexCoord);
123
123
  outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);
124
124
  HOOK_afterFragment();
125
125
  return outColor;
@@ -0,0 +1,32 @@
1
+ const baseComputeShader = `
2
+ struct ComputeUniforms {
3
+ uTotalCount: vec3<i32>,
4
+ uPhysicalCount: vec3<i32>,
5
+ }
6
+ @group(0) @binding(0) var<uniform> uniforms: ComputeUniforms;
7
+
8
+ @compute @workgroup_size(8, 8, 1)
9
+ fn main(
10
+ @builtin(global_invocation_id) globalId: vec3<u32>,
11
+ @builtin(local_invocation_id) localId: vec3<u32>,
12
+ @builtin(workgroup_id) workgroupId: vec3<u32>,
13
+ @builtin(local_invocation_index) localIndex: u32
14
+ ) {
15
+ let totalIterations = u32(uniforms.uTotalCount.x) * u32(uniforms.uTotalCount.y) * u32(uniforms.uTotalCount.z);
16
+ let physicalId = globalId.x + globalId.y * (u32(uniforms.uPhysicalCount.x)) + globalId.z * (u32(uniforms.uPhysicalCount.x) * u32(uniforms.uPhysicalCount.y));
17
+
18
+ if (physicalId >= totalIterations) {
19
+ return;
20
+ }
21
+
22
+ var index = vec3<i32>(0);
23
+ index.x = i32(physicalId % u32(uniforms.uTotalCount.x));
24
+ let remainingY = physicalId / u32(uniforms.uTotalCount.x);
25
+ index.y = i32(remainingY % u32(uniforms.uTotalCount.y));
26
+ index.z = i32(remainingY / u32(uniforms.uTotalCount.y));
27
+
28
+ HOOK_iteration(index);
29
+ }
30
+ `;
31
+
32
+ export { baseComputeShader };
@@ -0,0 +1,31 @@
1
+ // _p5_hash: "Hash without Sine" by Dave Hoskins (https://www.shadertoy.com/view/4djSRW)
2
+ // Mixing constants: R₂ sequence by Martin Roberts (https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/)
3
+ // α₁ = 1/φ₂ = 0.7548776662 (plastic constant reciprocal)
4
+ // α₂ = 1/φ₂² = 0.5698402910
5
+ // 1/φ = 0.6180339887 (golden ratio conjugate)
6
+ //
7
+ // Compute shader version: invocationId is passed in from main via @builtin(global_invocation_id).
8
+
9
+ var randomComputeWGSL = `
10
+ var<private> _p5_randomCallIndex: i32 = 0;
11
+
12
+ fn _p5_hash(p: vec3<f32>) -> f32 {
13
+ var p3 = fract(p * vec3<f32>(0.1031, 0.1030, 0.0973));
14
+ p3 = p3 + dot(p3, p3.yxz + 33.33);
15
+ return fract((p3.x + p3.y) * p3.z);
16
+ }
17
+
18
+ fn random(seed: f32, invocationId: vec3<u32>) -> f32 {
19
+ let id = vec3<f32>(invocationId);
20
+ let callIndex = f32(_p5_randomCallIndex);
21
+ _p5_randomCallIndex = _p5_randomCallIndex + 1;
22
+ let s = fract(seed * 0.7548776662);
23
+ return _p5_hash(vec3<f32>(
24
+ id.x + s,
25
+ id.y + callIndex * 0.5698402910,
26
+ id.z + s + callIndex * 0.6180339887
27
+ ));
28
+ }
29
+ `;
30
+
31
+ export { randomComputeWGSL as default };
@@ -0,0 +1,30 @@
1
+ // _p5_hash: "Hash without Sine" by Dave Hoskins (https://www.shadertoy.com/view/4djSRW)
2
+ // Mixing constants: R₂ sequence by Martin Roberts (https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/)
3
+ // α₁ = 1/φ₂ = 0.7548776662 (plastic constant reciprocal)
4
+ // α₂ = 1/φ₂² = 0.5698402910
5
+ // 1/φ = 0.6180339887 (golden ratio conjugate)
6
+ //
7
+ // Vertex shader version: vertexId is passed in from main via @builtin(vertex_index).
8
+
9
+ var randomVertWGSL = `
10
+ var<private> _p5_randomCallIndex: i32 = 0;
11
+
12
+ fn _p5_hash(p: vec3<f32>) -> f32 {
13
+ var p3 = fract(p * vec3<f32>(0.1031, 0.1030, 0.0973));
14
+ p3 = p3 + dot(p3, p3.yxz + 33.33);
15
+ return fract((p3.x + p3.y) * p3.z);
16
+ }
17
+
18
+ fn random(seed: f32, vertexId: f32) -> f32 {
19
+ let callIndex = f32(_p5_randomCallIndex);
20
+ _p5_randomCallIndex = _p5_randomCallIndex + 1;
21
+ let s = fract(seed * 0.7548776662);
22
+ return _p5_hash(vec3<f32>(
23
+ vertexId + s,
24
+ vertexId * 0.5698402910 + callIndex * 0.6180339887,
25
+ s + callIndex * 0.7548776662
26
+ ));
27
+ }
28
+ `;
29
+
30
+ export { randomVertWGSL as default };
@@ -0,0 +1,30 @@
1
+ // _p5_hash: "Hash without Sine" by Dave Hoskins (https://www.shadertoy.com/view/4djSRW)
2
+ // Mixing constants: R₂ sequence by Martin Roberts (https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/)
3
+ // α₁ = 1/φ₂ = 0.7548776662 (plastic constant reciprocal)
4
+ // α₂ = 1/φ₂² = 0.5698402910
5
+ // 1/φ = 0.6180339887 (golden ratio conjugate)
6
+ //
7
+ // Fragment shader version: pixelCoord is passed in from main via @builtin(position).
8
+
9
+ var randomWGSL = `
10
+ var<private> _p5_randomCallIndex: i32 = 0;
11
+
12
+ fn _p5_hash(p: vec3<f32>) -> f32 {
13
+ var p3 = fract(p * vec3<f32>(0.1031, 0.1030, 0.0973));
14
+ p3 = p3 + dot(p3, p3.yxz + 33.33);
15
+ return fract((p3.x + p3.y) * p3.z);
16
+ }
17
+
18
+ fn random(seed: f32, pixelCoord: vec2<f32>) -> f32 {
19
+ let callIndex = f32(_p5_randomCallIndex);
20
+ _p5_randomCallIndex = _p5_randomCallIndex + 1;
21
+ let s = fract(seed * 0.7548776662);
22
+ return _p5_hash(vec3<f32>(
23
+ pixelCoord.x + s,
24
+ pixelCoord.y + callIndex * 0.5698402910,
25
+ s + callIndex * 0.6180339887
26
+ ));
27
+ }
28
+ `;
29
+
30
+ export { randomWGSL as default };
@@ -362,7 +362,7 @@ fn main(input: StrokeFragmentInput) -> @location(0) vec4<f32> {
362
362
  discard;
363
363
  }
364
364
  }
365
- var col = HOOK_getFinalColor(inputs.color);
365
+ var col = HOOK_getFinalColor(inputs.color, vec2<f32>(0.0, 0.0));
366
366
  col = vec4<f32>(col.rgb, 1.0) * col.a;
367
367
  HOOK_afterFragment();
368
368
  return vec4<f32>(col);
@@ -416,9 +416,9 @@ fn main(input: FragmentInput) -> @location(0) vec4<f32> {
416
416
  inputs.emissiveMaterial
417
417
  );
418
418
 
419
- var outColor = HOOK_getFinalColor(
420
- HOOK_combineColors(components)
421
- );
419
+ var outColor = HOOK_getFinalColor(
420
+ HOOK_combineColors(components), input.vTexCoord
421
+ );
422
422
  outColor = vec4<f32>(outColor.rgb * outColor.a, outColor.a);
423
423
  HOOK_afterFragment();
424
424
  return outColor;
@@ -1,7 +1,11 @@
1
- import { BaseType, DataType, NodeTypeToName, NodeType, OpCode, OpCodeToSymbol, isStructType, StatementType, BlockType } from '../strands/ir_types.js';
1
+ import noiseWGSL from './shaders/functions/noise3DWGSL.js';
2
+ import randomWGSL from './shaders/functions/randomWGSL.js';
3
+ import randomVertWGSL from './shaders/functions/randomVertWGSL.js';
4
+ import randomComputeWGSL from './shaders/functions/randomComputeWGSL.js';
5
+ import { INSTANCE_ID_VARYING_NAME, BaseType, DataType, NodeTypeToName, NodeType, OpCode, OpCodeToSymbol, isStructType, StatementType, BlockType } from '../strands/ir_types.js';
2
6
  import { getNodeDataFromID, extractNodeTypeInfo } from '../strands/ir_dag.js';
3
7
  import { internalError } from '../strands/strands_FES.js';
4
- import { v as variableNode, c as createStrandsNode, f as functionCallNode } from '../ir_builders-Cd6rU9Vm.js';
8
+ import { v as variableNode, c as createStrandsNode, s as scalarLiteralNode, f as functionCallNode } from '../ir_builders-C2ebb6Lu.js';
5
9
  import '../strands/ir_cfg.js';
6
10
  import '../strands/strands_builtins.js';
7
11
 
@@ -206,10 +210,10 @@ const wgslBackend = {
206
210
  // Add texture and sampler bindings for sampler2D uniforms to both vertex and fragment declarations
207
211
  if (!strandsContext.renderer || !strandsContext.baseShader) return;
208
212
 
209
- // Get the next available binding index from the renderer
210
213
  let bindingIndex = strandsContext.renderer.getNextBindingIndex({
211
- vert: strandsContext.baseShader.vertSrc(),
212
- frag: strandsContext.baseShader.fragSrc(),
214
+ vert: strandsContext.baseShader._vertSrc,
215
+ frag: strandsContext.baseShader._fragSrc,
216
+ compute: strandsContext.baseShader._computeSrc,
213
217
  });
214
218
 
215
219
  for (const {name, typeInfo} of strandsContext.uniforms) {
@@ -226,6 +230,38 @@ const wgslBackend = {
226
230
  }
227
231
  }
228
232
  },
233
+ addStorageBufferBindingsToDeclarations(strandsContext) {
234
+ if (!strandsContext.renderer || !strandsContext.baseShader) return;
235
+
236
+ const isComputeShader = strandsContext.baseShader.shaderType === 'compute';
237
+ let bindingIndex = strandsContext.renderer.getNextBindingIndex({
238
+ vert: strandsContext.baseShader._vertSrc,
239
+ frag: strandsContext.baseShader._fragSrc,
240
+ compute: strandsContext.baseShader._computeSrc,
241
+ });
242
+
243
+ for (const {name, typeInfo} of strandsContext.uniforms) {
244
+ if (typeInfo.baseType === 'storage') {
245
+ const accessMode = isComputeShader ? 'read_write' : 'read';
246
+ let declaration;
247
+ if (typeInfo.schema) {
248
+ const structTypeName = `${name}Element`;
249
+ declaration = `struct ${structTypeName} ${typeInfo.schema.structBody}\n@group(0) @binding(${bindingIndex}) var<storage, ${accessMode}> ${name}: array<${structTypeName}>;`;
250
+ } else {
251
+ declaration = `@group(0) @binding(${bindingIndex}) var<storage, ${accessMode}> ${name}: array<f32>;`;
252
+ }
253
+
254
+ if (isComputeShader) {
255
+ strandsContext.computeDeclarations.add(declaration);
256
+ } else {
257
+ strandsContext.vertexDeclarations.add(declaration);
258
+ strandsContext.fragmentDeclarations.add(declaration);
259
+ }
260
+
261
+ bindingIndex += 1;
262
+ }
263
+ }
264
+ },
229
265
  getTypeName(baseType, dimension) {
230
266
  const primitiveTypeName = TypeNames[baseType + dimension];
231
267
  if (!primitiveTypeName) {
@@ -233,6 +269,19 @@ const wgslBackend = {
233
269
  }
234
270
  return primitiveTypeName;
235
271
  },
272
+ getNoiseShaderSnippet() {
273
+ return noiseWGSL;
274
+ },
275
+ getRandomFragmentShaderSnippet() {
276
+ return randomWGSL;
277
+ },
278
+ getRandomVertexShaderSnippet() {
279
+ return randomVertWGSL;
280
+ },
281
+ getRandomComputeShaderSnippet() {
282
+ return randomComputeWGSL;
283
+ },
284
+
236
285
  generateHookUniformKey(name, typeInfo) {
237
286
  // For sampler2D types, we don't add them to the uniform struct,
238
287
  // but we still need them in the shader's hooks object so that
@@ -240,6 +289,11 @@ const wgslBackend = {
240
289
  if (typeInfo.baseType === 'sampler2D') {
241
290
  return `${name}: sampler2D`; // Signal that this should not be added to uniform struct
242
291
  }
292
+ // For storage buffers, we don't add them to the uniform struct
293
+ // Instead, they become separate storage buffer bindings
294
+ if (typeInfo.baseType === 'storage') {
295
+ return null; // Signal that this should not be added to uniform struct
296
+ }
243
297
  return `${name}: ${this.getTypeName(typeInfo.baseType, typeInfo.dimension)}`;
244
298
  },
245
299
  generateVaryingVariable(varName, typeInfo) {
@@ -266,9 +320,13 @@ const wgslBackend = {
266
320
  // Generate just a semicolon (unless suppressed)
267
321
  generationContext.write(semicolon);
268
322
  } else if (node.statementType === StatementType.EARLY_RETURN) {
269
- const exprNodeID = node.dependsOn[0];
270
- const expr = this.generateExpression(generationContext, dag, exprNodeID);
271
- generationContext.write(`return ${expr}${semicolon}`);
323
+ if (node.dependsOn && node.dependsOn.length > 0) {
324
+ const exprNodeID = node.dependsOn[0];
325
+ const expr = this.generateExpression(generationContext, dag, exprNodeID);
326
+ generationContext.write(`return ${expr}${semicolon}`);
327
+ } else {
328
+ generationContext.write(`return${semicolon}`);
329
+ }
272
330
  }
273
331
  },
274
332
  generateAssignment(generationContext, dag, nodeID) {
@@ -280,6 +338,17 @@ const wgslBackend = {
280
338
  const targetNode = getNodeDataFromID(dag, targetNodeID);
281
339
  const semicolon = generationContext.suppressSemicolon ? '' : ';';
282
340
 
341
+ // Check if target is an array access (storage buffer assignment)
342
+ if (targetNode.opCode === OpCode.Binary.ARRAY_ACCESS) {
343
+ const [bufferID, indexID] = targetNode.dependsOn;
344
+ const bufferExpr = this.generateExpression(generationContext, dag, bufferID);
345
+ const indexExpr = this.generateExpression(generationContext, dag, indexID);
346
+ const sourceExpr = this.generateExpression(generationContext, dag, sourceNodeID);
347
+ const fieldSuffix = targetNode.identifier ? `.${targetNode.identifier}` : '';
348
+ generationContext.write(`${bufferExpr}[i32(${indexExpr})]${fieldSuffix} = ${sourceExpr}${semicolon}`);
349
+ return;
350
+ }
351
+
283
352
  // Check if target is a swizzle assignment
284
353
  if (targetNode.opCode === OpCode.Unary.SWIZZLE) {
285
354
  const parentID = targetNode.dependsOn[0];
@@ -337,6 +406,10 @@ const wgslBackend = {
337
406
  return `var ${tmp}: ${typeName} = ${expr};`;
338
407
  },
339
408
  generateReturnStatement(strandsContext, generationContext, rootNodeID, returnType) {
409
+ if (!returnType) {
410
+ generationContext.write('return;');
411
+ return;
412
+ }
340
413
  const dag = strandsContext.dag;
341
414
  const rootNode = getNodeDataFromID(dag, rootNodeID);
342
415
  if (isStructType(returnType)) {
@@ -377,9 +450,15 @@ const wgslBackend = {
377
450
  }
378
451
  }
379
452
 
380
- // Check if this is a uniform variable (but not a texture)
453
+ // Detect instanceID usage in fragment context and rewrite to varying name
454
+ if (node.identifier === this.instanceIdReference() && generationContext.shaderContext === 'fragment') {
455
+ generationContext.strandsContext._instanceIDUsedInFragment = true;
456
+ return INSTANCE_ID_VARYING_NAME;
457
+ }
458
+
459
+ // Check if this is a uniform variable (but not a texture or storage buffer)
381
460
  const uniform = generationContext.strandsContext?.uniforms?.find(uniform => uniform.name === node.identifier);
382
- if (uniform && uniform.typeInfo.baseType !== 'sampler2D') {
461
+ if (uniform && uniform.typeInfo.baseType !== 'sampler2D' && uniform.typeInfo.baseType !== 'storage') {
383
462
  return `hooks.${node.identifier}`;
384
463
  }
385
464
 
@@ -398,6 +477,13 @@ const wgslBackend = {
398
477
  const deps = node.dependsOn.map((dep) => this.generateExpression(generationContext, dag, dep));
399
478
  return `${T}(${deps.join(', ')})`;
400
479
  }
480
+ if (node.opCode === OpCode.Nary.TERNARY) {
481
+ const [condID, trueID, falseID] = node.dependsOn;
482
+ const cond = this.generateExpression(generationContext, dag, condID);
483
+ const trueExpr = this.generateExpression(generationContext, dag, trueID);
484
+ const falseExpr = this.generateExpression(generationContext, dag, falseID);
485
+ return `select(${falseExpr}, ${trueExpr}, ${cond})`;
486
+ }
401
487
  if (node.opCode === OpCode.Nary.FUNCTION_CALL) {
402
488
  // Convert mod() function calls to % operator in WGSL
403
489
  if (node.identifier === 'mod' && node.dependsOn.length === 2) {
@@ -419,6 +505,18 @@ const wgslBackend = {
419
505
  }
420
506
 
421
507
  const functionArgs = node.dependsOn.map(arg =>this.generateExpression(generationContext, dag, arg));
508
+
509
+ if (node.identifier === 'random') {
510
+ const ctx = generationContext.shaderContext;
511
+ if (ctx === 'fragment') {
512
+ functionArgs.push('_p5FragPos.xy');
513
+ } else if (ctx === 'vertex') {
514
+ functionArgs.push('f32(_p5VertexId)');
515
+ } else if (ctx === 'compute') {
516
+ functionArgs.push('_p5GlobalId');
517
+ }
518
+ }
519
+
422
520
  return `${node.identifier}(${functionArgs.join(', ')})`;
423
521
  }
424
522
  if (node.opCode === OpCode.Binary.MEMBER_ACCESS) {
@@ -432,6 +530,13 @@ const wgslBackend = {
432
530
  const parentExpr = this.generateExpression(generationContext, dag, parentID);
433
531
  return `${parentExpr}.${node.swizzle}`;
434
532
  }
533
+ if (node.opCode === OpCode.Binary.ARRAY_ACCESS) {
534
+ const [bufferID, indexID] = node.dependsOn;
535
+ const bufferExpr = this.generateExpression(generationContext, dag, bufferID);
536
+ const indexExpr = this.generateExpression(generationContext, dag, indexID);
537
+ const fieldSuffix = node.identifier ? `.${node.identifier}` : '';
538
+ return `${bufferExpr}[i32(${indexExpr})]${fieldSuffix}`;
539
+ }
435
540
  if (node.dependsOn.length === 2) {
436
541
  const [lID, rID] = node.dependsOn;
437
542
  const left = this.generateExpression(generationContext, dag, lID);
@@ -505,12 +610,25 @@ const wgslBackend = {
505
610
  const samplerVariable = variableNode(strandsContext, { baseType: BaseType.SAMPLER, dimension: 1 }, samplerIdentifier);
506
611
  const samplerNode = createStrandsNode(samplerVariable.id, samplerVariable.dimension, strandsContext);
507
612
 
508
- // Create the augmented args: [texture, sampler, coords]
509
- const augmentedArgs = [textureArg, samplerNode, coordsArg];
510
-
511
- const { id, dimension } = functionCallNode(strandsContext, 'textureSample', augmentedArgs, {
613
+ // Create a LOD literal node (0.0) so we can use textureSampleLevel instead
614
+ // of textureSample. textureSample doesn't let you use uniform values in control
615
+ // flow, whereas textureSampleLevel does. While we don't have mipmaps, we don't
616
+ // miss out.
617
+ // TODO: if we *do* add mipmap support, update this logic -- we'd need to hoist
618
+ // the texture lookup out of the control flow.
619
+ const lodLiteral = scalarLiteralNode(
620
+ strandsContext,
621
+ { dimension: 1, baseType: BaseType.FLOAT },
622
+ 0.0
623
+ );
624
+ const lodNode = createStrandsNode(lodLiteral.id, lodLiteral.dimension, strandsContext);
625
+
626
+ // Create the augmented args: [texture, sampler, coords, lod]
627
+ const augmentedArgs = [textureArg, samplerNode, coordsArg, lodNode];
628
+
629
+ const { id, dimension } = functionCallNode(strandsContext, 'textureSampleLevel', augmentedArgs, {
512
630
  overloads: [{
513
- params: [DataType.sampler2D, DataType.sampler, DataType.float2],
631
+ params: [DataType.sampler2D, DataType.sampler, DataType.float2, DataType.float1],
514
632
  returnType: DataType.float4
515
633
  }]
516
634
  });
@@ -520,6 +638,10 @@ const wgslBackend = {
520
638
  instanceIdReference() {
521
639
  return 'instanceID';
522
640
  },
641
+
642
+ generateInstanceIDVarying() {
643
+ return { name: INSTANCE_ID_VARYING_NAME, declaration: `${INSTANCE_ID_VARYING_NAME}: i32`, source: 'i32(instanceID)', interpolation: 'flat' };
644
+ },
523
645
  };
524
646
 
525
647
  export { wgslBackend };