@expofp/renderer 0.0.0-experimental.2d6c681

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 (280) hide show
  1. package/README.md +145 -0
  2. package/dist/Renderer.d.ts +102 -0
  3. package/dist/Renderer.js +96 -0
  4. package/dist/controls/CameraControls.d.ts +108 -0
  5. package/dist/controls/CameraControls.js +203 -0
  6. package/dist/controls/Controls.d.ts +53 -0
  7. package/dist/controls/Controls.js +54 -0
  8. package/dist/controls/camera-api.d.ts +226 -0
  9. package/dist/controls/camera-api.js +179 -0
  10. package/dist/controls/gestures.d.ts +71 -0
  11. package/dist/controls/gestures.js +98 -0
  12. package/dist/controls/handlers/DoubleTapZoom.d.ts +23 -0
  13. package/dist/controls/handlers/DoubleTapZoom.js +82 -0
  14. package/dist/controls/handlers/Inertia.d.ts +60 -0
  15. package/dist/controls/handlers/Inertia.js +222 -0
  16. package/dist/controls/handlers/Pan.d.ts +23 -0
  17. package/dist/controls/handlers/Pan.js +212 -0
  18. package/dist/controls/handlers/Pitch.d.ts +19 -0
  19. package/dist/controls/handlers/Pitch.js +200 -0
  20. package/dist/controls/handlers/Roll.d.ts +30 -0
  21. package/dist/controls/handlers/Roll.js +196 -0
  22. package/dist/controls/handlers/Zoom.d.ts +28 -0
  23. package/dist/controls/handlers/Zoom.js +34 -0
  24. package/dist/controls/hooks.d.ts +136 -0
  25. package/dist/controls/hooks.js +200 -0
  26. package/dist/controls/index.d.ts +9 -0
  27. package/dist/controls/index.js +7 -0
  28. package/dist/controls/zoom-to.d.ts +89 -0
  29. package/dist/controls/zoom-to.js +91 -0
  30. package/dist/core/InstanceUniforms.d.ts +108 -0
  31. package/dist/core/InstanceUniforms.js +298 -0
  32. package/dist/core/SquareDataTexture.d.ts +277 -0
  33. package/dist/core/SquareDataTexture.js +476 -0
  34. package/dist/core/batch/BatchedMesh.d.ts +74 -0
  35. package/dist/core/batch/BatchedMesh.js +155 -0
  36. package/dist/core/instanced/FrustumCulling.d.ts +141 -0
  37. package/dist/core/instanced/FrustumCulling.js +246 -0
  38. package/dist/core/instanced/InstancedMesh.d.ts +302 -0
  39. package/dist/core/instanced/InstancedMesh.js +497 -0
  40. package/dist/core/instanced/Instances.d.ts +119 -0
  41. package/dist/core/instanced/Instances.js +230 -0
  42. package/dist/core/instanced/Raycasting.d.ts +118 -0
  43. package/dist/core/instanced/Raycasting.js +134 -0
  44. package/dist/core/instanced/instanced-material.d.ts +77 -0
  45. package/dist/core/instanced/instanced-material.js +228 -0
  46. package/dist/core/lines/BatchedLines.d.ts +137 -0
  47. package/dist/core/lines/BatchedLines.js +215 -0
  48. package/dist/core/lines/FrustumCullingLines.d.ts +44 -0
  49. package/dist/core/lines/FrustumCullingLines.js +85 -0
  50. package/dist/core/lines/RaycastingLines.d.ts +45 -0
  51. package/dist/core/lines/RaycastingLines.js +91 -0
  52. package/dist/core/lines/lines-material.d.ts +47 -0
  53. package/dist/core/lines/lines-material.js +124 -0
  54. package/dist/core/shader-source.d.ts +70 -0
  55. package/dist/core/shader-source.js +110 -0
  56. package/dist/core/sprites/BatchedSprites.d.ts +115 -0
  57. package/dist/core/sprites/BatchedSprites.js +173 -0
  58. package/dist/core/sprites/RaycastingSprites.d.ts +41 -0
  59. package/dist/core/sprites/RaycastingSprites.js +71 -0
  60. package/dist/core/sprites/sprite-material.d.ts +59 -0
  61. package/dist/core/sprites/sprite-material.js +192 -0
  62. package/dist/core/text/BatchedText.d.ts +233 -0
  63. package/dist/core/text/BatchedText.js +473 -0
  64. package/dist/core/text/MemberTexture.d.ts +90 -0
  65. package/dist/core/text/MemberTexture.js +289 -0
  66. package/dist/core/text/Text.d.ts +88 -0
  67. package/dist/core/text/Text.js +230 -0
  68. package/dist/core/text/batched-text-material.d.ts +56 -0
  69. package/dist/core/text/batched-text-material.js +123 -0
  70. package/dist/core/text/glyph-packing.d.ts +17 -0
  71. package/dist/core/text/glyph-packing.js +61 -0
  72. package/dist/core/typed-array.d.ts +16 -0
  73. package/dist/core/typed-array.js +20 -0
  74. package/dist/debug/components/DebugPanelProvider.d.ts +20 -0
  75. package/dist/debug/components/DebugPanelProvider.js +50 -0
  76. package/dist/debug/components/DebugTools.d.ts +22 -0
  77. package/dist/debug/components/DebugTools.js +28 -0
  78. package/dist/debug/components/Gui.d.ts +22 -0
  79. package/dist/debug/components/Gui.js +42 -0
  80. package/dist/debug/components/MemoryInfo.d.ts +22 -0
  81. package/dist/debug/components/MemoryInfo.js +42 -0
  82. package/dist/debug/components/Panel.d.ts +31 -0
  83. package/dist/debug/components/Panel.js +104 -0
  84. package/dist/debug/components/Stats.d.ts +9 -0
  85. package/dist/debug/components/Stats.js +24 -0
  86. package/dist/debug/debug-chunk.d.ts +26 -0
  87. package/dist/debug/debug-chunk.js +32 -0
  88. package/dist/debug/debug-context.d.ts +41 -0
  89. package/dist/debug/debug-context.js +59 -0
  90. package/dist/debug/index.d.ts +41 -0
  91. package/dist/debug/index.js +43 -0
  92. package/dist/events/LayoutDriver.d.ts +36 -0
  93. package/dist/events/LayoutDriver.js +192 -0
  94. package/dist/events/PointerMissed.d.ts +23 -0
  95. package/dist/events/PointerMissed.js +58 -0
  96. package/dist/events/def-culling.d.ts +46 -0
  97. package/dist/events/def-culling.js +69 -0
  98. package/dist/events/layout-targets.d.ts +151 -0
  99. package/dist/events/layout-targets.js +397 -0
  100. package/dist/events/placement/box.d.ts +59 -0
  101. package/dist/events/placement/box.js +96 -0
  102. package/dist/events/placement/grid.d.ts +161 -0
  103. package/dist/events/placement/grid.js +235 -0
  104. package/dist/events/placement/index.d.ts +2 -0
  105. package/dist/events/placement/index.js +1 -0
  106. package/dist/events/placement/pass.d.ts +70 -0
  107. package/dist/events/placement/pass.js +617 -0
  108. package/dist/events/pointer.d.ts +194 -0
  109. package/dist/events/pointer.js +356 -0
  110. package/dist/external/ExternalCanvas.d.ts +56 -0
  111. package/dist/external/ExternalCanvas.js +177 -0
  112. package/dist/external/external-camera.d.ts +18 -0
  113. package/dist/external/external-camera.js +103 -0
  114. package/dist/external/external-events.d.ts +14 -0
  115. package/dist/external/external-events.js +33 -0
  116. package/dist/index.d.ts +13 -0
  117. package/dist/index.js +14 -0
  118. package/dist/objects/Background.d.ts +31 -0
  119. package/dist/objects/Background.js +37 -0
  120. package/dist/objects/containers/SlotContainer.d.ts +312 -0
  121. package/dist/objects/containers/SlotContainer.js +311 -0
  122. package/dist/objects/containers/hooks.d.ts +61 -0
  123. package/dist/objects/containers/hooks.js +196 -0
  124. package/dist/objects/containers/index.d.ts +6 -0
  125. package/dist/objects/containers/index.js +4 -0
  126. package/dist/objects/containers/keys.d.ts +10 -0
  127. package/dist/objects/containers/keys.js +18 -0
  128. package/dist/objects/containers/mesh-writes.d.ts +106 -0
  129. package/dist/objects/containers/mesh-writes.js +97 -0
  130. package/dist/objects/containers/props.d.ts +33 -0
  131. package/dist/objects/containers/props.js +1 -0
  132. package/dist/objects/images/ImageInstance.d.ts +29 -0
  133. package/dist/objects/images/ImageInstance.js +152 -0
  134. package/dist/objects/images/Images.d.ts +49 -0
  135. package/dist/objects/images/Images.js +394 -0
  136. package/dist/objects/images/atlas-packer.d.ts +129 -0
  137. package/dist/objects/images/atlas-packer.js +287 -0
  138. package/dist/objects/images/image-container.d.ts +74 -0
  139. package/dist/objects/images/image-container.js +100 -0
  140. package/dist/objects/images/index.d.ts +2 -0
  141. package/dist/objects/images/index.js +1 -0
  142. package/dist/objects/images/pack-sources.d.ts +190 -0
  143. package/dist/objects/images/pack-sources.js +272 -0
  144. package/dist/objects/images/texture-budget.d.ts +120 -0
  145. package/dist/objects/images/texture-budget.js +215 -0
  146. package/dist/objects/images/worker/atlas-worker.d.ts +56 -0
  147. package/dist/objects/images/worker/atlas-worker.js +123 -0
  148. package/dist/objects/images/worker/client.d.ts +62 -0
  149. package/dist/objects/images/worker/client.js +249 -0
  150. package/dist/objects/images/worker/index.d.ts +10 -0
  151. package/dist/objects/images/worker/index.js +1 -0
  152. package/dist/objects/layer/Layer.d.ts +28 -0
  153. package/dist/objects/layer/Layer.js +134 -0
  154. package/dist/objects/layer/index.d.ts +2 -0
  155. package/dist/objects/layer/index.js +1 -0
  156. package/dist/objects/layer/layer-context.d.ts +74 -0
  157. package/dist/objects/layer/layer-context.js +82 -0
  158. package/dist/objects/layer/mount-scheduler.d.ts +131 -0
  159. package/dist/objects/layer/mount-scheduler.js +289 -0
  160. package/dist/objects/layer/visibility-gui.d.ts +19 -0
  161. package/dist/objects/layer/visibility-gui.js +93 -0
  162. package/dist/objects/lighting/Lighting.d.ts +92 -0
  163. package/dist/objects/lighting/Lighting.js +384 -0
  164. package/dist/objects/lighting/OcclusionPasses.d.ts +23 -0
  165. package/dist/objects/lighting/OcclusionPasses.js +271 -0
  166. package/dist/objects/lighting/index.d.ts +11 -0
  167. package/dist/objects/lighting/index.js +10 -0
  168. package/dist/objects/lighting/lighting-gui.d.ts +24 -0
  169. package/dist/objects/lighting/lighting-gui.js +114 -0
  170. package/dist/objects/lighting/occluder.d.ts +34 -0
  171. package/dist/objects/lighting/occluder.js +62 -0
  172. package/dist/objects/lighting/occlusion-targets.d.ts +143 -0
  173. package/dist/objects/lighting/occlusion-targets.js +276 -0
  174. package/dist/objects/lines/LineInstance.d.ts +21 -0
  175. package/dist/objects/lines/LineInstance.js +73 -0
  176. package/dist/objects/lines/Lines.d.ts +17 -0
  177. package/dist/objects/lines/Lines.js +31 -0
  178. package/dist/objects/lines/index.d.ts +2 -0
  179. package/dist/objects/lines/index.js +1 -0
  180. package/dist/objects/lines/line-container.d.ts +35 -0
  181. package/dist/objects/lines/line-container.js +40 -0
  182. package/dist/objects/materials/ColorMaterial.d.ts +92 -0
  183. package/dist/objects/materials/ColorMaterial.js +124 -0
  184. package/dist/objects/materials/LineMaterial.d.ts +44 -0
  185. package/dist/objects/materials/LineMaterial.js +74 -0
  186. package/dist/objects/materials/ModelMaterial.d.ts +53 -0
  187. package/dist/objects/materials/ModelMaterial.js +111 -0
  188. package/dist/objects/materials/TextureMaterial.d.ts +33 -0
  189. package/dist/objects/materials/TextureMaterial.js +48 -0
  190. package/dist/objects/materials/effects/ao/ambient-occlusion-pass.d.ts +77 -0
  191. package/dist/objects/materials/effects/ao/ambient-occlusion-pass.js +296 -0
  192. package/dist/objects/materials/effects/ao/ambient-occlusion-patch.d.ts +29 -0
  193. package/dist/objects/materials/effects/ao/ambient-occlusion-patch.js +115 -0
  194. package/dist/objects/materials/effects/ao/blur-pass.d.ts +61 -0
  195. package/dist/objects/materials/effects/ao/blur-pass.js +101 -0
  196. package/dist/objects/materials/effects/ao/index.d.ts +129 -0
  197. package/dist/objects/materials/effects/ao/index.js +72 -0
  198. package/dist/objects/materials/effects/color-dim.d.ts +115 -0
  199. package/dist/objects/materials/effects/color-dim.js +206 -0
  200. package/dist/objects/materials/effects/contact-shadow.d.ts +100 -0
  201. package/dist/objects/materials/effects/contact-shadow.js +167 -0
  202. package/dist/objects/materials/effects/effects-context.d.ts +53 -0
  203. package/dist/objects/materials/effects/effects-context.js +24 -0
  204. package/dist/objects/materials/shared.d.ts +24 -0
  205. package/dist/objects/materials/shared.js +72 -0
  206. package/dist/objects/models/ModelInstance.d.ts +22 -0
  207. package/dist/objects/models/ModelInstance.js +33 -0
  208. package/dist/objects/models/Models.d.ts +22 -0
  209. package/dist/objects/models/Models.js +186 -0
  210. package/dist/objects/models/gltf-cache.d.ts +17 -0
  211. package/dist/objects/models/gltf-cache.js +50 -0
  212. package/dist/objects/models/index.d.ts +2 -0
  213. package/dist/objects/models/index.js +1 -0
  214. package/dist/objects/models/model-asset.d.ts +48 -0
  215. package/dist/objects/models/model-asset.js +77 -0
  216. package/dist/objects/models/model-container.d.ts +123 -0
  217. package/dist/objects/models/model-container.js +207 -0
  218. package/dist/objects/shapes/ShapeInstance.d.ts +26 -0
  219. package/dist/objects/shapes/ShapeInstance.js +39 -0
  220. package/dist/objects/shapes/Shapes.d.ts +21 -0
  221. package/dist/objects/shapes/Shapes.js +131 -0
  222. package/dist/objects/shapes/index.d.ts +2 -0
  223. package/dist/objects/shapes/index.js +1 -0
  224. package/dist/objects/shapes/shape-container.d.ts +141 -0
  225. package/dist/objects/shapes/shape-container.js +242 -0
  226. package/dist/objects/shapes/shape-geometry.d.ts +27 -0
  227. package/dist/objects/shapes/shape-geometry.js +94 -0
  228. package/dist/objects/text/TextInstance.d.ts +22 -0
  229. package/dist/objects/text/TextInstance.js +95 -0
  230. package/dist/objects/text/Texts.d.ts +30 -0
  231. package/dist/objects/text/Texts.js +43 -0
  232. package/dist/objects/text/index.d.ts +2 -0
  233. package/dist/objects/text/index.js +1 -0
  234. package/dist/objects/text/text-container.d.ts +121 -0
  235. package/dist/objects/text/text-container.js +229 -0
  236. package/dist/objects/text/text-layout.d.ts +92 -0
  237. package/dist/objects/text/text-layout.js +180 -0
  238. package/dist/space/Camera.d.ts +103 -0
  239. package/dist/space/Camera.js +321 -0
  240. package/dist/space/Scene.d.ts +47 -0
  241. package/dist/space/Scene.js +127 -0
  242. package/dist/space/Space.d.ts +325 -0
  243. package/dist/space/Space.js +359 -0
  244. package/dist/space/VisibleRect.d.ts +45 -0
  245. package/dist/space/VisibleRect.js +129 -0
  246. package/dist/space/camera-handle.d.ts +67 -0
  247. package/dist/space/camera-handle.js +63 -0
  248. package/dist/space/camera-kind.d.ts +26 -0
  249. package/dist/space/camera-kind.js +30 -0
  250. package/dist/space/canvas-size.d.ts +24 -0
  251. package/dist/space/canvas-size.js +20 -0
  252. package/dist/space/clipping.d.ts +69 -0
  253. package/dist/space/clipping.js +130 -0
  254. package/dist/space/layout-measure.d.ts +67 -0
  255. package/dist/space/layout-measure.js +175 -0
  256. package/dist/space/viewport.d.ts +112 -0
  257. package/dist/space/viewport.js +67 -0
  258. package/dist/types/defs.d.ts +234 -0
  259. package/dist/types/defs.js +1 -0
  260. package/dist/types/events.d.ts +198 -0
  261. package/dist/types/events.js +1 -0
  262. package/dist/types/guards.d.ts +21 -0
  263. package/dist/types/guards.js +52 -0
  264. package/dist/update/index.d.ts +3 -0
  265. package/dist/update/index.js +2 -0
  266. package/dist/update/slot-writers.d.ts +30 -0
  267. package/dist/update/slot-writers.js +76 -0
  268. package/dist/update/store.d.ts +41 -0
  269. package/dist/update/store.js +104 -0
  270. package/dist/utils/arrays.d.ts +13 -0
  271. package/dist/utils/arrays.js +20 -0
  272. package/dist/utils/colors.d.ts +19 -0
  273. package/dist/utils/colors.js +27 -0
  274. package/dist/utils/hot-state.d.ts +41 -0
  275. package/dist/utils/hot-state.js +37 -0
  276. package/dist/utils/logging.d.ts +29 -0
  277. package/dist/utils/logging.js +55 -0
  278. package/dist/utils/math.d.ts +52 -0
  279. package/dist/utils/math.js +76 -0
  280. package/package.json +79 -0
@@ -0,0 +1,207 @@
1
+ import { Matrix4, Quaternion, Vector3 } from 'three';
2
+ import { SlotContainer } from '../containers';
3
+ import { DIM_SCHEMA } from '../materials/effects/color-dim';
4
+ /**
5
+ * The per-instance uniform a model mesh reads its opacity from. The container owns the name: it
6
+ * declares the schema on bind and writes each slot's value, and hands the name to `ModelMaterial`,
7
+ * which patches its shader to multiply by it.
8
+ */
9
+ export const OPACITY_UNIFORM = 'instanceOpacity';
10
+ /**
11
+ * Every kind of model batch, **in the order they draw within a layer** — which is what `Models`
12
+ * numbers them by.
13
+ */
14
+ export const MODEL_KINDS = ['opaque', 'translucent'];
15
+ /**
16
+ * Which batch a model belongs in: `translucent` exactly when its `opacity` is below 1.
17
+ *
18
+ * A partition rather than a per-instance flag, because `transparent` is a **material** property —
19
+ * three decides which render list an *object* belongs to, never which instance — so two copies of
20
+ * one model differing only in opacity cannot share a mesh. `opacity` is updatable, so a def can
21
+ * cross the boundary at any time; that is what `reportKindMismatch` is for.
22
+ * @param def - The model to classify.
23
+ * @returns The kind of batch it draws from.
24
+ */
25
+ export function modelKindOf(def) {
26
+ return (def.opacity ?? 1) < 1 ? 'translucent' : 'opaque';
27
+ }
28
+ /**
29
+ * One model part's slot bookkeeping: every def in the layer drawing that part gets an instance, so
30
+ * twenty copies of a chair are one draw call rather than twenty.
31
+ *
32
+ * Much simpler than a shape container. The geometry is the part's — shared, never per instance — so
33
+ * there is no geometry cache and no capacity pre-scan, and `InstancedMeshImpl` grows its own buffers,
34
+ * so nothing here has to size anything. What is left is a slot per def and the transform each one
35
+ * draws with.
36
+ */
37
+ export class ModelContainer extends SlotContainer {
38
+ /** The constructor args R3F builds the mesh from; a frozen snapshot, never mutated. */
39
+ args;
40
+ /** Which kind of member this container holds, and so which render list its mesh draws from. */
41
+ kind;
42
+ /**
43
+ * What the mesh drawing this part must carry, so that three sees the mirror the plan's frame
44
+ * requires and {@link place}'s plan-space answers land where they say. See {@link PLAN_FROM_GLTF}.
45
+ */
46
+ meshMatrix = PLAN_FROM_GLTF;
47
+ /** The model's bounds in plan units; every instance is fitted against them. */
48
+ extents;
49
+ /**
50
+ * Builds the bookkeeping for one model part.
51
+ * @param extents - The model's own bounds, y-up, as `bakeGltf` left them.
52
+ * @param count - How many defs the layer holds for this part, used as the mesh's initial capacity.
53
+ * @param kind - Which of the layer's batches this is; its material is built for that one alone.
54
+ */
55
+ constructor(extents, count, kind = 'opaque') {
56
+ super({ schema: MODEL_SCHEMA });
57
+ // Mapped once, here, so that everything below reads plan units — `fitToBounds` included.
58
+ this.extents = extents.clone().applyMatrix4(PLAN_FROM_GLTF);
59
+ this.kind = kind;
60
+ this.args = [{ capacity: Math.max(count, 1) }];
61
+ }
62
+ /**
63
+ * Paint order is not implemented for models, so this does nothing.
64
+ *
65
+ * Not an omission, and not a limitation either — the mesh takes a `customSort` like any other, and
66
+ * wiring the inherited pass to it would take a line. Def order is simply the wrong rule here. It
67
+ * is the 2D painter's rule, and it earns its keep only where nothing writes depth; a model does
68
+ * write depth, so which of two overlapping models is in front is settled by the depth buffer
69
+ * whatever order they draw in, and a *translucent* one wants farthest-first — depth order, which
70
+ * def order cannot express and which the mesh already applies for itself (`sortObjects`, see
71
+ * `Models`). Nor would def order reach across a layer: a model layer is one mesh per source
72
+ * material, and no sort inside a mesh orders a def against one drawn from another part.
73
+ *
74
+ * A no-op rather than the inherited pass, so a caller that orders every other layer kind does not
75
+ * quietly appear to order this one.
76
+ */
77
+ setPaintOrder() {
78
+ // Nothing: the depth buffer orders models, and a layer's parts are separate meshes regardless.
79
+ }
80
+ /**
81
+ * Writes a def's placement into its slot: the model's extents fitted into `bounds`.
82
+ *
83
+ * The mesh carries {@link PLAN_FROM_GLTF}, so its instances are placed in the model's own space,
84
+ * while {@link fitToBounds} answers in the plan's. Conjugating moves the placement across — undo
85
+ * the mesh's map, place, redo it — and is unavoidable: the instance matrix is innermost, so
86
+ * anything the mesh applies outside it has to be taken back inside.
87
+ * @param slot - The instance's slot.
88
+ * @param def - The def supplying the bounds.
89
+ */
90
+ place(slot, def) {
91
+ this.mesh.setMatrixAt(slot,
92
+ // Read right to left: into the plan, placed there, and back out again.
93
+ _placement
94
+ .multiplyMatrices(GLTF_FROM_PLAN, fitToBounds(this.extents, def.bounds))
95
+ .multiply(PLAN_FROM_GLTF));
96
+ }
97
+ /**
98
+ * Writes a def's opacity into its slot — **clamped to opaque unless this batch blends**.
99
+ *
100
+ * An opaque batch's material is `transparent: false`, which three answers by dropping
101
+ * `NormalBlending` to `NoBlending` (`WebGLState.setMaterial`). With blending off a fragment alpha
102
+ * below 1 is not composited but *written*, into a framebuffer the page shows through — so the
103
+ * model does not fade, it turns white.
104
+ *
105
+ * That is not a hypothetical: an opacity leaving 1 re-splits the layer, and until React commits it
106
+ * the def is still in this batch with its new opacity already written. Clamping is what makes the
107
+ * gap invisible — the model simply stays solid until the batch that can blend it takes over —
108
+ * without anything having to be fast enough.
109
+ * @param slot - The instance's slot.
110
+ * @param def - The def supplying the opacity.
111
+ */
112
+ setOpacity(slot, def) {
113
+ const opacity = this.kind === 'translucent' ? (def.opacity ?? 1) : 1;
114
+ this.mesh.setUniformAt(slot, OPACITY_UNIFORM, opacity);
115
+ }
116
+ addInstance() {
117
+ // `addInstances` grows the buffers itself if the layer outgrows its sized capacity, so there is
118
+ // nothing to pre-scan; the id comes back through the creation callback.
119
+ let slot = -1;
120
+ this.mesh.addInstances(1, (id) => {
121
+ slot = id;
122
+ });
123
+ return slot;
124
+ }
125
+ removeInstance(slot) {
126
+ this.mesh.removeInstances(slot);
127
+ }
128
+ }
129
+ /**
130
+ * Composes the transform that lands a baked model inside a def's bounds.
131
+ *
132
+ * The model is scaled uniformly — by the *smaller* of the two axis ratios, so it fits inside the
133
+ * rect rather than being stretched to it — then spun about z by the bounds' rotation and moved so its
134
+ * center sits at the bounds' center with its base at the bounds' elevation.
135
+ *
136
+ * Uniform scale is what keeps a model's proportions: a chair placed in a rect twice as wide as it is
137
+ * deep should not become a wide chair.
138
+ *
139
+ * Plan units throughout, in and out — {@link ModelContainer.place} is what carries the answer into
140
+ * the frame the mesh draws in.
141
+ * @param extents - The model's bounds, in plan units.
142
+ * @param bounds - Where the def wants it, in plan space.
143
+ * @returns The shared scratch matrix, composed. Valid until the next call.
144
+ */
145
+ export function fitToBounds(extents, bounds) {
146
+ extents.getSize(_size);
147
+ extents.getCenter(_center);
148
+ const scale = Math.min(_size.x > 0 ? bounds.size.x / _size.x : 1, _size.y > 0 ? bounds.size.y / _size.y : 1);
149
+ // Centered on xy and sitting on its base in z, before anything else moves it.
150
+ _recenter.makeTranslation(-_center.x, -_center.y, -extents.min.z);
151
+ _position.set(bounds.center.x, bounds.center.y, bounds.elevation);
152
+ _quaternion.setFromAxisAngle(Z_AXIS, bounds.rotation);
153
+ _scale.setScalar(scale);
154
+ return _matrix.compose(_position, _quaternion, _scale).multiply(_recenter);
155
+ }
156
+ /**
157
+ * glTF → plan, and its own inverse.
158
+ *
159
+ * glTF is y-up with +z toward the viewer; the plan reads x right, y down, z up. Composed rather than
160
+ * written out as sixteen numbers because it *is* the pair legacy applies to a loaded scene — a mirror
161
+ * on y, then a quarter turn about x — and three composes a matrix as `T·R·S`, so the scale is the
162
+ * one that goes first: `(x, y, z)_gltf → (x, z, y)_plan`.
163
+ *
164
+ * The determinant is negative, and has to be: on screen the plan is left-handed — x right, y down, z
165
+ * toward the viewer — while glTF is right-handed, so the only maps between them that leave a model
166
+ * **unmirrored** are themselves mirrors.
167
+ *
168
+ * Which is why a **mesh** carries this and the geometry does not. three decides face orientation
169
+ * from `matrixWorld.determinant()` alone: a mirror baked into vertices is invisible to it and
170
+ * culling comes out inverted, drawing a closed model as a hollow shell; a mirror living only in the
171
+ * instance matrix is invisible to it too, and — worse — leaves the CPU raycaster correct while the
172
+ * rasterizer is wrong. The mesh transform is the one place three accounts for it.
173
+ *
174
+ * {@link ModelContainer.place} conjugates with it and with {@link GLTF_FROM_PLAN}.
175
+ */
176
+ const PLAN_FROM_GLTF = new Matrix4()
177
+ .makeRotationX(-Math.PI / 2)
178
+ .multiply(new Matrix4().makeScale(1, -1, 1));
179
+ /**
180
+ * Plan → glTF, the way back.
181
+ *
182
+ * It happens to equal {@link PLAN_FROM_GLTF} — swapping two axes twice returns you to where you
183
+ * started — so the inverse could be spelled with the same constant and cost nothing. It is not,
184
+ * because {@link ModelContainer.place} is a conjugation and should look like one: written with a
185
+ * single constant on both sides, it reads as an error, and it would quietly *become* one the day the
186
+ * mapping stops being its own inverse.
187
+ */
188
+ const GLTF_FROM_PLAN = PLAN_FROM_GLTF.clone().invert();
189
+ /**
190
+ * The schema the mesh declares so it can hold an {@link OPACITY_UNIFORM} and a dim per slot.
191
+ *
192
+ * Both in one call although the two come from different places: `InstanceUniforms.add` merges and
193
+ * rebuilds the backing texture, so declaring them separately would build it twice per bind.
194
+ */
195
+ const MODEL_SCHEMA = {
196
+ fragment: { [OPACITY_UNIFORM]: 'float' },
197
+ vertex: { ...DIM_SCHEMA.vertex },
198
+ };
199
+ const Z_AXIS = new Vector3(0, 0, 1);
200
+ const _size = new Vector3();
201
+ const _center = new Vector3();
202
+ const _position = new Vector3();
203
+ const _scale = new Vector3();
204
+ const _quaternion = new Quaternion();
205
+ const _recenter = new Matrix4();
206
+ const _matrix = new Matrix4();
207
+ const _placement = new Matrix4();
@@ -0,0 +1,26 @@
1
+ import { type ShapeDef } from '../../types/defs';
2
+ import { type InstanceProps } from '../containers';
3
+ import { type ShapeContainer } from './shape-container';
4
+ /** Props for {@link ShapeInstance}. */
5
+ export type ShapeInstanceProps = InstanceProps<ShapeDef, ShapeContainer>;
6
+ /**
7
+ * Keeps one shape's instance current: claims a slot on mount, writes the def's transform, color,
8
+ * and visibility on mount and after every `update(def)`, and releases the slot on unmount.
9
+ *
10
+ * Renders no scene node — the mesh belongs to the container. All shape-specific knowledge lives
11
+ * here rather than in the container, so "how does one `ShapeDef` become GPU state" is answered by
12
+ * the file you would open to ask it; visibility is the exception, because `hidden` is common to
13
+ * every def type and `useDefSync` writes it for all of them.
14
+ *
15
+ * Memoized at declaration, deliberately, despite the React Compiler: a structural change hands the
16
+ * layer a fresh `defs` snapshot, so its compiled cache rebuilds every instance element and
17
+ * element-identity bailout never applies — only `memo`'s prop-equality bailout keeps the unchanged
18
+ * siblings from re-rendering. The bailout works because both props are identity-stable across
19
+ * structural changes — paint order is deliberately not a prop; the container writes it for the
20
+ * whole sibling list in one pass (`useContainer`). Correctness never depends on the memo: without
21
+ * it every instance re-renders to the same result.
22
+ * @param props - The def and its container.
23
+ * @returns `null`; the instance lives in the mesh, not the scene graph.
24
+ */
25
+ export declare const ShapeInstance: import("react").MemoExoticComponent<(props: ShapeInstanceProps) => null>;
26
+ //# sourceMappingURL=ShapeInstance.d.ts.map
@@ -0,0 +1,39 @@
1
+ import { memo } from 'react';
2
+ import { createLogger } from '../../utils/logging';
3
+ import { shapeToMatrix, useDefSync, writeColorIfChanged, writeMatrixIfChanged, } from '../containers';
4
+ import { shapeKindOf } from './shape-container';
5
+ const logger = createLogger('shape-instance');
6
+ /**
7
+ * Keeps one shape's instance current: claims a slot on mount, writes the def's transform, color,
8
+ * and visibility on mount and after every `update(def)`, and releases the slot on unmount.
9
+ *
10
+ * Renders no scene node — the mesh belongs to the container. All shape-specific knowledge lives
11
+ * here rather than in the container, so "how does one `ShapeDef` become GPU state" is answered by
12
+ * the file you would open to ask it; visibility is the exception, because `hidden` is common to
13
+ * every def type and `useDefSync` writes it for all of them.
14
+ *
15
+ * Memoized at declaration, deliberately, despite the React Compiler: a structural change hands the
16
+ * layer a fresh `defs` snapshot, so its compiled cache rebuilds every instance element and
17
+ * element-identity bailout never applies — only `memo`'s prop-equality bailout keeps the unchanged
18
+ * siblings from re-rendering. The bailout works because both props are identity-stable across
19
+ * structural changes — paint order is deliberately not a prop; the container writes it for the
20
+ * whole sibling list in one pass (`useContainer`). Correctness never depends on the memo: without
21
+ * it every instance re-renders to the same result.
22
+ * @param props - The def and its container.
23
+ * @returns `null`; the instance lives in the mesh, not the scene graph.
24
+ */
25
+ export const ShapeInstance = memo(function ShapeInstance(props) {
26
+ const { def, container } = props;
27
+ useDefSync(container, def, () => container.geometryFor(def.shape), (slot) => {
28
+ writeMatrixIfChanged(container.mesh, slot, shapeToMatrix(def.shape));
29
+ writeColorIfChanged(container.mesh, slot, def, logger);
30
+ // Checked on every sync rather than only behind a color write, because a def leaves its batch's
31
+ // kind two ways: its alpha crosses 1, or its `shape` is replaced with one of another kind.
32
+ // Neither re-renders this layer on its own — an update reaches the instance and stops there — so
33
+ // this is where a batch learns it is holding something it should not. `shapeKindOf` returns
34
+ // before it looks at the color for a planar shape, so a flat layer pays nothing for the check.
35
+ if (shapeKindOf(def) !== container.kind)
36
+ container.reportKindMismatch();
37
+ });
38
+ return null;
39
+ });
@@ -0,0 +1,21 @@
1
+ import { type ShapeDef } from '../../types/defs';
2
+ import { type DefsProps } from '../containers';
3
+ /** Props for {@link Shapes}. */
4
+ export type ShapesProps = DefsProps<ShapeDef>;
5
+ /**
6
+ * Draws a collection of shapes as instances of one `BatchedMesh`, so they render in a single draw
7
+ * call.
8
+ *
9
+ * The container owns the capacity, the geometry cache, and paint order; the mesh itself is created
10
+ * by R3F from the container's capacity and bound back through `ref`, so R3F owns its lifetime —
11
+ * including
12
+ * disposal on unmount and StrictMode's simulated remount, where the mesh survives because the
13
+ * element is never deleted. Each def gets a {@link ShapeInstance} that owns the writes for its own
14
+ * slot; refs attach before passive effects run, so the mesh is bound before the first claim.
15
+ * Membership is reconciled by React: a new `defs` array re-renders this component, and React mounts
16
+ * or unmounts exactly the defs that entered or left — there is no differ of our own.
17
+ * @param props - The shapes to draw.
18
+ * @returns The mesh and one instance component per shape.
19
+ */
20
+ export declare function Shapes(props: ShapesProps): import("react").JSX.Element;
21
+ //# sourceMappingURL=Shapes.d.ts.map
@@ -0,0 +1,131 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { BatchedMesh } from '../../core/batch/BatchedMesh';
4
+ import { defKey, useContainer } from '../containers';
5
+ import { useOccluder } from '../lighting/occluder';
6
+ import { ColorMaterial } from '../materials/ColorMaterial';
7
+ import { SHAPE_KINDS, ShapeContainer, shapeKindOf } from './shape-container';
8
+ import { ShapeInstance } from './ShapeInstance';
9
+ /**
10
+ * Draws a collection of shapes as instances of one `BatchedMesh`, so they render in a single draw
11
+ * call.
12
+ *
13
+ * The container owns the capacity, the geometry cache, and paint order; the mesh itself is created
14
+ * by R3F from the container's capacity and bound back through `ref`, so R3F owns its lifetime —
15
+ * including
16
+ * disposal on unmount and StrictMode's simulated remount, where the mesh survives because the
17
+ * element is never deleted. Each def gets a {@link ShapeInstance} that owns the writes for its own
18
+ * slot; refs attach before passive effects run, so the mesh is bound before the first claim.
19
+ * Membership is reconciled by React: a new `defs` array re-renders this component, and React mounts
20
+ * or unmounts exactly the defs that entered or left — there is no differ of our own.
21
+ * @param props - The shapes to draw.
22
+ * @returns The mesh and one instance component per shape.
23
+ */
24
+ export function Shapes(props) {
25
+ const { defs } = props;
26
+ // Handed to each batch as a prefix of *its* members rather than apportioned between them: the
27
+ // batches are three at most, so a slice can overshoot the scheduler's budget by that factor at
28
+ // worst, and the scheduler measures what a slice actually cost and sizes the next one from it.
29
+ // Apportioning would cost a walk of the layer's defs per render to find where the prefix falls.
30
+ const mounted = props.mounted ?? defs.length;
31
+ // Held in state rather than derived each render, the same "adjust on prop change" shape `Images`
32
+ // uses for its pack — because the split has a second trigger besides `defs`. A def's color and its
33
+ // shape are ordinary slot writes that never re-render this component, so either one crossing a
34
+ // kind boundary would otherwise leave the def in a batch that no longer suits it. `repartition` is
35
+ // what the containers call when they notice. It is also what carries the mounted batches forward,
36
+ // so the updater form is load-bearing in both places: a re-split must not forget which exist.
37
+ const [partition, setPartition] = useState(() => partitionShapes(defs, {}));
38
+ if (partition.defs !== defs)
39
+ setPartition((current) => partitionShapes(defs, current.batches));
40
+ /** Re-splits the current membership, for a batch that found a member of the wrong kind. */
41
+ function repartition() {
42
+ setPartition((current) => partitionShapes(defs, current.batches));
43
+ }
44
+ const { batches } = partition;
45
+ // A batch is **sticky**: once mounted it stays, standing empty rather than unmounting when its
46
+ // last member leaves. A color whose alpha oscillates across 1 (a selection pulse) would otherwise
47
+ // tear down and rebuild a batch per crossing — a `ShapeContainer`, a `BatchedMesh` with its typed
48
+ // arrays and data textures, and a material whose compiled program three frees along with the last
49
+ // reference to it. Between two batches that both already exist, the same crossing is a
50
+ // `deleteInstance` and an `addInstance`. That is what an entry in `batches` means: the kind has
51
+ // held a member at some point, so its batch renders whether or not it holds one now.
52
+ //
53
+ // Mounting all three up front was rejected instead: the tax is the material, not the mesh, and it
54
+ // would fall on every shape layer of every plan, including the many with no solid content at all.
55
+ // Stickiness charges it once, and only to a layer that has actually held that kind. An emptied
56
+ // batch keeps its buffers, since the container never shrinks, and draws nothing — three's
57
+ // multi-draw path returns early at zero draws.
58
+ //
59
+ // `renderOrder` is each kind's place in `SHAPE_KINDS` rather than insertion order, for the reason
60
+ // layers carry one: these batches tie on `groupOrder`, so the tiebreak would fall to
61
+ // `material.id`, which ascends in *mount* order. A layer that starts with only solids and later
62
+ // gains a flat rect would mount the planar batch second, give it the higher id, and draw it after
63
+ // the solid — the inversion Phase 1 removed at layer level, reappearing one level down.
64
+ //
65
+ // The translucent batch's number barely matters on its own: `transparent: true` puts it in three's
66
+ // transparent list, drawn after every opaque object in the scene whatever the order says. It is
67
+ // numbered anyway so the intent reads correctly and so it still orders against any other
68
+ // translucent batch tying with it.
69
+ return (_jsx(_Fragment, { children: SHAPE_KINDS.map((kind, renderOrder) => {
70
+ const members = batches[kind];
71
+ if (!members)
72
+ return null;
73
+ return (_jsx(ShapeBatch, { defs: members, mounted: mounted, kind: kind, renderOrder: renderOrder, onKindMismatch: repartition }, kind));
74
+ }) }));
75
+ }
76
+ /**
77
+ * Splits a layer's shapes three ways — the flat ones, the opaque volumes, and the translucent
78
+ * volumes — by the same {@link shapeKindOf} an instance checks itself against.
79
+ * @param defs - The layer's shapes, in paint order.
80
+ * @param mounted - The batches already mounted, whose entries are carried forward emptied.
81
+ * @returns The membership and the batches to render, each still in paint order.
82
+ */
83
+ function partitionShapes(defs, mounted) {
84
+ const batches = {};
85
+ for (const kind of SHAPE_KINDS)
86
+ if (mounted[kind])
87
+ batches[kind] = [];
88
+ for (const def of defs)
89
+ (batches[shapeKindOf(def)] ??= []).push(def);
90
+ return { defs, batches };
91
+ }
92
+ /**
93
+ * One `BatchedMesh` and its instances — a layer's flat shapes or its volumes, never both. It may
94
+ * hold none: `Shapes` keeps a batch mounted after its last member leaves, and an empty one draws
95
+ * nothing until a def of its kind arrives.
96
+ * @param props - The shapes, and which kind they are.
97
+ * @returns The mesh and one instance component per shape.
98
+ */
99
+ function ShapeBatch(props) {
100
+ const { defs, kind, renderOrder, onKindMismatch } = props;
101
+ // Only the instances are staged; the container is sized, reserved and ordered from every member.
102
+ const mounted = props.mounted ?? defs.length;
103
+ // Sized from the first render's defs; membership changes grow the mesh in place, never rebuild
104
+ // it. The container is bookkeeping plus CPU-side template geometries, so the copy StrictMode's
105
+ // double-invoked initializer discards and the one an unmount strands are both plain garbage.
106
+ //
107
+ // Stickiness costs this nothing: a batch mounts on the render that first fills it, so the defs
108
+ // sizing the container are a real membership rather than the empty one it may later stand at.
109
+ const container = useContainer(() => new ShapeContainer(defs, kind), defs, onKindMismatch);
110
+ const occluder = useOccluder();
111
+ // Declares the incoming membership before React mounts the delta, so a change that outgrows the
112
+ // mesh resizes each buffer once, to exactly this size, instead of once per claiming instance.
113
+ // During render on purpose: instances claim in their mount effects, which run before any effect
114
+ // of this component could — and the call writes idempotent bookkeeping, so a repeated or
115
+ // discarded render is harmless.
116
+ container.reserve(defs);
117
+ // `args` is diffed element-wise and a change would dispose and rebuild the mesh, orphaning every
118
+ // claimed slot — `capacity` is a frozen snapshot, so that never happens. The `ref` arrow is
119
+ // deliberate: `ref={container.bind}` would make the React Compiler infer `container` itself as a
120
+ // ref and reject its render-time reads.
121
+ //
122
+ // The opaque volumes are what the depth prepass draws, so their batch asks for occlusion: the
123
+ // translucent ones stay out because a surface you can see through must not cast full occlusion,
124
+ // and the planar ones stay out because that is what keeps ambient occlusion and the contact
125
+ // shadow disjoint. A batch of either of those kinds asks with `null`, which is also what a
126
+ // detaching `ref` says — so the layer number and the scene's registry are `useOccluder`'s alone.
127
+ return (_jsxs(_Fragment, { children: [_jsx(BatchedMesh, { args: container.capacity, ref: (mesh) => {
128
+ container.bind(mesh);
129
+ occluder(kind === 'solid' ? mesh : null);
130
+ }, customSort: container.customSort, renderOrder: renderOrder, children: _jsx(ColorMaterial, { solid: kind !== 'planar', translucent: kind === 'translucent' }) }), defs.slice(0, mounted).map((def) => (_jsx(ShapeInstance, { def: def, container: container }, defKey(def))))] }));
131
+ }
@@ -0,0 +1,2 @@
1
+ export { Shapes, type ShapesProps } from './Shapes';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ export { Shapes } from './Shapes';
@@ -0,0 +1,141 @@
1
+ import { type Shape } from '@expofp/geometry';
2
+ import { type BufferGeometry } from 'three';
3
+ import { type BatchedMeshImpl } from '../../core/batch/BatchedMesh';
4
+ import { type ShapeDef } from '../../types/defs';
5
+ import { SlotContainer } from '../containers';
6
+ /**
7
+ * Every kind of shape batch, **in the order they draw within a layer** — which is what `Shapes`
8
+ * takes each batch's `renderOrder` from, so this list is the one place that order is stated.
9
+ *
10
+ * Solids draw after the flat shapes, so a volume occludes the flat content of its own layer rather
11
+ * than being painted over by it — the same rule that governs whole layers, applied within one.
12
+ */
13
+ export declare const SHAPE_KINDS: readonly ['planar', 'solid', 'translucent'];
14
+ /**
15
+ * Which of a layer's three shape batches a def belongs in, and what that batch is for:
16
+ *
17
+ * - `planar` — flat content: unlit, no depth writes, placed by draw order alone.
18
+ * - `solid` — opaque volumes: lit and depth-writing, drawn in the ordered stream.
19
+ * - `translucent` — the same volumes, drawn in three's transparent pass.
20
+ *
21
+ * One value rather than the `solid`/`translucent` pair it replaces. The pair could express a
22
+ * translucent-but-flat batch, which nothing partitions for and no material describes, and it split
23
+ * one fact across a field each reader needed and a field none did. This is also the "kind" that
24
+ * {@link SlotContainer.reportKindMismatch} and its handler have always named.
25
+ *
26
+ * Derived from {@link SHAPE_KINDS} rather than listed again, so the two cannot drift.
27
+ */
28
+ export type ShapeKind = (typeof SHAPE_KINDS)[number];
29
+ /**
30
+ * The batch a def belongs in.
31
+ *
32
+ * A shape is **solid** exactly when it is a `Mesh` whose vertices span more than one z — elevation is
33
+ * placement, not volume, so a quad or a lifted-but-flat mesh stays planar and is placed correctly by
34
+ * draw order alone. A solid is **translucent** when its color carries an alpha below 1. That has to
35
+ * be a partition rather than a per-instance flag because `transparent` is a *material* property:
36
+ * three decides which render list an object belongs to, never which instance, so two solids of
37
+ * differing transparency cannot share a mesh.
38
+ *
39
+ * **Only volumes split on translucency.** A flat shape with an alpha below 1 stays `planar`, because
40
+ * flat content already blends without leaving the opaque list: `sharedParams` reaches alpha blending
41
+ * through `CustomBlending` rather than through `transparent: true`, which three honors either way,
42
+ * and nothing planar writes depth. So array order is the whole of it — a translucent flat shape
43
+ * blends over whatever precedes it, and an opaque one authored later paints straight over it. That
44
+ * last part is a capability, not an accident: ordering an opaque fill after a translucent one is how
45
+ * a caller occludes it, and a batch of its own would take that away by forcing it after every opaque
46
+ * object in the scene, where nothing in its layer could cover it again.
47
+ *
48
+ * A volume has no such freedom. It writes depth and is depth-tested, so what ends up on top is the
49
+ * depth buffer's answer rather than the author's, and blending correctly means being drawn after the
50
+ * opaque content it blends with — which is a render pass, and therefore a batch.
51
+ *
52
+ * One function rather than a predicate in `Shapes` that places a def and another in `ShapeInstance`
53
+ * that notices it no longer belongs. Those are the same question, and two expressions of it can
54
+ * drift — a def placed by one rule and checked by the other would either sit in the wrong batch or
55
+ * ask to be re-split forever. A planar shape returns before the color is looked at, so the parse
56
+ * costs nothing on the many layers holding only flat content.
57
+ * @param def - The def to classify.
58
+ * @returns The kind of batch it draws from.
59
+ */
60
+ export declare function shapeKindOf(def: ShapeDef): ShapeKind;
61
+ /**
62
+ * One shape layer's mesh bookkeeping: reserved buffer space, slot allocation, which geometries the
63
+ * mesh has registered, and the paint-order table.
64
+ *
65
+ * Slot allocation is the mesh's own — `addInstance`/`deleteInstance` maintain its free list — so
66
+ * what this adds is what the mesh will not track: how much vertex and index space is reserved, and
67
+ * which of its geometry ids each template was given. Building the templates is not its work either;
68
+ * they come from {@link shapeGeometry}, which caches them across every container.
69
+ */
70
+ export declare class ShapeContainer extends SlotContainer<BufferGeometry, BatchedMeshImpl, ShapeDef> {
71
+ /**
72
+ * The mesh's constructor args, sized to hold the defs the container was created with exactly.
73
+ * Passed as `<batchedMesh args={container.capacity}>`; R3F diffs `args` element-wise and a change
74
+ * disposes and rebuilds the mesh, orphaning every claimed slot — so this is a snapshot taken at
75
+ * creation, which growth never mutates.
76
+ */
77
+ readonly capacity: [instances: number, vertices: number, indices: number];
78
+ /**
79
+ * Which kind of member this container holds, and so what its mesh is laid out for: a volume batch
80
+ * carries creased normals for the lit material and is non-indexed because that is what
81
+ * `toCreasedNormals` produces, and a translucent one draws in three's transparent pass. A member
82
+ * whose color stops matching is in the wrong pass, which is what {@link reportKindMismatch} exists
83
+ * to correct.
84
+ */
85
+ readonly kind: ShapeKind;
86
+ /**
87
+ * Which id the mesh gave each geometry it has registered. Per container even though the templates
88
+ * behind them are shared, because an id is the mesh's own numbering into its own buffers.
89
+ */
90
+ private readonly geometryIds;
91
+ /** What is allocated now, which growth mutates — unlike the {@link capacity} snapshot. */
92
+ private readonly allocated;
93
+ /**
94
+ * What the next growth jumps to, refreshed by {@link reserve}. Kept apart from {@link allocated}:
95
+ * a reservation must not resize anything by itself, only size the resize an overflowing claim was
96
+ * going to do anyway.
97
+ */
98
+ private readonly target;
99
+ /**
100
+ * Builds a shape layer's container, sized to hold `defs` exactly so a layer that mounts full never
101
+ * reallocates. The mesh itself is R3F's: the component renders `<batchedMesh>` from
102
+ * {@link capacity} and hands it over through {@link SlotContainer.bind}, and R3F disposes it on
103
+ * unmount. Nothing else needs disposal — `addGeometry` copies the template geometries' data into
104
+ * the mesh, so the templates are never uploaded to the GPU. They are not this container's to free
105
+ * in any case: {@link shapeGeometry} shares them across every container, and each is collected
106
+ * with the shape that keyed it.
107
+ * @param defs - The layer's shapes, used only to size the initial capacity.
108
+ * @param kind - Which of the layer's batches this is. A batched mesh mirrors its first geometry's
109
+ * attributes *and* its index-ness, and three rejects any later geometry that disagrees — so this
110
+ * decides the layout for every geometry in it, the shared unit plane included. It also decides
111
+ * the render pass its members belong to, and therefore which colors are at home in it.
112
+ */
113
+ constructor(defs: readonly ShapeDef[], kind?: ShapeKind);
114
+ /**
115
+ * The geometry a shape draws with, in this container's layout. Resolved through the shared cache,
116
+ * so a shape drawn by two containers — the same volume in the opaque and the translucent batch —
117
+ * is built once and handed to both.
118
+ * @param shape - The shape to resolve.
119
+ * @returns The geometry, built on first use.
120
+ */
121
+ geometryFor(shape: Shape): BufferGeometry;
122
+ /**
123
+ * Declares the membership this container is about to hold, setting the size the next growth jumps
124
+ * to. The component calls this from render, before React mounts the instances that will claim in
125
+ * their effects — so a membership change that outgrows the buffers costs one resize per buffer,
126
+ * sized exactly, instead of one per claim. Bookkeeping only: nothing resizes until a claim or
127
+ * geometry registration actually overflows, so a reservation from a discarded or repeated render
128
+ * is harmless.
129
+ * @param defs - The shapes this container is about to hold.
130
+ */
131
+ reserve(defs: readonly ShapeDef[]): void;
132
+ protected addInstance(geometry: BufferGeometry): number;
133
+ protected removeInstance(slot: number): void;
134
+ /**
135
+ * Registers a geometry with the mesh if it is not already, growing the buffers first.
136
+ * @param geometry - The geometry to register.
137
+ * @returns The mesh's geometry id.
138
+ */
139
+ private geometryIdFor;
140
+ }
141
+ //# sourceMappingURL=shape-container.d.ts.map