@expofp/renderer 3.2.5 → 3.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/README.md +111 -0
  2. package/dist/Renderer.d.ts +88 -0
  3. package/dist/Renderer.js +53 -0
  4. package/dist/controls/CameraControls.d.ts +102 -0
  5. package/dist/controls/CameraControls.js +239 -0
  6. package/dist/controls/camera-api.d.ts +206 -0
  7. package/dist/controls/camera-api.js +181 -0
  8. package/dist/controls/camera-move.d.ts +13 -0
  9. package/dist/controls/camera-move.js +18 -0
  10. package/dist/controls/camera-state.d.ts +40 -0
  11. package/dist/controls/camera-state.js +65 -0
  12. package/dist/controls/gestures.d.ts +70 -0
  13. package/dist/controls/gestures.js +84 -0
  14. package/dist/controls/handlers/Inertia.d.ts +111 -0
  15. package/dist/controls/handlers/Inertia.js +215 -0
  16. package/dist/controls/handlers/Pan.d.ts +23 -0
  17. package/dist/controls/handlers/Pan.js +32 -0
  18. package/dist/controls/handlers/Pitch.d.ts +94 -0
  19. package/dist/controls/handlers/Pitch.js +191 -0
  20. package/dist/controls/handlers/Roll.d.ts +103 -0
  21. package/dist/controls/handlers/Roll.js +182 -0
  22. package/dist/controls/handlers/Zoom.d.ts +15 -0
  23. package/dist/controls/handlers/Zoom.js +26 -0
  24. package/dist/controls/index.d.ts +56 -0
  25. package/dist/controls/index.js +58 -0
  26. package/dist/controls/zoom-to.d.ts +89 -0
  27. package/dist/controls/zoom-to.js +91 -0
  28. package/dist/core/InstanceUniforms.d.ts +93 -0
  29. package/dist/core/InstanceUniforms.js +266 -0
  30. package/dist/core/SquareDataTexture.d.ts +277 -0
  31. package/dist/core/SquareDataTexture.js +476 -0
  32. package/dist/core/batch/BatchedMesh.d.ts +74 -0
  33. package/dist/core/batch/BatchedMesh.js +149 -0
  34. package/dist/core/instanced/FrustumCulling.d.ts +141 -0
  35. package/dist/core/instanced/FrustumCulling.js +246 -0
  36. package/dist/core/instanced/InstancedMesh.d.ts +302 -0
  37. package/dist/core/instanced/InstancedMesh.js +497 -0
  38. package/dist/core/instanced/Instances.d.ts +119 -0
  39. package/dist/core/instanced/Instances.js +230 -0
  40. package/dist/core/instanced/Raycasting.d.ts +118 -0
  41. package/dist/core/instanced/Raycasting.js +134 -0
  42. package/dist/core/instanced/instanced-material.d.ts +77 -0
  43. package/dist/core/instanced/instanced-material.js +224 -0
  44. package/dist/core/lines/BatchedLines.d.ts +135 -0
  45. package/dist/core/lines/BatchedLines.js +213 -0
  46. package/dist/core/lines/FrustumCulling.d.ts +43 -0
  47. package/dist/core/lines/FrustumCulling.js +85 -0
  48. package/dist/core/lines/Raycasting.d.ts +44 -0
  49. package/dist/core/lines/Raycasting.js +91 -0
  50. package/dist/core/lines/lines-material.d.ts +47 -0
  51. package/dist/core/lines/lines-material.js +115 -0
  52. package/dist/core/shader-source.d.ts +50 -0
  53. package/dist/core/shader-source.js +84 -0
  54. package/dist/core/text/BatchedText.d.ts +227 -0
  55. package/dist/core/text/BatchedText.js +467 -0
  56. package/dist/core/text/MemberTexture.d.ts +90 -0
  57. package/dist/core/text/MemberTexture.js +194 -0
  58. package/dist/core/text/Text.d.ts +73 -0
  59. package/dist/core/text/Text.js +215 -0
  60. package/dist/core/text/batched-text-material.d.ts +56 -0
  61. package/dist/core/text/batched-text-material.js +121 -0
  62. package/dist/core/text/glyph-packing.d.ts +17 -0
  63. package/dist/core/text/glyph-packing.js +61 -0
  64. package/dist/core/typed-array.d.ts +16 -0
  65. package/dist/core/typed-array.js +20 -0
  66. package/dist/debug/components/DebugPanelProvider.d.ts +20 -0
  67. package/dist/debug/components/DebugPanelProvider.js +38 -0
  68. package/dist/debug/components/DebugTools.d.ts +22 -0
  69. package/dist/debug/components/DebugTools.js +28 -0
  70. package/dist/debug/components/Gui.d.ts +20 -0
  71. package/dist/debug/components/Gui.js +32 -0
  72. package/dist/debug/components/MemoryInfo.d.ts +22 -0
  73. package/dist/debug/components/MemoryInfo.js +42 -0
  74. package/dist/debug/components/Panel.d.ts +31 -0
  75. package/dist/debug/components/Panel.js +98 -0
  76. package/dist/debug/components/Stats.d.ts +9 -0
  77. package/dist/debug/components/Stats.js +24 -0
  78. package/dist/debug/debug-context.d.ts +41 -0
  79. package/dist/debug/debug-context.js +59 -0
  80. package/dist/debug/index.d.ts +51 -0
  81. package/dist/debug/index.js +64 -0
  82. package/dist/events/LayoutDriver.d.ts +31 -0
  83. package/dist/events/LayoutDriver.js +88 -0
  84. package/dist/events/PointerMissed.d.ts +23 -0
  85. package/dist/events/PointerMissed.js +58 -0
  86. package/dist/events/def-culling.d.ts +46 -0
  87. package/dist/events/def-culling.js +69 -0
  88. package/dist/events/layout-targets.d.ts +104 -0
  89. package/dist/events/layout-targets.js +245 -0
  90. package/dist/events/pointer.d.ts +118 -0
  91. package/dist/events/pointer.js +168 -0
  92. package/dist/external/ExternalCanvas.d.ts +56 -0
  93. package/dist/external/ExternalCanvas.js +178 -0
  94. package/dist/external/external-camera.d.ts +17 -0
  95. package/dist/external/external-camera.js +82 -0
  96. package/dist/external/external-events.d.ts +14 -0
  97. package/dist/external/external-events.js +33 -0
  98. package/dist/index.d.ts +11 -783
  99. package/dist/index.js +12 -8031
  100. package/dist/objects/Background.d.ts +31 -0
  101. package/dist/objects/Background.js +37 -0
  102. package/dist/objects/Lighting.d.ts +20 -0
  103. package/dist/objects/Lighting.js +85 -0
  104. package/dist/objects/containers/SlotContainer.d.ts +266 -0
  105. package/dist/objects/containers/SlotContainer.js +251 -0
  106. package/dist/objects/containers/hooks.d.ts +46 -0
  107. package/dist/objects/containers/hooks.js +118 -0
  108. package/dist/objects/containers/index.d.ts +6 -0
  109. package/dist/objects/containers/index.js +4 -0
  110. package/dist/objects/containers/keys.d.ts +10 -0
  111. package/dist/objects/containers/keys.js +18 -0
  112. package/dist/objects/containers/mesh-writes.d.ts +106 -0
  113. package/dist/objects/containers/mesh-writes.js +97 -0
  114. package/dist/objects/containers/props.d.ts +23 -0
  115. package/dist/objects/containers/props.js +1 -0
  116. package/dist/objects/images/ImageInstance.d.ts +29 -0
  117. package/dist/objects/images/ImageInstance.js +83 -0
  118. package/dist/objects/images/Images.d.ts +37 -0
  119. package/dist/objects/images/Images.js +204 -0
  120. package/dist/objects/images/atlas-packer.d.ts +128 -0
  121. package/dist/objects/images/atlas-packer.js +315 -0
  122. package/dist/objects/images/image-container.d.ts +51 -0
  123. package/dist/objects/images/image-container.js +70 -0
  124. package/dist/objects/images/index.d.ts +2 -0
  125. package/dist/objects/images/index.js +1 -0
  126. package/dist/objects/images/texture-budget.d.ts +120 -0
  127. package/dist/objects/images/texture-budget.js +215 -0
  128. package/dist/objects/layer/Layer.d.ts +27 -0
  129. package/dist/objects/layer/Layer.js +114 -0
  130. package/dist/objects/layer/index.d.ts +2 -0
  131. package/dist/objects/layer/index.js +1 -0
  132. package/dist/objects/layer/mount-scheduler.d.ts +102 -0
  133. package/dist/objects/layer/mount-scheduler.js +145 -0
  134. package/dist/objects/layer/visibility-gui.d.ts +19 -0
  135. package/dist/objects/layer/visibility-gui.js +93 -0
  136. package/dist/objects/lines/LineInstance.d.ts +21 -0
  137. package/dist/objects/lines/LineInstance.js +73 -0
  138. package/dist/objects/lines/Lines.d.ts +17 -0
  139. package/dist/objects/lines/Lines.js +29 -0
  140. package/dist/objects/lines/index.d.ts +2 -0
  141. package/dist/objects/lines/index.js +1 -0
  142. package/dist/objects/lines/line-container.d.ts +35 -0
  143. package/dist/objects/lines/line-container.js +40 -0
  144. package/dist/objects/materials/ColorMaterial.d.ts +85 -0
  145. package/dist/objects/materials/ColorMaterial.js +89 -0
  146. package/dist/objects/materials/LineMaterial.d.ts +44 -0
  147. package/dist/objects/materials/LineMaterial.js +72 -0
  148. package/dist/objects/materials/ModelMaterial.d.ts +41 -0
  149. package/dist/objects/materials/ModelMaterial.js +84 -0
  150. package/dist/objects/materials/TextureMaterial.d.ts +30 -0
  151. package/dist/objects/materials/TextureMaterial.js +29 -0
  152. package/dist/objects/materials/dim.d.ts +123 -0
  153. package/dist/objects/materials/dim.js +213 -0
  154. package/dist/objects/materials/shared.d.ts +6 -0
  155. package/dist/objects/materials/shared.js +51 -0
  156. package/dist/objects/models/ModelInstance.d.ts +22 -0
  157. package/dist/objects/models/ModelInstance.js +33 -0
  158. package/dist/objects/models/Models.d.ts +22 -0
  159. package/dist/objects/models/Models.js +177 -0
  160. package/dist/objects/models/gltf-cache.d.ts +17 -0
  161. package/dist/objects/models/gltf-cache.js +50 -0
  162. package/dist/objects/models/index.d.ts +2 -0
  163. package/dist/objects/models/index.js +1 -0
  164. package/dist/objects/models/model-asset.d.ts +48 -0
  165. package/dist/objects/models/model-asset.js +77 -0
  166. package/dist/objects/models/model-container.d.ts +139 -0
  167. package/dist/objects/models/model-container.js +227 -0
  168. package/dist/objects/shapes/ShapeInstance.d.ts +26 -0
  169. package/dist/objects/shapes/ShapeInstance.js +40 -0
  170. package/dist/objects/shapes/Shapes.d.ts +21 -0
  171. package/dist/objects/shapes/Shapes.js +120 -0
  172. package/dist/objects/shapes/index.d.ts +2 -0
  173. package/dist/objects/shapes/index.js +1 -0
  174. package/dist/objects/shapes/shape-container.d.ts +165 -0
  175. package/dist/objects/shapes/shape-container.js +272 -0
  176. package/dist/objects/shapes/shape-geometry.d.ts +27 -0
  177. package/dist/objects/shapes/shape-geometry.js +94 -0
  178. package/dist/objects/text/TextInstance.d.ts +22 -0
  179. package/dist/objects/text/TextInstance.js +95 -0
  180. package/dist/objects/text/Texts.d.ts +30 -0
  181. package/dist/objects/text/Texts.js +41 -0
  182. package/dist/objects/text/index.d.ts +2 -0
  183. package/dist/objects/text/index.js +1 -0
  184. package/dist/objects/text/text-container.d.ts +105 -0
  185. package/dist/objects/text/text-container.js +206 -0
  186. package/dist/objects/text/text-layout.d.ts +83 -0
  187. package/dist/objects/text/text-layout.js +154 -0
  188. package/dist/space/Camera.d.ts +103 -0
  189. package/dist/space/Camera.js +321 -0
  190. package/dist/space/Scene.d.ts +44 -0
  191. package/dist/space/Scene.js +112 -0
  192. package/dist/space/Space.d.ts +288 -0
  193. package/dist/space/Space.js +329 -0
  194. package/dist/space/VisibleRect.d.ts +72 -0
  195. package/dist/space/VisibleRect.js +129 -0
  196. package/dist/space/camera-handle.d.ts +67 -0
  197. package/dist/space/camera-handle.js +63 -0
  198. package/dist/space/camera-kind.d.ts +26 -0
  199. package/dist/space/camera-kind.js +30 -0
  200. package/dist/space/canvas-size.d.ts +24 -0
  201. package/dist/space/canvas-size.js +20 -0
  202. package/dist/space/clipping.d.ts +55 -0
  203. package/dist/space/clipping.js +108 -0
  204. package/dist/space/layout-measure.d.ts +65 -0
  205. package/dist/space/layout-measure.js +173 -0
  206. package/dist/space/viewport.d.ts +112 -0
  207. package/dist/space/viewport.js +67 -0
  208. package/dist/types/defs.d.ts +169 -0
  209. package/dist/types/defs.js +1 -0
  210. package/dist/types/events.d.ts +183 -0
  211. package/dist/types/events.js +1 -0
  212. package/dist/types/guards.d.ts +14 -0
  213. package/dist/types/guards.js +42 -0
  214. package/dist/update/index.d.ts +3 -0
  215. package/dist/update/index.js +2 -0
  216. package/dist/update/slot-writers.d.ts +30 -0
  217. package/dist/update/slot-writers.js +72 -0
  218. package/dist/update/store.d.ts +41 -0
  219. package/dist/update/store.js +98 -0
  220. package/dist/utils/arrays.d.ts +13 -0
  221. package/dist/utils/arrays.js +20 -0
  222. package/dist/utils/colors.d.ts +19 -0
  223. package/dist/utils/colors.js +27 -0
  224. package/dist/utils/logging.d.ts +26 -0
  225. package/dist/utils/logging.js +47 -0
  226. package/dist/utils/math.d.ts +24 -0
  227. package/dist/utils/math.js +27 -0
  228. package/package.json +67 -19
@@ -0,0 +1,85 @@
1
+ import { Vector3, Vector4 } from 'three';
2
+ import { FrustumCulling as InstancedFrustumCulling, } from '../instanced/FrustumCulling';
3
+ /**
4
+ * Culling for a line batch: each member tested by where it is *drawn*, not where it runs.
5
+ *
6
+ * A member's bounds are its two endpoints, which is the segment. What covers pixels is the ribbon the
7
+ * shader expands around it — after projection, by a width counted in screen pixels — so a member with
8
+ * both ends off screen still shows if that ribbon crosses the edge, and culling on the ends alone
9
+ * pops it there.
10
+ *
11
+ * The margin is added here rather than to the bounds themselves because it is not a property of the
12
+ * member: it depends on the camera, and on how far *this* member is from it. Which is also why it is
13
+ * per member rather than one figure for the batch — a member already knows its own width, and a batch
14
+ * of hairlines beside one wide route would otherwise be padded by the widest of them.
15
+ */
16
+ export class FrustumCulling extends InstancedFrustumCulling {
17
+ instanceSphere(host, id, camera, target) {
18
+ const sphere = super.instanceSphere(host, id, camera, target);
19
+ sphere.radius += this.drawnMargin(host, id, camera, sphere);
20
+ return sphere;
21
+ }
22
+ /**
23
+ * How far past its endpoints one member draws, in the units its bounds are measured in.
24
+ *
25
+ * Measured at the far side of the member, since a screen-space width covers more of the plan the
26
+ * further away it is — the near side would under-pad the end that is further off.
27
+ * @param host - The batch being culled.
28
+ * @param id - The member.
29
+ * @param camera - The camera the width is measured from.
30
+ * @param sphere - What encloses it, in the batch's own space.
31
+ * @returns The margin.
32
+ */
33
+ drawnMargin(host, id, camera, sphere) {
34
+ const material = host.material;
35
+ const held = host.getWidthAt(id);
36
+ // A member that set no width of its own draws at the material's.
37
+ const width = held < 0 ? material.linewidth : held;
38
+ if (width <= 0)
39
+ return 0;
40
+ if (material.worldUnits)
41
+ return width / 2;
42
+ // Nothing to measure a screen-space width against until the batch has been sized to a viewport,
43
+ // which it does before this pass runs.
44
+ if (material.resolution.x === 0)
45
+ return 0;
46
+ // The distance is world-space and the radius it pads is the batch's own, which agree because a
47
+ // line batch is placed by translation — a layer moves one, nothing scales it.
48
+ _worldCenter.copy(sphere.center).applyMatrix4(host.matrixWorld);
49
+ const near = camera.near ?? 0;
50
+ const distance = _cameraPosition
51
+ .setFromMatrixPosition(camera.matrixWorld)
52
+ .distanceTo(_worldCenter);
53
+ return worldSpaceHalfWidth(camera, Math.max(near, distance + sphere.radius), width, material);
54
+ }
55
+ }
56
+ /**
57
+ * Half a screen-space width, in plan units, at a distance from the camera.
58
+ *
59
+ * Vendored from three's `LineSegments2`, where `getWorldSpaceHalfWidth` is module-private and takes
60
+ * its width off a module global. Worth vendoring rather than approximating: it is the same formula
61
+ * three expands a line's *pick* bounds by, so what a batch culls and what it can be picked by are
62
+ * decided the same way.
63
+ * @param camera - The camera in use.
64
+ * @param distance - How far from it the width is measured.
65
+ * @param width - The full width, in the pixels the material's resolution counts.
66
+ * @param material - The material, for the resolution those pixels are counted against.
67
+ * @returns Half the width, in plan units.
68
+ */
69
+ function worldSpaceHalfWidth(camera, distance, width, material) {
70
+ // Transform into clip space, adjust the x and y values by the pixel width offset, then transform
71
+ // back into world space to get world offset. Note clip space is [-1, 1] so full width does not
72
+ // need to be halved.
73
+ const resolution = material.resolution;
74
+ _clipToWorld.set(0, 0, -distance, 1).applyMatrix4(camera.projectionMatrix);
75
+ _clipToWorld.multiplyScalar(1 / _clipToWorld.w);
76
+ _clipToWorld.x = width / resolution.width;
77
+ _clipToWorld.y = width / resolution.height;
78
+ _clipToWorld.applyMatrix4(camera.projectionMatrixInverse);
79
+ _clipToWorld.multiplyScalar(1 / _clipToWorld.w);
80
+ return Math.abs(Math.max(_clipToWorld.x, _clipToWorld.y));
81
+ }
82
+ // Scratch, each filled and consumed within one synchronous call.
83
+ const _worldCenter = new Vector3();
84
+ const _cameraPosition = new Vector3();
85
+ const _clipToWorld = new Vector4();
@@ -0,0 +1,44 @@
1
+ import { type Object3D, Vector3 } from 'three';
2
+ import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
3
+ import { type RaycastHost as InstancedRaycastHost, Raycasting as InstancedRaycasting } from '../instanced/Raycasting';
4
+ /** What {@link Raycasting} reads off the batch it tests, beyond what every instanced mesh offers. */
5
+ export interface RaycastHost extends InstancedRaycastHost {
6
+ /** The material, whose width a member with none of its own draws at. */
7
+ material: LineMaterial;
8
+ /**
9
+ * Reads a member's endpoints.
10
+ * @param id - The member.
11
+ * @param start - Receives the segment start.
12
+ * @param end - Receives the segment end.
13
+ */
14
+ getPointsAt(id: number, start: Vector3, end: Vector3): void;
15
+ /**
16
+ * A member's own width, or a negative value while it has none.
17
+ * @param id - The member.
18
+ * @returns The width.
19
+ */
20
+ getWidthAt(id: number): number;
21
+ }
22
+ /**
23
+ * Picking for a line batch: each member tested in screen space, at the width it actually draws at.
24
+ *
25
+ * A line is picked by where it is *drawn*, not merely where it runs — a 10px route has to answer
26
+ * anywhere on its ribbon, not only along its centerline — and that ribbon is expanded after
27
+ * projection, so the test needs the camera and the viewport. Three has the math, in
28
+ * `LineSegments2.raycast`, and this is how a batch reaches it: one scratch line, holding one
29
+ * segment, stood up per member.
30
+ *
31
+ * Only {@link prepareInstance} differs from the instanced sweep this inherits. The whole-batch
32
+ * early-out, the frustum-culled candidates, the visibility test and naming the member each hit
33
+ * belongs to are the same questions for a segment as for a quad — and answering them in world space
34
+ * is what lets this inherit them rather than restate them, since three's screen-space math projects
35
+ * the ray through the camera and needs it there.
36
+ *
37
+ * The width is the one thing three has nowhere to read per segment: it is material state. So the
38
+ * scratch carries a material of its own, written per test, and nothing touches the batch's — which
39
+ * is in use by the draw the pointer is moving over.
40
+ */
41
+ export declare class Raycasting extends InstancedRaycasting {
42
+ protected prepareInstance(host: RaycastHost, objectIndex: number): Object3D;
43
+ }
44
+ //# sourceMappingURL=Raycasting.d.ts.map
@@ -0,0 +1,91 @@
1
+ import { Box3, Sphere, Vector3 } from 'three';
2
+ import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
3
+ import { LineSegments2 } from 'three/addons/lines/LineSegments2.js';
4
+ import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js';
5
+ import { Raycasting as InstancedRaycasting, } from '../instanced/Raycasting';
6
+ /**
7
+ * Picking for a line batch: each member tested in screen space, at the width it actually draws at.
8
+ *
9
+ * A line is picked by where it is *drawn*, not merely where it runs — a 10px route has to answer
10
+ * anywhere on its ribbon, not only along its centerline — and that ribbon is expanded after
11
+ * projection, so the test needs the camera and the viewport. Three has the math, in
12
+ * `LineSegments2.raycast`, and this is how a batch reaches it: one scratch line, holding one
13
+ * segment, stood up per member.
14
+ *
15
+ * Only {@link prepareInstance} differs from the instanced sweep this inherits. The whole-batch
16
+ * early-out, the frustum-culled candidates, the visibility test and naming the member each hit
17
+ * belongs to are the same questions for a segment as for a quad — and answering them in world space
18
+ * is what lets this inherit them rather than restate them, since three's screen-space math projects
19
+ * the ray through the camera and needs it there.
20
+ *
21
+ * The width is the one thing three has nowhere to read per segment: it is material state. So the
22
+ * scratch carries a material of its own, written per test, and nothing touches the batch's — which
23
+ * is in use by the draw the pointer is moving over.
24
+ */
25
+ export class Raycasting extends InstancedRaycasting {
26
+ prepareInstance(host, objectIndex) {
27
+ host.getPointsAt(objectIndex, _start, _end);
28
+ // Written straight into the interleaved buffer the geometry already owns. `setPositions` would
29
+ // allocate that buffer and both attribute views over it again, per member per pick.
30
+ _segment.set([_start.x, _start.y, _start.z, _end.x, _end.y, _end.z]);
31
+ encloseSegment();
32
+ const held = host.getWidthAt(objectIndex);
33
+ _material.linewidth = held < 0 ? host.material.linewidth : held;
34
+ _material.worldUnits = host.material.worldUnits;
35
+ _material.resolution.copy(host.material.resolution);
36
+ // The batch's own placement. A per-instance transform, if a line ever carries one, composes in
37
+ // here — which is why this hands back a placed object rather than a geometry.
38
+ _line.matrixWorld.copy(host.matrixWorld);
39
+ return _line;
40
+ }
41
+ }
42
+ /**
43
+ * Fills the scratch geometry's bounds from the segment just written into it.
44
+ *
45
+ * Written rather than invalidated: three computes them when it finds them null, and both its
46
+ * `LineSegmentsGeometry` implementations allocate as they do — where a segment's box and sphere are
47
+ * two lines of arithmetic over the endpoints already in hand.
48
+ */
49
+ function encloseSegment() {
50
+ const geometry = _line.geometry;
51
+ const box = (geometry.boundingBox ??= new Box3()).makeEmpty();
52
+ box.expandByPoint(_start).expandByPoint(_end);
53
+ const sphere = (geometry.boundingSphere ??= new Sphere());
54
+ box.getCenter(sphere.center);
55
+ sphere.radius = _start.distanceTo(_end) / 2;
56
+ }
57
+ /**
58
+ * A geometry holding exactly one segment, whose endpoints are rewritten per test.
59
+ * @returns The geometry.
60
+ */
61
+ function makeSegmentGeometry() {
62
+ const geometry = new LineSegmentsGeometry();
63
+ // The only way to get the interleaved buffer and its two attribute views built; what it is filled
64
+ // with here is irrelevant, since every test overwrites it.
65
+ geometry.setPositions(new Float32Array(FLOATS_PER_SEGMENT));
66
+ return geometry;
67
+ }
68
+ /**
69
+ * The buffer a segment geometry holds its endpoints in.
70
+ * @param geometry - The geometry to read.
71
+ * @returns The endpoints, interleaved as `xyz xyz`.
72
+ */
73
+ function segmentArrayOf(geometry) {
74
+ const start = geometry.getAttribute('instanceStart');
75
+ return start.data.array;
76
+ }
77
+ /** Floats one segment occupies: `x, y, z` for each end. */
78
+ const FLOATS_PER_SEGMENT = 6;
79
+ /**
80
+ * The line every member is tested through.
81
+ *
82
+ * One object rather than one per member, for the reason the instanced sweep keeps a scratch mesh:
83
+ * what differs between members is the data, and three's raycast reads all of it off the object it is
84
+ * called on. Never rendered, so neither its geometry nor its material holds anything to free — and
85
+ * its material is its own, so a test writes no state the batch is drawing with.
86
+ */
87
+ const _line = new LineSegments2(makeSegmentGeometry(), new LineMaterial());
88
+ const _material = _line.material;
89
+ const _segment = segmentArrayOf(_line.geometry);
90
+ const _start = new Vector3();
91
+ const _end = new Vector3();
@@ -0,0 +1,47 @@
1
+ import { type WebGLProgramParametersWithUniforms } from 'three';
2
+ import { type LineMaterial } from 'three/addons/lines/LineMaterial.js';
3
+ /**
4
+ * The material side of a line batch: what three's line shader has to become before it can draw
5
+ * members that differ in width and tint.
6
+ *
7
+ * Free functions holding no state, as `instanced-material.ts` is — {@link patchLineMaterial} hands
8
+ * back its own undo rather than parking it on the caller.
9
+ *
10
+ * It lives here rather than in the material component for the reason the instancing patch does: the
11
+ * material is three's, and a `<LineMaterial>` has no business knowing whether it is drawing a batch
12
+ * or a plain `LineSegments2`. What it must keep working with is either.
13
+ */
14
+ /** The per-instance segment start, named for the attribute three's line shader reads. */
15
+ export declare const START_UNIFORM = "instanceStart";
16
+ /** The per-instance segment end, named for the attribute three's line shader reads. */
17
+ export declare const END_UNIFORM = "instanceEnd";
18
+ /** The per-instance width. Distinct from three's `linewidth`, which it falls back to. */
19
+ export declare const WIDTH_UNIFORM = "lineWidth";
20
+ /**
21
+ * Wraps a material so the programs it compiles read a batch's per-instance endpoints, width and tint.
22
+ *
23
+ * Installed *inside* the wrap an instanced draw puts on, so that by the time this runs the prologue
24
+ * it reads is already in the shader — which is why the batch installs this one first.
25
+ * @param material - The material the batch is about to draw with.
26
+ * @returns Restores the material exactly as it arrived. Call it once, after the draw.
27
+ */
28
+ export declare function patchLineMaterial(material: LineMaterial): () => void;
29
+ /**
30
+ * Points three's line shader at a batch's per-instance endpoints, width and tint.
31
+ *
32
+ * Deliberately small, and small in a particular way: the endpoints are *not* rewritten anywhere. The
33
+ * batch declares them under the names three's own shader already reads — `instanceStart` and
34
+ * `instanceEnd` — so the uniform prologue lands two locals of `main` that shadow the attributes, and
35
+ * every line of three's projection, near-plane trimming and cap math goes on reading what it always
36
+ * read. What is left is to drop the attribute declarations that no longer feed anything, resolve the
37
+ * width the shader expands by, and carry the instance color's alpha into an output that drops it.
38
+ *
39
+ * Does nothing until a batch's prologue is in the shader — the guard `applyDim` uses, and for the
40
+ * same reason: a material may be compiled for something that is not a batch at all, and the GLSL
41
+ * below reads names only the prologue brings into scope.
42
+ * @param shader - The program parameters to edit.
43
+ * @returns Whether the shader was patched.
44
+ * @throws {Error} If the prologue is present but the shader around it has moved what this anchors on.
45
+ */
46
+ export declare function patchLineShader(shader: WebGLProgramParametersWithUniforms): boolean;
47
+ //# sourceMappingURL=lines-material.d.ts.map
@@ -0,0 +1,115 @@
1
+ import { openMain, replaceAnchor } from '../shader-source';
2
+ /**
3
+ * The material side of a line batch: what three's line shader has to become before it can draw
4
+ * members that differ in width and tint.
5
+ *
6
+ * Free functions holding no state, as `instanced-material.ts` is — {@link patchLineMaterial} hands
7
+ * back its own undo rather than parking it on the caller.
8
+ *
9
+ * It lives here rather than in the material component for the reason the instancing patch does: the
10
+ * material is three's, and a `<LineMaterial>` has no business knowing whether it is drawing a batch
11
+ * or a plain `LineSegments2`. What it must keep working with is either.
12
+ */
13
+ /** The per-instance segment start, named for the attribute three's line shader reads. */
14
+ export const START_UNIFORM = 'instanceStart';
15
+ /** The per-instance segment end, named for the attribute three's line shader reads. */
16
+ export const END_UNIFORM = 'instanceEnd';
17
+ /** The per-instance width. Distinct from three's `linewidth`, which it falls back to. */
18
+ export const WIDTH_UNIFORM = 'lineWidth';
19
+ /**
20
+ * Wraps a material so the programs it compiles read a batch's per-instance endpoints, width and tint.
21
+ *
22
+ * Installed *inside* the wrap an instanced draw puts on, so that by the time this runs the prologue
23
+ * it reads is already in the shader — which is why the batch installs this one first.
24
+ * @param material - The material the batch is about to draw with.
25
+ * @returns Restores the material exactly as it arrived. Call it once, after the draw.
26
+ */
27
+ export function patchLineMaterial(material) {
28
+ seedColorChunk(material);
29
+ // Saved raw rather than bound, for the reason `patchInstancedMaterial` spells out: restoring a
30
+ // bound copy would put a *different* function back, and the next draw would bind that one in turn.
31
+ /* eslint-disable-next-line @typescript-eslint/unbound-method -- restored verbatim; called with `.call`. */
32
+ const baseOnBeforeCompile = material.onBeforeCompile;
33
+ material.onBeforeCompile = (shader, renderer) => {
34
+ patchLineShader(shader);
35
+ baseOnBeforeCompile.call(material, shader, renderer);
36
+ };
37
+ return () => {
38
+ material.onBeforeCompile = baseOnBeforeCompile;
39
+ };
40
+ }
41
+ /**
42
+ * Puts the chunk three seeds `vColor` in into the material's own source, once.
43
+ *
44
+ * That chunk is the only place an instanced draw has to fold a per-instance color in, and the line
45
+ * shader is the one stock material that does not include it. It goes into the *source* rather than
46
+ * into a compiling shader because the instancing injection looks for it before the patch below runs,
47
+ * and finding it is what makes it bind the colors texture at all.
48
+ *
49
+ * Left in place afterwards rather than undone with the wrap: it expands to nothing without the color
50
+ * defines, which only an instanced draw sets — so the material still draws a plain `LineSegments2` —
51
+ * and putting it back would change the source three keys its program cache by, on every draw.
52
+ * @param material - The material to seed.
53
+ */
54
+ function seedColorChunk(material) {
55
+ if (material.vertexShader.includes(COLOR_VERTEX_CHUNK))
56
+ return;
57
+ material.vertexShader = openMain(material.vertexShader, COLOR_VERTEX_CHUNK);
58
+ }
59
+ /**
60
+ * Points three's line shader at a batch's per-instance endpoints, width and tint.
61
+ *
62
+ * Deliberately small, and small in a particular way: the endpoints are *not* rewritten anywhere. The
63
+ * batch declares them under the names three's own shader already reads — `instanceStart` and
64
+ * `instanceEnd` — so the uniform prologue lands two locals of `main` that shadow the attributes, and
65
+ * every line of three's projection, near-plane trimming and cap math goes on reading what it always
66
+ * read. What is left is to drop the attribute declarations that no longer feed anything, resolve the
67
+ * width the shader expands by, and carry the instance color's alpha into an output that drops it.
68
+ *
69
+ * Does nothing until a batch's prologue is in the shader — the guard `applyDim` uses, and for the
70
+ * same reason: a material may be compiled for something that is not a batch at all, and the GLSL
71
+ * below reads names only the prologue brings into scope.
72
+ * @param shader - The program parameters to edit.
73
+ * @returns Whether the shader was patched.
74
+ * @throws {Error} If the prologue is present but the shader around it has moved what this anchors on.
75
+ */
76
+ export function patchLineShader(shader) {
77
+ const vertex = shader.vertexShader;
78
+ if (!vertex.includes(WIDTH_UNIFORM))
79
+ return false;
80
+ // Two statements, in this order for a reason: the first reads the material uniform while it is
81
+ // still the only `linewidth` in scope, and the second declares the local that takes over from
82
+ // there on. Nothing downstream is rewritten, which is the point — three's own `offset *= linewidth`
83
+ // (screen space) and `float hw = linewidth * 0.5` (world units) both keep working, so flipping
84
+ // `material.worldUnits` needs no change here. The fallback triggers on a *negative* width, not on
85
+ // zero, so a member given zero expands to no area and draws nothing, which is what asking for a
86
+ // zero-width line means.
87
+ const resolveWidth = /* glsl */ `
88
+ float materialLineWidth = linewidth;
89
+ float linewidth = ${WIDTH_UNIFORM} < 0.0 ? materialLineWidth : ${WIDTH_UNIFORM};
90
+ `;
91
+ // The attributes stand unused once the prologue shadows them, so a compiler would drop them anyway
92
+ // — but an attribute that looks like the data source and is not would be read as one. Dropped with
93
+ // a plain replace, because a three that stopped declaring them is not a three this breaks on.
94
+ shader.vertexShader = replaceAnchor(vertex.replace(ATTRIBUTE_DECLARATIONS, ''), WIDTH_ANCHOR, `${resolveWidth}\n${WIDTH_ANCHOR}`);
95
+ // The instance color reaches `diffuseColor` through three's own `<color_fragment>`, which the line
96
+ // shader does include — but only its rgb survives, because the output discards the diffuse alpha
97
+ // and carries a coverage of its own. Folding the tint's alpha into that is all that is left.
98
+ shader.fragmentShader = replaceAnchor(shader.fragmentShader, FRAGMENT_OUTPUT,
99
+ /* glsl */ `gl_FragColor = vec4( diffuseColor.rgb, alpha * vColor.a );`);
100
+ return true;
101
+ }
102
+ /**
103
+ * The attribute declarations the per-instance endpoints replace. Matched loosely, because their
104
+ * indentation is three's and not ours to depend on.
105
+ */
106
+ const ATTRIBUTE_DECLARATIONS = new RegExp(`attribute\\s+vec3\\s+${START_UNIFORM}\\s*;\\s*attribute\\s+vec3\\s+${END_UNIFORM}\\s*;`);
107
+ /**
108
+ * Where the width resolve goes: ahead of the first statement of three's own `main`, which puts it
109
+ * after the prologue that declares the per-instance width and before everything that expands by it.
110
+ */
111
+ const WIDTH_ANCHOR = /*glsl*/ `float aspect = resolution.x / resolution.y;`;
112
+ /** The line shader's output, which the instance color's alpha is folded into. */
113
+ const FRAGMENT_OUTPUT = /*glsl*/ `gl_FragColor = vec4( diffuseColor.rgb, alpha );`;
114
+ /** Three's per-vertex color chunk, which an instanced draw substitutes its own for. */
115
+ const COLOR_VERTEX_CHUNK = /*glsl*/ `#include <color_vertex>`;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Editing a shader's source around the one construct it cannot be missing: the opening of `main`.
3
+ *
4
+ * Every injection here used to key on a chunk `#include` instead — the instancing declarations rode
5
+ * on `batching_pars_vertex`, which three includes in every mesh shader. That only holds for the
6
+ * materials three itself builds: a hand-written `ShaderMaterial` (three's `LineMaterial` among them)
7
+ * or a shader a glTF extension supplies may include no chunk at all, and a patch anchored on one that
8
+ * is not there silently does nothing — leaving a program that references identifiers nothing
9
+ * declared. `main` is the anchor that survives both.
10
+ *
11
+ * Both helpers throw when they cannot find it, for the same reason: what reaches the driver
12
+ * otherwise is a compile error naming an undeclared identifier, a long way from the patch that
13
+ * failed to declare it.
14
+ */
15
+ /**
16
+ * Declares GLSL above `main`, where file-scope declarations belong — samplers, varyings, functions.
17
+ * @param source - The shader source.
18
+ * @param glsl - The declarations to add.
19
+ * @returns The source, with the declarations ahead of `main`.
20
+ * @throws {Error} If the source has no `main`.
21
+ */
22
+ export declare function declareBeforeMain(source: string, glsl: string): string;
23
+ /**
24
+ * Inserts GLSL as `main`'s first statements.
25
+ *
26
+ * Successive calls each insert at the top, so **the last caller runs first**. That is what lets a
27
+ * value be supplied to a prologue already injected: the instanced meshes fetch their per-instance
28
+ * uniforms by an index resolved from `gl_InstanceID`, and the resolve is written afterwards
29
+ * precisely so it lands ahead of the fetch that reads it.
30
+ * @param source - The shader source.
31
+ * @param glsl - The statements to run first.
32
+ * @returns The source, with the statements opening `main`.
33
+ * @throws {Error} If the source has no `main`.
34
+ */
35
+ export declare function openMain(source: string, glsl: string): string;
36
+ /**
37
+ * Rewrites the one place a shader spells `anchor`.
38
+ *
39
+ * For a patch that has to land somewhere a shader's *own* text decides — before the statement that
40
+ * reads a value, or over the line that outputs one. Throws for the same reason the two above do: an
41
+ * anchor that has moved leaves the shader compiling and drawing, just not the way the patch meant,
42
+ * and that is the failure worth being loud about.
43
+ * @param source - The shader source.
44
+ * @param anchor - The text to rewrite. Keep it in the replacement to insert around it.
45
+ * @param glsl - What takes its place.
46
+ * @returns The rewritten source.
47
+ * @throws {Error} If the source does not contain the anchor.
48
+ */
49
+ export declare function replaceAnchor(source: string, anchor: string, glsl: string): string;
50
+ //# sourceMappingURL=shader-source.d.ts.map
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Editing a shader's source around the one construct it cannot be missing: the opening of `main`.
3
+ *
4
+ * Every injection here used to key on a chunk `#include` instead — the instancing declarations rode
5
+ * on `batching_pars_vertex`, which three includes in every mesh shader. That only holds for the
6
+ * materials three itself builds: a hand-written `ShaderMaterial` (three's `LineMaterial` among them)
7
+ * or a shader a glTF extension supplies may include no chunk at all, and a patch anchored on one that
8
+ * is not there silently does nothing — leaving a program that references identifiers nothing
9
+ * declared. `main` is the anchor that survives both.
10
+ *
11
+ * Both helpers throw when they cannot find it, for the same reason: what reaches the driver
12
+ * otherwise is a compile error naming an undeclared identifier, a long way from the patch that
13
+ * failed to declare it.
14
+ */
15
+ /**
16
+ * Declares GLSL above `main`, where file-scope declarations belong — samplers, varyings, functions.
17
+ * @param source - The shader source.
18
+ * @param glsl - The declarations to add.
19
+ * @returns The source, with the declarations ahead of `main`.
20
+ * @throws {Error} If the source has no `main`.
21
+ */
22
+ export function declareBeforeMain(source, glsl) {
23
+ return rewriteMain(source, (main) => `${glsl}\n${main}`);
24
+ }
25
+ /**
26
+ * Inserts GLSL as `main`'s first statements.
27
+ *
28
+ * Successive calls each insert at the top, so **the last caller runs first**. That is what lets a
29
+ * value be supplied to a prologue already injected: the instanced meshes fetch their per-instance
30
+ * uniforms by an index resolved from `gl_InstanceID`, and the resolve is written afterwards
31
+ * precisely so it lands ahead of the fetch that reads it.
32
+ * @param source - The shader source.
33
+ * @param glsl - The statements to run first.
34
+ * @returns The source, with the statements opening `main`.
35
+ * @throws {Error} If the source has no `main`.
36
+ */
37
+ export function openMain(source, glsl) {
38
+ return rewriteMain(source, (main) => `${main}\n${glsl}`);
39
+ }
40
+ /**
41
+ * Rewrites the one place a shader spells `anchor`.
42
+ *
43
+ * For a patch that has to land somewhere a shader's *own* text decides — before the statement that
44
+ * reads a value, or over the line that outputs one. Throws for the same reason the two above do: an
45
+ * anchor that has moved leaves the shader compiling and drawing, just not the way the patch meant,
46
+ * and that is the failure worth being loud about.
47
+ * @param source - The shader source.
48
+ * @param anchor - The text to rewrite. Keep it in the replacement to insert around it.
49
+ * @param glsl - What takes its place.
50
+ * @returns The rewritten source.
51
+ * @throws {Error} If the source does not contain the anchor.
52
+ */
53
+ export function replaceAnchor(source, anchor, glsl) {
54
+ const at = source.indexOf(anchor);
55
+ if (at < 0)
56
+ throw new Error(`shader-source: the shader has no "${anchor}" to rewrite.`);
57
+ return source.slice(0, at) + glsl + source.slice(at + anchor.length);
58
+ }
59
+ /**
60
+ * Rewrites the first opening of `main`, splicing rather than replacing — so a `$` in the injected
61
+ * GLSL cannot be read as a replacement pattern.
62
+ * @param source - The shader source.
63
+ * @param rewrite - Receives the matched opening verbatim, whatever way it was spelled; returns what
64
+ * takes its place.
65
+ * @returns The rewritten source.
66
+ * @throws {Error} If the source has no `main`.
67
+ */
68
+ function rewriteMain(source, rewrite) {
69
+ const found = MAIN.exec(source);
70
+ if (!found) {
71
+ throw new Error('shader-source: the shader has no "void main()" to inject into.');
72
+ }
73
+ const main = found[0];
74
+ return source.slice(0, found.index) + rewrite(main) + source.slice(found.index + main.length);
75
+ }
76
+ /**
77
+ * The opening of `main`, spelled every way GLSL allows it: with or without the redundant `void`
78
+ * parameter, and with whitespace — a newline included — wherever it is legal.
79
+ *
80
+ * Neither anchored nor global, so it finds the first one, which is the one to patch: troika renames
81
+ * the shader's original `main` to `troikaOrigMain` and writes a new one, and a patch belongs in the
82
+ * new one. The rename also puts `main` where no whitespace precedes it, so this does not match it.
83
+ */
84
+ const MAIN = /void\s+main\s*\(\s*(?:void)?\s*\)\s*\{/;