@pacem/pacem-3d 1.0.0-bernoulli → 1.0.0-binet

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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-3d v1.0.0-bernoulli (https://js.pacem.it)
2
+ * @pacem/pacem-3d v1.0.0-binet (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -7,10 +7,12 @@
7
7
  'use strict';
8
8
 
9
9
  //namespace Pacem.Drawing3D {
10
+ /** Converts a `"x y z"` attribute string to/from a {@link Vector3D}. */
10
11
  const Point3DConverter = {
11
12
  convert: (attr) => pacemNumerical.Geometry.LinearAlgebra.Vector3D.parse(attr),
12
13
  convertBack: (prop) => `${prop.x || 0} ${prop.y || 0} ${prop.z || 0}`
13
14
  };
15
+ /** Converts a `"x y z"` (3 components) or `"n"` (single uniform component, broadcast to all 3 axes) attribute string to/from a {@link Vector3D}. Used e.g. for `scale`/`offset`. */
14
16
  const Point3DOrNumberConverter = {
15
17
  convert: (attr) => {
16
18
  const arr = pacemFoundation.parseAsNumericalArray(attr);
@@ -29,10 +31,12 @@
29
31
  },
30
32
  convertBack: (prop) => `${prop.x || 0} ${prop.y || 0} ${prop.z || 0}`
31
33
  };
34
+ /** Converts a `"x y z w"` attribute string to/from a {@link Quaternion}. Used e.g. for `rotate`. */
32
35
  const QuaternionConverter = {
33
36
  convert: (attr) => pacemNumerical.Geometry.LinearAlgebra.Quaternion.parse(attr),
34
37
  convertBack: (prop) => `${prop.x || 0} ${prop.y || 0} ${prop.z || 0} ${prop.w || 0}`
35
38
  };
39
+ /** Converts an attribute string to/from either a `boolean` (`"true"`/`"false"`) or a `number`. */
36
40
  const BooleanOrNumberConverter = {
37
41
  convert: (attr) => {
38
42
  if (attr === 'true')
@@ -44,7 +48,9 @@
44
48
  convertBack: (prop) => prop.toString()
45
49
  };
46
50
 
51
+ /** Middle segment shared by every custom element tag name in this package (e.g. `pacem-3d`, `pacem-3d-mesh`). */
47
52
  const TAG_MIDDLE_NAME = "3d";
53
+ /** Conversion factor from degrees to radians. */
48
54
  const DEG2RAD = Math.PI / 180;
49
55
 
50
56
  var __decorate$l = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
@@ -54,6 +60,12 @@
54
60
  return c > 3 && r && Object.defineProperty(target, key, r), r;
55
61
  };
56
62
  //namespace Pacem.Components.Drawing3D {
63
+ /**
64
+ * `<pacem-3d>`: the root 3d stage element. Hosts the {@link RenderableElement} scene graph (groups, meshes, lights,
65
+ * cameras) declared as children or provided via {@link datasource}, delegates all actual scene-building/rendering/
66
+ * hit-testing to the pluggable {@link adapter} ({@link Pacem3DAdapterElement}), and dispatches pointer interaction
67
+ * events ({@link RenderableEvent}/{@link DragEvent}) for the items under the cursor.
68
+ */
57
69
  let Pacem3DElement = class Pacem3DElement extends pacemCore.Components.PacemItemsContainerElement {
58
70
  constructor() {
59
71
  super(...arguments);
@@ -118,9 +130,11 @@
118
130
  validate(item) {
119
131
  return item instanceof RenderableElement;
120
132
  }
133
+ /** @readonly Gets the DOM element the scene is mounted into. */
121
134
  get stage() {
122
135
  return this._container;
123
136
  }
137
+ /** @readonly Gets the technology-dependent native scene instance, as reported by the active {@link adapter}. */
124
138
  get scene() {
125
139
  return this.adapter && this.adapter.getScene(this);
126
140
  }
@@ -265,9 +279,11 @@
265
279
  super.disconnectedCallback();
266
280
  }
267
281
  #size;
282
+ /** @readonly Gets the current viewport size, as last reported by the internal resize observer. */
268
283
  get size() {
269
284
  return this.#size;
270
285
  }
286
+ /** Renders the whole scene through the active {@link adapter}, or just updates the given `item` if provided. */
271
287
  render(item, deepUpdate, now = performance.now()) {
272
288
  if (!pacemCore.Utils.isNull(item)) {
273
289
  const adapter = this.adapter;
@@ -330,38 +346,49 @@
330
346
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
331
347
  return c > 3 && r && Object.defineProperty(target, key, r), r;
332
348
  };
349
+ /** Type guard checking whether `object` implements the {@link Stage} contract. */
333
350
  function isStage(object) {
334
351
  return !pacemCore.Utils.isNull(object) && 'render' in object && typeof object['render'] === 'function';
335
352
  }
353
+ /** Type guard checking whether `object` implements the {@link Renderable} contract (i.e. it is attached to a valid {@link Stage}). */
336
354
  function isRenderable(object) {
337
355
  return isStage(object?.stage);
338
356
  }
357
+ /** Type guard checking whether `object` implements the {@link Ui3DObject} contract (i.e. it exposes a {@link Ui3DObject.transformMatrix}). */
339
358
  function isUi3DObject(object) {
340
359
  return 'transformMatrix' in object && isRenderable(object);
341
360
  }
361
+ /** Type guard checking whether `object` implements the {@link Camera} contract. */
342
362
  function isCamera(object) {
343
363
  return 'type' in object && 'up' in object && 'lookAt' in object && isRenderable(object);
344
364
  }
365
+ /** Type guard checking whether `object` is a {@link PerspectiveCamera}. */
345
366
  function isPerspectiveCamera(object) {
346
367
  return isCamera(object) && 'type' in object && object.type === 'perspective';
347
368
  }
369
+ /** Type guard checking whether `object` is an {@link OrthographicCamera}. */
348
370
  function isOrthographicCamera(object) {
349
371
  return isCamera(object) && 'type' in object && object.type === 'orthographic';
350
372
  }
373
+ /** Type guard checking whether `object` implements the {@link Light} contract. */
351
374
  function isLight(object) {
352
375
  return 'type' in object && (object.type === 'ambient' || object.type === 'omni' || object.type === 'direction' || object.type == 'spot') && isRenderable(object);
353
376
  }
377
+ /** Type guard checking whether `object` implements the {@link Mesh} contract. */
354
378
  function isMesh(object) {
355
379
  return 'geometry' in object && isUi3DObject(object);
356
380
  }
381
+ /** Type guard checking whether `object` implements the {@link NodeGeometry} contract. */
357
382
  function isGeometry(object) {
358
383
  return 'positions' in object && pacemCore.Utils.isArray(object.positions);
359
384
  }
385
+ /** Type guard checking whether `object` implements the {@link MeshGeometry} contract (a {@link NodeGeometry} with triangle indices/normals/UVs). */
360
386
  function isMeshGeometry(object) {
361
387
  return ('triangleIndices' in object && pacemCore.Utils.isArray(object.triangleIndices)
362
388
  || 'normals' in object && pacemCore.Utils.isArray(object.normals)
363
389
  || 'textureCoordinates' in object && pacemCore.Utils.isArray(object.textureCoordinates)) && isGeometry(object);
364
390
  }
391
+ /** Type guard checking whether `object` implements the {@link Group} contract. */
365
392
  function isGroup(object) {
366
393
  return 'childRenderables' in object && pacemCore.Utils.isArray(object['childRenderables']) && isUi3DObject(object);
367
394
  }
@@ -397,6 +424,7 @@
397
424
  const orthogonal = pacemNumerical.Geometry.LinearAlgebra.Vector3D.cross(vAB, vAC);
398
425
  return pacemNumerical.Geometry.LinearAlgebra.Vector3D.unit(orthogonal);
399
426
  }
427
+ /** Base class for the custom UI events dispatched by 3d elements/the stage, carrying the source pointer's projected {@link point} in 3d world space alongside the original DOM event. */
400
428
  class UI3DEvent extends pacemCore.CustomUIEvent {
401
429
  constructor(type, eventInit, originalEvent, point) {
402
430
  super(type, eventInit, originalEvent);
@@ -408,24 +436,36 @@
408
436
  return this.#point;
409
437
  }
410
438
  }
439
+ /** Event dispatched while a {@link Renderable} item is being dragged (`itemdragstart`/`itemdrag`/`itemdragend`-like events), carrying a {@link DragEventArgs} payload. */
411
440
  class DragEvent extends UI3DEvent {
412
441
  }
442
+ /** Event dispatched for pointer interactions (click/over/out/down/up) with a {@link Renderable} item, carrying the item itself as its `detail`. */
413
443
  class RenderableEvent extends UI3DEvent {
414
444
  constructor(type, args, originalEvent, p) {
415
445
  super(type, { detail: args, bubbles: true, cancelable: true }, originalEvent, p);
416
446
  }
417
447
  }
448
+ /** Identifies which aspect of a {@link Renderable} changed since the last render, so the adapter can update only the stale part of the underlying native object. */
418
449
  var StalePropertyFlag;
419
450
  (function (StalePropertyFlag) {
451
+ /** The object's {@link Renderable.position} changed. */
420
452
  StalePropertyFlag["Position"] = "position";
453
+ /** The object's {@link Ui3DObject.transformMatrix} (rotate/scale/offset) changed. */
421
454
  StalePropertyFlag["Transform"] = "transform";
455
+ /** The mesh/line {@link NodeGeometry} changed. */
422
456
  StalePropertyFlag["Geometry"] = "geometry";
457
+ /** The group's child collection changed. */
423
458
  StalePropertyFlag["Children"] = "children";
459
+ /** The mesh's {@link Material}/{@link Mesh.backMaterial} changed. */
424
460
  StalePropertyFlag["Material"] = "material";
461
+ /** A {@link Light}-specific property (color, intensity, target, ...) changed. */
425
462
  StalePropertyFlag["Light"] = "light";
463
+ /** A {@link Camera}-specific property (near, far, fov, frustum, ...) changed. */
426
464
  StalePropertyFlag["Camera"] = "camera";
465
+ /** The object's {@link Renderable.hide} visibility changed. */
427
466
  StalePropertyFlag["Visibility"] = "visibility";
428
467
  })(StalePropertyFlag || (StalePropertyFlag = {}));
468
+ /** Type guard checking whether `object` implements the {@link Interaction} contract. */
429
469
  function isInteraction(object) {
430
470
  return typeof object === 'object' && 'time' in object && typeof object['time'] === 'number';
431
471
  }
@@ -439,12 +479,20 @@
439
479
  GROUP_SELECTOR: pacemCore.P + '-' + TAG_MIDDLE_NAME + '-group',
440
480
  DEFAULT_COORDS: { x: 0, y: 0, z: 0 }
441
481
  };
482
+ /**
483
+ * Abstract base of every scene-graph custom element usable inside a {@link Pacem3DElement} stage (groups, meshes,
484
+ * native objects, lights, cameras). Implements {@link Renderable}, wires the element into the ancestor stage/parent
485
+ * chain, and tracks which aspects of the object are stale (via {@link StalePropertyFlag}) so the active
486
+ * {@link Pacem3DAdapterElement} can update only what changed on the next render.
487
+ */
442
488
  class RenderableElement extends pacemCore.Components.PacemCrossItemsContainerElement {
443
489
  constructor() {
444
490
  super(...arguments);
491
+ /** Gets or sets the element's position, in 3d world (or parent-local) space. */
445
492
  this.position = Constants.DEFAULT_COORDS;
446
493
  this.#staleFlags = [];
447
494
  }
495
+ /** When implemented in a derived class (e.g. {@link Pacem3DGroupElement}), determines whether `_` may be nested under this element. Denies any child by default. */
448
496
  validate(_) {
449
497
  // by default no children allowed (Group will except)
450
498
  return false;
@@ -453,9 +501,11 @@
453
501
  // override
454
502
  return this.parent || this.stage;
455
503
  }
504
+ /** @readonly Gets the ancestor {@link Pacem3DElement} stage this element belongs to. */
456
505
  get stage() {
457
506
  return this['_scene'] = this['_scene'] || pacemCore.CustomElementUtils.findAncestorOfType(this, Pacem3DElement);
458
507
  }
508
+ /** @readonly Gets the closest ancestor {@link RenderableElement} (e.g. the containing group), if any. */
459
509
  get parent() {
460
510
  return this['_drawableParent'] = this['_drawableParent'] || pacemCore.CustomElementUtils.findAncestor(this, i => i instanceof RenderableElement);
461
511
  }
@@ -479,6 +529,7 @@
479
529
  }
480
530
  }
481
531
  #staleFlags;
532
+ /** @readonly Gets the list of {@link StalePropertyFlag}s accumulated since the element was last rendered/updated by the adapter. */
482
533
  get flags() {
483
534
  return this.#staleFlags;
484
535
  }
@@ -502,6 +553,11 @@
502
553
  __decorate$k([
503
554
  pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.Boolean })
504
555
  ], RenderableElement.prototype, "inert", void 0);
556
+ /**
557
+ * Abstract base of every {@link RenderableElement} that also carries an affine 3d transform (rotate/scale/offset,
558
+ * combined into {@link transformMatrix}). Extended by {@link Pacem3DGroupElement}, {@link Pacem3DMeshElement} and
559
+ * {@link Pacem3DObjectElement}.
560
+ */
505
561
  class Ui3DElement extends RenderableElement {
506
562
  // #endregion
507
563
  propertyChangedCallback(name, old, val, first) {
@@ -573,12 +629,14 @@
573
629
  __decorate$k([
574
630
  pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.Number })
575
631
  ], Ui3DElement.prototype, "translateZ", void 0);
632
+ /** `<pacem-3d-group>`: a {@link Ui3DElement} container that groups child {@link RenderableElement}s (declarative or from {@link datasource}) so its own transform applies to all of them. */
576
633
  let Pacem3DGroupElement = class Pacem3DGroupElement extends Ui3DElement {
577
634
  validate(child) {
578
635
  // overrides default denial
579
636
  return child instanceof RenderableElement;
580
637
  }
581
638
  #children = [];
639
+ /** @readonly Gets the renderable children currently belonging to the group. */
582
640
  get childRenderables() {
583
641
  return this.#children;
584
642
  }
@@ -599,6 +657,7 @@
599
657
  Pacem3DGroupElement = __decorate$k([
600
658
  pacemCore.CustomElement({ tagName: Constants.GROUP_SELECTOR })
601
659
  ], Pacem3DGroupElement);
660
+ /** `<pacem-3d-mesh>`: a {@link Ui3DElement} that renders a {@link NodeGeometry} (mesh or line) with a front/back {@link Material}. */
602
661
  let Pacem3DMeshElement = class Pacem3DMeshElement extends Ui3DElement {
603
662
  propertyChangedCallback(name, old, val, first) {
604
663
  super.propertyChangedCallback(name, old, val, first);
@@ -625,6 +684,7 @@
625
684
  Pacem3DMeshElement = __decorate$k([
626
685
  pacemCore.CustomElement({ tagName: Constants.MESH_SELECTOR })
627
686
  ], Pacem3DMeshElement);
687
+ /** `<pacem-3d-object>`: a {@link Ui3DElement} that loads/embeds a pre-built 3d asset (`obj`, `fbx`, or a native scene-graph object) instead of an explicit {@link Pacem3DMeshElement} geometry. */
628
688
  let Pacem3DObjectElement = class Pacem3DObjectElement extends Ui3DElement {
629
689
  propertyChangedCallback(name, old, val, first) {
630
690
  super.propertyChangedCallback(name, old, val, first);
@@ -642,12 +702,17 @@
642
702
  Pacem3DObjectElement = __decorate$k([
643
703
  pacemCore.CustomElement({ tagName: Constants.OBJECT_SELECTOR })
644
704
  ], Pacem3DObjectElement);
705
+ /** `<pacem-3d-light>`: a {@link RenderableElement} light source (point/omni, spot or directional) illuminating the scene. */
645
706
  let Pacem3DLightElement = class Pacem3DLightElement extends RenderableElement {
646
707
  constructor() {
647
708
  super(...arguments);
709
+ /** Gets or sets the light intensity/brightness. */
648
710
  this.intensity = .85;
711
+ /** Gets or sets the point the light is aimed at (spotlight-specific). */
649
712
  this.target = Constants.DEFAULT_COORDS;
713
+ /** Gets or sets the light color. */
650
714
  this.color = '#fff';
715
+ /** Gets or sets the kind of light source. */
651
716
  this.type = 'omni';
652
717
  }
653
718
  propertyChangedCallback(name, old, val, first) {
@@ -677,12 +742,21 @@
677
742
  Pacem3DLightElement = __decorate$k([
678
743
  pacemCore.CustomElement({ tagName: Constants.LIGHT_SELECTOR })
679
744
  ], Pacem3DLightElement);
745
+ /**
746
+ * Abstract base of the camera custom elements ({@link Pacem3DPerspectiveCameraElement}, {@link Pacem3DOrthographicCameraElement})
747
+ * that can be added to a {@link Pacem3DElement} stage. Implements {@link Camera}, tracking the eye-to-target
748
+ * {@link boundingSphere} used e.g. by {@link Pacem3DAdapterElement.zoomFit}.
749
+ */
680
750
  class Pacem3DCameraElement extends RenderableElement {
681
751
  constructor() {
682
752
  super(...arguments);
753
+ /** Gets or sets the near clipping plane distance. */
683
754
  this.near = 0.1;
755
+ /** Gets or sets the far clipping plane distance. */
684
756
  this.far = 1000.0;
757
+ /** Gets or sets the up vector, in world coordinates. */
685
758
  this.up = pacemNumerical.Geometry.LinearAlgebra.Vector3D.j();
759
+ /** Gets or sets the target the camera looks at, in world coordinates. */
686
760
  this.lookAt = Constants.DEFAULT_COORDS;
687
761
  }
688
762
  #sphere;
@@ -694,6 +768,7 @@
694
768
  const currentValue = this.#sphere = { center, radius };
695
769
  this.dispatchEvent(new pacemCore.PropertyChangeEvent({ currentValue, propertyName: 'boundingSphere', oldValue }));
696
770
  }
771
+ /** @readonly Gets the sphere centered on {@link lookAt} with radius equal to the eye-to-target distance. */
697
772
  get boundingSphere() {
698
773
  return this.#sphere;
699
774
  }
@@ -723,15 +798,20 @@
723
798
  __decorate$k([
724
799
  pacemCore.Watch({ emit: false, converter: Point3DConverter })
725
800
  ], Pacem3DCameraElement.prototype, "lookAt", void 0);
801
+ /** `<pacem-3d-perspective-camera>`: a {@link Pacem3DCameraElement} that projects the scene through a perspective frustum (vanishing point), defined by {@link fov} and {@link aspect}. */
726
802
  let Pacem3DPerspectiveCameraElement = class Pacem3DPerspectiveCameraElement extends Pacem3DCameraElement {
727
803
  constructor() {
728
804
  super(...arguments);
805
+ /** Gets or sets the vertical field of view, in degrees. */
729
806
  this.fov = 45;
807
+ /** Gets or sets the viewport width/height ratio. */
730
808
  this.aspect = 1;
731
809
  }
810
+ /** @readonly Gets the camera discriminator: always `"perspective"`. */
732
811
  get type() {
733
812
  return "perspective";
734
813
  }
814
+ /** @readonly Gets the viewport width/height ratio (alias of {@link aspect}). */
735
815
  get aspectRatio() {
736
816
  return this.aspect;
737
817
  }
@@ -754,14 +834,20 @@
754
834
  Pacem3DPerspectiveCameraElement = __decorate$k([
755
835
  pacemCore.CustomElement({ tagName: Constants.PERSPECTIVE_CAMERA_SELECTOR })
756
836
  ], Pacem3DPerspectiveCameraElement);
837
+ /** `<pacem-3d-orthographic-camera>`: a {@link Pacem3DCameraElement} that projects the scene through a fixed, non-diminishing (parallel) frustum, defined by the {@link top}/{@link left}/{@link bottom}/{@link right} coordinates. */
757
838
  let Pacem3DOrthographicCameraElement = class Pacem3DOrthographicCameraElement extends Pacem3DCameraElement {
758
839
  constructor() {
759
840
  super(...arguments);
841
+ /** Gets or sets the top frustum coordinate (e.g. 1). */
760
842
  this.top = 1;
843
+ /** Gets or sets the left frustum coordinate (e.g. -1). */
761
844
  this.left = -1;
845
+ /** Gets or sets the bottom frustum coordinate (e.g. -1). */
762
846
  this.bottom = -1;
847
+ /** Gets or sets the right frustum coordinate (e.g. 1). */
763
848
  this.right = 1;
764
849
  }
850
+ /** @readonly Gets the camera discriminator: always `"orthographic"`. */
765
851
  get type() {
766
852
  return "orthographic";
767
853
  }
@@ -792,12 +878,19 @@
792
878
  Pacem3DOrthographicCameraElement = __decorate$k([
793
879
  pacemCore.CustomElement({ tagName: Constants.ORTHO_CAMERA_SELECTOR })
794
880
  ], Pacem3DOrthographicCameraElement);
881
+ /**
882
+ * Pluggable rendering-backend contract assignable to a {@link Pacem3DElement}'s `adapter` property. Concrete adapters
883
+ * ({@link Pacem3DThreeAdapterElement} for Three.js/WebGL, {@link Pacem3DWebgpuAdapterElement} for WebGPU) are
884
+ * responsible for initializing/disposing the underlying native scene and DOM surface, sizing it, translating the
885
+ * {@link RenderableElement} scene graph into native objects, hit-testing (raycasting) and producing snapshot images.
886
+ */
795
887
  class Pacem3DAdapterElement extends pacemCore.PacemEventTarget {
796
888
  }
797
889
 
798
890
  //namespace Pacem.Components.Drawing3D {
799
891
  /** @deprecated*/
800
892
  class Pacem3DDetector {
893
+ /** Probes the browser for WebGL support by attempting to create a rendering context, populating {@link info} and {@link supported}. */
801
894
  constructor() {
802
895
  this._detected = {
803
896
  supported: false, info: {}
@@ -861,9 +954,11 @@
861
954
  addLine('misc', 'Supported Extensions', ctx.getSupportedExtensions() || []);
862
955
  }
863
956
  }
957
+ /** @readonly Gets the detected WebGL capabilities/limits, grouped by section (`main`, `bits`, `shader`, `tex`, `misc`). */
864
958
  get info() {
865
959
  return this._detected.info;
866
960
  }
961
+ /** @readonly Gets whether a WebGL rendering context could be created on this browser/device. */
867
962
  get supported() {
868
963
  return this._detected.supported;
869
964
  }
@@ -876,6 +971,7 @@
876
971
  return c > 3 && r && Object.defineProperty(target, key, r), r;
877
972
  };
878
973
  //namespace Pacem.Drawing3D {
974
+ /** Identifies the shading model (and, correspondingly, the concrete `Material` shape) applied to a mesh's faces. */
879
975
  var KnownShader;
880
976
  (function (KnownShader) {
881
977
  KnownShader["Basic"] = "basic";
@@ -885,34 +981,49 @@
885
981
  KnownShader["Line"] = "line";
886
982
  KnownShader["Custom"] = "custom";
887
983
  })(KnownShader || (KnownShader = {}));
984
+ /** Type guard checking whether `obj` implements the {@link Material} contract. */
888
985
  function isMaterial(obj) {
889
986
  return /*'color' in obj &&*/ 'shader' in obj;
890
987
  }
988
+ /** Type guard checking whether `obj` implements the {@link BasicMaterial} contract. */
891
989
  function isBasicMaterial(obj) {
892
990
  return isMaterial(obj) && obj.shader === KnownShader.Basic;
893
991
  }
992
+ /** Type guard checking whether `obj` implements the {@link LineMaterial} contract. */
894
993
  function isLineMaterial(obj) {
895
994
  return isMaterial(obj) && obj.shader === KnownShader.Line;
896
995
  }
996
+ /** Type guard checking whether `obj` implements the {@link LambertMaterial} contract. */
897
997
  function isLambertMaterial(obj) {
898
998
  return isMaterial(obj) && obj.shader === KnownShader.Lambert;
899
999
  }
1000
+ /** Type guard checking whether `obj` implements the {@link PhongMaterial} contract. */
900
1001
  function isPhongMaterial(obj) {
901
1002
  return isMaterial(obj) && obj.shader === KnownShader.Phong;
902
1003
  }
1004
+ /** Type guard checking whether `obj` implements the {@link StandardMaterial} contract. */
903
1005
  function isStandardMaterial(obj) {
904
1006
  return isMaterial(obj) && obj.shader === KnownShader.Standard;
905
1007
  }
1008
+ /** Type guard checking whether `obj` implements the {@link ShaderMaterial} contract. */
906
1009
  function isShaderMaterial(obj) {
907
1010
  return isMaterial(obj) && obj.shader === KnownShader.Custom;
908
1011
  }
909
1012
  //namespace Pacem.Components.Drawing3D {
1013
+ /**
1014
+ * Abstract base of the declarative material custom elements — {@link BasicMaterialElement}, {@link LambertMaterialElement},
1015
+ * {@link LineMaterialElement}, {@link PhongMaterialElement} and {@link StandardMaterialElement} — that assemble a
1016
+ * {@link Material} (with its `shader`-specific extra properties) out of shared and shader-specific watched attributes,
1017
+ * ready to be assigned to a `Pacem3DMeshElement`'s `material`/`backMaterial` property.
1018
+ */
910
1019
  class MaterialElement extends pacemCore.PacemEventTarget {
1020
+ /** @param shader The {@link KnownShader} this element's {@link createMaterial} produces. */
911
1021
  constructor(shader) {
912
1022
  super();
913
1023
  this.#shader = shader;
914
1024
  }
915
1025
  #shader;
1026
+ /** Gets the shading model this element produces materials for. */
916
1027
  get shader() {
917
1028
  return this.#shader;
918
1029
  }
@@ -947,11 +1058,13 @@
947
1058
  super.viewActivatedCallback();
948
1059
  this.updateMaterial();
949
1060
  }
1061
+ /** Recomputes {@link material} via {@link createMaterial}, flagging it as {@link StalePropertyFlag.Material}. */
950
1062
  updateMaterial() {
951
1063
  this.createMaterial().then(m => {
952
1064
  this.material = pacemCore.Utils.extend(m, { flags: [StalePropertyFlag.Material] });
953
1065
  });
954
1066
  }
1067
+ /** Builds the base {@link Material} out of the shared watched attributes (`opacity`, `wireframe`, `color`, `hide`, `map`); overridden by subclasses to add their shader-specific properties. */
955
1068
  async createMaterial() {
956
1069
  return {
957
1070
  opacity: this.opacity ?? 1.0,
@@ -991,6 +1104,7 @@
991
1104
  return c > 3 && r && Object.defineProperty(target, key, r), r;
992
1105
  };
993
1106
  //namespace Pacem.Components.Drawing3D {
1107
+ /** `<pacem-3d-material-basic>`: produces a {@link BasicMaterial} (unlit shading model): rendered at flat color, unaffected by scene lighting. */
994
1108
  let BasicMaterialElement = class BasicMaterialElement extends MaterialElement {
995
1109
  constructor() {
996
1110
  super(KnownShader.Basic);
@@ -1007,10 +1121,12 @@
1007
1121
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1008
1122
  };
1009
1123
  //namespace Pacem.Components.Drawing3D {
1124
+ /** `<pacem-3d-material-lambert>`: produces a {@link LambertMaterial} (Lambertian shading model): matte, non-specular reflection of scene lighting, with no glossy highlights. */
1010
1125
  let LambertMaterialElement = class LambertMaterialElement extends MaterialElement {
1011
1126
  constructor() {
1012
1127
  super(KnownShader.Lambert);
1013
1128
  }
1129
+ /** Builds the {@link LambertMaterial}, adding `emissiveColor`, `reflectivity` and `refractionRatio` to the base {@link Material}. */
1014
1130
  async createMaterial() {
1015
1131
  return pacemCore.Utils.extend({
1016
1132
  emissiveColor: this.emissiveColor || '#000',
@@ -1051,6 +1167,7 @@
1051
1167
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1052
1168
  };
1053
1169
  //namespace Pacem.Components.Drawing3D {
1170
+ /** `<pacem-3d-material-line>`: produces a {@link LineMaterial} for rendering {@link PacemLineElement} polylines (stroke width, joins, caps, dash pattern). */
1054
1171
  let LineMaterialElement = class LineMaterialElement extends MaterialElement {
1055
1172
  constructor() {
1056
1173
  super(KnownShader.Line);
@@ -1068,6 +1185,7 @@
1068
1185
  }
1069
1186
  }
1070
1187
  }
1188
+ /** Builds the {@link LineMaterial}, adding `lineWidth`, `lineJoin`, `lineCap` and `dashArray` to the base {@link Material}. */
1071
1189
  async createMaterial() {
1072
1190
  return pacemCore.Utils.extend({
1073
1191
  lineWidth: this.lineWidth ?? 1,
@@ -1105,10 +1223,12 @@
1105
1223
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1106
1224
  };
1107
1225
  //namespace Pacem.Components.Drawing3D {
1226
+ /** `<pacem-3d-material-phong>`: produces a {@link PhongMaterial} (Phong shading model): diffuse reflection plus a specular highlight of adjustable shininess. */
1108
1227
  let PhongMaterialElement = class PhongMaterialElement extends MaterialElement {
1109
1228
  constructor() {
1110
1229
  super(KnownShader.Phong);
1111
1230
  }
1231
+ /** Builds the {@link PhongMaterial}, adding the diffuse (`emissiveColor`, `reflectivity`, `refractionRatio`) and specular (`specularColor`, `shininess`, `flatShading`) properties to the base {@link Material}. */
1112
1232
  async createMaterial() {
1113
1233
  return pacemCore.Utils.extend({
1114
1234
  emissiveColor: this.emissiveColor || '#000',
@@ -1164,10 +1284,12 @@
1164
1284
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1165
1285
  };
1166
1286
  //namespace Pacem.Components.Drawing3D {
1287
+ /** `<pacem-3d-material-standard>`: produces a {@link StandardMaterial} using a physically-based (metalness/roughness) shading model. */
1167
1288
  let StandardMaterialElement = class StandardMaterialElement extends MaterialElement {
1168
1289
  constructor() {
1169
1290
  super(KnownShader.Standard);
1170
1291
  }
1292
+ /** Builds the {@link StandardMaterial}, adding `emissiveColor`, `refractionRatio`, `metalness`, `roughness` and `flatShading` to the base {@link Material}. */
1171
1293
  async createMaterial() {
1172
1294
  return pacemCore.Utils.extend({
1173
1295
  emissiveColor: this.emissiveColor || '#000',
@@ -1218,6 +1340,12 @@
1218
1340
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1219
1341
  };
1220
1342
  //namespace Pacem.Components.Drawing3D {
1343
+ /**
1344
+ * Abstract base of the declarative geometric-primitive custom elements — {@link PacemBoxElement}, {@link PacemConeElement},
1345
+ * {@link PacemCylinderElement}, {@link PacemLineElement}, {@link PacemPlaneElement}, {@link PacemSphereElement},
1346
+ * {@link PacemTorusElement} and the {@link PolyhedronElement} family — that compute a {@link NodeGeometry} out of their
1347
+ * own shape parameters, ready to be assigned to a `Pacem3DMeshElement`'s `geometry` property.
1348
+ */
1221
1349
  class Pacem3DPrimitiveElement extends pacemCore.PacemEventTarget {
1222
1350
  viewActivatedCallback() {
1223
1351
  super.viewActivatedCallback();
@@ -1231,6 +1359,13 @@
1231
1359
  ], Pacem3DPrimitiveElement.prototype, "geometry", void 0);
1232
1360
 
1233
1361
  //namespace Pacem.Drawing3D {
1362
+ /**
1363
+ * Property decorator factory that turns the decorated field into an accessor backed by a private field, invoking
1364
+ * `callback` with the property name and old/new values whenever it is set to a different value. Used by
1365
+ * {@link NodeGeometry}/{@link LineGeometry}/{@link MeshGeometry} to flag themselves as dirty ({@link StalePropertyFlag.Geometry})
1366
+ * whenever their vertex data changes.
1367
+ * @param callback Invoked (with `this` bound to the decorated instance) on every effective change of the property.
1368
+ */
1234
1369
  function NotifyChange(callback) {
1235
1370
  return (target, prop, descriptor) => {
1236
1371
  const backingField = `_${prop}_${pacemCore.Utils.uniqueCode()}_backingField`;
@@ -1265,10 +1400,15 @@
1265
1400
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1266
1401
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1267
1402
  };
1403
+ /**
1404
+ * Abstract base for the concrete geometry classes ({@link LineGeometry}, {@link MeshGeometry}) that back a
1405
+ * {@link Pacem3DMeshElement}'s `geometry` property, providing shared bounding-volume computation helpers.
1406
+ */
1268
1407
  class NodeGeometry {
1269
1408
  constructor(positions = []) {
1270
1409
  this.positions = positions;
1271
1410
  }
1411
+ /** Computes the centroid (average) of the given positions. */
1272
1412
  static barycenter(positions) {
1273
1413
  var bary = { x: 0, y: 0, z: 0 };
1274
1414
  if (!pacemCore.Utils.isNullOrEmpty(positions)) {
@@ -1286,6 +1426,7 @@
1286
1426
  }
1287
1427
  return bary;
1288
1428
  }
1429
+ /** Computes the axis-aligned {@link Box3D} enclosing the given positions. */
1289
1430
  static boundingBox(positions) {
1290
1431
  const output = {
1291
1432
  minX: +Infinity, minY: +Infinity, minZ: +Infinity,
@@ -1304,6 +1445,7 @@
1304
1445
  }
1305
1446
  return output;
1306
1447
  }
1448
+ /** Marks the geometry as stale, flagging it with {@link StalePropertyFlag.Geometry} for the next render. */
1307
1449
  setAsDirty() {
1308
1450
  setSelfAsDirty.call(this);
1309
1451
  }
@@ -1312,27 +1454,32 @@
1312
1454
  const me = this;
1313
1455
  me.flags = [/* no further specifications, other than 'geometry' */ StalePropertyFlag.Geometry];
1314
1456
  }
1457
+ /** A {@link NodeGeometry} rendered as a polyline through its {@link positions} (no faces/triangles). */
1315
1458
  class LineGeometry extends NodeGeometry {
1316
1459
  }
1317
1460
  __decorate$c([
1318
1461
  NotifyChange(setSelfAsDirty)
1319
1462
  ], LineGeometry.prototype, "positions", void 0);
1463
+ /** A {@link MeshGeometry} implementation with lazily-computed {@link barycenter}/{@link boundingBox} and settable {@link boundingSphere}, used as the `geometry` of a {@link Pacem3DMeshElement}. */
1320
1464
  class MeshGeometry extends NodeGeometry {
1321
1465
  #boundingBox;
1322
1466
  #boundingSphere;
1323
1467
  #barycenter;
1468
+ /** Gets or sets the centroid of {@link positions}; computed on first access if not explicitly set. */
1324
1469
  get barycenter() {
1325
1470
  return this.#barycenter ??= NodeGeometry.barycenter(this.positions);
1326
1471
  }
1327
1472
  set barycenter(point) {
1328
1473
  this.#barycenter = point;
1329
1474
  }
1475
+ /** Gets or sets the axis-aligned bounding box; computed from {@link positions} on first access if not explicitly set. */
1330
1476
  get boundingBox() {
1331
1477
  return this.#boundingBox ??= NodeGeometry.boundingBox(this.positions);
1332
1478
  }
1333
1479
  set boundingBox(bbox) {
1334
1480
  this.#boundingBox = bbox;
1335
1481
  }
1482
+ /** Gets or sets the bounding sphere. */
1336
1483
  get boundingSphere() {
1337
1484
  return this.#boundingSphere;
1338
1485
  }
@@ -1367,7 +1514,9 @@
1367
1514
  };
1368
1515
  var PacemBoxElement_1;
1369
1516
  //namespace Pacem.Components.Drawing3D {
1517
+ /** `<pacem-3d-primitive-box>`: computes the {@link MeshGeometry} of a rectangular cuboid (box). */
1370
1518
  let PacemBoxElement = PacemBoxElement_1 = class PacemBoxElement extends Pacem3DPrimitiveElement {
1519
+ /** Builds the {@link MeshGeometry} of a box with the given size and per-axis segment subdivisions (all default to `1`). */
1371
1520
  static createMeshGeometry(width, height, depth, widthSegments, heightSegments, depthSegments) {
1372
1521
  const w = width || 1, h = height || 1, d = depth || 1, sw = widthSegments || 1, sh = heightSegments || 1, sd = depthSegments || 1;
1373
1522
  const positions = [];
@@ -1541,6 +1690,7 @@
1541
1690
  //
1542
1691
  return geom;
1543
1692
  }
1693
+ /** Computes the box geometry from the default (unset) shape parameters. */
1544
1694
  createDefaultGeometry() {
1545
1695
  return PacemBoxElement_1.createMeshGeometry();
1546
1696
  }
@@ -1588,7 +1738,9 @@
1588
1738
  };
1589
1739
  var PacemConeElement_1;
1590
1740
  //namespace Pacem.Components.Drawing3D {
1741
+ /** `<pacem-3d-primitive-cone>`: computes the {@link MeshGeometry} of a cone (a circular base tapering to an apex point). */
1591
1742
  let PacemConeElement = PacemConeElement_1 = class PacemConeElement extends Pacem3DPrimitiveElement {
1743
+ /** Builds the {@link MeshGeometry} of a cone with the given base radius, height, side count, lateral-surface segments and base-cap segments. */
1592
1744
  static createMeshGeometry(radius = 1, height = 1, sides = 18, heightSegments = 5, capSegments = 1) {
1593
1745
  const r = radius;
1594
1746
  const nodes = [], uv = [], indices = [];
@@ -1699,6 +1851,7 @@
1699
1851
  // TODO: barycenter, boundingBox, boundingSphere
1700
1852
  return geom;
1701
1853
  }
1854
+ /** Computes the cone geometry from the default (unset) shape parameters. */
1702
1855
  createDefaultGeometry() {
1703
1856
  return PacemConeElement_1.createMeshGeometry();
1704
1857
  }
@@ -1742,7 +1895,9 @@
1742
1895
  };
1743
1896
  var PacemCylinderElement_1;
1744
1897
  //namespace Pacem.Components.Drawing3D {
1898
+ /** `<pacem-3d-primitive-cylinder>`: computes the {@link MeshGeometry} of a cylinder (two parallel circular caps joined by a straight lateral surface). */
1745
1899
  let PacemCylinderElement = PacemCylinderElement_1 = class PacemCylinderElement extends Pacem3DPrimitiveElement {
1900
+ /** Builds the {@link MeshGeometry} of a cylinder with the given radius, height, side count, lateral-surface segments and cap segments. */
1746
1901
  static createMeshGeometry(radius = 1, height = 1, sides = 18, heightSegments = 5, capSegments = 1) {
1747
1902
  const r = radius;
1748
1903
  const nodes = [], uv = [], indices = [];
@@ -1899,6 +2054,7 @@
1899
2054
  // TODO: barycenter, boundingBox, boundingSphere
1900
2055
  return geom;
1901
2056
  }
2057
+ /** Computes the cylinder geometry from the default (unset) shape parameters. */
1902
2058
  createDefaultGeometry() {
1903
2059
  return PacemCylinderElement_1.createMeshGeometry();
1904
2060
  }
@@ -1943,10 +2099,13 @@
1943
2099
  var PacemLineElement_1;
1944
2100
  //namespace Pacem.Components.Drawing3D {
1945
2101
  const DEFAULT_LINE = [{ x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 }];
2102
+ /** `<pacem-3d-primitive-line>`: computes the {@link LineGeometry} of a polyline connecting a sequence of points. */
1946
2103
  let PacemLineElement = PacemLineElement_1 = class PacemLineElement extends Pacem3DPrimitiveElement {
2104
+ /** Builds the {@link LineGeometry} out of the given vertices (a single segment from `(0,0,0)` to `(0,1,0)` when omitted). */
1947
2105
  static createLineGeometry(positions) {
1948
2106
  return new LineGeometry(positions || DEFAULT_LINE);
1949
2107
  }
2108
+ /** Computes the line geometry from the default (unset) vertex positions. */
1950
2109
  createDefaultGeometry() {
1951
2110
  return PacemLineElement_1.createLineGeometry();
1952
2111
  }
@@ -1974,7 +2133,9 @@
1974
2133
  };
1975
2134
  var PacemPlaneElement_1;
1976
2135
  //namespace Pacem.Components.Drawing3D {
2136
+ /** `<pacem-3d-primitive-plane>`: computes the {@link MeshGeometry} of a flat rectangular surface lying on the xz plane. */
1977
2137
  let PacemPlaneElement = PacemPlaneElement_1 = class PacemPlaneElement extends Pacem3DPrimitiveElement {
2138
+ /** Builds the {@link MeshGeometry} of a flat rectangle with the given width, length and per-axis segment subdivisions (defaulting to `1`x`1`, 4 segments each way). */
1978
2139
  static createMeshGeometry(width, length, widthSegments, lengthSegments) {
1979
2140
  width ||= 1.0;
1980
2141
  length ||= 1.0;
@@ -2019,6 +2180,7 @@
2019
2180
  // TODO: barycenter, boundingBox, boundingSphere
2020
2181
  return geom;
2021
2182
  }
2183
+ /** Computes the plane geometry from the default (unset) shape parameters. */
2022
2184
  createDefaultGeometry() {
2023
2185
  return PacemPlaneElement_1.createMeshGeometry();
2024
2186
  }
@@ -2074,6 +2236,12 @@
2074
2236
  }
2075
2237
  };
2076
2238
  const Point3D = pacemNumerical.Geometry.LinearAlgebra.Vector3D;
2239
+ /**
2240
+ * Abstract base of the regular-polyhedron (Platonic solid) primitive custom elements — {@link PacemTetrahedronElement},
2241
+ * {@link PacemOctahedronElement}, {@link PacemHexahedronElement}, {@link PacemIcosahedronElement} and
2242
+ * {@link PacemDodecahedronElement} — each computing the {@link MeshGeometry} of a solid inscribed in a sphere of the
2243
+ * given {@link radius}.
2244
+ */
2077
2245
  class PolyhedronElement extends Pacem3DPrimitiveElement {
2078
2246
  propertyChangedCallback(name, old, val, first) {
2079
2247
  super.propertyChangedCallback(name, old, val, first);
@@ -2084,6 +2252,7 @@
2084
2252
  _assignMeshGeometry(radius) {
2085
2253
  this.geometry = this.createMeshGeometry(radius > .0 ? radius : 1.0);
2086
2254
  }
2255
+ /** Computes the polyhedron geometry using a radius of `1` when {@link radius} hasn't been explicitly set. */
2087
2256
  createDefaultGeometry() {
2088
2257
  return this.createMeshGeometry(1.0);
2089
2258
  }
@@ -2092,7 +2261,9 @@
2092
2261
  pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.Number })
2093
2262
  ], PolyhedronElement.prototype, "radius", void 0);
2094
2263
  // #region Tetrahedron
2264
+ /** `<pacem-3d-primitive-tetrahedron>`: computes the {@link MeshGeometry} of a regular tetrahedron (a Platonic solid with 4 triangular faces). */
2095
2265
  let PacemTetrahedronElement = PacemTetrahedronElement_1 = class PacemTetrahedronElement extends PolyhedronElement {
2266
+ /** Builds the {@link MeshGeometry} of a tetrahedron inscribed in a sphere of the given radius (defaults to `1`). */
2096
2267
  static createMeshGeometry(radius) {
2097
2268
  radius ||= 1.0;
2098
2269
  const nodesCoordsString = "0 0.5774 -0.8165, 0 0.5774 0.8165, 0.8165 -0.5774 0, -0.8165 -0.5774 0";
@@ -2129,7 +2300,9 @@
2129
2300
  ], PacemTetrahedronElement);
2130
2301
  // #endregion
2131
2302
  // #region Octahedron
2303
+ /** `<pacem-3d-primitive-octahedron>`: computes the {@link MeshGeometry} of a regular octahedron (a Platonic solid with 8 triangular faces). */
2132
2304
  let PacemOctahedronElement = PacemOctahedronElement_1 = class PacemOctahedronElement extends PolyhedronElement {
2305
+ /** Builds the {@link MeshGeometry} of an octahedron inscribed in a sphere of the given radius (defaults to `1`). */
2133
2306
  static createMeshGeometry(radius) {
2134
2307
  radius ||= 1.0;
2135
2308
  // -------------------------------------------------------------------------------------------------
@@ -2168,7 +2341,9 @@
2168
2341
  ], PacemOctahedronElement);
2169
2342
  // #endregion
2170
2343
  // #region Hexahedron
2344
+ /** `<pacem-3d-primitive-hexahedron>`: computes the {@link MeshGeometry} of a regular hexahedron (a Platonic solid with 6 square faces, i.e. a cube). */
2171
2345
  let PacemHexahedronElement = PacemHexahedronElement_1 = class PacemHexahedronElement extends PolyhedronElement {
2346
+ /** Builds the {@link MeshGeometry} of a cube inscribed in a sphere of the given radius (defaults to `1`), as an equal-sided {@link PacemBoxElement}. */
2172
2347
  static createMeshGeometry(radius) {
2173
2348
  radius ||= 1.0;
2174
2349
  const inv_sqrt3 = 1.0 / Math.sqrt(3.0);
@@ -2186,7 +2361,9 @@
2186
2361
  ], PacemHexahedronElement);
2187
2362
  // #endregion
2188
2363
  // #region Icosahedron
2364
+ /** `<pacem-3d-primitive-icosahedron>`: computes the {@link MeshGeometry} of a regular icosahedron (a Platonic solid with 20 triangular faces). */
2189
2365
  let PacemIcosahedronElement = PacemIcosahedronElement_1 = class PacemIcosahedronElement extends PolyhedronElement {
2366
+ /** Builds the {@link MeshGeometry} of an icosahedron inscribed in a sphere of the given radius (defaults to `1`). */
2190
2367
  static createMeshGeometry(radius) {
2191
2368
  radius ||= 1.0;
2192
2369
  const nodesCoordsString = "0 0.850651 -0.525731, 0 0.850651 0.525731, 0.850651 0.525731 0, 0.525731 0 -0.850651, -0.525731 0 -0.850651, -0.850651 0.525731 0, -0.525731 0 0.850651, 0.525731 0 0.850651, 0.850651 -0.525731 0, 0 -0.850651 0.525731, 0 -0.850651 -0.525731, -0.850651 -0.525731 0";
@@ -2246,7 +2423,9 @@
2246
2423
  ], PacemIcosahedronElement);
2247
2424
  // #endregion
2248
2425
  // #region Dodecahedron
2426
+ /** `<pacem-3d-primitive-dodecahedron>`: computes the {@link MeshGeometry} of a regular dodecahedron (a Platonic solid with 12 pentagonal faces). */
2249
2427
  let PacemDodecahedronElement = PacemDodecahedronElement_1 = class PacemDodecahedronElement extends PolyhedronElement {
2428
+ /** Builds the {@link MeshGeometry} of a dodecahedron inscribed in a sphere of the given radius (defaults to `1`). */
2250
2429
  static createMeshGeometry(radius) {
2251
2430
  radius ||= 1.0;
2252
2431
  const n1 = Point3D.from(0.356822 * radius, 0.934172 * radius, 0 * radius);
@@ -2371,7 +2550,9 @@
2371
2550
  };
2372
2551
  var PacemSphereElement_1;
2373
2552
  //namespace Pacem.Components.Drawing3D {
2553
+ /** `<pacem-3d-primitive-sphere>`: computes the {@link MeshGeometry} of a UV sphere. */
2374
2554
  let PacemSphereElement = PacemSphereElement_1 = class PacemSphereElement extends Pacem3DPrimitiveElement {
2555
+ /** Builds the {@link MeshGeometry} of a sphere with the given radius (defaults to `1`) and tessellation (defaults to `8`). */
2375
2556
  static createMeshGeometry(radius = 1, segs = 8) {
2376
2557
  //
2377
2558
  const nodes = [];
@@ -2453,6 +2634,7 @@
2453
2634
  computeSharpVertexNormals(geom);
2454
2635
  return geom;
2455
2636
  }
2637
+ /** Computes the sphere geometry from the default (unset) shape parameters. */
2456
2638
  createDefaultGeometry() {
2457
2639
  return PacemSphereElement_1.createMeshGeometry();
2458
2640
  }
@@ -2484,7 +2666,9 @@
2484
2666
  };
2485
2667
  var PacemTorusElement_1;
2486
2668
  //namespace Pacem.Components.Drawing3D {
2669
+ /** `<pacem-3d-primitive-torus>`: computes the {@link MeshGeometry} of a torus (donut shape): a tube revolved around a central axis. */
2487
2670
  let PacemTorusElement = PacemTorusElement_1 = class PacemTorusElement extends Pacem3DPrimitiveElement {
2671
+ /** Builds the {@link MeshGeometry} of a torus with the given outer/inner radii and ring/tube segment counts. */
2488
2672
  static createMeshGeometry(radius = 1, innerRadius = .25, segments = 24, sides = 12) {
2489
2673
  //
2490
2674
  const nodes = [];
@@ -2535,6 +2719,7 @@
2535
2719
  computeSharpVertexNormals(geom);
2536
2720
  return geom;
2537
2721
  }
2722
+ /** Computes the torus geometry from the default (unset) shape parameters. */
2538
2723
  createDefaultGeometry() {
2539
2724
  return PacemTorusElement_1.createMeshGeometry();
2540
2725
  }
@@ -2582,21 +2767,29 @@
2582
2767
  const clipY = y / rect.height * -2 + 1;
2583
2768
  return { x: clipX, y: clipY };
2584
2769
  }
2770
+ /** Shared helpers used by the concrete `pacem-3d` rendering-backend adapters (Three.js, WebGPU): type guards, buffer flattening, camera lookup and pointer-to-clip-space conversion. */
2585
2771
  class AdapterUtils {
2772
+ /** Returns whether the given object is a {@link MeshGeometry} (has `positions` and `triangleIndices` arrays). */
2586
2773
  static isMeshGeometry(obj) {
2587
2774
  return 'positions' in obj && pacemCore.Utils.isArray(obj.positions)
2588
2775
  && 'triangleIndices' in obj && pacemCore.Utils.isArray(obj.triangleIndices);
2589
2776
  }
2777
+ /** Returns whether the given object is a {@link Vector3D} (has numeric `x`/`y`/`z`). */
2590
2778
  static isVector3D(obj) {
2591
2779
  return 'x' in obj && typeof obj.x === 'number'
2592
2780
  && 'y' in obj && typeof obj.y === 'number'
2593
2781
  && 'z' in obj && typeof obj.z === 'number';
2594
2782
  }
2783
+ /** Returns whether the given object is an {@link Rgba} color (has numeric `r`/`g`/`b`). */
2595
2784
  static isRgba(obj) {
2596
2785
  return 'r' in obj && typeof obj.r === 'number'
2597
2786
  && 'g' in obj && typeof obj.g === 'number'
2598
2787
  && 'b' in obj && typeof obj.b === 'number';
2599
2788
  }
2789
+ /**
2790
+ * Flattens an array of 3D vectors, UV coordinates, or RGBA colors into a single flat number array (interleaved components), ready for a GPU buffer.
2791
+ * @param array Array of {@link Vector3D}, {@link UVMap} entries, or {@link Rgba} values to flatten
2792
+ */
2600
2793
  static flattenVectorArray(array) {
2601
2794
  const retval = [];
2602
2795
  for (let v of array) {
@@ -2612,6 +2805,10 @@
2612
2805
  }
2613
2806
  return retval;
2614
2807
  }
2808
+ /**
2809
+ * Returns the first non-hidden {@link Camera} among the given renderables, if any.
2810
+ * @param items Renderable items to search
2811
+ */
2615
2812
  static findCamera(items) {
2616
2813
  return (items || []).find(r => isCamera(r) && !r.hide);
2617
2814
  }
@@ -2861,6 +3058,12 @@
2861
3058
  throw new Error('The wait for THREE adapter initialization timed out.');
2862
3059
  }
2863
3060
  }
3061
+ /**
3062
+ * `<pacem-3d-three-adapter>`: {@link Pacem3DAdapterElement} rendering backend built on top of the
3063
+ * {@link https://threejs.org/ | Three.js}/WebGL library (lazily loaded from a CDN on first use). Translates the
3064
+ * {@link RenderableElement} scene graph into `THREE.Object3D`s, renders them with a `THREE.WebGLRenderer` and
3065
+ * supports mouse-driven orbit controls via {@link orbit}.
3066
+ */
2864
3067
  let Pacem3DThreeAdapterElement = class Pacem3DThreeAdapterElement extends Pacem3DAdapterElement {
2865
3068
  constructor() {
2866
3069
  super(...arguments);
@@ -3284,43 +3487,74 @@
3284
3487
  ], Pacem3DThreeAdapterElement);
3285
3488
 
3286
3489
  //namespace Pacem.Drawing3D.WebGPU {
3490
+ /** Prefix prepended to every WebGPU resource label ({@link GPURenderPipeline}s, bind groups, layouts, shader modules, ...) created by this adapter, so they're easy to spot in browser devtools/profilers. */
3287
3491
  const LABEL_PREFIX = 'pacem-3d-';
3492
+ /** Maximum value representable by a 32-bit unsigned integer (2^32 - 1). Used as the color-picking "no object" sentinel `u32` id. */
3288
3493
  const U32_MAX = 4_294_967_295;
3289
3494
  // #region vertex state
3495
+ /** Vertex buffer slot (index into {@link GPUVertexState.buffers}) carrying mesh vertex positions (or the packed position+normal+uv buffer, see {@link PipelineOptions.packedVertices}). */
3290
3496
  const VERTEX_BUFFER_INDEX_MESH_POSITION = 0;
3497
+ /** Vertex buffer slot (index into {@link GPUVertexState.buffers}) carrying mesh vertex normals, when vertices aren't packed. */
3291
3498
  const VERTEX_BUFFER_INDEX_MESH_NORMAL = 1;
3499
+ /** Vertex buffer slot (index into {@link GPUVertexState.buffers}) carrying mesh UV coordinates, when vertices aren't packed. */
3292
3500
  const VERTEX_BUFFER_INDEX_MESH_UV = 2;
3501
+ /** `@location` index of the surface normal in the generated WGSL `VertexOutput` struct. */
3293
3502
  const VERTEX_OUTPUT_LOCATION_NORMAL = 0;
3503
+ /** `@location` index of the world-space vertex position in the generated WGSL `VertexOutput` struct. */
3294
3504
  const VERTEX_OUTPUT_LOCATION_VERTEX = 1;
3505
+ /** `@location` index of the UV coordinate in the generated WGSL `VertexOutput` struct. */
3295
3506
  const VERTEX_OUTPUT_LOCATION_UV = 2;
3507
+ /** `@location` index of the vertex/triangle index in the generated WGSL `VertexOutput` struct. */
3296
3508
  const VERTEX_OUTPUT_LOCATION_INDEX = 3;
3509
+ /** `@location` index of the picking ray data in the generated WGSL `VertexOutput` struct. */
3297
3510
  const VERTEX_OUTPUT_LOCATION_RAY = 4;
3511
+ /** `@location` index of the instance index in the generated WGSL `VertexOutput` struct. */
3298
3512
  const VERTEX_OUTPUT_LOCATION_INSTANCE = 5;
3513
+ /** `@group` index of the world/normal transform uniform bind group. */
3299
3514
  const UNIFORM_GROUP_INDEX_TRANSFORM = 1;
3515
+ /** `@group` index of the camera view/projection uniform bind group. */
3300
3516
  const UNIFORM_GROUP_INDEX_CAMERA = 0;
3517
+ /** `@group` index of the pointer/interaction uniform bind group. Deliberately aliased to {@link UNIFORM_GROUP_INDEX_CAMERA} so interaction data shares the camera's binding group. */
3301
3518
  const UNIFORM_GROUP_INDEX_INTERACTION = UNIFORM_GROUP_INDEX_CAMERA; // put it in camera same binding group
3519
+ /** `@group` index of the line-geometry storage bind group (positions + line properties). */
3302
3520
  const STORAGE_GROUP_INDEX_LINE = 2;
3521
+ /** `@group` index of the color-picking (object-id) uniform bind group. */
3303
3522
  const UNIFORM_GROUP_INDEX_COLORPICKING = 2;
3523
+ /** `@binding` index of the world/normal transform uniform, within {@link UNIFORM_GROUP_INDEX_TRANSFORM}. */
3304
3524
  const UNIFORM_BINDING_INDEX_TRANSFORM = 0;
3525
+ /** `@binding` index of the camera view/projection uniform, within {@link UNIFORM_GROUP_INDEX_CAMERA}. */
3305
3526
  const UNIFORM_BINDING_INDEX_CAMERA = 0;
3527
+ /** `@binding` index of the pointer/interaction uniform, within {@link UNIFORM_GROUP_INDEX_INTERACTION}. */
3306
3528
  const UNIFORM_BINDING_INDEX_INTERACTION = 1;
3307
3529
  // #endregion
3308
3530
  // #region fragment state
3531
+ /** `@group` index of the material (storage buffer + optional texture/sampler) bind group. */
3309
3532
  const MATERIAL_GROUP_INDEX = 2;
3533
+ /** `@group` index of the per-light uniform bind group, immediately following {@link MATERIAL_GROUP_INDEX}. */
3310
3534
  const UNIFORM_GROUP_INDEX_LIGHTING = MATERIAL_GROUP_INDEX + 1;
3535
+ /** `@binding` index of the material storage buffer, within {@link MATERIAL_GROUP_INDEX}. */
3311
3536
  const STORAGE_MATERIAL_INDEX = 0;
3537
+ /** `@binding` index of the material's texture sampler, within {@link MATERIAL_GROUP_INDEX}. */
3312
3538
  const SAMPLER_BINDING_INDEX = 1;
3539
+ /** `@binding` index of the material's texture, within {@link MATERIAL_GROUP_INDEX}. */
3313
3540
  const TEXTURE_BINDING_INDEX = 2;
3541
+ /** WGSL identifier used to reference the running vertex/fragment color in generated shader fragments (spliced in and out by texture/lighting stages). */
3314
3542
  const VERTEX_COLOR_REFERENCE = 'color';
3543
+ /** WGSL identifier used to reference the accumulated ambient-light contribution in generated fragment shader code. */
3315
3544
  const AMBIENT_COLOR_REFERENCE = 'ambient';
3545
+ /** WGSL identifier used to reference the accumulated (directional/omni) lighting contribution in generated fragment shader code. */
3316
3546
  const LIGHTING_COLOR_REFERENCE = 'lighting';
3317
3547
  // #endregion
3318
3548
  // #region WGSL
3549
+ /** Name of the WGSL struct describing per-vertex input attributes, used by generated vertex shader code. */
3319
3550
  const WGSL_VERTEXINPUT_STRUCT_NAME = 'VertexInput';
3551
+ /** Name of the WGSL struct describing vertex-stage output / fragment-stage input, used by generated vertex and fragment shader code. */
3320
3552
  const WGSL_VERTEXOUTPUT_STRUCT_NAME = 'VertexOutput';
3553
+ /** Name of the WGSL struct describing the pointer/interaction uniform data, used by generated shader code. */
3321
3554
  const WGSL_INTERACTIONDATA_STRUCT_NAME = 'InteractionData';
3322
3555
  // #endregion
3323
3556
  // Kinda polyfill for WebGPU constants, as TypeScript 6.0 does include WebGPU types but does NOT include these constants for magic numbers:
3557
+ /** Polyfill for the WebGPU `GPUBufferUsage` bitflags (`lib.dom` declares the `GPUBufferUsageFlags` type but not these numeric flag values). Combine with `|` when creating a {@link GPUBuffer}, e.g. `GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST`. */
3324
3558
  var GPUBufferUsage;
3325
3559
  (function (GPUBufferUsage) {
3326
3560
  GPUBufferUsage[GPUBufferUsage["COPY_SRC"] = 4] = "COPY_SRC";
@@ -3334,6 +3568,7 @@
3334
3568
  GPUBufferUsage[GPUBufferUsage["UNIFORM"] = 64] = "UNIFORM";
3335
3569
  GPUBufferUsage[GPUBufferUsage["VERTEX"] = 32] = "VERTEX";
3336
3570
  })(GPUBufferUsage || (GPUBufferUsage = {}));
3571
+ /** Polyfill for the WebGPU `GPUTextureUsage` bitflags (`lib.dom` declares the `GPUTextureUsageFlags` type but not these numeric flag values). Combine with `|` when creating a {@link GPUTexture}. */
3337
3572
  var GPUTextureUsage;
3338
3573
  (function (GPUTextureUsage) {
3339
3574
  GPUTextureUsage[GPUTextureUsage["COPY_SRC"] = 1] = "COPY_SRC";
@@ -3342,18 +3577,21 @@
3342
3577
  GPUTextureUsage[GPUTextureUsage["STORAGE_BINDING"] = 8] = "STORAGE_BINDING";
3343
3578
  GPUTextureUsage[GPUTextureUsage["TEXTURE_BINDING"] = 4] = "TEXTURE_BINDING";
3344
3579
  })(GPUTextureUsage || (GPUTextureUsage = {}));
3580
+ /** Polyfill for the WebGPU `GPUShaderStage` bitflags (`lib.dom` declares the `GPUShaderStageFlags` type but not these numeric flag values). Combine with `|` in {@link GPUBindGroupLayoutEntry.visibility}, e.g. `GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT`. */
3345
3581
  var GPUShaderStage;
3346
3582
  (function (GPUShaderStage) {
3347
3583
  GPUShaderStage[GPUShaderStage["VERTEX"] = 1] = "VERTEX";
3348
3584
  GPUShaderStage[GPUShaderStage["FRAGMENT"] = 2] = "FRAGMENT";
3349
3585
  GPUShaderStage[GPUShaderStage["COMPUTE"] = 4] = "COMPUTE";
3350
3586
  })(GPUShaderStage || (GPUShaderStage = {}));
3587
+ /** Polyfill for the WebGPU `GPUMapMode` flags (`lib.dom` declares the type but not these numeric values). Used with {@link GPUBuffer.mapAsync}. */
3351
3588
  var GPUMapMode;
3352
3589
  (function (GPUMapMode) {
3353
3590
  GPUMapMode[GPUMapMode["READ"] = 1] = "READ";
3354
3591
  GPUMapMode[GPUMapMode["WRITE"] = 2] = "WRITE";
3355
3592
  })(GPUMapMode || (GPUMapMode = {}));
3356
3593
 
3594
+ /** Categorizes a bind group by the role of the resources it carries (mesh transforms, camera, texture, light, interaction data), used to look up {@link BindGroupDefinition}s via {@link WGSL.bindings}. */
3357
3595
  var WGSLBindingType;
3358
3596
  (function (WGSLBindingType) {
3359
3597
  WGSLBindingType["Mesh"] = "mesh";
@@ -3364,7 +3602,20 @@
3364
3602
  })(WGSLBindingType || (WGSLBindingType = {}));
3365
3603
 
3366
3604
  //namespace Pacem.Drawing3D.WebGPU {
3605
+ /**
3606
+ * Low-level factory for typed {@link GPUBuffer}s (vertex, uniform, storage, index) used throughout the WebGPU backend
3607
+ * to upload CPU-side geometry/material/uniform data to the GPU. Vertex and index buffers are populated immediately
3608
+ * (via `mappedAtCreation`); uniform and storage buffers are allocated empty, sized to `data`, and are expected to be
3609
+ * populated later via {@link GPUQueue.writeBuffer} (see {@link Buffer}/`abstractions.ts` and the `fresh`-tracking
3610
+ * buffer accessors in `renderablebuffer.ts`).
3611
+ */
3367
3612
  class Buffers {
3613
+ /**
3614
+ * Creates a {@link GPUBuffer} usable as a vertex buffer (`VERTEX | COPY_DST`), writing `data` into it immediately.
3615
+ * @param device The {@link GPUDevice} to allocate the buffer on.
3616
+ * @param data Vertex attribute data (e.g. flattened positions/normals/uv coordinates).
3617
+ * @param label Optional debug label.
3618
+ */
3368
3619
  static create(device, data, label = '') {
3369
3620
  const buffer = device.createBuffer({
3370
3621
  size: data.byteLength, label,
@@ -3387,6 +3638,12 @@
3387
3638
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
3388
3639
  });
3389
3640
  }
3641
+ /**
3642
+ * Creates a {@link GPUBuffer} usable as an index buffer (`INDEX | COPY_DST`), writing `data` into it immediately.
3643
+ * @param device The {@link GPUDevice} to allocate the buffer on.
3644
+ * @param data 16-bit vertex indices.
3645
+ * @param label Optional debug label.
3646
+ */
3390
3647
  static createIndexed(device, data, label = '') {
3391
3648
  const buffer = device.createBuffer({
3392
3649
  size: data.byteLength, label,
@@ -3398,6 +3655,10 @@
3398
3655
  return buffer;
3399
3656
  }
3400
3657
  }
3658
+ /**
3659
+ * Helpers to build {@link GPUVertexBufferLayout}s describing how a vertex buffer's contents map to shader `@location`
3660
+ * inputs, for use in a {@link GPURenderPipelineDescriptor.vertex} state.
3661
+ */
3401
3662
  class BufferLayouts {
3402
3663
  static createVertex(shaderLocationStart = 0, size = 3, ...sizes) {
3403
3664
  const attributes = [];
@@ -3419,6 +3680,7 @@
3419
3680
  }
3420
3681
  }
3421
3682
 
3683
+ /** Fallback options used by {@link getBasicMaterialFragmentWgsl}: mirrors the `VERTEX_OUTPUT_LOCATION_*` layout produced by `mesh-vertex.ts` and reads the diffuse color from {@link MATERIAL_GROUP_INDEX}/{@link STORAGE_MATERIAL_INDEX}. */
3422
3684
  const DefaultBasicMaterialFragmentOptions = {
3423
3685
  outputNormalLocation: VERTEX_OUTPUT_LOCATION_NORMAL,
3424
3686
  outputVertexLocation: VERTEX_OUTPUT_LOCATION_VERTEX,
@@ -3429,9 +3691,16 @@
3429
3691
  storageMaterialGroupIndex: MATERIAL_GROUP_INDEX,
3430
3692
  storageMaterialBindingIndex: STORAGE_MATERIAL_INDEX,
3431
3693
  };
3694
+ /** Joins a {@link WGSLFragmentExtra} string array (`definitions`/`bindings`) with CRLF newlines, or returns an empty string when omitted. */
3432
3695
  function adaptWGSLStringArray$1(array = []) {
3433
3696
  return array.join('\r\n');
3434
3697
  }
3698
+ /**
3699
+ * Turns a {@link WGSLFragmentExtra.modifiers} array into a block of WGSL statements to inline into `main`. Entries
3700
+ * already ending with `;` are emitted verbatim; bare expressions are wrapped into `${VERTEX_COLOR_REFERENCE} = <expr>;`
3701
+ * so lighting/texture snippets (see {@link ambientLight}/{@link directionalLight}/{@link omniLight}/{@link textureMap})
3702
+ * can either replace or contribute to the running `color`/`ambient`/`lighting` accumulators.
3703
+ */
3435
3704
  function adaptWGSLModifierStringArray$1(array = []) {
3436
3705
  let output = '';
3437
3706
  for (let modifier of array) {
@@ -3445,6 +3714,21 @@
3445
3714
  }
3446
3715
  return output;
3447
3716
  }
3717
+ /**
3718
+ * Composes the fragment shader for the "basic" (unlit-by-default) material: it reads a flat `diffuseColor` from a
3719
+ * storage buffer, then lets `extensions.modifiers` (lighting/texture snippets) accumulate into the `color`,
3720
+ * `ambient` and `lighting` WGSL variables ({@link VERTEX_COLOR_REFERENCE}/{@link AMBIENT_COLOR_REFERENCE}/{@link LIGHTING_COLOR_REFERENCE}).
3721
+ * If nothing wrote a `lighting` color (alpha stays `0`), it is treated as fully lit (`white`) so the material
3722
+ * renders unshaded when no light snippet is attached; the final output is `color * lighting + ambient`.
3723
+ *
3724
+ * Splices `extensions.definitions`/`extensions.bindings`/`extensions.modifiers` (see {@link WGSLFragmentExtra}) into
3725
+ * the `// definitions`, `// bindings` and body sections of the generated source, alongside the fixed
3726
+ * `diffuseColor` storage binding and the `@fragment fn main` signature matching `mesh-vertex.ts`'s `VertexOutput`.
3727
+ *
3728
+ * @param extensions Extra WGSL `definitions`/`bindings`/`modifiers` to merge in (e.g. lighting/texture contributions).
3729
+ * @param options Location/binding overrides; defaults to {@link DefaultBasicMaterialFragmentOptions}, matching the vertex-stage outputs produced by `mesh-vertex.ts`.
3730
+ * @returns Complete WGSL fragment-shader source with a single `@fragment fn main` entry point returning `@location(0) vec4f`.
3731
+ */
3448
3732
  function getBasicMaterialFragmentWgsl(extensions = {}, options = DefaultBasicMaterialFragmentOptions) {
3449
3733
  const opts = pacemCore.Utils.extend({}, DefaultBasicMaterialFragmentOptions, options);
3450
3734
  const definitions = adaptWGSLStringArray$1(extensions.definitions), bindings = adaptWGSLStringArray$1(extensions.bindings), modifiers = adaptWGSLModifierStringArray$1(extensions.modifiers);
@@ -3481,15 +3765,22 @@ const white = vec4f(1,1,1,1);
3481
3765
  }`;
3482
3766
  }
3483
3767
 
3768
+ /** Fallback options used by {@link getLineMaterialFragmentWgsl}: reads instance/vertex index from locations 0/1 (matching `line-vertex.ts`'s `VertexOutput`) and the color storage buffer at {@link STORAGE_GROUP_INDEX_LINE}` + 1`/{@link STORAGE_MATERIAL_INDEX}. */
3484
3769
  const DefaultLineMaterialFragmentOptions = {
3485
3770
  outputInstanceIndexLocation: 0,
3486
3771
  outputVertexIndexLocation: 1,
3487
3772
  storageMaterialGroupIndex: STORAGE_GROUP_INDEX_LINE + 1,
3488
3773
  storageMaterialBindingIndex: STORAGE_MATERIAL_INDEX,
3489
3774
  };
3775
+ /** Joins a {@link WGSLFragmentExtra} string array (`definitions`/`bindings`) with CRLF newlines, or returns an empty string when omitted. */
3490
3776
  function adaptWGSLStringArray(array = []) {
3491
3777
  return array.join('\r\n');
3492
3778
  }
3779
+ /**
3780
+ * Turns a {@link WGSLFragmentExtra.modifiers} array into a block of WGSL statements to inline into `main`. Entries
3781
+ * already ending with `;` are emitted verbatim (assumed to be full statements); bare expressions are wrapped into
3782
+ * `output = <expr>;` so each modifier can either replace or contribute to the fragment's `output` color variable.
3783
+ */
3493
3784
  function adaptWGSLModifierStringArray(array = []) {
3494
3785
  let output = '';
3495
3786
  for (let modifier of array) {
@@ -3503,6 +3794,20 @@ const white = vec4f(1,1,1,1);
3503
3794
  }
3504
3795
  return output;
3505
3796
  }
3797
+ /**
3798
+ * Composes the fragment shader for line/polyline rendering: it looks up the current vertex's color from a
3799
+ * per-vertex `vertexColors` storage buffer (indexed modulo its length via `getVertexColor`, so a shorter palette
3800
+ * cycles/repeats across a longer line) and returns it as `@location(0) vec4f`, honoring any `extensions.modifiers`
3801
+ * applied afterwards (e.g. dashing, fading).
3802
+ *
3803
+ * Splices `extensions.definitions`/`extensions.bindings`/`extensions.modifiers` (see {@link WGSLFragmentExtra},
3804
+ * typically produced by the light snippets in this folder) into the `// definitions`, `// bindings` and body
3805
+ * sections of the generated source, in addition to the fixed `vertexColors` storage binding.
3806
+ *
3807
+ * @param extensions Extra WGSL `definitions`/`bindings`/`modifiers` to merge in (e.g. lighting contributions).
3808
+ * @param options Binding/location overrides; defaults to {@link DefaultLineMaterialFragmentOptions}, matching the vertex-stage outputs produced by `line-vertex.ts`.
3809
+ * @returns Complete WGSL fragment-shader source with a single `@fragment fn main` entry point.
3810
+ */
3506
3811
  function getLineMaterialFragmentWgsl(extensions = {}, options = DefaultLineMaterialFragmentOptions) {
3507
3812
  const opts = pacemCore.Utils.extend({}, DefaultLineMaterialFragmentOptions, options);
3508
3813
  const definitions = adaptWGSLStringArray(extensions.definitions), bindings = adaptWGSLStringArray(extensions.bindings), modifiers = adaptWGSLModifierStringArray(extensions.modifiers);
@@ -3534,7 +3839,18 @@ fn getVertexColor(index: u32) -> vec4f {
3534
3839
  }
3535
3840
 
3536
3841
  //namespace Pacem.Drawing3D.WebGPU {
3842
+ /**
3843
+ * Factory that converts a scene-facing {@link Material3D} (`../../materials/material.ts`) into its WebGPU-side
3844
+ * counterpart ({@link Material}, `abstractions.ts`): a {@link BasicMaterial} or {@link LineMaterial} wrapping the GPU
3845
+ * buffer/texture/WGSL-fragment logic consumed when building render pipelines (see `pipelinefactory.ts`).
3846
+ */
3537
3847
  class Materials {
3848
+ /**
3849
+ * Wraps `material` in the {@link Material} implementation matching its `shader`.
3850
+ * @param material The scene material to wrap.
3851
+ * @returns A {@link BasicMaterial} or {@link LineMaterial}, depending on `material.shader`.
3852
+ * @throws {TypeError} If `material.shader` isn't one of the supported {@link KnownShader} values.
3853
+ */
3538
3854
  static create(material) {
3539
3855
  if (isBasicMaterial(material)) {
3540
3856
  return new BasicMaterial(material);
@@ -3576,6 +3892,11 @@ fn getVertexColor(index: u32) -> vec4f {
3576
3892
  return retval;
3577
3893
  }
3578
3894
  }
3895
+ /**
3896
+ * WebGPU-side counterpart of a {@link BasicMaterial3D}: supplies the diffuse-color storage buffer (inherited from
3897
+ * {@link MaterialBase.buffer}) and, when the material has a `texture`, allocates the backing {@link GPUTexture}
3898
+ * (pixel data is copied in later by the mesh pipeline's material bind-group setup, see `pipelinefactory.ts`).
3899
+ */
3579
3900
  class BasicMaterial extends MaterialBase {
3580
3901
  get shader() {
3581
3902
  return KnownShader.Basic;
@@ -3586,6 +3907,10 @@ fn getVertexColor(index: u32) -> vec4f {
3586
3907
  throw new TypeError("Basic material expected.");
3587
3908
  }
3588
3909
  }
3910
+ /**
3911
+ * Allocates the (uninitialized) {@link GPUTexture} for the material's `texture`, if any.
3912
+ * @param key Must be `'texture'`; any other {@link MaterialKey} (or a material without a `texture`) yields `null`.
3913
+ */
3589
3914
  texture({ device }, key = 'texture') {
3590
3915
  const material = this.material;
3591
3916
  if (key === 'texture' && !pacemCore.Utils.isNull(material.texture)) {
@@ -3600,8 +3925,14 @@ fn getVertexColor(index: u32) -> vec4f {
3600
3925
  return null;
3601
3926
  }
3602
3927
  // TODO: put it in the material instance/interface
3928
+ /** Basic-material fragment shader WGSL source generator; see `wgsl/basic-material-fragment.ts`. */
3603
3929
  static { this.fragment = getBasicMaterialFragmentWgsl; }
3604
3930
  }
3931
+ /**
3932
+ * WebGPU-side counterpart of a {@link LineMaterial3D}: supplies the diffuse-color storage buffer (inherited from
3933
+ * {@link MaterialBase.buffer}) plus a dedicated `'line'` uniform buffer holding the canvas size and half line-width,
3934
+ * consumed by the line vertex shader (`wgsl/line-vertex.ts`).
3935
+ */
3605
3936
  class LineMaterial extends MaterialBase {
3606
3937
  get shader() {
3607
3938
  return KnownShader.Line;
@@ -3612,6 +3943,12 @@ fn getVertexColor(index: u32) -> vec4f {
3612
3943
  throw new TypeError("Line material expected.");
3613
3944
  }
3614
3945
  }
3946
+ /**
3947
+ * Returns the `'line'` uniform buffer (canvas width/height and half line-width) for `key === 'line'`; otherwise
3948
+ * defers to the inherited diffuse-color buffer.
3949
+ * @param ctx WebGPU context supplying the device and canvas.
3950
+ * @param key Which buffer to build; only `'line'` is handled specially.
3951
+ */
3615
3952
  buffer(ctx, key) {
3616
3953
  switch (key) {
3617
3954
  case 'line':
@@ -3623,18 +3960,38 @@ fn getVertexColor(index: u32) -> vec4f {
3623
3960
  return super.buffer(ctx);
3624
3961
  }
3625
3962
  }
3963
+ /** Lines have no texture support; always returns `null`. */
3626
3964
  texture(_) {
3627
3965
  return null;
3628
3966
  }
3967
+ /** Line-material fragment shader WGSL source generator; see `wgsl/line-material-fragment.ts`. */
3629
3968
  static { this.fragment = getLineMaterialFragmentWgsl; }
3630
3969
  }
3631
3970
 
3632
3971
  //namespace Pacem.Drawing3D.WebGPU {
3972
+ /** Placeholder identifiers (`input`, `data`) fed to a {@link VertexInputModifierDelegate}/{@link VertexOutputModifierDelegate} callback when {@link extractMeshVertexModifierFunctionComponents} needs to materialize its WGSL source before parsing it. */
3633
3973
  const defaultVertexGeometryModifierArgs = ['input', 'data'];
3634
3974
  //const defaultVertexGeometryModifier: VertexInputModifierDelegate = (...defaultVertexGeometryModifierArgs) => {
3635
3975
  // const [inputRef, dataRef] = defaultVertexGeometryModifierArgs;
3636
3976
  // return `fn echo(${inputRef}: ${WGSL_VERTEXINPUT_STRUCT_NAME}, ${dataRef}: ${WGSL_INTERACTIONDATA_STRUCT_NAME}) -> ${WGSL_VERTEXINPUT_STRUCT_NAME} { return ${inputRef}; }`;
3637
3977
  //};
3978
+ /**
3979
+ * Parses a user-supplied vertex-stage modifier (see {@link VertexInputModifierDelegate}/{@link VertexOutputModifierDelegate},
3980
+ * consumed by `mesh-vertex.ts`'s `getMeshVertexWgsl`) into its WGSL function name, parameter names and body, so the
3981
+ * caller can inline it as a standalone `fn` definition and reference it by name from the generated vertex shader.
3982
+ *
3983
+ * If `wgsl` is a callback rather than a raw string, it is first invoked with the placeholder argument names in
3984
+ * {@link defaultVertexGeometryModifierArgs} to obtain its WGSL source. The resulting source is then matched against
3985
+ * `fn <name>(<arg0>: <ArgType>, <arg1>: InteractionData) -> <ArgType> { <body> }`, where `<ArgType>` must be either
3986
+ * `VertexInput` ({@link WGSL_VERTEXINPUT_STRUCT_NAME}) or `VertexOutput` ({@link WGSL_VERTEXOUTPUT_STRUCT_NAME}) and
3987
+ * must match on both sides of the signature (an input modifier both takes and returns `VertexInput`, an output
3988
+ * modifier both takes and returns `VertexOutput`), with the second parameter always typed
3989
+ * {@link WGSL_INTERACTIONDATA_STRUCT_NAME}.
3990
+ *
3991
+ * @param wgsl Raw WGSL function source, or a delegate that produces it when called with `(inputRef, interactionDataRef)`.
3992
+ * @returns The parsed function's `name`, its two parameter `args` (in order), and its `body` (the statements between the braces, trimmed).
3993
+ * @throws If the source doesn't match the required single-function signature, or if its parameter types are inconsistent with the modifier contract.
3994
+ */
3638
3995
  function extractMeshVertexModifierFunctionComponents(wgsl) {
3639
3996
  if (typeof wgsl !== 'string') {
3640
3997
  wgsl = wgsl(...defaultVertexGeometryModifierArgs);
@@ -3661,6 +4018,7 @@ fn getVertexColor(index: u32) -> vec4f {
3661
4018
  }
3662
4019
  }
3663
4020
 
4021
+ /** Fallback options used by {@link getMeshVertexWgsl}: the standard `VERTEX_OUTPUT_LOCATION_*`/`VERTEX_BUFFER_INDEX_MESH_*` layout and the transform/camera/interaction uniform coordinates shared across mesh pipelines. */
3664
4022
  const DefaultMeshVertexOptions = {
3665
4023
  outputNormalLocation: VERTEX_OUTPUT_LOCATION_NORMAL,
3666
4024
  outputVertexLocation: VERTEX_OUTPUT_LOCATION_VERTEX,
@@ -3678,6 +4036,23 @@ fn getVertexColor(index: u32) -> vec4f {
3678
4036
  uniformInteractionBindingIndex: UNIFORM_BINDING_INDEX_INTERACTION,
3679
4037
  uniformInteractionGroupIndex: UNIFORM_GROUP_INDEX_INTERACTION,
3680
4038
  };
4039
+ /**
4040
+ * Composes the standard vertex shader used to draw mesh geometry: it transforms each vertex position by the
4041
+ * world/view/projection matrices, transforms the normal by the (precomputed) normal matrix, passes UVs through
4042
+ * unchanged, and computes an eye-to-vertex `ray` (normalized `worldPosition - camera.position`) plus the built-in
4043
+ * vertex/instance indices — everything the fragment shaders in this folder (see `basic-material-fragment.ts`,
4044
+ * `standard-material-fragment.ts`) expect to find on `VertexOutput`.
4045
+ *
4046
+ * Declares the `VertexOutput`/`VertexInput`/`Transforms`/`Camera`/`InteractionData` WGSL structs (using the
4047
+ * `WGSL_*_STRUCT_NAME` constants so downstream shaders can reference them by the same names), binds the
4048
+ * `transforms`/`camera`/`interaction` uniforms at the configured `@group`/`@binding` coordinates, and — if
4049
+ * `options.inputModifier`/`options.outputModifier` are supplied (raw WGSL or a {@link VertexInputModifierDelegate}/{@link VertexOutputModifierDelegate}) —
4050
+ * parses them via {@link extractMeshVertexModifierFunctionComponents} and inlines them as extra `fn` definitions
4051
+ * invoked before returning, letting user code rewrite the input geometry or tweak the computed output per-vertex.
4052
+ *
4053
+ * @param options Location/binding overrides and optional input/output WGSL modifiers; defaults to {@link DefaultMeshVertexOptions}.
4054
+ * @returns Complete WGSL vertex-shader source with a single `@vertex fn main` entry point returning `VertexOutput`.
4055
+ */
3681
4056
  function getMeshVertexWgsl(options = DefaultMeshVertexOptions) {
3682
4057
  if (options != DefaultMeshVertexOptions) {
3683
4058
  options = pacemCore.Utils.extend({}, DefaultMeshVertexOptions, options);
@@ -3769,6 +4144,7 @@ ${modifierFnOutput}
3769
4144
  }`;
3770
4145
  }
3771
4146
 
4147
+ /** Fallback options used by {@link getDefaultLineVertexWgsl}/{@link getFatLineVertexWgsl}: positions/dimensions read from {@link STORAGE_GROUP_INDEX_LINE}, transform/camera uniforms from the shared {@link UNIFORM_GROUP_INDEX_TRANSFORM}/{@link UNIFORM_GROUP_INDEX_CAMERA} coordinates. */
3772
4148
  const DefaultLineVertexOptions = {
3773
4149
  inputPositionLocation: VERTEX_BUFFER_INDEX_MESH_POSITION,
3774
4150
  outputInstanceIndexLocation: 0,
@@ -3782,6 +4158,7 @@ ${modifierFnOutput}
3782
4158
  uniformWorldGroupIndex: UNIFORM_GROUP_INDEX_TRANSFORM,
3783
4159
  uniformWorldBindingIndex: UNIFORM_BINDING_INDEX_TRANSFORM,
3784
4160
  };
4161
+ /** Builds the `VertexOutput` struct shared by both line vertex shaders below: clip-space `position` plus flat `instance`/`index` varyings, at the configured output locations, consumed by `line-material-fragment.ts`. */
3785
4162
  function getVertexOutputStructDefinition(opts) {
3786
4163
  return `struct ${WGSL_VERTEXOUTPUT_STRUCT_NAME} {
3787
4164
  @builtin(position) position : vec4f, // clip space position
@@ -3789,6 +4166,19 @@ ${modifierFnOutput}
3789
4166
  @location(${opts.outputVertexIndexLocation}) @interpolate(flat) index : u32, // vertex index
3790
4167
  }`;
3791
4168
  }
4169
+ /**
4170
+ * Composes the vertex shader for thin (native-line-width, 1px) line rendering: each vertex reads its `x`/`y`/`z`
4171
+ * directly out of a flattened `positions: array<f32>` storage buffer (three floats per point, indexed by the
4172
+ * built-in `vertex_index`) rather than a vertex-buffer attribute, then applies the usual world/view/projection
4173
+ * transform chain. `instance_index` is passed through unchanged so the fragment stage
4174
+ * (`line-material-fragment.ts`) can look up a per-vertex color.
4175
+ *
4176
+ * Declares `VertexOutput`/`Transforms`/`Camera` WGSL structs and binds `transforms`/`camera`/`positions` at the
4177
+ * configured `@group`/`@binding` coordinates.
4178
+ *
4179
+ * @param options Location/binding overrides; defaults to {@link DefaultLineVertexOptions}.
4180
+ * @returns Complete WGSL vertex-shader source with a single `@vertex fn main` entry point returning `VertexOutput`.
4181
+ */
3792
4182
  function getDefaultLineVertexWgsl(options = DefaultLineVertexOptions) {
3793
4183
  const opts = pacemCore.Utils.extend({}, DefaultLineVertexOptions, options);
3794
4184
  return `${getVertexOutputStructDefinition(opts)}
@@ -3825,6 +4215,25 @@ struct Camera {
3825
4215
  index);
3826
4216
  }`;
3827
4217
  }
4218
+ /**
4219
+ * Composes the vertex shader for "fat" (screen-space-thickness) line rendering: unlike
4220
+ * {@link getDefaultLineVertexWgsl}, it expects to be invoked with 6 vertices per line segment (a quad split into
4221
+ * two triangles, per the index-to-corner mapping documented in `computeOffsetPosition`'s inline diagram) and
4222
+ * offsets each corner perpendicular to the segment's screen-space direction by half of a uniform pixel width, so
4223
+ * the line renders at a constant on-screen thickness regardless of distance from the camera.
4224
+ *
4225
+ * `computeClipPosition` reads a point's `x`/`y`/`z` out of the flattened `positions: array<f32>` storage buffer
4226
+ * (as in {@link getDefaultLineVertexWgsl}) and transforms it to clip space; `computeOffsetPosition` then derives
4227
+ * the two endpoints' NDC direction, builds a perpendicular offset scaled by `dimensions.halfLineWidth` and the
4228
+ * viewport size, and picks/offsets the correct quad corner based on `vertex_index` (segment endpoint is
4229
+ * `instance_index`/`instance_index + 1`).
4230
+ *
4231
+ * Declares `VertexOutput`/`Transforms`/`Camera`/`Pixels` WGSL structs and binds `transforms`/`camera`/`positions`/`dimensions`
4232
+ * at the configured `@group`/`@binding` coordinates (`dimensions` carries `viewportWidth`, `viewportHeight` and `halfLineWidth` in pixels).
4233
+ *
4234
+ * @param options Location/binding overrides; defaults to {@link DefaultLineVertexOptions}.
4235
+ * @returns Complete WGSL vertex-shader source with a single `@vertex fn main` entry point returning `VertexOutput`.
4236
+ */
3828
4237
  function getFatLineVertexWgsl(options = DefaultLineVertexOptions) {
3829
4238
  const opts = pacemCore.Utils.extend({}, DefaultLineVertexOptions, options);
3830
4239
  return `${getVertexOutputStructDefinition(opts)}
@@ -3942,7 +4351,17 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
3942
4351
 
3943
4352
  //namespace Pacem.Drawing3D.WebGPU {
3944
4353
  /**
3945
- * Generate a WGSL piece of code that eases 3D picking using color strategy.
4354
+ * Composes the fragment shader used by the {@link Raycaster} off-screen pass for GPU-based, color-coded object
4355
+ * picking: instead of computing a lit color, it simply outputs the renderable's own numeric `id` (bound as a
4356
+ * `ColorPicker` uniform) into a single-channel `r32uint` render target, so the CPU side can later read back the
4357
+ * pixel under the pointer and resolve it to the {@link Renderable} that was drawn there.
4358
+ *
4359
+ * Declares the `ColorPicker` struct (`id: u32`) and its `var<uniform>` binding at `@binding(0)` within the given
4360
+ * `@group`; unlike the material fragments in this folder it takes no {@link WGSLFragmentExtra} — it is not meant to
4361
+ * be extended with lighting/texture snippets.
4362
+ *
4363
+ * @param groupIndex `@group` index at which the per-renderable `ColorPicker` uniform is bound; defaults to {@link UNIFORM_GROUP_INDEX_COLORPICKING}.
4364
+ * @returns Complete WGSL fragment-shader source with a single `@fragment fn main` entry point returning `@location(0) u32`.
3946
4365
  */
3947
4366
  function getColorPickingFragmentWgsl(groupIndex = UNIFORM_GROUP_INDEX_COLORPICKING) {
3948
4367
  return `struct ColorPicker {
@@ -3958,9 +4377,25 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
3958
4377
  `;
3959
4378
  }
3960
4379
 
4380
+ /** Fallback references used by {@link textureMap} when not overridden: reads UV coordinates from the `uv` varying. */
3961
4381
  const DefaultTextureMapReferences = {
3962
4382
  uv: 'uv',
3963
4383
  };
4384
+ /**
4385
+ * Builds a {@link WGSLFragmentExtra} that samples a 2D texture and tints it by a base color: `textureSample(tex, sampler, uv) * inputColor`.
4386
+ * Declares a fresh `sampler`/`texture_2d<f32>` pair (uniquely named per call via {@link Utils.uniqueCode}) as `bindings`
4387
+ * at consecutive `@binding` indices (`binding` for the sampler, `binding + 1` for the texture) within the given
4388
+ * `@group`, and emits no `definitions`.
4389
+ *
4390
+ * As `modifiers`, either a single bare sample-and-multiply expression (single consumer), or, when
4391
+ * `references.outputColors` lists multiple output variable names, one assignment per name — the first computes the
4392
+ * sample, the rest copy it — so several downstream color channels (e.g. diffuse and ambient) can share one texture fetch.
4393
+ *
4394
+ * @param references WGSL identifiers for the UV coordinates (`uv`, defaults to `'uv'`), the base color to tint (`inputColor`, required) and, optionally, one or more output variable names (`outputColors`) to assign the sampled color to.
4395
+ * @param group `@group` index at which the generated sampler/texture pair is bound.
4396
+ * @param binding `@binding` index (within `group`) for the sampler; the texture is bound at `binding + 1`. Defaults to {@link SAMPLER_BINDING_INDEX}.
4397
+ * @returns Fragment-shader `bindings`/`modifiers` meant to be spliced into a material fragment shader (see {@link getBasicMaterialFragmentWgsl}/{@link getStandardMaterialFragmentWgsl}).
4398
+ */
3964
4399
  function textureMap(references = DefaultTextureMapReferences, group, binding = SAMPLER_BINDING_INDEX) {
3965
4400
  const key = pacemCore.Utils.uniqueCode();
3966
4401
  const samplerReference = `sampler_${key}`;
@@ -3984,9 +4419,27 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
3984
4419
  };
3985
4420
  }
3986
4421
 
4422
+ /** Fallback references used by {@link directionalLight} when not overridden: reads the world-space `normal` varying and treats the base color as `white`. */
3987
4423
  const DefaultDirectionalLightReferences = {
3988
4424
  normal: 'normal', inputColor: 'white'
3989
4425
  };
4426
+ /**
4427
+ * Builds a {@link WGSLFragmentExtra} implementing a directional (sun-like, parallel-rays) light: a Lambertian
4428
+ * diffuse term computed from the dot product between the surface normal and the (negated, normalized) light
4429
+ * direction, modulated by the light's color and intensity. Unlike {@link omniLight}, it carries no position/falloff
4430
+ * or specular term.
4431
+ *
4432
+ * Emits, as `definitions`, a `DirectionalLight` struct (`color: vec4f`, `direction: vec3f`, `intensity: f32`) and a
4433
+ * `computeDirectional` helper function; as `bindings`, a `var<uniform>` declaration for the light data at the given
4434
+ * `@group`/`@binding` coordinates; and, as `modifiers`, either a statement that accumulates the contribution into
4435
+ * `references.outputColor` (`+=`) when provided, or a bare expression otherwise (for single-light use sites that
4436
+ * assign it directly).
4437
+ *
4438
+ * @param references WGSL identifiers for the surface normal (`normal`), the base color to shade (`inputColor`) and, optionally, the color variable to accumulate into (`outputColor`).
4439
+ * @param group `@group` index at which the generated `DirectionalLight` uniform is bound.
4440
+ * @param binding `@binding` index (within `group`) at which the generated `DirectionalLight` uniform is bound.
4441
+ * @returns Fragment-shader `definitions`/`bindings`/`modifiers` meant to be spliced into a material fragment shader (see {@link getBasicMaterialFragmentWgsl}/{@link getStandardMaterialFragmentWgsl}).
4442
+ */
3990
4443
  function directionalLight(references, group, binding) {
3991
4444
  const key = pacemCore.Utils.uniqueCode();
3992
4445
  const lightReference = `directional_${key}`;
@@ -4015,9 +4468,27 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
4015
4468
  };
4016
4469
  }
4017
4470
 
4471
+ /** Fallback references used by {@link omniLight} when not overridden: reads the world-space `normal`/`vertex`/`ray` varyings, treats the base color as `white` and disables the specular term (`empty` color, `0` shininess). */
4018
4472
  const DefaultOmniLightReferences = {
4019
4473
  normal: 'normal', position: 'vertex', ray: 'ray', inputColor: 'white', specularColor: 'empty', shininess: '0'
4020
4474
  };
4475
+ /**
4476
+ * Builds a {@link WGSLFragmentExtra} implementing an omnidirectional (point) light with a Blinn-Phong-like
4477
+ * specular term: a Lambertian diffuse factor from the normal-to-light direction (light position minus surface
4478
+ * position, i.e. no distance falloff), plus a specular highlight from the half-vector between the light direction
4479
+ * and the eye-to-vertex ray, scaled by `shininess`. Unlike {@link directionalLight}, the light has a `position` in
4480
+ * world space rather than a constant direction.
4481
+ *
4482
+ * Emits, as `definitions`, an `OmniLight` struct (`color: vec4f`, `position: vec3f`, `intensity: f32`) and a
4483
+ * `computeOmni` helper function; as `bindings`, a `var<uniform>` declaration for the light data at the given
4484
+ * `@group`/`@binding` coordinates; and, as `modifiers`, either a statement that accumulates the contribution into
4485
+ * `references.outputColor` (`+=`) when provided, or a bare expression otherwise.
4486
+ *
4487
+ * @param references WGSL identifiers for the surface normal (`normal`), world-space position (`position`), eye-to-vertex ray (`ray`), base color (`inputColor`), specular color (`specularColor`), shininess factor (`shininess`) and, optionally, the color variable to accumulate into (`outputColor`).
4488
+ * @param group `@group` index at which the generated `OmniLight` uniform is bound.
4489
+ * @param binding `@binding` index (within `group`) at which the generated `OmniLight` uniform is bound.
4490
+ * @returns Fragment-shader `definitions`/`bindings`/`modifiers` meant to be spliced into a material fragment shader (see {@link getBasicMaterialFragmentWgsl}/{@link getStandardMaterialFragmentWgsl}).
4491
+ */
4021
4492
  function omniLight(references, group, binding) {
4022
4493
  const key = pacemCore.Utils.uniqueCode();
4023
4494
  const lightReference = `omni_${key}`;
@@ -4054,6 +4525,20 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
4054
4525
  };
4055
4526
  }
4056
4527
 
4528
+ /**
4529
+ * Builds a {@link WGSLFragmentExtra} implementing the ambient light contribution to a fragment shader: it multiplies the
4530
+ * fragment's base color by a uniform light color/intensity, uniformly across the surface (no dependency on normal or
4531
+ * view direction, unlike {@link directionalLight}/{@link omniLight}).
4532
+ *
4533
+ * Emits, as `definitions`, an `AmbientLight` struct (`color: vec4f`, `intensity: f32`) and a `computeAmbient` helper
4534
+ * function; as `bindings`, a `var<uniform>` declaration for the light data at the given `@group`/`@binding`
4535
+ * coordinates; and, as `modifiers`, a statement assigning the computed color into `references.outputColor`.
4536
+ *
4537
+ * @param references WGSL identifiers for the color to shade (`inputColor`) and the variable to assign the ambient-lit result to (`outputColor`). Both are required.
4538
+ * @param group `@group` index at which the generated `AmbientLight` uniform is bound.
4539
+ * @param binding `@binding` index (within `group`) at which the generated `AmbientLight` uniform is bound.
4540
+ * @returns Fragment-shader `definitions`/`bindings`/`modifiers` meant to be spliced into a material fragment shader (see {@link getBasicMaterialFragmentWgsl}/{@link getStandardMaterialFragmentWgsl}).
4541
+ */
4057
4542
  function ambientLight(references, group, binding) {
4058
4543
  const key = pacemCore.Utils.uniqueCode();
4059
4544
  const lightReference = `ambient_${key}`;
@@ -4080,6 +4565,11 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
4080
4565
  const Vector3D$3 = pacemNumerical.Geometry.LinearAlgebra.Vector3D;
4081
4566
  const Matrix3D$2 = pacemNumerical.Geometry.LinearAlgebra.Matrix3D;
4082
4567
  const DEFAULT_SIZE = { width: 1, height: 1 };
4568
+ /**
4569
+ * Factory that converts a scene-facing {@link Camera3D} (`../../types.ts`) into its WebGPU-side counterpart
4570
+ * ({@link Camera}, `abstractions.ts`): either a {@link PerspectiveCamera} or an {@link OrthographicCamera}, exposing
4571
+ * `view()`/`projection()` matrices consumed when building the camera's uniform buffer (see `renderablebuffer.ts`).
4572
+ */
4083
4573
  class Cameras {
4084
4574
  /**
4085
4575
  * Creates a new webgpu {@link Camera} given a canonic one.
@@ -4153,13 +4643,24 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
4153
4643
  assertNearFarCorrectness(near, far);
4154
4644
  return Matrix3D$2.from(2 / (right - left), 0, 0, 0, 0, 2 / (top - bottom), 0, 0, 0, 0, 1 / (near - far), 0, (left + right) / (left - right), (top + bottom) / (bottom - top), near / (near - far), 1);
4155
4645
  }
4646
+ /**
4647
+ * WebGPU-side counterpart of an {@link OrthographicCamera3D}: computes the view matrix from `position`/`lookAt`/`up`,
4648
+ * and an orthographic projection matrix from the camera's `left`/`right`/`top`/`bottom`/`near`/`far` planes, scaled
4649
+ * by the eye-to-target distance and (if given) the viewport aspect ratio.
4650
+ */
4156
4651
  class OrthographicCamera {
4157
4652
  constructor(_camera) {
4158
4653
  this._camera = _camera;
4159
4654
  }
4655
+ /** Computes the view matrix for `camera`; see {@link view} for a bound instance-method equivalent. */
4160
4656
  static view(camera) {
4161
4657
  return view(camera.position, camera.lookAt, camera.up);
4162
4658
  }
4659
+ /**
4660
+ * Computes the orthographic projection matrix for `camera`.
4661
+ * @param camera Source orthographic camera.
4662
+ * @param size Viewport size, used to correct the projected frustum for aspect ratio; defaults to a 1:1 viewport.
4663
+ */
4163
4664
  static projection(camera, size = DEFAULT_SIZE) {
4164
4665
  const eye = camera.position, target = camera.lookAt;
4165
4666
  const z = Vector3D$3.subtract(target, eye);
@@ -4173,27 +4674,42 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
4173
4674
  const right = camera.right * w, left = camera.left * w;
4174
4675
  return orthographic(left, right, top, bottom, camera.near, camera.far);
4175
4676
  }
4677
+ /** Computes the view matrix for the wrapped camera. See {@link Camera.view}. */
4176
4678
  view() {
4177
4679
  return OrthographicCamera.view(this._camera);
4178
4680
  }
4681
+ /** Computes the orthographic projection matrix for the wrapped camera. See {@link Camera.projection}. */
4179
4682
  projection(size) {
4180
4683
  return OrthographicCamera.projection(this._camera, size);
4181
4684
  }
4182
4685
  }
4686
+ /**
4687
+ * WebGPU-side counterpart of a {@link PerspectiveCamera3D}: computes the view matrix from `position`/`lookAt`/`up`,
4688
+ * and a perspective projection matrix from the camera's vertical field of view, aspect ratio and near/far clip planes.
4689
+ */
4183
4690
  class PerspectiveCamera {
4184
4691
  constructor(_camera) {
4185
4692
  this._camera = _camera;
4186
4693
  }
4694
+ /** Computes the view matrix for `camera`; see {@link view} for a bound instance-method equivalent. */
4187
4695
  static view(camera) {
4188
4696
  return view(camera.position, camera.lookAt, camera.up);
4189
4697
  }
4698
+ /**
4699
+ * Computes the perspective projection matrix for `camera`, combining its `aspectRatio` with `size`'s
4700
+ * (`size.width / size.height`) to correct for non-square viewports.
4701
+ * @param camera Source perspective camera.
4702
+ * @param size Viewport size, used to correct for aspect ratio; defaults to a 1:1 viewport.
4703
+ */
4190
4704
  static projection(camera, size = DEFAULT_SIZE) {
4191
4705
  const aspectRatio = (size.width / size.height) / camera.aspectRatio;
4192
4706
  return perspective(camera.fov, aspectRatio, camera.near, camera.far);
4193
4707
  }
4708
+ /** Computes the view matrix for the wrapped camera. See {@link Camera.view}. */
4194
4709
  view() {
4195
4710
  return PerspectiveCamera.view(this._camera);
4196
4711
  }
4712
+ /** Computes the perspective projection matrix for the wrapped camera. See {@link Camera.projection}. */
4197
4713
  projection(size) {
4198
4714
  return PerspectiveCamera.projection(this._camera, size);
4199
4715
  }
@@ -4228,6 +4744,13 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
4228
4744
  }
4229
4745
  throw new Error(`Renderable is not supported.`);
4230
4746
  }
4747
+ /**
4748
+ * Drives one render (or raycast) pass over a fixed snapshot of the scene: iterates the meshes accumulated in a
4749
+ * {@link RenderableBufferSet}, invoking a callback for each mesh together with the shared camera and lights, and
4750
+ * seals (clears the stale-property flags of) every buffer that participated in a successful draw. Rebuilt by
4751
+ * `renderer.ts`'s `RendererClass` whenever the scene graph changes (see `update`), and driven once per frame by
4752
+ * `RendererClass.render`/`RendererClass.raycast`.
4753
+ */
4231
4754
  class RenderLooper {
4232
4755
  constructor(_set) {
4233
4756
  this._set = _set;
@@ -4236,6 +4759,13 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
4236
4759
  get items() {
4237
4760
  return this._set;
4238
4761
  }
4762
+ /**
4763
+ * Invokes `render(mesh, camera, lights)` for every non-disposed mesh buffer in the set, skipping meshes for
4764
+ * which `render` returns `false` (e.g. hidden items). After each successful call, clears the
4765
+ * {@link StalePropertyFlag}s on the mesh buffer and, the first time each occurs, on the shared camera/light
4766
+ * buffers; group buffers are always sealed at the end regardless of whether any mesh rendered.
4767
+ * @param render Callback invoked once per mesh with its buffer, the (single) camera buffer and all light buffers; return `false` to skip that mesh (its flags are left untouched).
4768
+ */
4239
4769
  loop(render) {
4240
4770
  const { meshes, cameras, groups, lights } = this._set;
4241
4771
  const camera = cameras[0];
@@ -4266,7 +4796,18 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
4266
4796
  }
4267
4797
  }
4268
4798
  }
4799
+ /**
4800
+ * Factory for {@link ManagedBuffer}s wrapping non-{@link Renderable} scene data — geometry ({@link NodeGeometry}/
4801
+ * {@link MeshGeometry}), material ({@link Material3D}) and interaction (pointer/time/viewport) state — so this data
4802
+ * can be shared and cached across the {@link RenderableBuffer}s that reference it (e.g. a geometry instance reused by
4803
+ * several meshes), rather than re-uploaded to the GPU per mesh.
4804
+ */
4269
4805
  class ManagedBuffers {
4806
+ /**
4807
+ * Wraps `item` in the {@link ManagedBuffer} implementation matching its runtime type.
4808
+ * @param item A geometry ({@link NodeGeometry}/{@link MeshGeometry}), material ({@link Material3D}) or {@link Interaction} instance.
4809
+ * @throws {Error} If `item`'s type isn't recognized.
4810
+ */
4270
4811
  static create(item) {
4271
4812
  if (isGeometry(item)) {
4272
4813
  item.key ??= 'geometry_' + pacemCore.Utils.uniqueCode();
@@ -4281,10 +4822,28 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
4281
4822
  throw new Error(`Object is not supported.`);
4282
4823
  }
4283
4824
  }
4825
+ /**
4826
+ * Factory and stale-flag utilities for {@link RenderableBuffer}s wrapping {@link Renderable} scene items (groups,
4827
+ * meshes, cameras, lights). Callers (see `renderer.ts`'s `RendererClass`) cache one buffer per item across frames, so
4828
+ * the underlying GPU resources persist until the item is removed from the scene.
4829
+ */
4284
4830
  class RenderableBuffers {
4831
+ /**
4832
+ * Wraps `item` in the {@link RenderableBuffer} implementation matching its kind (group/mesh/camera/light),
4833
+ * recursively wrapping a group's children.
4834
+ * @param item The scene renderable to wrap.
4835
+ * @param args Extra constructor arguments forwarded to mesh buffers (the shared geometry/material {@link ManagedBuffer} caches; see `renderer.ts`).
4836
+ * @throws {Error} If `item`'s type isn't recognized.
4837
+ */
4285
4838
  static create(item, ...args) {
4286
4839
  return createRenderableBuffer(item, null, ...args);
4287
4840
  }
4841
+ /**
4842
+ * Clears the {@link StalePropertyFlag}s on `buffer`'s underlying item, marking it as up to date until the next
4843
+ * scene-graph mutation flags it again.
4844
+ * @param buffer The buffer whose item's flags should be cleared.
4845
+ * @param includeParent Whether to also clear the parent group buffer's flags, recursively up the hierarchy.
4846
+ */
4288
4847
  static clearFlags(buffer, includeParent = false) {
4289
4848
  buffer.item.flags.splice(0);
4290
4849
  if (includeParent) {
@@ -4294,6 +4853,11 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
4294
4853
  }
4295
4854
  }
4296
4855
  }
4856
+ /**
4857
+ * Returns the union of `buffer`'s own {@link StalePropertyFlag}s with those of all its ancestor group buffers —
4858
+ * the effective set of "what changed" a re-render of this item needs to account for.
4859
+ * @param buffer The buffer to compute the effective flag set for.
4860
+ */
4297
4861
  static allFlags(buffer) {
4298
4862
  const bufferBase = buffer;
4299
4863
  const parentBufferBase = bufferBase.parent;
@@ -5046,8 +5610,17 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
5046
5610
  }
5047
5611
  };
5048
5612
  const EMPTY_BIND_DELEGATE = (_) => { };
5613
+ /**
5614
+ * Factory for {@link RenderPipeline}s: given a {@link Context} and a set of {@link RenderableBuffer}s (a mesh/line plus its
5615
+ * camera and lights), builds the WGSL shaders, {@link GPUBindGroupLayout}s, {@link GPUPipelineLayout} and the underlying
5616
+ * {@link GPURenderPipeline}(s) needed to draw (and, separately, color-pick/raycast) that combination. Built pipelines are
5617
+ * memoized per {@link Context} and reused as long as the shader "recipe" (mesh material/shader, light count/types, WGSL
5618
+ * modifier version) doesn't change. This is the machinery {@link Pacem3DWebgpuAdapterElement}'s {@link Renderer} implementation
5619
+ * (`renderer.ts`) drives every frame.
5620
+ */
5049
5621
  class RenderPipelines {
5050
5622
  static { this._memoizer = new WeakMap(); }
5623
+ /** Clears the {@link RenderPipeline} cache associated with the given {@link Context} (e.g. on device loss/adapter teardown). Does not explicitly destroy the underlying GPU objects — they're released with the {@link GPUDevice}. */
5051
5624
  static dispose(ctx) {
5052
5625
  const memoizer = this._memoizer;
5053
5626
  const cache = memoizer.get(ctx);
@@ -5056,6 +5629,12 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
5056
5629
  memoizer.delete(ctx);
5057
5630
  }
5058
5631
  }
5632
+ /**
5633
+ * Builds (or reuses, via {@link create}'s cache) the render pipeline for `items` and wraps it as a color-picking-only
5634
+ * {@link RenderPipeline}, whose `frame` delegate draws object ids into an `r32uint` target instead of the normal color target.
5635
+ * Returns `null` if the resolved pipeline isn't a mesh or line pipeline (i.e. doesn't support raycasting).
5636
+ * @param items The mesh/line, camera and lights to raycast, in the same shape expected by {@link create}.
5637
+ */
5059
5638
  static createColorPicking(ctx, items) {
5060
5639
  const renderPipeline = RenderPipelines.create(ctx, items);
5061
5640
  if (renderPipeline instanceof MeshRenderPipelineClass
@@ -5066,6 +5645,20 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
5066
5645
  }
5067
5646
  return null;
5068
5647
  }
5648
+ /**
5649
+ * Builds (or returns the memoized) {@link RenderPipeline} for the given renderables: the first buffer must wrap a
5650
+ * {@link Mesh} (with either a {@link MeshGeometry} or a {@link LineMaterial}), followed by its {@link Camera} buffer
5651
+ * and zero or more {@link Light} buffers. Generates the mesh/line's WGSL vertex and fragment shaders (via
5652
+ * {@link MeshWGSLBuilder}/{@link LineWGSLBuilder}), compiles them into {@link GPUShaderModule}s, creates the
5653
+ * {@link GPUBindGroupLayout}s/{@link GPUPipelineLayout} and the resulting {@link GPURenderPipeline} (plus a matching
5654
+ * color-picking pipeline). The pipeline is cached per {@link Context} keyed by shader "recipe" + WGSL modifiers version,
5655
+ * so repeated calls with an equivalent recipe skip rebuilding.
5656
+ * @param ctx WebGPU context (device/canvas/format) to build the pipeline against.
5657
+ * @param items `[mesh, camera, ...lights]` renderable buffers.
5658
+ * @param pipelineOptions Optional overrides for primitive topology, multisampling, vertex packing and WGSL modifiers.
5659
+ * @returns The built (or cached) {@link RenderPipeline}.
5660
+ * @throws {Error} If no mesh is provided, the mesh's geometry/material combination isn't supported, or the first argument isn't a mesh.
5661
+ */
5069
5662
  static create(ctx, [mesh, camera, ...lights], pipelineOptions) {
5070
5663
  const memoizer = RenderPipelines._memoizer;
5071
5664
  let cache = memoizer.get(ctx);
@@ -6206,6 +6799,12 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
6206
6799
  // #endregion
6207
6800
 
6208
6801
  //namespace Pacem.Drawing3D.WebGPU {
6802
+ /**
6803
+ * Factory/registry for {@link Raycaster}s: builds (or returns the memoized) color-picking {@link Raycaster} for a
6804
+ * given {@link Context}, used by the {@link Renderer}'s `raycast` implementation (`renderer.ts`) to hit-test pointer
6805
+ * clicks/hovers against rendered items. Raycasters are memoized per {@link Context} so their off-screen render
6806
+ * target/readback buffer are reused across calls instead of being reallocated every raycast.
6807
+ */
6209
6808
  class Raycasters {
6210
6809
  // learn from:
6211
6810
  // https://webglfundamentals.org/webgl/lessons/webgl-qna-how-to-get-the-3d-coordinates-of-a-mouse-click.html
@@ -6213,6 +6812,7 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
6213
6812
  // https://shi-yan.github.io/webgpuunleashed/Control/object_picking.html
6214
6813
  // https://gist.github.com/greggman/57c3c0d3cf1db14f1baad2d9b0094397 (color picking)
6215
6814
  static { this._memoizer = new WeakMap(); }
6815
+ /** Destroys and clears all {@link Raycaster}s memoized for `ctx` (e.g. on device loss/adapter teardown). */
6216
6816
  static dispose(ctx) {
6217
6817
  const memoizer = this._memoizer;
6218
6818
  if (memoizer.has(ctx)) {
@@ -6397,12 +6997,28 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
6397
6997
 
6398
6998
  //namespace Pacem.Drawing3D.WebGPU {
6399
6999
  const Vector3D$1 = pacemNumerical.Geometry.LinearAlgebra.Vector3D;
7000
+ /** Reason a WebGPU {@link Renderer} failed to initialize, as reported via a rejected {@link Renderers.create} promise (see {@link WebGPUIssue}). */
6400
7001
  var WebGPUIssueCause;
6401
7002
  (function (WebGPUIssueCause) {
7003
+ /** `navigator.gpu` is unavailable — the browser/OS doesn't support WebGPU at all. */
6402
7004
  WebGPUIssueCause[WebGPUIssueCause["NotSupported"] = 0] = "NotSupported";
7005
+ /** WebGPU is supported, but no {@link GPUAdapter}/{@link GPUDevice} could be obtained (e.g. disabled via flags, denied, or hardware unsupported). */
6403
7006
  WebGPUIssueCause[WebGPUIssueCause["Disabled"] = 1] = "Disabled";
6404
7007
  })(WebGPUIssueCause || (WebGPUIssueCause = {}));
7008
+ /**
7009
+ * Factory for the WebGPU {@link Renderer} consumed by {@link Pacem3DWebgpuAdapterElement}. Requests a
7010
+ * {@link GPUAdapter}/{@link GPUDevice}, configures the stage canvas's {@link GPUCanvasContext}, and wraps everything
7011
+ * in a {@link Renderer} implementation that owns: the per-frame render/raycast loop (via {@link RenderLooper}), the
7012
+ * mesh/geometry/material buffer caches ({@link RenderableBuffers}/{@link ManagedBuffers}), the multisample/depth
7013
+ * render targets, and pointer hit-testing ({@link Raycasters}).
7014
+ */
6405
7015
  class Renderers {
7016
+ /**
7017
+ * Creates and asynchronously initializes a WebGPU {@link Renderer} for `stage`'s canvas.
7018
+ * @param stage The owning `<pacem-3d>` element whose wrapper hosts the render canvas.
7019
+ * @param config Whether to enable per-frame WebGPU validation error scopes (a perf hog — debug builds only) and optional WGSL shader modifiers.
7020
+ * @returns A promise resolving to the ready {@link Renderer}, or rejecting with a {@link WebGPUIssue} if WebGPU isn't supported or no adapter/device could be obtained.
7021
+ */
6406
7022
  static create(stage, config) {
6407
7023
  return new Promise((resolve, reject) => {
6408
7024
  if (!navigator.gpu) {
@@ -6834,11 +7450,13 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
6834
7450
  const WEBGPU_RAYCAST_CALLBACK_PROP = 'WebGPURaycastCallback';
6835
7451
  const WEBGPU_RAYCAST_PIXEL_PROP = 'WebGPURaycastPixel';
6836
7452
  const GETVAL = pacemCore.CustomElementUtils.getAttachedPropertyValue, SETVAL = pacemCore.CustomElementUtils.setAttachedPropertyValue, DELVAL = pacemCore.CustomElementUtils.deleteAttachedPropertyValue;
7453
+ /** `<pacem-3d-wgsl-script>`: declares a WGSL snippet (vertex input/output or fragment stage) that a parent {@link Pacem3DWebgpuAdapterElement} splices into its render pipeline shaders, tracked via a content {@link version} hash. */
6837
7454
  let Pacem3DWgslScriptElement = class Pacem3DWgslScriptElement extends pacemCore.Components.PacemItemElement {
6838
7455
  findContainer() {
6839
7456
  return pacemCore.CustomElementUtils.findAncestor(this, n => n instanceof Pacem3DWebgpuAdapterElement);
6840
7457
  }
6841
7458
  #version = '';
7459
+ /** @readonly Gets a hash of the current {@link wgsl} content, changing whenever it does. */
6842
7460
  get version() {
6843
7461
  return this.#version;
6844
7462
  }
@@ -6881,6 +7499,12 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
6881
7499
  Pacem3DWgslScriptElement = __decorate$2([
6882
7500
  pacemCore.CustomElement({ tagName: pacemCore.P + '-3d-wgsl-script' })
6883
7501
  ], Pacem3DWgslScriptElement);
7502
+ /**
7503
+ * `<pacem-3d-webgpu-adapter>`: {@link Pacem3DAdapterElement} rendering backend built directly on the browser's
7504
+ * WebGPU API. Manages one {@link Renderer} per stage it is assigned to, hosts child {@link Pacem3DWgslScriptElement}
7505
+ * shader customizations (via {@link modifiers}), and falls back gracefully (see {@link supported}/{@link active})
7506
+ * when WebGPU isn't available.
7507
+ */
6884
7508
  let Pacem3DWebgpuAdapterElement = class Pacem3DWebgpuAdapterElement extends Pacem3DAdapterElement {
6885
7509
  constructor() {
6886
7510
  super(...arguments);
@@ -6902,9 +7526,11 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
6902
7526
  renderer?.resize(size);
6903
7527
  }
6904
7528
  #items;
7529
+ /** @readonly Gets the child {@link Pacem3DWgslScriptElement} shader-customization items currently registered. */
6905
7530
  get items() {
6906
7531
  return this.#items;
6907
7532
  }
7533
+ /** Registers a child {@link Pacem3DWgslScriptElement}, re-parsing the WGSL {@link modifiers} it contributes. */
6908
7534
  register(item) {
6909
7535
  const items = this.#items;
6910
7536
  if (item instanceof Pacem3DWgslScriptElement && !items.includes(item)) {
@@ -6915,6 +7541,7 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
6915
7541
  }
6916
7542
  return false;
6917
7543
  }
7544
+ /** Unregisters a previously-registered {@link Pacem3DWgslScriptElement}, re-parsing the WGSL {@link modifiers} accordingly. */
6918
7545
  unregister(item) {
6919
7546
  const items = this.#items;
6920
7547
  const ndx = items.indexOf(item);
@@ -6930,9 +7557,11 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
6930
7557
  #renderers;
6931
7558
  #supported;
6932
7559
  #active;
7560
+ /** @readonly Gets whether WebGPU is supported by the current browser/device. */
6933
7561
  get supported() {
6934
7562
  return this.#supported;
6935
7563
  }
7564
+ /** @readonly Gets whether the adapter is currently able to render (false once a {@link WebGPUIssue} disables it). */
6936
7565
  get active() {
6937
7566
  return this.#active;
6938
7567
  }
@@ -7395,6 +8024,11 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
7395
8024
  scene.removeEventListener('render', this._renderHandler, false);
7396
8025
  }
7397
8026
  }
8027
+ /**
8028
+ * `<pacem-3d-orbit-camera>`: a behavior element that, applied to a {@link Pacem3DCameraElement}, attaches pointer/wheel
8029
+ * driven orbit (rotate/pan/zoom around a target) interaction by instantiating and configuring an
8030
+ * {@link OrbitCameraBehavior} for each decorated element.
8031
+ */
7398
8032
  let PacemOrbitCameraBehaviorElement = class PacemOrbitCameraBehaviorElement extends pacemCore.Behaviors.PacemBehavior {
7399
8033
  constructor() {
7400
8034
  super(...arguments);
@@ -7575,7 +8209,12 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
7575
8209
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7576
8210
  };
7577
8211
  //namespace Pacem.Drawing3D {
8212
+ /** Parses the text content of a Wavefront `.obj` file into a {@link MeshGeometry} (vertices, triangle indices, UVs and normals). */
7578
8213
  class OBJParser {
8214
+ /**
8215
+ * Parses `v` (vertex), `vt` (texture coordinate), `vn` (normal) and `f` (face) records out of `content`,
8216
+ * triangulating any face with more than 3 vertices (fan triangulation).
8217
+ */
7579
8218
  static parse(content) {
7580
8219
  // vertices
7581
8220
  const vertices = [];
@@ -7648,7 +8287,12 @@ fn computeOffsetPosition(p0: vec4f, p1: vec4f, index: u32) -> vec4f {
7648
8287
  return new MeshGeometry(vertices, positions, uv, normals);
7649
8288
  }
7650
8289
  }
8290
+ /** Model-file parsing entry point, registered as the `parse3D` markup transformer for converting raw asset text into a {@link NodeGeometry} or {@link Material}. */
7651
8291
  class Parser3D {
8292
+ /**
8293
+ * Parses `content` according to `type` into a {@link NodeGeometry} (currently only the `'obj'` Wavefront format,
8294
+ * via {@link OBJParser}) or a {@link Material}. Throws for any other/unsupported `type` (e.g. `'mtl'`, not yet implemented).
8295
+ */
7652
8296
  static parseGeometry(content, type) {
7653
8297
  switch (type.toLowerCase()) {
7654
8298
  case 'obj':