p5.tree 0.0.2 → 0.0.4

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.
@@ -2,16 +2,16 @@ import p5 from 'p5';
2
2
 
3
3
  /**
4
4
  * @file Adds Tree rendering functions to the p5 prototype.
5
- * @version 0.0.2
5
+ * @version 0.0.4
6
6
  * @author JP Charalambos
7
7
  * @license GPL-3.0-only
8
8
  *
9
9
  * @description
10
- * A p5.js WEBGL addon for shader development and space transformations.
10
+ * A p5.js 3D addon for matrix queries, shader workflows, and space transformations.
11
11
  *
12
12
  * Camera path recording/playback section.
13
13
  *
14
- * Requires WEBGL (p5.Camera).
14
+ * Requires 3D renderer (p5.Camera).
15
15
  *
16
16
  * Camera API:
17
17
  * camera.path : p5.Camera[]
@@ -38,7 +38,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
38
38
  const CONST = value => ({ value, writable: false, enumerable: true, configurable: false });
39
39
 
40
40
  Object.defineProperties(p5.Tree, {
41
- VERSION: CONST('0.0.2'),
41
+ VERSION: CONST('0.0.4'),
42
42
 
43
43
  NONE: CONST(0),
44
44
 
@@ -48,6 +48,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
48
48
  NDC: CONST('NDC'),
49
49
  SCREEN: CONST('SCREEN'),
50
50
  MODEL: CONST('MODEL'),
51
+ OBJECT: CONST('MODEL'), // alias of MODEL (shader terminology)
51
52
 
52
53
  // Points and vectors
53
54
  ORIGIN: CONST(Object.freeze([0, 0, 0])),
@@ -68,9 +69,6 @@ p5.registerAddon((p5, fn, lifecycles) => {
68
69
  Z: CONST(1 << 4),
69
70
  _Z: CONST(1 << 5),
70
71
  LABELS: CONST(1 << 6),
71
-
72
- DOTS: CONST(0),
73
- SOLID: CONST(1),
74
72
 
75
73
  // bullsEye
76
74
  CIRCLE: CONST(0),
@@ -99,13 +97,13 @@ p5.registerAddon((p5, fn, lifecycles) => {
99
97
 
100
98
  /**
101
99
  * @private
102
- * Returns the WEBGL renderer or undefined.
103
- * @param {p5} pInst
104
- * @returns {p5.RendererGL|undefined}
100
+ * Returns the active 3D renderer (WEBGL or WEBGPU) or undefined.
101
+ * @param {p5} pInst - The p5 instance.
102
+ * @returns {p5.Renderer3D|undefined} The active 3D renderer if available.
105
103
  */
106
- const _rendererGL = function (pInst) {
107
- const r = pInst._renderer;
108
- return r instanceof p5.RendererGL ? r : undefined;
104
+ const _renderer3D = function (pInst) {
105
+ const r = pInst?._renderer;
106
+ return r instanceof p5.Renderer3D ? r : undefined;
109
107
  };
110
108
 
111
109
  // ---------------------------------------------------------------------------
@@ -218,34 +216,34 @@ p5.registerAddon((p5, fn, lifecycles) => {
218
216
  * Returns the current projection matrix (immutable copy).
219
217
  * @returns {p5.Matrix}
220
218
  */
221
- p5.RendererGL.prototype.pMatrix = function () {
219
+ p5.Renderer3D.prototype.pMatrix = function () {
222
220
  return this.states.uPMatrix.clone();
223
221
  };
224
222
 
225
223
  /**
226
224
  * Returns the current projection matrix (immutable copy).
227
- * Requires WEBGL.
225
+ * Requires 3D renderer.
228
226
  * @returns {p5.Matrix}
229
227
  */
230
228
  fn.pMatrix = function () {
231
- return _rendererGL(this).pMatrix();
229
+ return _renderer3D(this).pMatrix();
232
230
  };
233
231
 
234
232
  /**
235
233
  * Returns the current model matrix (immutable copy).
236
234
  * @returns {p5.Matrix}
237
235
  */
238
- p5.RendererGL.prototype.mMatrix = function () {
236
+ p5.Renderer3D.prototype.mMatrix = function () {
239
237
  return this.states.uModelMatrix.clone();
240
238
  };
241
239
 
242
240
  /**
243
241
  * Returns the current model matrix (immutable copy).
244
- * Requires WEBGL.
242
+ * Requires 3D renderer.
245
243
  * @returns {p5.Matrix}
246
244
  */
247
245
  fn.mMatrix = function () {
248
- return _rendererGL(this).mMatrix();
246
+ return _renderer3D(this).mMatrix();
249
247
  };
250
248
 
251
249
  /**
@@ -269,46 +267,46 @@ p5.registerAddon((p5, fn, lifecycles) => {
269
267
  * Prefers the renderer cached view matrix when available.
270
268
  * @returns {p5.Matrix}
271
269
  */
272
- p5.RendererGL.prototype.vMatrix = function () {
270
+ p5.Renderer3D.prototype.vMatrix = function () {
273
271
  return (this.states.uViewMatrix || this.states.curCamera.cameraMatrix).clone();
274
272
  };
275
273
 
276
274
  /**
277
275
  * Returns the current view matrix (world -> camera) (immutable copy).
278
- * Requires WEBGL.
276
+ * Requires 3D renderer.
279
277
  * @returns {p5.Matrix}
280
278
  */
281
279
  fn.vMatrix = function () {
282
- return _rendererGL(this).vMatrix();
280
+ return _renderer3D(this).vMatrix();
283
281
  };
284
282
 
285
283
  /**
286
284
  * Returns the current eye matrix (camera -> world) (immutable).
287
285
  * @returns {p5.Matrix}
288
286
  */
289
- p5.RendererGL.prototype.eMatrix = function () {
287
+ p5.Renderer3D.prototype.eMatrix = function () {
290
288
  return _invert(this.states.uViewMatrix || this.states.curCamera.cameraMatrix);
291
289
  };
292
290
 
293
291
  /**
294
292
  * Returns the current eye matrix (camera -> world) (immutable).
295
- * Requires WEBGL.
293
+ * Requires 3D renderer.
296
294
  * @returns {p5.Matrix}
297
295
  */
298
296
  fn.eMatrix = function () {
299
- return _rendererGL(this).eMatrix();
297
+ return _renderer3D(this).eMatrix();
300
298
  };
301
299
 
302
300
  /**
303
301
  * lMatrix({ from, to }):
304
- * Position transform (mat4) mapping points from `from` space to `to` space.
302
+ * Location transform (mat4) mapping points from `from` space to `to` space.
305
303
  * treegl semantics: to^-1 * from.
306
304
  * @param {object} [opts]
307
305
  * @param {p5.Matrix} [opts.from=new p5.Matrix()] Source frame matrix.
308
306
  * @param {p5.Matrix} [opts.to=this.eMatrix()] Target frame matrix.
309
307
  * @returns {p5.Matrix}
310
308
  */
311
- p5.RendererGL.prototype.lMatrix = function ({
309
+ p5.Renderer3D.prototype.lMatrix = function ({
312
310
  from = new p5.Matrix(4),
313
311
  to = this.eMatrix()
314
312
  } = {}) {
@@ -317,15 +315,15 @@ p5.registerAddon((p5, fn, lifecycles) => {
317
315
 
318
316
  /**
319
317
  * lMatrix({ from, to }):
320
- * Position transform (mat4) mapping points from `from` space to `to` space.
321
- * Requires WEBGL.
318
+ * Location transform (mat4) mapping points from `from` space to `to` space.
319
+ * Requires 3D renderer.
322
320
  * @param {object} [opts]
323
321
  * @param {p5.Matrix} [opts.from]
324
322
  * @param {p5.Matrix} [opts.to]
325
323
  * @returns {p5.Matrix}
326
324
  */
327
325
  fn.lMatrix = function (opts = {}) {
328
- return _rendererGL(this).lMatrix(opts);
326
+ return _renderer3D(this).lMatrix(opts);
329
327
  };
330
328
 
331
329
  /**
@@ -339,7 +337,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
339
337
  * @param {p5.Matrix} [opts.matrix] Precomputed mat4 override.
340
338
  * @returns {p5.Matrix} mat3
341
339
  */
342
- p5.RendererGL.prototype.dMatrix = function ({
340
+ p5.Renderer3D.prototype.dMatrix = function ({
343
341
  from = new p5.Matrix(4),
344
342
  to = this.eMatrix(),
345
343
  matrix
@@ -357,7 +355,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
357
355
  /**
358
356
  * dMatrix({ from, to, matrix }):
359
357
  * Direction transform (mat3) mapping vectors from `from` space to `to` space.
360
- * Requires WEBGL.
358
+ * Requires 3D renderer.
361
359
  * @param {object} [opts]
362
360
  * @param {p5.Matrix} [opts.from]
363
361
  * @param {p5.Matrix} [opts.to]
@@ -365,7 +363,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
365
363
  * @returns {p5.Matrix} mat3
366
364
  */
367
365
  fn.dMatrix = function (opts = {}) {
368
- return _rendererGL(this).dMatrix(opts);
366
+ return _renderer3D(this).dMatrix(opts);
369
367
  };
370
368
 
371
369
  /**
@@ -376,7 +374,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
376
374
  * @param {p5.Matrix} [opts.mMatrix=this.mMatrix()] Model matrix.
377
375
  * @returns {p5.Matrix}
378
376
  */
379
- p5.RendererGL.prototype.mvMatrix = function ({
377
+ p5.Renderer3D.prototype.mvMatrix = function ({
380
378
  vMatrix = this.vMatrix(),
381
379
  mMatrix
382
380
  } = {}) {
@@ -386,14 +384,14 @@ p5.registerAddon((p5, fn, lifecycles) => {
386
384
  /**
387
385
  * mvMatrix({ vMatrix, mMatrix }):
388
386
  * ModelView matrix (mat4) = M * V (p5-v2 convention).
389
- * Requires WEBGL.
387
+ * Requires 3D renderer.
390
388
  * @param {object} [opts]
391
389
  * @param {p5.Matrix} [opts.vMatrix]
392
390
  * @param {p5.Matrix} [opts.mMatrix]
393
391
  * @returns {p5.Matrix}
394
392
  */
395
393
  fn.mvMatrix = function (opts = {}) {
396
- return _rendererGL(this).mvMatrix(opts);
394
+ return _renderer3D(this).mvMatrix(opts);
397
395
  };
398
396
 
399
397
  /**
@@ -405,7 +403,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
405
403
  * @param {p5.Matrix} [opts.mvMatrix=this.mvMatrix({ mMatrix, vMatrix })] Optional MV matrix override.
406
404
  * @returns {p5.Matrix} mat3
407
405
  */
408
- p5.RendererGL.prototype.nMatrix = function ({
406
+ p5.Renderer3D.prototype.nMatrix = function ({
409
407
  vMatrix,
410
408
  mMatrix,
411
409
  mvMatrix = this.mvMatrix({ mMatrix, vMatrix })
@@ -416,7 +414,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
416
414
  /**
417
415
  * nMatrix({ vMatrix, mMatrix, mvMatrix }):
418
416
  * Normal matrix (mat3) = inverseTranspose(linear_part(MV)).
419
- * Requires WEBGL.
417
+ * Requires 3D renderer.
420
418
  * @param {object} [opts]
421
419
  * @param {p5.Matrix} [opts.vMatrix]
422
420
  * @param {p5.Matrix} [opts.mMatrix]
@@ -424,7 +422,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
424
422
  * @returns {p5.Matrix} mat3
425
423
  */
426
424
  fn.nMatrix = function (opts = {}) {
427
- return _rendererGL(this).nMatrix(opts);
425
+ return _renderer3D(this).nMatrix(opts);
428
426
  };
429
427
 
430
428
  /**
@@ -437,7 +435,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
437
435
  * @param {p5.Matrix} [opts.mvMatrix=this.mvMatrix({ mMatrix, vMatrix })] Optional MV matrix override.
438
436
  * @returns {p5.Matrix}
439
437
  */
440
- p5.RendererGL.prototype.pmvMatrix = function ({
438
+ p5.Renderer3D.prototype.pmvMatrix = function ({
441
439
  pMatrix = this.pMatrix(),
442
440
  vMatrix,
443
441
  mMatrix,
@@ -449,7 +447,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
449
447
  /**
450
448
  * pmvMatrix({ pMatrix, vMatrix, mMatrix, mvMatrix }):
451
449
  * PMV (mat4) = M * V * P (p5-v2 convention).
452
- * Requires WEBGL.
450
+ * Requires 3D renderer.
453
451
  * @param {object} [opts]
454
452
  * @param {p5.Matrix} [opts.pMatrix]
455
453
  * @param {p5.Matrix} [opts.vMatrix]
@@ -458,7 +456,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
458
456
  * @returns {p5.Matrix}
459
457
  */
460
458
  fn.pmvMatrix = function (opts = {}) {
461
- return _rendererGL(this).pmvMatrix(opts);
459
+ return _renderer3D(this).pmvMatrix(opts);
462
460
  };
463
461
 
464
462
  /**
@@ -469,7 +467,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
469
467
  * @param {p5.Matrix} [opts.vMatrix=this.vMatrix()] View matrix.
470
468
  * @returns {p5.Matrix}
471
469
  */
472
- p5.RendererGL.prototype.pvMatrix = function ({
470
+ p5.Renderer3D.prototype.pvMatrix = function ({
473
471
  pMatrix = this.pMatrix(),
474
472
  vMatrix
475
473
  } = {}) {
@@ -479,14 +477,14 @@ p5.registerAddon((p5, fn, lifecycles) => {
479
477
  /**
480
478
  * pvMatrix({ pMatrix, vMatrix }):
481
479
  * PV (mat4) = V * P (p5-v2 convention).
482
- * Requires WEBGL.
480
+ * Requires 3D renderer.
483
481
  * @param {object} [opts]
484
482
  * @param {p5.Matrix} [opts.pMatrix]
485
483
  * @param {p5.Matrix} [opts.vMatrix]
486
484
  * @returns {p5.Matrix}
487
485
  */
488
486
  fn.pvMatrix = function (opts = {}) {
489
- return _rendererGL(this).pvMatrix(opts);
487
+ return _renderer3D(this).pvMatrix(opts);
490
488
  };
491
489
 
492
490
  /**
@@ -498,7 +496,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
498
496
  * @param {p5.Matrix} [opts.pvMatrix=this.pvMatrix({ pMatrix, vMatrix })] Optional PV matrix override.
499
497
  * @returns {p5.Matrix}
500
498
  */
501
- p5.RendererGL.prototype.ipvMatrix = function ({
499
+ p5.Renderer3D.prototype.ipvMatrix = function ({
502
500
  pMatrix,
503
501
  vMatrix,
504
502
  pvMatrix = this.pvMatrix({ pMatrix, vMatrix })
@@ -508,12 +506,12 @@ p5.registerAddon((p5, fn, lifecycles) => {
508
506
 
509
507
  /**
510
508
  * ipvMatrix({ pMatrix, vMatrix, pvMatrix }):
511
- * Inverse(PV) (mat4). Requires WEBGL.
509
+ * Inverse(PV) (mat4). Requires 3D renderer.
512
510
  * @param {object} [opts]
513
511
  * @returns {p5.Matrix}
514
512
  */
515
513
  fn.ipvMatrix = function (opts = {}) {
516
- return _rendererGL(this).ipvMatrix(opts);
514
+ return _renderer3D(this).ipvMatrix(opts);
517
515
  };
518
516
 
519
517
  // ---------------------------------------------------------------------------
@@ -532,17 +530,17 @@ p5.registerAddon((p5, fn, lifecycles) => {
532
530
  * Returns true if the current projection is orthographic.
533
531
  * @returns {boolean}
534
532
  */
535
- p5.RendererGL.prototype.isOrtho = function () {
533
+ p5.Renderer3D.prototype.isOrtho = function () {
536
534
  return this.pMatrix().isOrtho();
537
535
  };
538
536
 
539
537
  /**
540
538
  * Returns true if the current projection is orthographic.
541
- * Requires WEBGL.
539
+ * Requires 3D renderer.
542
540
  * @returns {boolean}
543
541
  */
544
542
  fn.isOrtho = function () {
545
- return _rendererGL(this).isOrtho();
543
+ return _renderer3D(this).isOrtho();
546
544
  };
547
545
 
548
546
  /**
@@ -603,7 +601,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
603
601
  * Near plane distance for the current projection.
604
602
  * @returns {number}
605
603
  */
606
- p5.RendererGL.prototype.nPlane = function () {
604
+ p5.Renderer3D.prototype.nPlane = function () {
607
605
  return this.pMatrix().nPlane();
608
606
  };
609
607
 
@@ -611,7 +609,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
611
609
  * Far plane distance for the current projection.
612
610
  * @returns {number}
613
611
  */
614
- p5.RendererGL.prototype.fPlane = function () {
612
+ p5.Renderer3D.prototype.fPlane = function () {
615
613
  return this.pMatrix().fPlane();
616
614
  };
617
615
 
@@ -619,7 +617,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
619
617
  * Left plane for the current projection.
620
618
  * @returns {number}
621
619
  */
622
- p5.RendererGL.prototype.lPlane = function () {
620
+ p5.Renderer3D.prototype.lPlane = function () {
623
621
  return this.pMatrix().lPlane();
624
622
  };
625
623
 
@@ -627,7 +625,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
627
625
  * Right plane for the current projection.
628
626
  * @returns {number}
629
627
  */
630
- p5.RendererGL.prototype.rPlane = function () {
628
+ p5.Renderer3D.prototype.rPlane = function () {
631
629
  return this.pMatrix().rPlane();
632
630
  };
633
631
 
@@ -635,7 +633,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
635
633
  * Top plane for the current projection.
636
634
  * @returns {number}
637
635
  */
638
- p5.RendererGL.prototype.tPlane = function () {
636
+ p5.Renderer3D.prototype.tPlane = function () {
639
637
  return this.pMatrix().tPlane();
640
638
  };
641
639
 
@@ -643,62 +641,62 @@ p5.registerAddon((p5, fn, lifecycles) => {
643
641
  * Bottom plane for the current projection.
644
642
  * @returns {number}
645
643
  */
646
- p5.RendererGL.prototype.bPlane = function () {
644
+ p5.Renderer3D.prototype.bPlane = function () {
647
645
  return this.pMatrix().bPlane();
648
646
  };
649
647
 
650
648
  /**
651
649
  * Near plane distance for the current projection.
652
- * Requires WEBGL.
650
+ * Requires 3D renderer.
653
651
  * @returns {number}
654
652
  */
655
653
  fn.nPlane = function () {
656
- return _rendererGL(this).nPlane();
654
+ return _renderer3D(this).nPlane();
657
655
  };
658
656
 
659
657
  /**
660
658
  * Far plane distance for the current projection.
661
- * Requires WEBGL.
659
+ * Requires 3D renderer.
662
660
  * @returns {number}
663
661
  */
664
662
  fn.fPlane = function () {
665
- return _rendererGL(this).fPlane();
663
+ return _renderer3D(this).fPlane();
666
664
  };
667
665
 
668
666
  /**
669
667
  * Left plane for the current projection.
670
- * Requires WEBGL.
668
+ * Requires 3D renderer.
671
669
  * @returns {number}
672
670
  */
673
671
  fn.lPlane = function () {
674
- return _rendererGL(this).lPlane();
672
+ return _renderer3D(this).lPlane();
675
673
  };
676
674
 
677
675
  /**
678
676
  * Right plane for the current projection.
679
- * Requires WEBGL.
677
+ * Requires 3D renderer.
680
678
  * @returns {number}
681
679
  */
682
680
  fn.rPlane = function () {
683
- return _rendererGL(this).rPlane();
681
+ return _renderer3D(this).rPlane();
684
682
  };
685
683
 
686
684
  /**
687
685
  * Top plane for the current projection.
688
- * Requires WEBGL.
686
+ * Requires 3D renderer.
689
687
  * @returns {number}
690
688
  */
691
689
  fn.tPlane = function () {
692
- return _rendererGL(this).tPlane();
690
+ return _renderer3D(this).tPlane();
693
691
  };
694
692
 
695
693
  /**
696
694
  * Bottom plane for the current projection.
697
- * Requires WEBGL.
695
+ * Requires 3D renderer.
698
696
  * @returns {number}
699
697
  */
700
698
  fn.bPlane = function () {
701
- return _rendererGL(this).bPlane();
699
+ return _renderer3D(this).bPlane();
702
700
  };
703
701
 
704
702
  /**
@@ -729,7 +727,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
729
727
  * Vertical field of view (radians) of the current projection.
730
728
  * @returns {number|undefined}
731
729
  */
732
- p5.RendererGL.prototype.fov = function () {
730
+ p5.Renderer3D.prototype.fov = function () {
733
731
  return this.pMatrix().fov();
734
732
  };
735
733
 
@@ -737,26 +735,26 @@ p5.registerAddon((p5, fn, lifecycles) => {
737
735
  * Horizontal field of view (radians) of the current projection.
738
736
  * @returns {number|undefined}
739
737
  */
740
- p5.RendererGL.prototype.hfov = function () {
738
+ p5.Renderer3D.prototype.hfov = function () {
741
739
  return this.pMatrix().hfov();
742
740
  };
743
741
 
744
742
  /**
745
743
  * Vertical field of view (radians) of the current projection.
746
- * Requires WEBGL.
744
+ * Requires 3D renderer.
747
745
  * @returns {number|undefined}
748
746
  */
749
747
  fn.fov = function () {
750
- return _rendererGL(this).fov();
748
+ return _renderer3D(this).fov();
751
749
  };
752
750
 
753
751
  /**
754
752
  * Horizontal field of view (radians) of the current projection.
755
- * Requires WEBGL.
753
+ * Requires 3D renderer.
756
754
  * @returns {number|undefined}
757
755
  */
758
756
  fn.hfov = function () {
759
- return _rendererGL(this).hfov();
757
+ return _renderer3D(this).hfov();
760
758
  };
761
759
 
762
760
  // --- private keys (shared internal state across protos) ---
@@ -1339,7 +1337,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1339
1337
 
1340
1338
  /*
1341
1339
  // treegl approach:
1342
- p5.RendererGL.prototype.beginHUD = function () {
1340
+ p5.Renderer3D.prototype.beginHUD = function () {
1343
1341
  if (this._hudActive === true) return;
1344
1342
  const p = this._pInst;
1345
1343
  const gl = this.drawingContext;
@@ -1376,7 +1374,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1376
1374
  this._hudActive = true;
1377
1375
  };
1378
1376
 
1379
- p5.RendererGL.prototype.endHUD = function () {
1377
+ p5.Renderer3D.prototype.endHUD = function () {
1380
1378
  if (this._hudActive !== true) return;
1381
1379
  const p = this._pInst;
1382
1380
  const gl = this.drawingContext;
@@ -1397,46 +1395,54 @@ p5.registerAddon((p5, fn, lifecycles) => {
1397
1395
  };
1398
1396
  */
1399
1397
 
1400
- p5.RendererGL.prototype.beginHUD = function () {
1398
+ p5.Renderer3D.prototype.beginHUD = function () {
1401
1399
  if (this._hudActive === true) return;
1402
1400
  const p = this._pInst;
1403
- if (!p) return;
1404
- const gl = this.drawingContext;
1405
1401
  const states = this.states;
1406
- if (!gl || !states) return;
1407
- p.push(); // isolate all subsequent HUD drawing from caller state
1402
+ if (!p || !states) return;
1403
+ p.push();
1408
1404
  p.resetShader();
1409
- // Ensure HUD space does NOT inherit the user's current model transforms
1410
- // (e.g. push()/translate()
1411
1405
  p.resetMatrix();
1412
- // ---------------------
1413
- // --- HUD setup ---
1414
1406
  this._hudPrevCam = states.curCamera;
1415
- this._hudDepthWasEnabled = gl.isEnabled(gl.DEPTH_TEST);
1416
- gl.flush();
1417
- gl.disable(gl.DEPTH_TEST);
1407
+ this._hudDepthMode = undefined;
1408
+ this._hudDepthWasEnabled = undefined;
1409
+ if (typeof this.clearDepth === 'function') {
1410
+ this.flushDraw?.();
1411
+ this.clearDepth(1);
1412
+ this._hudDepthMode = 'clearDepth';
1413
+ } else {
1414
+ const gl = this.drawingContext;
1415
+ if (gl && typeof gl.isEnabled === 'function' && gl.DEPTH_TEST !== undefined) {
1416
+ this._hudDepthWasEnabled = gl.isEnabled(gl.DEPTH_TEST);
1417
+ gl.flush?.();
1418
+ gl.disable(gl.DEPTH_TEST);
1419
+ this._hudDepthMode = 'depthTestToggle';
1420
+ }
1421
+ }
1418
1422
  if (this._hudCam === undefined) this._hudCam = p.createCamera();
1419
1423
  const z = 1e6;
1420
- // HUD coordinates: x in [0, width], y in [0, height]
1421
1424
  this._hudCam.ortho(0, p.width, -p.height, 0, -z, z);
1422
- // this._hudCam.ortho(0, p.width, 0, -p.height, -z, z); // <- flipped
1423
1425
  this._hudCam.camera(0, 0, 1, 0, 0, 0, 0, 1, 0);
1424
1426
  p.setCamera(this._hudCam);
1425
1427
  this._hudActive = true;
1426
1428
  };
1427
1429
 
1428
- p5.RendererGL.prototype.endHUD = function () {
1430
+ p5.Renderer3D.prototype.endHUD = function () {
1429
1431
  if (this._hudActive !== true) return;
1430
1432
  const p = this._pInst;
1431
- const gl = this.drawingContext;
1432
- const states = this.states;
1433
- if (p === undefined || gl === undefined || states === undefined) return;
1434
- gl.flush();
1435
- this._hudDepthWasEnabled ? gl.enable(gl.DEPTH_TEST) : gl.disable(gl.DEPTH_TEST);
1436
- p.pop(); // calls: this.pop(this._rendererState);
1433
+ if (!p) return;
1434
+ if (this._hudDepthMode === 'depthTestToggle') {
1435
+ const gl = this.drawingContext;
1436
+ if (gl && gl.DEPTH_TEST !== undefined) {
1437
+ gl.flush?.();
1438
+ this._hudDepthWasEnabled ? gl.enable(gl.DEPTH_TEST) : gl.disable(gl.DEPTH_TEST);
1439
+ }
1440
+ }
1441
+ p.pop();
1437
1442
  this._hudPrevCam !== undefined && p.setCamera(this._hudPrevCam);
1438
1443
  this._hudPrevCam = undefined;
1439
1444
  this._hudDepthWasEnabled = undefined;
1445
+ this._hudDepthMode = undefined;
1440
1446
  this._hudActive = false;
1441
1447
  };
1442
1448
 
@@ -1444,7 +1450,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1444
1450
  // Space transforms: mapLocation / mapDirection
1445
1451
  // ---------------------------------------------------------------------------
1446
1452
 
1447
- p5.RendererGL.prototype._parseTransformArgs = function (defaultMainArg, ...args) {
1453
+ p5.Renderer3D.prototype._parseTransformArgs = function (defaultMainArg, ...args) {
1448
1454
  let mainArg = defaultMainArg;
1449
1455
  const options = {};
1450
1456
  for (const arg of args) {
@@ -1462,7 +1468,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1462
1468
  // ---------------------------------------------------------------------------
1463
1469
 
1464
1470
  fn.mapLocation = function (...args) {
1465
- return _rendererGL(this)?.mapLocation(...args);
1471
+ return _renderer3D(this)?.mapLocation(...args);
1466
1472
  };
1467
1473
 
1468
1474
  /**
@@ -1479,12 +1485,12 @@ p5.registerAddon((p5, fn, lifecycles) => {
1479
1485
  * @param {p5.Matrix} [opts.ipvMatrix]
1480
1486
  * @returns {p5.Vector}
1481
1487
  */
1482
- p5.RendererGL.prototype.mapLocation = function (...args) {
1488
+ p5.Renderer3D.prototype.mapLocation = function (...args) {
1483
1489
  const { mainArg, options } = this._parseTransformArgs(p5.Tree.ORIGIN, ...args);
1484
1490
  return this._location(mainArg, options);
1485
1491
  };
1486
1492
 
1487
- p5.RendererGL.prototype._location = function (
1493
+ p5.Renderer3D.prototype._location = function (
1488
1494
  point = p5.Tree.ORIGIN,
1489
1495
  {
1490
1496
  from = p5.Tree.EYE,
@@ -1506,25 +1512,25 @@ p5.registerAddon((p5, fn, lifecycles) => {
1506
1512
  to = this.mMatrix({ eMatrix });
1507
1513
  }
1508
1514
  if ((from == p5.Tree.WORLD) && (to == p5.Tree.SCREEN)) {
1509
- return this._worldToScreenPosition({ point, pMatrix, vMatrix, pvMatrix });
1515
+ return this._worldToScreenLocation({ point, pMatrix, vMatrix, pvMatrix });
1510
1516
  }
1511
1517
  if ((from == p5.Tree.SCREEN) && (to == p5.Tree.WORLD)) {
1512
- return this._screenToWorldPosition({ point, pMatrix, vMatrix, pvMatrix, ipvMatrix });
1518
+ return this._screenToWorldLocation({ point, pMatrix, vMatrix, pvMatrix, ipvMatrix });
1513
1519
  }
1514
1520
  if (from == p5.Tree.SCREEN && to == p5.Tree.NDC) {
1515
- return this._screenToNDCPosition(point);
1521
+ return this._screenToNDCLocation(point);
1516
1522
  }
1517
1523
  if (from == p5.Tree.NDC && to == p5.Tree.SCREEN) {
1518
- return this._ndcToScreenPosition(point);
1524
+ return this._ndcToScreenLocation(point);
1519
1525
  }
1520
1526
  if (from == p5.Tree.WORLD && to == p5.Tree.NDC) {
1521
- return this._screenToNDCPosition(
1522
- this._worldToScreenPosition({ point, pMatrix, vMatrix, pvMatrix })
1527
+ return this._screenToNDCLocation(
1528
+ this._worldToScreenLocation({ point, pMatrix, vMatrix, pvMatrix })
1523
1529
  );
1524
1530
  }
1525
1531
  if (from == p5.Tree.NDC && to == p5.Tree.WORLD) {
1526
- return this._screenToWorldPosition({
1527
- point: this._ndcToScreenPosition(point),
1532
+ return this._screenToWorldLocation({
1533
+ point: this._ndcToScreenLocation(point),
1528
1534
  pMatrix,
1529
1535
  vMatrix,
1530
1536
  pvMatrix,
@@ -1536,8 +1542,8 @@ p5.registerAddon((p5, fn, lifecycles) => {
1536
1542
  ? (vMatrix ?? this.vMatrix())
1537
1543
  : to.copy().invert(to)
1538
1544
  ).mult4(
1539
- this._screenToWorldPosition({
1540
- point: this._ndcToScreenPosition(point),
1545
+ this._screenToWorldLocation({
1546
+ point: this._ndcToScreenLocation(point),
1541
1547
  pMatrix,
1542
1548
  vMatrix,
1543
1549
  pvMatrix,
@@ -1546,8 +1552,8 @@ p5.registerAddon((p5, fn, lifecycles) => {
1546
1552
  );
1547
1553
  }
1548
1554
  if ((from instanceof p5.Matrix || from == p5.Tree.EYE) && to == p5.Tree.NDC) {
1549
- return this._screenToNDCPosition(
1550
- this._worldToScreenPosition({
1555
+ return this._screenToNDCLocation(
1556
+ this._worldToScreenLocation({
1551
1557
  point: (from == p5.Tree.EYE
1552
1558
  ? (eMatrix ?? this.eMatrix())
1553
1559
  : from
@@ -1578,11 +1584,11 @@ p5.registerAddon((p5, fn, lifecycles) => {
1578
1584
  ? (vMatrix ?? this.vMatrix())
1579
1585
  : to.copy().invert(to)
1580
1586
  ).mult4(
1581
- this._screenToWorldPosition({ point, pMatrix, vMatrix, pvMatrix, ipvMatrix })
1587
+ this._screenToWorldLocation({ point, pMatrix, vMatrix, pvMatrix, ipvMatrix })
1582
1588
  );
1583
1589
  }
1584
1590
  if ((from instanceof p5.Matrix || from == p5.Tree.EYE) && to == p5.Tree.SCREEN) {
1585
- return this._worldToScreenPosition({
1591
+ return this._worldToScreenLocation({
1586
1592
  point: (from == p5.Tree.EYE
1587
1593
  ? (eMatrix ?? this.eMatrix())
1588
1594
  : from
@@ -1602,7 +1608,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1602
1608
  return point;
1603
1609
  };
1604
1610
 
1605
- p5.RendererGL.prototype._ndcToScreenPosition = function (point) {
1611
+ p5.Renderer3D.prototype._ndcToScreenLocation = function (point) {
1606
1612
  return new p5.Vector(
1607
1613
  p5.prototype.map(point.x, -1, 1, 0, this.width),
1608
1614
  p5.prototype.map(point.y, -1, 1, 0, this.height),
@@ -1610,7 +1616,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1610
1616
  );
1611
1617
  };
1612
1618
 
1613
- p5.RendererGL.prototype._screenToNDCPosition = function (point) {
1619
+ p5.Renderer3D.prototype._screenToNDCLocation = function (point) {
1614
1620
  return new p5.Vector(
1615
1621
  p5.prototype.map(point.x, 0, this.width, -1, 1),
1616
1622
  p5.prototype.map(point.y, 0, this.height, -1, 1),
@@ -1618,7 +1624,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1618
1624
  );
1619
1625
  };
1620
1626
 
1621
- p5.RendererGL.prototype._worldToScreenPosition = function ({
1627
+ p5.Renderer3D.prototype._worldToScreenLocation = function ({
1622
1628
  point = new p5.Vector(0, 0, 0.5),
1623
1629
  pMatrix,
1624
1630
  vMatrix,
@@ -1641,7 +1647,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1641
1647
  return new p5.Vector(target[0], target[1], target[2]);
1642
1648
  };
1643
1649
 
1644
- p5.RendererGL.prototype._screenToWorldPosition = function ({
1650
+ p5.Renderer3D.prototype._screenToWorldLocation = function ({
1645
1651
  point = new p5.Vector(this.width / 2, this.height / 2, 0.5),
1646
1652
  pMatrix,
1647
1653
  vMatrix,
@@ -1671,7 +1677,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1671
1677
  // ---------------------------------------------------------------------------
1672
1678
 
1673
1679
  fn.mapDirection = function (...args) {
1674
- return _rendererGL(this)?.mapDirection(...args);
1680
+ return _renderer3D(this)?.mapDirection(...args);
1675
1681
  };
1676
1682
 
1677
1683
  /**
@@ -1686,12 +1692,12 @@ p5.registerAddon((p5, fn, lifecycles) => {
1686
1692
  * @param {p5.Matrix} [opts.pMatrix]
1687
1693
  * @returns {p5.Vector}
1688
1694
  */
1689
- p5.RendererGL.prototype.mapDirection = function (...args) {
1695
+ p5.Renderer3D.prototype.mapDirection = function (...args) {
1690
1696
  const { mainArg, options } = this._parseTransformArgs(p5.Tree._k, ...args);
1691
1697
  return this._direction(mainArg, options);
1692
1698
  };
1693
1699
 
1694
- p5.RendererGL.prototype._direction = function (
1700
+ p5.Renderer3D.prototype._direction = function (
1695
1701
  vector = p5.Tree._k,
1696
1702
  {
1697
1703
  from = p5.Tree.EYE,
@@ -1785,7 +1791,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1785
1791
  return vector;
1786
1792
  };
1787
1793
 
1788
- p5.RendererGL.prototype._worldToScreenDirection = function (vector, pMatrix) {
1794
+ p5.Renderer3D.prototype._worldToScreenDirection = function (vector, pMatrix) {
1789
1795
  pMatrix = pMatrix ?? this.pMatrix();
1790
1796
  const eyeVector = this._direction(vector, { from: p5.Tree.WORLD, to: p5.Tree.EYE });
1791
1797
  let dx = eyeVector.x;
@@ -1806,7 +1812,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1806
1812
  return new p5.Vector(dx, dy, dz);
1807
1813
  };
1808
1814
 
1809
- p5.RendererGL.prototype._screenToWorldDirection = function (vector, pMatrix) {
1815
+ p5.Renderer3D.prototype._screenToWorldDirection = function (vector, pMatrix) {
1810
1816
  pMatrix = pMatrix ?? this.pMatrix();
1811
1817
 
1812
1818
  let dx = vector.x;
@@ -1830,11 +1836,11 @@ p5.registerAddon((p5, fn, lifecycles) => {
1830
1836
  return this._direction(new p5.Vector(dx, dy, dz), { from: p5.Tree.EYE, to: p5.Tree.WORLD });
1831
1837
  };
1832
1838
 
1833
- p5.RendererGL.prototype._ndcToScreenDirection = function (vector) {
1839
+ p5.Renderer3D.prototype._ndcToScreenDirection = function (vector) {
1834
1840
  return new p5.Vector(this.width * vector.x / 2, this.height * vector.y / 2, vector.z / 2);
1835
1841
  };
1836
1842
 
1837
- p5.RendererGL.prototype._screenToNDCDirection = function (vector) {
1843
+ p5.Renderer3D.prototype._screenToNDCDirection = function (vector) {
1838
1844
  return new p5.Vector(2 * vector.x / this.width, 2 * vector.y / this.height, 2 * vector.z);
1839
1845
  };
1840
1846
 
@@ -1851,13 +1857,13 @@ p5.registerAddon((p5, fn, lifecycles) => {
1851
1857
  * - In orthographic projection, the ratio is constant.
1852
1858
  * - In perspective projection, the ratio depends on eye-space depth.
1853
1859
  *
1854
- * Requires WEBGL.
1860
+ * Requires 3D renderer.
1855
1861
  *
1856
1862
  * @param {p5.Vector|number[]} [point=p5.Tree.ORIGIN] World-space point.
1857
1863
  * @returns {number|undefined} World units per pixel at the given point.
1858
1864
  */
1859
1865
  fn.pixelRatio = function (point) {
1860
- return _rendererGL(this)?.pixelRatio(point);
1866
+ return _renderer3D(this)?.pixelRatio(point);
1861
1867
  };
1862
1868
 
1863
1869
  /**
@@ -1865,7 +1871,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1865
1871
  * @param {p5.Vector|number[]} [point=p5.Tree.ORIGIN]
1866
1872
  * @returns {number}
1867
1873
  */
1868
- p5.RendererGL.prototype.pixelRatio = function (point = p5.Tree.ORIGIN) {
1874
+ p5.Renderer3D.prototype.pixelRatio = function (point = p5.Tree.ORIGIN) {
1869
1875
  return this.isOrtho()
1870
1876
  ? Math.abs(this.tPlane() - this.bPlane()) / this.height
1871
1877
  : 2 * Math.abs(
@@ -1898,27 +1904,27 @@ p5.registerAddon((p5, fn, lifecycles) => {
1898
1904
 
1899
1905
  /**
1900
1906
  * Returns a pointer position in *pixel* coordinates from an arbitrary (x, y) pair.
1901
- * Delegates to the WEBGL renderer.
1907
+ * Delegates to the active 3D renderer.
1902
1908
  *
1903
1909
  * Accepts parameters in any order:
1904
1910
  * - `number, number` → pointerX, pointerY
1905
1911
  * - optional `boolean` → `flip`
1906
1912
  *
1907
1913
  * @param {...(number|boolean)} args
1908
- * @returns {number[]|undefined} `[x, y]` in pixels, or undefined if not in WEBGL.
1914
+ * @returns {number[]|undefined} `[x, y]` in pixels, or undefined if no 3D renderer is active.
1909
1915
  */
1910
1916
  fn.pointerPosition = function (...args) {
1911
- return _rendererGL(this)?.pointerPosition(...args);
1917
+ return _renderer3D(this)?.pointerPosition(...args);
1912
1918
  };
1913
1919
 
1914
1920
  /**
1915
1921
  * Returns the canvas resolution in *pixel* coordinates.
1916
- * Delegates to the WEBGL renderer.
1922
+ * Delegates to the active 3D renderer.
1917
1923
  *
1918
- * @returns {number[]|undefined} `[width, height]` in pixels, or undefined if not in WEBGL.
1924
+ * @returns {number[]|undefined} `[width, height]` in pixels, or undefined if no 3D renderer is active.
1919
1925
  */
1920
1926
  fn.resolution = function () {
1921
- return _rendererGL(this)?.resolution();
1927
+ return _renderer3D(this)?.resolution();
1922
1928
  };
1923
1929
 
1924
1930
  /**
@@ -1931,7 +1937,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1931
1937
  * @param {...(number|boolean)} args
1932
1938
  * @returns {number[]} `[x, y]` in pixels (includes pixelDensity scaling).
1933
1939
  */
1934
- p5.RendererGL.prototype.pointerPosition = function (...args) {
1940
+ p5.Renderer3D.prototype.pointerPosition = function (...args) {
1935
1941
  let pointerX;
1936
1942
  let pointerY;
1937
1943
  let flip = true;
@@ -1951,7 +1957,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
1951
1957
  * Returns the canvas resolution in *pixel* coordinates.
1952
1958
  * @returns {number[]} `[width, height]` in pixels (includes pixelDensity scaling).
1953
1959
  */
1954
- p5.RendererGL.prototype.resolution = function () {
1960
+ p5.Renderer3D.prototype.resolution = function () {
1955
1961
  const pd = this.pixelDensity();
1956
1962
  return [pd * this.width, pd * this.height];
1957
1963
  };
@@ -1961,11 +1967,51 @@ p5.registerAddon((p5, fn, lifecycles) => {
1961
1967
  // -------------------------------------------------------------------------
1962
1968
 
1963
1969
  fn.axes = function (opts) {
1964
- _rendererGL(this)?.axes(opts);
1970
+ _renderer3D(this)?.axes(opts);
1965
1971
  return this;
1966
1972
  };
1967
1973
 
1968
- p5.RendererGL.prototype.axes = function ({
1974
+ /**
1975
+ * Draws 3D reference axes (X, Y, Z) centered at the origin in model space,
1976
+ * using the current stroke settings.
1977
+ *
1978
+ * Each axis can be enabled independently using bitwise flags, and optional
1979
+ * axis labels (X, Y, Z) can be rendered near the positive ends.
1980
+ *
1981
+ * @method axes
1982
+ * @for p5.Renderer3D
1983
+ * @param {Object} [opts] Axes options.
1984
+ * @param {Number} [opts.size=100] Length of each axis in world units.
1985
+ * @param {Array<String>} [opts.colors=['Red','Lime','DodgerBlue']]
1986
+ * Stroke colors for X, Y, and Z axes respectively.
1987
+ * @param {Number} [opts.bits=p5.Tree.LABELS | p5.Tree.X | p5.Tree.Y | p5.Tree.Z]
1988
+ * Bitmask controlling which axes and labels are drawn.
1989
+ *
1990
+ * @example
1991
+ * function draw() {
1992
+ * background(30);
1993
+ * orbitControl();
1994
+ * axes({ size: 300 });
1995
+ * }
1996
+ *
1997
+ * @example
1998
+ * // Draw only X and Z axes, no labels
1999
+ * axes({
2000
+ * size: 200,
2001
+ * bits: p5.Tree.X | p5.Tree.Z
2002
+ * });
2003
+ *
2004
+ * @example
2005
+ * // Draw full axes in both positive and negative directions
2006
+ * axes({
2007
+ * size: 150,
2008
+ * bits: p5.Tree.X | p5.Tree._X |
2009
+ * p5.Tree.Y | p5.Tree._Y |
2010
+ * p5.Tree.Z | p5.Tree._Z |
2011
+ * p5.Tree.LABELS
2012
+ * });
2013
+ */
2014
+ p5.Renderer3D.prototype.axes = function ({
1969
2015
  size = 100,
1970
2016
  colors = ['Red', 'Lime', 'DodgerBlue'],
1971
2017
  bits = p5.Tree.LABELS | p5.Tree.X | p5.Tree.Y | p5.Tree.Z
@@ -2010,51 +2056,40 @@ p5.registerAddon((p5, fn, lifecycles) => {
2010
2056
  };
2011
2057
 
2012
2058
  fn.grid = function (opts) {
2013
- _rendererGL(this)?.grid(opts);
2059
+ _renderer3D(this)?.grid(opts);
2014
2060
  return this;
2015
2061
  };
2016
2062
 
2017
- p5.RendererGL.prototype.grid = function ({
2063
+ /**
2064
+ * Draws a simple X/Y reference grid on the Z=0 plane in the current model space.
2065
+ *
2066
+ * The grid is centered at the origin and spans from `-size` to `+size` on both X and Y.
2067
+ * It draws `subdivisions + 1` lines in each direction (including the borders).
2068
+ *
2069
+ * @method grid
2070
+ * @for p5.Renderer3D
2071
+ * @param {Object} [opts] Grid options.
2072
+ * @param {Number} [opts.size=100] Half-extent of the grid in world units.
2073
+ * @param {Number} [opts.subdivisions=10] Number of subdivisions per side (must be >= 1).
2074
+ * @example
2075
+ * function draw() {
2076
+ * background(30);
2077
+ * orbitControl();
2078
+ * grid({ size: 300, subdivisions: 20 });
2079
+ * }
2080
+ */
2081
+ p5.Renderer3D.prototype.grid = function ({
2018
2082
  size = 100,
2019
- subdivisions = 10,
2020
- style = p5.Tree.SOLID,
2021
- weight = 1,
2022
- minorSubdivisions = 5
2083
+ subdivisions = 10
2023
2084
  } = {}) {
2024
2085
  const p = this._pInst;
2025
- if (!p) return;
2086
+ if (!p) return;
2087
+ subdivisions = Math.max(1, subdivisions);
2026
2088
  p.push();
2027
- if (style === p5.Tree.DOTS) {
2028
- let posi = 0;
2029
- let posj = 0;
2030
- p.strokeWeight(weight * 2);
2031
- p.beginShape(p.POINTS);
2032
- for (let i = 0; i <= subdivisions; ++i) {
2033
- posi = size * (2.0 * i / subdivisions - 1.0);
2034
- for (let j = 0; j <= subdivisions; ++j) {
2035
- posj = size * (2.0 * j / subdivisions - 1.0);
2036
- p.vertex(posi, posj, 0);
2037
- }
2038
- }
2039
- p.endShape();
2040
- const internalSub = Math.max(1, minorSubdivisions | 0);
2041
- const subSubdivisions = subdivisions * internalSub;
2042
- p.strokeWeight(weight);
2043
- p.beginShape(p.POINTS);
2044
- for (let i = 0; i <= subSubdivisions; ++i) {
2045
- posi = size * (2.0 * i / subSubdivisions - 1.0);
2046
- for (let j = 0; j <= subSubdivisions; ++j) {
2047
- posj = size * (2.0 * j / subSubdivisions - 1.0);
2048
- ((i % internalSub) !== 0 || (j % internalSub) !== 0) && p.vertex(posi, posj, 0);
2049
- }
2050
- }
2051
- p.endShape();
2052
- } else {
2053
- for (let i = 0; i <= subdivisions; ++i) {
2054
- const pos = size * (2.0 * i / subdivisions - 1.0);
2055
- p.line(pos, -size, 0, pos, +size, 0);
2056
- p.line(-size, pos, 0, size, pos, 0);
2057
- }
2089
+ for (let i = 0; i <= subdivisions; ++i) {
2090
+ const pos = size * (2.0 * i / subdivisions - 1.0);
2091
+ p.line(pos, -size, 0, pos, +size, 0);
2092
+ p.line(-size, pos, 0, +size, pos, 0);
2058
2093
  }
2059
2094
  p.pop();
2060
2095
  };
@@ -2070,7 +2105,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2070
2105
  * `p5.Tree.SCREEN`. In that case, `size` is interpreted in *world units* and
2071
2106
  * converted to pixels using `pixelRatio()` at the corresponding world point.
2072
2107
  *
2073
- * Requires WEBGL.
2108
+ * Requires 3D renderer.
2074
2109
  *
2075
2110
  * @param {object} [opts]
2076
2111
  * @param {p5.Matrix} [opts.mMatrix] Model-space matrix origin to compute (x, y) from.
@@ -2085,7 +2120,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2085
2120
  * @returns {boolean|undefined}
2086
2121
  */
2087
2122
  fn.mousePicking = function (opts) {
2088
- return _rendererGL(this)?.mousePicking(opts);
2123
+ return _renderer3D(this)?.mousePicking(opts);
2089
2124
  };
2090
2125
 
2091
2126
  /**
@@ -2095,16 +2130,16 @@ p5.registerAddon((p5, fn, lifecycles) => {
2095
2130
  * `p5.Tree.SCREEN`. In that case, `size` is interpreted in *world units* and
2096
2131
  * converted to pixels using `pixelRatio()` at the corresponding world point.
2097
2132
  *
2098
- * Requires WEBGL.
2133
+ * Requires 3D renderer.
2099
2134
  *
2100
2135
  * @param {...any} args
2101
2136
  * @returns {boolean|undefined}
2102
2137
  */
2103
2138
  fn.pointerPicking = function (...args) {
2104
- return _rendererGL(this)?.pointerPicking(...args);
2139
+ return _renderer3D(this)?.pointerPicking(...args);
2105
2140
  };
2106
2141
 
2107
- p5.RendererGL.prototype.mousePicking = function ({
2142
+ p5.Renderer3D.prototype.mousePicking = function ({
2108
2143
  mMatrix = this.mMatrix(),
2109
2144
  x,
2110
2145
  y,
@@ -2130,7 +2165,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2130
2165
  * @param {...any} args
2131
2166
  * @returns {boolean}
2132
2167
  */
2133
- p5.RendererGL.prototype.pointerPicking = function (...args) {
2168
+ p5.Renderer3D.prototype.pointerPicking = function (...args) {
2134
2169
  let pointerX;
2135
2170
  let pointerY;
2136
2171
  const config = {};
@@ -2192,7 +2227,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2192
2227
  * @param {number} [opts.radius=100] Radius in current space.
2193
2228
  * @param {number} [opts.detail=50] Segment count.
2194
2229
  */
2195
- p5.RendererGL.prototype._circle = function ({
2230
+ p5.Renderer3D.prototype._circle = function ({
2196
2231
  filled = false,
2197
2232
  x = this.width / 2,
2198
2233
  y = this.height / 2,
@@ -2244,11 +2279,11 @@ p5.registerAddon((p5, fn, lifecycles) => {
2244
2279
  * @param {p5.Matrix} [opts.pvMatrix] Projection-view matrix override.
2245
2280
  */
2246
2281
  fn.cross = function (opts) {
2247
- _rendererGL(this)?.cross(opts);
2282
+ _renderer3D(this)?.cross(opts);
2248
2283
  return this;
2249
2284
  };
2250
2285
 
2251
- p5.RendererGL.prototype.cross = function ({
2286
+ p5.Renderer3D.prototype.cross = function ({
2252
2287
  mMatrix = this.mMatrix(),
2253
2288
  x,
2254
2289
  y,
@@ -2297,11 +2332,11 @@ p5.registerAddon((p5, fn, lifecycles) => {
2297
2332
  * @param {p5.Matrix} [opts.pvMatrix] Projection-view matrix override.
2298
2333
  */
2299
2334
  fn.bullsEye = function (opts) {
2300
- _rendererGL(this)?.bullsEye(opts);
2335
+ _renderer3D(this)?.bullsEye(opts);
2301
2336
  return this;
2302
2337
  };
2303
2338
 
2304
- p5.RendererGL.prototype.bullsEye = function ({
2339
+ p5.Renderer3D.prototype.bullsEye = function ({
2305
2340
  mMatrix = this.mMatrix(),
2306
2341
  x,
2307
2342
  y,
@@ -2348,22 +2383,22 @@ p5.registerAddon((p5, fn, lifecycles) => {
2348
2383
  // ---------------------------------------------------------------------------
2349
2384
 
2350
2385
  fn.viewFrustum = function (opts) {
2351
- _rendererGL(this)?.viewFrustum(opts);
2386
+ _renderer3D(this)?.viewFrustum(opts);
2352
2387
  return this;
2353
2388
  };
2354
2389
 
2355
2390
  /**
2356
- * Displays a view frustum, either from a pg (p5.Graphics / p5.RendererGL) or from eMatrix/pMatrix.
2391
+ * Displays a view frustum, either from a pg (p5.Graphics / p5.Renderer3D) or from eMatrix/pMatrix.
2357
2392
  *
2358
2393
  * @param {Object} [opts]
2359
2394
  * @param {p5.Matrix} [opts.vMatrix=this.vMatrix()] desired view matrix (world -> this eye) for drawing the frustum.
2360
- * @param {p5.RendererGL|p5.Graphics} [opts.pg] renderer/pg whose frustum is to be displayed.
2395
+ * @param {p5.Renderer3D|p5.Graphics} [opts.pg] renderer/pg whose frustum is to be displayed.
2361
2396
  * @param {p5.Matrix} [opts.eMatrix=pg?.eMatrix()] eye matrix defining frustum pose (eye -> world).
2362
2397
  * @param {p5.Matrix} [opts.pMatrix=pg?.pMatrix()] projection matrix defining frustum projection.
2363
2398
  * @param {number} [opts.bits=p5.Tree.NEAR|p5.Tree.FAR] bitmask (NEAR/FAR/BODY/APEX).
2364
2399
  * @param {Function|false|null} [opts.viewer=...] callback drawn at the frustum origin (in frustum space).
2365
2400
  */
2366
- p5.RendererGL.prototype.viewFrustum = function ({
2401
+ p5.Renderer3D.prototype.viewFrustum = function ({
2367
2402
  vMatrix = this.vMatrix(),
2368
2403
  pg,
2369
2404
  eMatrix = pg?.eMatrix(),
@@ -2494,7 +2529,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2494
2529
  * @returns {number} One of p5.Tree.VISIBLE, p5.Tree.INVISIBLE, p5.Tree.SEMIVISIBLE.
2495
2530
  */
2496
2531
  fn.visibility = function (...args) {
2497
- return _rendererGL(this).visibility(...args);
2532
+ return _renderer3D(this).visibility(...args);
2498
2533
  };
2499
2534
 
2500
2535
  /**
@@ -2502,7 +2537,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2502
2537
  * @returns {Object}
2503
2538
  */
2504
2539
  fn.bounds = function (opts = {}) {
2505
- return _rendererGL(this).bounds(opts);
2540
+ return _renderer3D(this).bounds(opts);
2506
2541
  };
2507
2542
 
2508
2543
  /**
@@ -2510,7 +2545,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2510
2545
  * @returns {number}
2511
2546
  */
2512
2547
  fn.distanceToBound = function (...args) {
2513
- return _rendererGL(this).distanceToBound(...args);
2548
+ return _renderer3D(this).distanceToBound(...args);
2514
2549
  };
2515
2550
 
2516
2551
  /**
@@ -2522,7 +2557,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2522
2557
  *
2523
2558
  * @private
2524
2559
  */
2525
- p5.RendererGL.prototype._parseVisibilityArgs = function (...args) {
2560
+ p5.Renderer3D.prototype._parseVisibilityArgs = function (...args) {
2526
2561
  let corner1;
2527
2562
  let corner2;
2528
2563
  let center;
@@ -2591,7 +2626,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2591
2626
  * @param {Object} [opts.bounds] Frustum plane equations (defaults to this.bounds()).
2592
2627
  * @returns {number} One of p5.Tree.VISIBLE, p5.Tree.INVISIBLE, p5.Tree.SEMIVISIBLE.
2593
2628
  */
2594
- p5.RendererGL.prototype.visibility = function (...args) {
2629
+ p5.Renderer3D.prototype.visibility = function (...args) {
2595
2630
  const { corner1, corner2, center, radius, bounds } = this._parseVisibilityArgs(...args);
2596
2631
  const b = bounds ?? this.bounds();
2597
2632
  return center ? (radius ? this._ballVisibility(center, radius, b) : this._pointVisibility(center, b))
@@ -2599,7 +2634,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2599
2634
  : (console.error('[p5.tree] visibility: could not parse query.'), p5.Tree.INVISIBLE));
2600
2635
  };
2601
2636
 
2602
- p5.RendererGL.prototype._pointVisibility = function (point, bounds = this.bounds()) {
2637
+ p5.Renderer3D.prototype._pointVisibility = function (point, bounds = this.bounds()) {
2603
2638
  for (const key in bounds) {
2604
2639
  const d = this.distanceToBound(point, key, bounds);
2605
2640
  if (d > 0) return p5.Tree.INVISIBLE;
@@ -2608,7 +2643,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2608
2643
  return p5.Tree.VISIBLE;
2609
2644
  };
2610
2645
 
2611
- p5.RendererGL.prototype._ballVisibility = function (center, radius, bounds = this.bounds()) {
2646
+ p5.Renderer3D.prototype._ballVisibility = function (center, radius, bounds = this.bounds()) {
2612
2647
  let allInForAllPlanes = true;
2613
2648
  for (const key in bounds) {
2614
2649
  const d = this.distanceToBound(center, key, bounds);
@@ -2618,7 +2653,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2618
2653
  return allInForAllPlanes ? p5.Tree.VISIBLE : p5.Tree.SEMIVISIBLE;
2619
2654
  };
2620
2655
 
2621
- p5.RendererGL.prototype._boxVisibility = function (corner1, corner2, bounds = this.bounds()) {
2656
+ p5.Renderer3D.prototype._boxVisibility = function (corner1, corner2, bounds = this.bounds()) {
2622
2657
  const asVec3 = v =>
2623
2658
  v instanceof p5.Vector ? v : new p5.Vector(v?.[0] ?? 0, v?.[1] ?? 0, v?.[2] ?? 0);
2624
2659
  corner1 = asVec3(corner1);
@@ -2653,7 +2688,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2653
2688
  * @param {p5.Matrix} [opts.eMatrix] Eye matrix (eye -> world).
2654
2689
  * @returns {Object} Object keyed by p5.Tree.LEFT/RIGHT/NEAR/FAR/TOP/BOTTOM.
2655
2690
  */
2656
- p5.RendererGL.prototype.bounds = function ({
2691
+ p5.Renderer3D.prototype.bounds = function ({
2657
2692
  vMatrix,
2658
2693
  eMatrix
2659
2694
  } = {}) {
@@ -2725,7 +2760,7 @@ p5.registerAddon((p5, fn, lifecycles) => {
2725
2760
  * @param {Object} [bounds] Plane equations (defaults to this.bounds()).
2726
2761
  * @returns {number}
2727
2762
  */
2728
- p5.RendererGL.prototype.distanceToBound = function (...args) {
2763
+ p5.Renderer3D.prototype.distanceToBound = function (...args) {
2729
2764
  let point;
2730
2765
  let key;
2731
2766
  let bounds = this.bounds();