@expofp/renderer 3.2.5 → 3.27.1

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,89 @@
1
+ import { Box, type Point2Like } from '@expofp/geometry';
2
+ import { type PerspectiveCamera } from 'three';
3
+ import { type Space } from '../space/Space';
4
+ /**
5
+ * The part of the canvas a target is framed inside, in **device** pixels, as the camera's own
6
+ * framing measures it.
7
+ *
8
+ * The offset is from the drawing buffer's centre rather than from its corner, because that is the
9
+ * only thing the fit does with it — a region centred on the canvas frames exactly as no region at
10
+ * all, and says so by being all zeroes.
11
+ */
12
+ export interface FramingRegion {
13
+ /** How wide the region is. */
14
+ width: number;
15
+ /** How tall it is. */
16
+ height: number;
17
+ /** How far its centre sits to the right of the buffer's centre; negative for left. */
18
+ offsetX: number;
19
+ /** How far below; negative for above. */
20
+ offsetY: number;
21
+ }
22
+ /**
23
+ * What a camera's projection was offset by — three's own `camera.view`, which `VisibleRect` is what
24
+ * writes. Aliased rather than restated so the two can never drift.
25
+ */
26
+ export type ViewOffset = PerspectiveCamera['view'];
27
+ /** How {@link fitBox} should frame its target. */
28
+ export interface FitOptions {
29
+ /** The target, in plan units. */
30
+ target: Box;
31
+ /** The scene's space, which takes the target into world units. */
32
+ space: Space;
33
+ /** How far the plan is turned, in radians — the camera's azimuth, negated. */
34
+ bearing: number;
35
+ /** The region to frame the target inside. */
36
+ region: FramingRegion;
37
+ /** Fraction of the region left as margin on each side; `0.25` spends half of it. */
38
+ padding?: number;
39
+ /** The closest the fit may bring the plan, as a zoom factor. */
40
+ maxZoom?: number;
41
+ }
42
+ /** Where the camera has to be for a target to fill its region. */
43
+ export interface Fit {
44
+ /** Where the camera should look, in world units. */
45
+ center: Point2Like;
46
+ /** The zoom factor that frames the target. */
47
+ zoom: number;
48
+ }
49
+ /**
50
+ * Where the camera has to be for a box of the plan to fill a region of the canvas.
51
+ *
52
+ * Pure arithmetic, kept out of the hook so the part that can be wrong is the part that can be
53
+ * tested. It rests on the one invariant the viewbox fit exists to establish — at zoom factor `1` one
54
+ * world unit is one device pixel — which is what lets a world size and a pixel size be divided by
55
+ * each other below.
56
+ *
57
+ * The target is measured **in the camera's own frame**: its world box is turned about its centre by
58
+ * the bearing before its bounds are taken, so a plan the user has rolled frames the box they can see
59
+ * rather than its axis-aligned shadow. Turning about the centre is what leaves the centre alone,
60
+ * which is why only the size of that box is read.
61
+ *
62
+ * The pan target then picks up the region's offset from the buffer's centre, which is what lands the
63
+ * box in the middle of *that region* rather than in the middle of the canvas. The offset is divided
64
+ * by the zoom because it arrives as a distance in pixels and has to leave as one in world units, and
65
+ * turned by the bearing for the same reason the box was.
66
+ * @param options - The target, and how to frame it.
67
+ * @returns The framing, or `undefined` for a target with no area, which nothing can be fitted to.
68
+ */
69
+ export declare function fitBox(options: FitOptions): Fit | undefined;
70
+ /**
71
+ * The region a caller named, in the coordinates the fit works in.
72
+ *
73
+ * A `within` rect is given in css pixels of the **canvas**, which is where a host's own panels are.
74
+ * The fit works in the camera's framing instead — the canvas as it would be with no visible rect —
75
+ * so a rect has to come back through whatever `VisibleRect` put on the projection: a uniform scale
76
+ * about the rect's centre, and nothing else. With no view offset that reduces to a change of origin
77
+ * and a multiplication by the device pixel ratio.
78
+ *
79
+ * This is the one place in the renderer where a region of the canvas and the camera's framing meet,
80
+ * and it is the method whose whole job is relating the two. Everything else stays clear of it.
81
+ * @param within - The region, in css pixels from the canvas's top-left corner; omitted means all of it.
82
+ * @param width - The canvas width in css pixels.
83
+ * @param height - The canvas height in css pixels.
84
+ * @param dpr - Device pixels per css pixel.
85
+ * @param view - The camera's view offset, when one is in force.
86
+ * @returns The region in device pixels, offset from the buffer's centre.
87
+ */
88
+ export declare function framingRegion(within: Box | undefined, width: number, height: number, dpr: number, view?: ViewOffset | null): FramingRegion;
89
+ //# sourceMappingURL=zoom-to.d.ts.map
@@ -0,0 +1,91 @@
1
+ import { Box, Point, rectBounds } from '@expofp/geometry';
2
+ /**
3
+ * Where the camera has to be for a box of the plan to fill a region of the canvas.
4
+ *
5
+ * Pure arithmetic, kept out of the hook so the part that can be wrong is the part that can be
6
+ * tested. It rests on the one invariant the viewbox fit exists to establish — at zoom factor `1` one
7
+ * world unit is one device pixel — which is what lets a world size and a pixel size be divided by
8
+ * each other below.
9
+ *
10
+ * The target is measured **in the camera's own frame**: its world box is turned about its centre by
11
+ * the bearing before its bounds are taken, so a plan the user has rolled frames the box they can see
12
+ * rather than its axis-aligned shadow. Turning about the centre is what leaves the centre alone,
13
+ * which is why only the size of that box is read.
14
+ *
15
+ * The pan target then picks up the region's offset from the buffer's centre, which is what lands the
16
+ * box in the middle of *that region* rather than in the middle of the canvas. The offset is divided
17
+ * by the zoom because it arrives as a distance in pixels and has to leave as one in world units, and
18
+ * turned by the bearing for the same reason the box was.
19
+ * @param options - The target, and how to frame it.
20
+ * @returns The framing, or `undefined` for a target with no area, which nothing can be fitted to.
21
+ */
22
+ export function fitBox(options) {
23
+ const { target, space, bearing, region, padding = 0, maxZoom } = options;
24
+ // The bounds of the target once the plan is turned — geometry's own rect maths, given the world
25
+ // box as a rect with a rotation. Turning is about the centre, so the centre below is still the
26
+ // target's and only the extent has changed.
27
+ const world = worldBounds(target, space);
28
+ const source = rectBounds({ center: world.center, size: world.size, rotation: bearing });
29
+ const { width: sourceWidth, height: sourceHeight } = source.size;
30
+ if (sourceWidth <= 0 || sourceHeight <= 0)
31
+ return undefined;
32
+ if (region.width <= 0 || region.height <= 0)
33
+ return undefined;
34
+ const margin = 1 - 2 * padding;
35
+ const fitted = Math.min((region.width * margin) / sourceWidth, (region.height * margin) / sourceHeight);
36
+ const zoom = maxZoom === undefined ? fitted : Math.min(fitted, maxZoom);
37
+ if (!(zoom > 0))
38
+ return undefined;
39
+ const pan = new Point(-region.offsetX / zoom, -region.offsetY / zoom).rotate(bearing, ORIGIN);
40
+ return { center: { x: source.center.x + pan.x, y: source.center.y + pan.y }, zoom };
41
+ }
42
+ /**
43
+ * The region a caller named, in the coordinates the fit works in.
44
+ *
45
+ * A `within` rect is given in css pixels of the **canvas**, which is where a host's own panels are.
46
+ * The fit works in the camera's framing instead — the canvas as it would be with no visible rect —
47
+ * so a rect has to come back through whatever `VisibleRect` put on the projection: a uniform scale
48
+ * about the rect's centre, and nothing else. With no view offset that reduces to a change of origin
49
+ * and a multiplication by the device pixel ratio.
50
+ *
51
+ * This is the one place in the renderer where a region of the canvas and the camera's framing meet,
52
+ * and it is the method whose whole job is relating the two. Everything else stays clear of it.
53
+ * @param within - The region, in css pixels from the canvas's top-left corner; omitted means all of it.
54
+ * @param width - The canvas width in css pixels.
55
+ * @param height - The canvas height in css pixels.
56
+ * @param dpr - Device pixels per css pixel.
57
+ * @param view - The camera's view offset, when one is in force.
58
+ * @returns The region in device pixels, offset from the buffer's centre.
59
+ */
60
+ export function framingRegion(within, width, height, dpr, view) {
61
+ if (!within) {
62
+ return { width: width * dpr, height: height * dpr, offsetX: 0, offsetY: 0 };
63
+ }
64
+ // What the framing was scaled by, and where its centre landed — read straight back off the
65
+ // parameters `VisibleRect` handed `setViewOffset`.
66
+ const scale = view?.enabled ? view.fullWidth / view.width : 1;
67
+ const landingX = view?.enabled ? view.fullWidth / 2 - view.offsetX : width / 2;
68
+ const landingY = view?.enabled ? view.fullHeight / 2 - view.offsetY : height / 2;
69
+ return {
70
+ width: (within.size.width / scale) * dpr,
71
+ height: (within.size.height / scale) * dpr,
72
+ offsetX: ((within.center.x - landingX) / scale) * dpr,
73
+ offsetY: ((within.center.y - landingY) / scale) * dpr,
74
+ };
75
+ }
76
+ /**
77
+ * The target's bounds in world space.
78
+ *
79
+ * Two corners are the whole box: the plan-to-world mapping scales and translates, so opposite
80
+ * corners stay opposite. `fromMinMax` normalizes them, so a transform that mirrors an axis and
81
+ * hands them over the other way round still comes out right.
82
+ * @param target - The target, in plan units.
83
+ * @param space - The scene's space.
84
+ * @returns The bounds, in world units.
85
+ */
86
+ function worldBounds(target, space) {
87
+ const min = space.planToWorld({ ...target.min, z: 0 });
88
+ const max = space.planToWorld({ ...target.max, z: 0 });
89
+ return Box.fromMinMax(min, max);
90
+ }
91
+ const ORIGIN = { x: 0, y: 0 };
@@ -0,0 +1,93 @@
1
+ import { type WebGLProgramParametersWithUniforms } from 'three';
2
+ import { SquareDataTexture, type UniformType, type UniformValue, type UniformValueObj } from './SquareDataTexture';
3
+ /** The GLSL types a per-instance uniform may take, keyed by the name it gets in the shader. */
4
+ export type UniformSchema = Record<string, UniformType>;
5
+ /** Per-instance uniforms to declare, split by the shader stage that reads them. */
6
+ export interface UniformSchemaShader {
7
+ /** Uniforms read in the vertex shader. */
8
+ vertex?: UniformSchema;
9
+ /** Uniforms read in the fragment shader. */
10
+ fragment?: UniformSchema;
11
+ }
12
+ /**
13
+ * The per-instance uniform store shared by all core classes: a {@link SquareDataTexture} plus the schema
14
+ * describing how uniforms pack into it.
15
+ *
16
+ * Ported from the identical uniform code both three.ez packages carry (`Uniforms.ts` in each), with
17
+ * one change: uniforms are **added**, not declared once. {@link add} merges into whatever is already
18
+ * there and rebuilds the texture, carrying existing values across — so a material and its container
19
+ * can each declare what they need without coordinating, and a declaration after instances are
20
+ * written keeps them. Removal is deliberately unsupported; rebuild the mesh if it is ever needed.
21
+ *
22
+ * The shader wiring lives here as well ({@link patchShader}), because every mesh does the same two
23
+ * things with it: bind the texture, and open `main` with the prologue that fetches from it. What
24
+ * differs between them — where the per-instance index comes from, and what each adds around the
25
+ * prologue — is what they pass in.
26
+ */
27
+ export declare class InstanceUniforms {
28
+ /** The backing texture, or `null` until {@link add} declares the first uniform. */
29
+ texture: SquareDataTexture | null;
30
+ /** Everything declared so far, merged across calls, so a rebuild recomputes the whole layout. */
31
+ private declared;
32
+ /**
33
+ * Declares uniforms, merging with any already declared and rebuilding the texture.
34
+ *
35
+ * The layout packs every uniform into as few texels as it can, so adding one generally moves the
36
+ * others — hence the rebuild-and-copy rather than an in-place append. Redeclaring a name with a
37
+ * different type replaces it.
38
+ * @param schema - The uniforms to add.
39
+ * @param capacity - Instances the texture must hold.
40
+ */
41
+ add(schema: UniformSchemaShader, capacity: number): void;
42
+ /**
43
+ * Reads one instance's uniform.
44
+ * @param id - The instance to read.
45
+ * @param name - The uniform's name.
46
+ * @param target - Receives the result; unused for a `float`, which is returned directly.
47
+ * @returns The value.
48
+ */
49
+ get(id: number, name: string, target?: UniformValueObj): UniformValue;
50
+ /**
51
+ * Writes one instance's uniform and marks its row for upload.
52
+ * @param id - The instance to write.
53
+ * @param name - The uniform's name.
54
+ * @param value - The value to write.
55
+ */
56
+ set(id: number, name: string, value: UniformValue): void;
57
+ /**
58
+ * Grows the texture to a new instance capacity, keeping what is written.
59
+ * @param capacity - The new capacity.
60
+ */
61
+ resize(capacity: number): void;
62
+ /**
63
+ * Points a compiling shader at these uniforms: binds the texture, and opens `main` in both stages
64
+ * with the prologue that fetches this instance's values and brings them into scope under the
65
+ * names they were declared with.
66
+ *
67
+ * Does nothing and answers `false` while no uniform is declared — there is no texture to bind and
68
+ * nothing to name, so a caller whose own GLSL reads those names has to skip it too.
69
+ * @param shader - The program parameters to edit.
70
+ * @param indexName - The per-instance index the fetch addresses by, in shader scope where the
71
+ * prologue lands (a float; see {@link SquareDataTexture}). A caller computing it inside `main`
72
+ * supplies it with a later {@link openMain}, which lands ahead of this fetch.
73
+ * @param body - GLSL to run straight after the fetch, in both stages. Where code reading the
74
+ * uniforms goes, since nothing ahead of the prologue can see them.
75
+ * @returns Whether the shader was patched.
76
+ * @throws {Error} If either stage has no `main` to inject into.
77
+ */
78
+ patchShader(shader: WebGLProgramParametersWithUniforms, indexName: string, body?: string): boolean;
79
+ /**
80
+ * Adopts another store's schema and an independent copy of its values, for cloning a mesh.
81
+ * @param source - The store to copy.
82
+ */
83
+ copyFrom(source: InstanceUniforms): void;
84
+ /** Frees the texture. */
85
+ dispose(): void;
86
+ /**
87
+ * The texture, or a readable failure when nothing declared a uniform.
88
+ * @returns The texture.
89
+ * @throws {Error} If {@link add} has not been called.
90
+ */
91
+ private require;
92
+ }
93
+ //# sourceMappingURL=InstanceUniforms.d.ts.map
@@ -0,0 +1,266 @@
1
+ import { Matrix3, Matrix4, Vector2, Vector3, Vector4, } from 'three';
2
+ import { declareBeforeMain, openMain } from './shader-source';
3
+ import { SquareDataTexture, } from './SquareDataTexture';
4
+ /**
5
+ * The per-instance uniform store shared by all core classes: a {@link SquareDataTexture} plus the schema
6
+ * describing how uniforms pack into it.
7
+ *
8
+ * Ported from the identical uniform code both three.ez packages carry (`Uniforms.ts` in each), with
9
+ * one change: uniforms are **added**, not declared once. {@link add} merges into whatever is already
10
+ * there and rebuilds the texture, carrying existing values across — so a material and its container
11
+ * can each declare what they need without coordinating, and a declaration after instances are
12
+ * written keeps them. Removal is deliberately unsupported; rebuild the mesh if it is ever needed.
13
+ *
14
+ * The shader wiring lives here as well ({@link patchShader}), because every mesh does the same two
15
+ * things with it: bind the texture, and open `main` with the prologue that fetches from it. What
16
+ * differs between them — where the per-instance index comes from, and what each adds around the
17
+ * prologue — is what they pass in.
18
+ */
19
+ export class InstanceUniforms {
20
+ /** The backing texture, or `null` until {@link add} declares the first uniform. */
21
+ texture = null;
22
+ /** Everything declared so far, merged across calls, so a rebuild recomputes the whole layout. */
23
+ declared = {};
24
+ /**
25
+ * Declares uniforms, merging with any already declared and rebuilding the texture.
26
+ *
27
+ * The layout packs every uniform into as few texels as it can, so adding one generally moves the
28
+ * others — hence the rebuild-and-copy rather than an in-place append. Redeclaring a name with a
29
+ * different type replaces it.
30
+ * @param schema - The uniforms to add.
31
+ * @param capacity - Instances the texture must hold.
32
+ */
33
+ add(schema, capacity) {
34
+ const vertex = { ...this.declared.vertex, ...schema.vertex };
35
+ const fragment = { ...this.declared.fragment, ...schema.fragment };
36
+ const layout = buildUniformLayout({ vertex, fragment });
37
+ const texture = new SquareDataTexture(Float32Array, layout.channels, layout.pixelsPerInstance, capacity, layout.uniformMap, layout.fetchInFragmentShader);
38
+ const previous = this.texture;
39
+ if (previous) {
40
+ copyUniforms(previous, texture, declaredTypes(this.declared), capacity);
41
+ previous.dispose();
42
+ }
43
+ this.texture = texture;
44
+ this.declared = { vertex, fragment };
45
+ }
46
+ /**
47
+ * Reads one instance's uniform.
48
+ * @param id - The instance to read.
49
+ * @param name - The uniform's name.
50
+ * @param target - Receives the result; unused for a `float`, which is returned directly.
51
+ * @returns The value.
52
+ */
53
+ get(id, name, target) {
54
+ return this.require().getUniformAt(id, name, target);
55
+ }
56
+ /**
57
+ * Writes one instance's uniform and marks its row for upload.
58
+ * @param id - The instance to write.
59
+ * @param name - The uniform's name.
60
+ * @param value - The value to write.
61
+ */
62
+ set(id, name, value) {
63
+ const texture = this.require();
64
+ texture.setUniformAt(id, name, value);
65
+ texture.enqueueUpdate(id);
66
+ }
67
+ /**
68
+ * Grows the texture to a new instance capacity, keeping what is written.
69
+ * @param capacity - The new capacity.
70
+ */
71
+ resize(capacity) {
72
+ this.texture?.resize(capacity);
73
+ }
74
+ /**
75
+ * Points a compiling shader at these uniforms: binds the texture, and opens `main` in both stages
76
+ * with the prologue that fetches this instance's values and brings them into scope under the
77
+ * names they were declared with.
78
+ *
79
+ * Does nothing and answers `false` while no uniform is declared — there is no texture to bind and
80
+ * nothing to name, so a caller whose own GLSL reads those names has to skip it too.
81
+ * @param shader - The program parameters to edit.
82
+ * @param indexName - The per-instance index the fetch addresses by, in shader scope where the
83
+ * prologue lands (a float; see {@link SquareDataTexture}). A caller computing it inside `main`
84
+ * supplies it with a later {@link openMain}, which lands ahead of this fetch.
85
+ * @param body - GLSL to run straight after the fetch, in both stages. Where code reading the
86
+ * uniforms goes, since nothing ahead of the prologue can see them.
87
+ * @returns Whether the shader was patched.
88
+ * @throws {Error} If either stage has no `main` to inject into.
89
+ */
90
+ patchShader(shader, indexName, body = '') {
91
+ const texture = this.texture;
92
+ if (!texture)
93
+ return false;
94
+ const glsl = texture.getUniformsGLSL(UNIFORMS_TEXTURE, indexName);
95
+ shader.uniforms[UNIFORMS_TEXTURE] = { value: texture };
96
+ shader.vertexShader = openMain(declareBeforeMain(shader.vertexShader, glsl.vertex.declarations), `${glsl.vertex.body}\n${body}`);
97
+ shader.fragmentShader = openMain(declareBeforeMain(shader.fragmentShader, glsl.fragment.declarations), `${glsl.fragment.body}\n${body}`);
98
+ return true;
99
+ }
100
+ /**
101
+ * Adopts another store's schema and an independent copy of its values, for cloning a mesh.
102
+ * @param source - The store to copy.
103
+ */
104
+ copyFrom(source) {
105
+ this.declared = {
106
+ vertex: { ...source.declared.vertex },
107
+ fragment: { ...source.declared.fragment },
108
+ };
109
+ this.texture = source.texture ? source.texture.clone() : null;
110
+ }
111
+ /** Frees the texture. */
112
+ dispose() {
113
+ this.texture?.dispose();
114
+ }
115
+ /**
116
+ * The texture, or a readable failure when nothing declared a uniform.
117
+ * @returns The texture.
118
+ * @throws {Error} If {@link add} has not been called.
119
+ */
120
+ require() {
121
+ if (!this.texture) {
122
+ throw new Error('InstanceUniforms: declare uniforms with "addUniforms" before reading or writing them.');
123
+ }
124
+ return this.texture;
125
+ }
126
+ }
127
+ /**
128
+ * Packs a schema into texels, largest uniform first so small ones fill the gaps.
129
+ * @param schema - Every uniform to lay out.
130
+ * @returns The layout.
131
+ */
132
+ function buildUniformLayout(schema) {
133
+ const vertexSchema = schema.vertex ?? {};
134
+ const fragmentSchema = schema.fragment ?? {};
135
+ const uniforms = [];
136
+ let totalSize = 0;
137
+ // Only when nothing is read in the vertex shader can the fetch move to the fragment shader.
138
+ let fetchInFragmentShader = true;
139
+ for (const name in vertexSchema) {
140
+ const type = vertexSchema[name];
141
+ const size = uniformSize(type);
142
+ totalSize += size;
143
+ uniforms.push({ name, type, size });
144
+ fetchInFragmentShader = false;
145
+ }
146
+ for (const name in fragmentSchema) {
147
+ if (vertexSchema[name])
148
+ continue;
149
+ const type = fragmentSchema[name];
150
+ const size = uniformSize(type);
151
+ totalSize += size;
152
+ uniforms.push({ name, type, size });
153
+ }
154
+ uniforms.sort((a, b) => b.size - a.size);
155
+ const uniformMap = new Map();
156
+ const filled = [];
157
+ for (const { name, size, type } of uniforms) {
158
+ uniformMap.set(name, { offset: uniformOffset(size, filled), size, type });
159
+ }
160
+ return {
161
+ channels: Math.min(totalSize, 4),
162
+ pixelsPerInstance: Math.ceil(totalSize / 4),
163
+ uniformMap,
164
+ fetchInFragmentShader,
165
+ };
166
+ }
167
+ /**
168
+ * Places one uniform, reusing a partly filled texel when it fits.
169
+ * @param size - Elements the uniform spans.
170
+ * @param filled - Elements used so far in each texel; extended as texels are opened.
171
+ * @returns The uniform's element offset within an instance's record.
172
+ */
173
+ function uniformOffset(size, filled) {
174
+ if (size < 4) {
175
+ for (let i = 0; i < filled.length; i++) {
176
+ if (filled[i] + size <= 4) {
177
+ const offset = i * 4 + filled[i];
178
+ filled[i] += size;
179
+ return offset;
180
+ }
181
+ }
182
+ }
183
+ const offset = filled.length * 4;
184
+ for (let remaining = size; remaining > 0; remaining -= 4) {
185
+ filled.push(remaining);
186
+ }
187
+ return offset;
188
+ }
189
+ /**
190
+ * Elements a uniform type occupies.
191
+ * @param type - The GLSL type.
192
+ * @returns Its element count.
193
+ * @throws {Error} If the type is not one this texture can store.
194
+ */
195
+ function uniformSize(type) {
196
+ switch (type) {
197
+ case 'float':
198
+ return 1;
199
+ case 'vec2':
200
+ return 2;
201
+ case 'vec3':
202
+ return 3;
203
+ case 'vec4':
204
+ return 4;
205
+ case 'mat3':
206
+ return 9;
207
+ case 'mat4':
208
+ return 16;
209
+ default:
210
+ throw new Error(`InstanceUniforms: invalid uniform type "${String(type)}".`);
211
+ }
212
+ }
213
+ /**
214
+ * Flattens a schema into the name-to-type pairs a rebuild has to carry across.
215
+ * @param schema - The declared uniforms.
216
+ * @returns Every uniform, whichever stage reads it.
217
+ */
218
+ function declaredTypes(schema) {
219
+ return { ...schema.vertex, ...schema.fragment };
220
+ }
221
+ /**
222
+ * Carries per-instance values from one layout to the next, so declaring a uniform does not discard
223
+ * what is already written. Goes through the public accessors, which is what makes it indifferent to
224
+ * the offsets moving underneath.
225
+ * @param previous - The texture being replaced.
226
+ * @param next - The texture replacing it.
227
+ * @param types - The uniforms to carry, by name.
228
+ * @param count - Instances to copy.
229
+ */
230
+ function copyUniforms(previous, next, types, count) {
231
+ for (const name in types) {
232
+ // Allocated once per uniform, not per instance, so a rebuild costs a handful of objects however
233
+ // many instances it carries across.
234
+ const target = uniformScratch(types[name]);
235
+ for (let id = 0; id < count; id++) {
236
+ next.setUniformAt(id, name, previous.getUniformAt(id, name, target));
237
+ }
238
+ }
239
+ }
240
+ /**
241
+ * A target for reading a uniform of the given type.
242
+ * @param type - The GLSL type.
243
+ * @returns The scratch object, or `undefined` for a `float`, which is read by value.
244
+ */
245
+ function uniformScratch(type) {
246
+ switch (type) {
247
+ case 'vec2':
248
+ return new Vector2();
249
+ case 'vec3':
250
+ return new Vector3();
251
+ case 'vec4':
252
+ return new Vector4();
253
+ case 'mat3':
254
+ return new Matrix3();
255
+ case 'mat4':
256
+ return new Matrix4();
257
+ default:
258
+ return undefined;
259
+ }
260
+ }
261
+ /**
262
+ * The sampler name the uniforms texture binds to. Private because both sides of the agreement it
263
+ * enforces — the generated GLSL and the `shader.uniforms[…]` entry — are set by
264
+ * {@link InstanceUniforms.patchShader}.
265
+ */
266
+ const UNIFORMS_TEXTURE = 'uniformsTexture';