@expofp/renderer 3.2.4 → 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 -8028
  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,497 @@
1
+ import { extend } from '@react-three/fiber';
2
+ import { Box3, Color, ColorManagement, InstancedBufferAttribute, Matrix4, Mesh, Sphere, } from 'three';
3
+ import { InstanceUniforms } from '../InstanceUniforms';
4
+ import { SquareDataTexture } from '../SquareDataTexture';
5
+ import { FrustumCulling } from './FrustumCulling';
6
+ import { patchInstancedMaterial, patchInstancedShaderChunks } from './instanced-material';
7
+ import { Instances } from './Instances';
8
+ import { Raycasting } from './Raycasting';
9
+ // Prepare three's `ShaderChunk` when this module loads — once, guarded — so a mesh built later finds
10
+ // the stock chunks it compiles against already widened.
11
+ patchInstancedShaderChunks();
12
+ /**
13
+ * A texture-backed instanced mesh: three's `InstancedMesh` regime, but with per-instance matrices,
14
+ * colors and uniforms held in data textures instead of vertex attributes — so instances can be
15
+ * added, removed, hidden, sorted and frustum-culled individually, and the buffers grow on demand.
16
+ *
17
+ * Ported from three.ez's instanced-mesh (`InstancedMesh2` and its `feature/*` and `utils/*`
18
+ * modules,
19
+ * {@link https://github.com/agargaro/instanced-mesh/tree/master/src/core | source}) as a plain
20
+ * subclass rather than a re-export of theirs. Only what the 2D layers use is kept: dynamic capacity,
21
+ * add/remove, per-instance visibility, sorting, linear per-instance frustum culling, and uniforms.
22
+ * BVH, LOD, skinning, morphing, and the `InstancedEntity` object layer are all dropped.
23
+ *
24
+ * Departures from upstream:
25
+ *
26
+ * - **Batched-style texture handling.** The data textures bind as ordinary shader uniforms and
27
+ * flush through {@link SquareDataTexture.update}, so there is no manual texture-unit binding,
28
+ * `updateTextures`/`bindTextures` split, or `PropertiesOverride` — the vendored texture already
29
+ * works this way for `BatchedMeshExt`.
30
+ * - **The draw list is a texture, not a vertex attribute.** Upstream stamps a `uint` index attribute
31
+ * onto whatever geometry it is handed; this fetches the instance id by `gl_InstanceID` from a texture of
32
+ * its own, the way `BatchedMeshExt` resolves `gl_DrawID`. So the mesh never writes to a geometry it
33
+ * does not own — several may draw one cached geometry — and the cull list flushes through
34
+ * {@link SquareDataTexture.update} in `onBeforeRender` like every other per-instance buffer, rather
35
+ * than needing an attribute that owns its GL buffer to beat three's upload in `projectObject`. It
36
+ * is a float for the same reason `BatchedMesh`'s is: see {@link SquareDataTexture}.
37
+ * - **Merged color/opacity API.** {@link setColorAt}/{@link getColorAt} take and return
38
+ * `Color | Vector4`, alpha in the fourth channel, as `BatchedMesh` does — no separate opacity
39
+ * methods.
40
+ * - **Uniforms are added, not initialized once** ({@link addUniforms}, {@link InstanceUniforms}).
41
+ * - **Bounds invalidate themselves** on every mutation that can move an instance.
42
+ * - **No caller-supplied geometry or material.** Like `BatchedMesh`, the constructor takes only
43
+ * options; it seeds a throwaway geometry and material that R3F's `<planeGeometry>` and
44
+ * `<TextureMaterial>` children replace on mount.
45
+ *
46
+ * Render it with {@link InstancedMesh}.
47
+ */
48
+ export class InstancedMeshExt extends Mesh {
49
+ /**
50
+ * The number of instances rendered in the last frame.
51
+ * @default 0
52
+ */
53
+ count = 0;
54
+ /** Marks this for three's instanced-draw path, which reads {@link count}. */
55
+ isInstancedMesh = true;
56
+ /** Placeholder three's instanced path dereferences; the real matrices live in a texture. */
57
+ instanceMatrix = new InstancedBufferAttribute(new Float32Array(0), 16);
58
+ /** Unused; present because three's instanced path reads it. */
59
+ instanceColor = null;
60
+ /**
61
+ * Unused, but must be `null`, not `undefined`: three enables its instanced-morph shader path on
62
+ * `isInstancedMesh && morphTexture !== null`, and an undefined field would satisfy that and emit
63
+ * morph GLSL that references an undeclared `MORPHTARGETS_COUNT`.
64
+ */
65
+ morphTexture = null;
66
+ /** Sorts the render list into draw order; assigned through the `customSort` JSX prop. */
67
+ customSort = null;
68
+ /** Whether each instance is frustum-culled individually. */
69
+ perObjectFrustumCulled = true;
70
+ /** Whether instances are depth-sorted (via {@link customSort}) before drawing. */
71
+ sortObjects = false;
72
+ /** Bounding box enclosing every instance; nulled on mutation and recomputed on demand. */
73
+ boundingBox = null;
74
+ /** Bounding sphere enclosing every instance; nulled on mutation and recomputed on demand. */
75
+ boundingSphere = null;
76
+ /**
77
+ * Tests the ray against individual instances. Assigned by a subclass whose instances are placed
78
+ * some other way, to one that knows how; see {@link Raycasting.prepareInstance}.
79
+ */
80
+ raycasting = new Raycasting();
81
+ /**
82
+ * Builds the draw list each draw. Assigned by a subclass whose instances cover more than their own
83
+ * bounds; see {@link FrustumCulling.instanceSphere}.
84
+ */
85
+ culling = new FrustumCulling();
86
+ /** Indirect rendering texture, for each `gl_InstanceID` stores indices of matrix/color/uniforms textures */
87
+ indexTexture;
88
+ /**
89
+ * Per-instance transforms: one `mat4` per instance (4 channels × 4 pixels), or `null` until the
90
+ * first {@link setMatrixAt} — a mesh whose instances are placed some other way never allocates it,
91
+ * and the shader reads identity in its absence.
92
+ */
93
+ matricesTexture = null;
94
+ /** Per-instance RGBA colors, or `null` until the first {@link setColorAt}. */
95
+ colorsTexture = null;
96
+ uniforms = new InstanceUniforms();
97
+ /** Which instances are live, visible and free to hand out. */
98
+ instances;
99
+ /** What the last draw culled to, or `null` before the first — what raycasting tests against. */
100
+ drawList = null;
101
+ /** Puts back what {@link patchInstancedMaterial} wrapped; null between draws. */
102
+ _unpatchMaterial = null;
103
+ constructor(params = {}) {
104
+ super();
105
+ const capacity = params.capacity && params.capacity > 0 ? params.capacity : DEFAULT_CAPACITY;
106
+ this.instances = new Instances(capacity, (grown) => this.resizeCapacity(grown));
107
+ this.indexTexture = new SquareDataTexture(Float32Array, 1, 1, capacity);
108
+ }
109
+ /**
110
+ * The instance capacity the textures currently hold.
111
+ */
112
+ get capacity() {
113
+ return this.instances.capacity;
114
+ }
115
+ /**
116
+ * The number of active instances.
117
+ */
118
+ get instancesCount() {
119
+ return this.instances.instancesCount;
120
+ }
121
+ /**
122
+ * Declares per-instance uniforms, merging with any already declared and rebuilding the backing
123
+ * texture (existing values carried across). See {@link InstanceUniforms.add}.
124
+ * @param schema - The uniforms to add.
125
+ */
126
+ addUniforms(schema) {
127
+ this.uniforms.add(schema, this.capacity);
128
+ this.materialsNeedUpdate();
129
+ }
130
+ /**
131
+ * Reads one instance's uniform.
132
+ * @param id - The index of instance to read.
133
+ * @param name - The uniform's name.
134
+ * @param target - Receives the result; unused for a `float`, returned directly.
135
+ * @returns The value.
136
+ */
137
+ getUniformAt(id, name, target) {
138
+ return this.uniforms.get(id, name, target);
139
+ }
140
+ /**
141
+ * Writes one instance's uniform and marks its row for upload.
142
+ * @param id - The index of instance to write.
143
+ * @param name - The uniform's name.
144
+ * @param value - The value to write.
145
+ */
146
+ setUniformAt(id, name, value) {
147
+ this.uniforms.set(id, name, value);
148
+ }
149
+ /**
150
+ * Adds `count` instances, reusing removed instances first, and reports each new id through
151
+ * `onCreation` so the caller can capture it. New instances start visible at the identity matrix.
152
+ * @param count - How many to add.
153
+ * @param onCreation - Called with each new instance's id.
154
+ * @returns `this`.
155
+ */
156
+ addInstances(count, onCreation) {
157
+ this.instances.addInstances(count, (id) => {
158
+ this.resetInstance(id);
159
+ onCreation?.(id);
160
+ });
161
+ return this;
162
+ }
163
+ /**
164
+ * Removes instances by id, freeing their instances for reuse and shrinking the iteration bound past
165
+ * any now-trailing inactive instances.
166
+ * @param ids - The instances to remove.
167
+ * @returns `this`.
168
+ */
169
+ removeInstances(...ids) {
170
+ this.instances.removeInstances(...ids);
171
+ this.invalidateBounds();
172
+ return this;
173
+ }
174
+ /**
175
+ * Gets the visibility of a specific instance.
176
+ * @param id - The index of the instance.
177
+ * @returns Whether it is visible.
178
+ */
179
+ getVisibleAt(id) {
180
+ return this.instances.getVisibleAt(id);
181
+ }
182
+ /**
183
+ * Sets the visibility of a specific instance.
184
+ * @param id - The index of the instance.
185
+ * @param visible - Whether it should be visible.
186
+ */
187
+ setVisibleAt(id, visible) {
188
+ this.instances.setVisibleAt(id, visible);
189
+ // The bounds still hold — a hidden instance is still live — but the draw list does not: it names
190
+ // what the last frame drew, and this changes that. See {@link invalidateBounds}.
191
+ this.drawList = null;
192
+ }
193
+ /**
194
+ * Reads an instance's transform. Tolerates a read before any transform was written — every
195
+ * instance is then untransformed, which is what the shader draws in the same state.
196
+ * @param id - The index of the instance.
197
+ * @param matrix - Target for the result.
198
+ * @returns The transform matrix.
199
+ */
200
+ getMatrixAt(id, matrix = _matrix) {
201
+ if (!this.matricesTexture)
202
+ return matrix.identity();
203
+ return matrix.fromArray(this.matricesTexture.data, id * 16);
204
+ }
205
+ /**
206
+ * Writes an instance's transform.
207
+ * @param id - The index of the instance.
208
+ * @param matrix - The transform matrix.
209
+ * @returns `this`.
210
+ */
211
+ setMatrixAt(id, matrix) {
212
+ if (!this.matricesTexture)
213
+ this.initMatricesTexture();
214
+ const texture = this.matricesTexture;
215
+ matrix.toArray(texture.data, id * 16);
216
+ texture.enqueueUpdate(id);
217
+ this.invalidateBounds();
218
+ return this;
219
+ }
220
+ getColorAt(id, target = _color) {
221
+ if (!this.colorsTexture)
222
+ return target.setScalar(1);
223
+ const data = this.colorsTexture.data;
224
+ return target.fromArray(data, id * 4);
225
+ }
226
+ /**
227
+ * Writes an instance's color, routing a `Vector4`'s alpha to the fourth channel.
228
+ * @param id - The instance.
229
+ * @param color - The color; pass a `Vector4` to also set alpha.
230
+ * @returns `this`.
231
+ */
232
+ setColorAt(id, color) {
233
+ if (!this.colorsTexture)
234
+ this.initColorsTexture();
235
+ const data = this.colorsTexture.data;
236
+ const colorObj = typeof color !== 'object' ? _color.set(color) : color;
237
+ colorObj.toArray(data, id * 4);
238
+ this.colorsTexture.enqueueUpdate(id);
239
+ return this;
240
+ }
241
+ /**
242
+ * Computes the bounding box that encloses all instances, and updates the `boundingBox` attribute.
243
+ */
244
+ computeBoundingBox() {
245
+ const count = this.instances.addedInstancesCount;
246
+ this.boundingBox ??= new Box3();
247
+ this.boundingBox.makeEmpty();
248
+ for (let i = 0; i < count; i++) {
249
+ if (!this.instances.getActiveAt(i))
250
+ continue;
251
+ this.boundingBox.union(this.getBoundingBoxAt(i, _box));
252
+ }
253
+ }
254
+ /**
255
+ * Computes the bounding sphere that encloses all instances, and updates the `boundingSphere` attribute.
256
+ */
257
+ computeBoundingSphere() {
258
+ const count = this.instances.addedInstancesCount;
259
+ this.boundingSphere ??= new Sphere();
260
+ this.boundingSphere.makeEmpty();
261
+ for (let i = 0; i < count; i++) {
262
+ if (!this.instances.getActiveAt(i))
263
+ continue;
264
+ this.boundingSphere.union(this.getBoundingSphereAt(i, _sphere));
265
+ }
266
+ }
267
+ /**
268
+ * Encloses one instance, in this mesh's space: the geometry's own sphere, moved onto the
269
+ * instance's transform.
270
+ *
271
+ * The seam frustum culling asks through, so that what encloses an instance is the mesh's business
272
+ * rather than something derived from a matrix every mesh is assumed to have — see
273
+ * {@link CullingHost}. Whole-mesh bounds go through it too, so a subclass placing its instances
274
+ * some other way overrides the one method and both follow.
275
+ * @param id - The instance.
276
+ * @param target - Receives the result.
277
+ * @returns The bounds.
278
+ */
279
+ getBoundingSphereAt(id, target) {
280
+ const geometry = this.geometry;
281
+ if (!geometry.boundingSphere)
282
+ geometry.computeBoundingSphere();
283
+ return target.copy(geometry.boundingSphere).applyMatrix4(this.getMatrixAt(id));
284
+ }
285
+ /**
286
+ * Encloses one instance in a box, in this mesh's space: the geometry's own, moved onto the
287
+ * instance's transform.
288
+ *
289
+ * The seam {@link computeBoundingBox} goes through, for the reason {@link getBoundingSphereAt} is
290
+ * the one culling asks through — a subclass overrides what encloses *one* instance, and every
291
+ * sweep over the set follows.
292
+ * @param id - The instance.
293
+ * @param target - Receives the result.
294
+ * @returns The bounds.
295
+ */
296
+ getBoundingBoxAt(id, target) {
297
+ const geometry = this.geometry;
298
+ if (!geometry.boundingBox)
299
+ geometry.computeBoundingBox();
300
+ return target.copy(geometry.boundingBox).applyMatrix4(this.getMatrixAt(id));
301
+ }
302
+ /** Frees the data textures, which this mesh allocated rather than three. */
303
+ dispose() {
304
+ this.dispatchEvent({ type: 'dispose' });
305
+ this.indexTexture.dispose();
306
+ this.matricesTexture?.dispose();
307
+ this.colorsTexture?.dispose();
308
+ this.uniforms.dispose();
309
+ }
310
+ /**
311
+ * Clears whatever an instance holds, as it is handed out.
312
+ *
313
+ * A new instance may reuse a freed id that still holds the previous tenant's state, and nothing
314
+ * else will clear it: `onCreation` belongs to the caller, which knows what it is about to write
315
+ * and not what was there before. A subclass keeping per-instance state of its own overrides this
316
+ * and clears that too.
317
+ *
318
+ * The matrix only when there is a matrices texture — without one every instance already reads
319
+ * identity, which is exactly what this would write, and writing it would allocate the texture this
320
+ * mesh has so far done without.
321
+ * @param id - The instance being handed out.
322
+ */
323
+ resetInstance(id) {
324
+ if (this.matricesTexture)
325
+ this.setMatrixAt(id, _matrix.identity());
326
+ }
327
+ /**
328
+ * Patches the material three is about to draw with, so the program it compiles reads this mesh's
329
+ * per-instance state — and hands back the undo.
330
+ *
331
+ * Applied and undone around every draw rather than once per material, because the material three
332
+ * hands over is not always the mesh's own: a scene's `overrideMaterial` is drawn with instead, and
333
+ * one left patched afterwards would carry this mesh's textures into every other object using it.
334
+ *
335
+ * The seam a subclass extends when its members need more of the shader than instancing — it wraps
336
+ * first, so that its own patch runs *inside* this one and finds the prologue already injected.
337
+ * @param material - The material about to draw.
338
+ * @returns Restores the material exactly as it arrived. Called once, after the draw.
339
+ */
340
+ patchMaterial(material) {
341
+ // A snapshot of the textures, which is enough: the patch is reapplied and undone around every
342
+ // draw, so nothing can swap one out between here and the compile that reads them.
343
+ return patchInstancedMaterial(material, {
344
+ indexTexture: this.indexTexture,
345
+ matricesTexture: this.matricesTexture,
346
+ colorsTexture: this.colorsTexture,
347
+ uniforms: this.uniforms,
348
+ });
349
+ }
350
+ /**
351
+ * Nulls the cached bounds so three's next frustum test recomputes them, and the draw list with
352
+ * them.
353
+ *
354
+ * The draw list describes what the **last frame** drew, and {@link raycast} tests against it — so
355
+ * any mutation that changes what would draw leaves it lying to the next pointer event. Nulling it
356
+ * falls back to sweeping every live instance, which is the honest answer until a frame rebuilds
357
+ * it: an instance added between a mutation and the next draw is otherwise unpickable, which reads
358
+ * as the pointer having left something it is still sitting on.
359
+ *
360
+ * Protected to be *called*, not overridden: a subclass places its instances its own way, so its
361
+ * own writes are the ones this class cannot know have moved something.
362
+ */
363
+ invalidateBounds() {
364
+ this.boundingBox = null;
365
+ this.boundingSphere = null;
366
+ this.drawList = null;
367
+ }
368
+ onBeforeRender(renderer, scene, camera, geometry, material, group) {
369
+ super.onBeforeRender(renderer, scene, camera, geometry, material, group);
370
+ this._unpatchMaterial = this.patchMaterial(material);
371
+ // Rebuild the draw list for this draw, then publish it: the ids into the texture the shader
372
+ // fetches by `gl_InstanceID`, the length into the count three's instanced draw reads.
373
+ const drawList = this.culling.performFrustumCulling(this, this.instances, camera, material);
374
+ this.indexTexture.data.set(drawList.ids.subarray(0, drawList.count));
375
+ // One mark per row the list touched; the texture merges the contiguous run into a single upload.
376
+ const perRow = this.indexTexture.image.width;
377
+ for (let i = 0; i < drawList.count; i += perRow)
378
+ this.indexTexture.enqueueUpdate(i);
379
+ this.drawList = drawList;
380
+ this.count = drawList.count;
381
+ if (this.count === 0)
382
+ return;
383
+ // Every per-instance buffer is a data texture, and each flushes what changed since the last
384
+ // frame — the draw list included, whose rows the write-out above just marked.
385
+ this.indexTexture.update(renderer);
386
+ this.matricesTexture?.update(renderer);
387
+ this.colorsTexture?.update(renderer);
388
+ this.uniforms.texture?.update(renderer);
389
+ }
390
+ onAfterRender(renderer, scene, camera, geometry, material, group) {
391
+ super.onAfterRender(renderer, scene, camera, geometry, material, group);
392
+ // Hands the material back exactly as it arrived. Strictly paired with the wrap above: three calls
393
+ // the two hooks around every draw, including the two-pass draw a transparent double-sided
394
+ // material takes.
395
+ this._unpatchMaterial?.();
396
+ this._unpatchMaterial = null;
397
+ }
398
+ onBeforeShadow(renderer, scene, camera, shadowCamera, geometry, depthMaterial, group) {
399
+ this.onBeforeRender(renderer, scene, shadowCamera, geometry, depthMaterial, group);
400
+ }
401
+ onAfterShadow(renderer, scene, camera, shadowCamera, geometry, depthMaterial, group) {
402
+ this.onAfterRender(renderer, scene, shadowCamera, geometry, depthMaterial, group);
403
+ // That pass culled to the light's frustum, which is the wrong set to raycast against.
404
+ this.drawList = null;
405
+ }
406
+ clone(recursive) {
407
+ const Constructor = this.constructor;
408
+ return new Constructor({ capacity: this.capacity }).copy(this, recursive);
409
+ }
410
+ copy(source, recursive) {
411
+ super.copy(source, recursive);
412
+ // `Mesh.copy` left the clone sharing the source's geometry. Sharing one is safe — nothing writes
413
+ // to it — but a clone is expected to be independent, so it gets its own.
414
+ this.geometry = source.geometry.clone();
415
+ this.instances.copyFrom(source.instances);
416
+ // The clone has not been drawn, so it has no draw list of its own yet and its `count` stays 0;
417
+ // its first render builds both. Until then nothing draws and raycasting falls back to sweeping
418
+ // every live instance. Neither is worth carrying over — three reads `count` only after
419
+ // `onBeforeRender`, which sets it, and nothing else here reads it at all.
420
+ this.drawList = null;
421
+ this.indexTexture = source.indexTexture.clone();
422
+ this.matricesTexture = source.matricesTexture ? source.matricesTexture.clone() : null;
423
+ this.colorsTexture = source.colorsTexture ? source.colorsTexture.clone() : null;
424
+ this.uniforms.copyFrom(source.uniforms);
425
+ if (source.boundingBox)
426
+ this.boundingBox = source.boundingBox.clone();
427
+ if (source.boundingSphere)
428
+ this.boundingSphere = source.boundingSphere.clone();
429
+ return this;
430
+ }
431
+ raycast(raycaster, intersects) {
432
+ this.raycasting.raycast(this, this.instances, this.drawList, raycaster, intersects);
433
+ }
434
+ /**
435
+ * Grows every per-instance texture to a new capacity, keeping what is written. {@link Instances} grows
436
+ * its own store and then calls this, so nothing here tracks capacity itself.
437
+ * @param newCapacity - The new capacity, always larger than the current one.
438
+ */
439
+ resizeCapacity(newCapacity) {
440
+ // `SquareDataTexture.resize` disposes each old GPU texture before swapping in the grown one. The
441
+ // draw list needs nothing carried across: culling rewrites it every frame from the live instances.
442
+ this.indexTexture.resize(newCapacity);
443
+ // The room this adds needs no identity fill, unlike the colors below: every instance handed out
444
+ // has its transform reset as it is created, so the only rows left zeroed are ones no instance
445
+ // occupies. A color is not reset there, so an instance in the new room would read the zero.
446
+ this.matricesTexture?.resize(newCapacity);
447
+ if (this.colorsTexture) {
448
+ // Measured before the resize, so the fill covers exactly the room it added.
449
+ const previousLength = this.colorsTexture.data.length;
450
+ this.colorsTexture.resize(newCapacity);
451
+ // The instances the grown room just added read opaque white, as the original allocation does.
452
+ this.colorsTexture.data.fill(1, previousLength);
453
+ }
454
+ this.uniforms.resize(newCapacity);
455
+ }
456
+ initMatricesTexture() {
457
+ this.matricesTexture = new SquareDataTexture(Float32Array, 4, 4, this.capacity);
458
+ // Identity into every row, because a data texture allocates zero-filled and a zero matrix
459
+ // collapses what it transforms to a point — so the instances added before this existed, whose
460
+ // reset found no texture to write into, would draw as nothing rather than as untransformed.
461
+ const data = this.matricesTexture.data;
462
+ for (let i = 0; i < data.length; i += 16) {
463
+ data[i] = 1;
464
+ data[i + 5] = 1;
465
+ data[i + 10] = 1;
466
+ data[i + 15] = 1;
467
+ }
468
+ this.materialsNeedUpdate();
469
+ }
470
+ initColorsTexture() {
471
+ this.colorsTexture = new SquareDataTexture(Float32Array, 4, 1, this.capacity);
472
+ this.colorsTexture.colorSpace = ColorManagement.workingColorSpace;
473
+ this.colorsTexture.data.fill(1);
474
+ this.materialsNeedUpdate();
475
+ }
476
+ /** Flags the material(s) for recompilation, e.g. after a texture is added. */
477
+ materialsNeedUpdate() {
478
+ const material = this.material;
479
+ if (Array.isArray(material)) {
480
+ for (const one of material)
481
+ one.needsUpdate = true;
482
+ }
483
+ else {
484
+ material.needsUpdate = true;
485
+ }
486
+ }
487
+ }
488
+ /**
489
+ * Registers the class as an R3F element, by value so the generated key cannot collide with the
490
+ * `InstancedMesh` three already puts in the catalog when the canvas mounts.
491
+ */
492
+ export const InstancedMesh = extend(InstancedMeshExt);
493
+ const DEFAULT_CAPACITY = 1000;
494
+ const _matrix = new Matrix4();
495
+ const _color = new Color();
496
+ const _box = new Box3();
497
+ const _sphere = new Sphere();
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The instance allocator: which instances are live, which are visible, which ids are free to hand
3
+ * out again, and how far the live range extends.
4
+ *
5
+ * Ported from three.ez's instanced-mesh — `feature/Instances.ts` for the lifecycle, the availability
6
+ * accessors from `InstancedMesh2.ts`, and `setInstancesArrayCount` from `feature/Capacity.ts`. The
7
+ * `InstancedEntity` object layer and the BVH bookkeeping woven through all three are dropped, so an
8
+ * instance is just an id.
9
+ *
10
+ * Upstream reaches these through prototype augmentation on `InstancedMesh2`; here they are a store
11
+ * the mesh holds, so frustum culling and raycasting can read availability without reaching into the
12
+ * mesh at all.
13
+ */
14
+ export declare class Instances {
15
+ /**
16
+ * What each instance holds, one entry each — so its length is the capacity, and there is no second
17
+ * count to keep in step with it. Never sparse; see {@link InstanceState}.
18
+ * @see `InstancedMesh2.availabilityArray` upstream, a pair of booleans per instance. One value
19
+ * cannot express "hidden and free" — which nothing wants, since the only path back to live is
20
+ * {@link addInstance}, and it hands the id to a different instance.
21
+ */
22
+ private instanceStates;
23
+ /** Ids freed by {@link removeInstances}, handed back out newest first. */
24
+ private freeIds;
25
+ private onResize;
26
+ private _activeInstancesCount;
27
+ private _addedInstancesCount;
28
+ /**
29
+ * @param capacity - How many instances the stores start out holding.
30
+ * @param onResize - Grows everything the owner keeps per instance, keeping what is written.
31
+ * Called only when adding would overflow what the stores hold.
32
+ */
33
+ constructor(capacity: number, onResize: (capacity: number) => void);
34
+ /**
35
+ * The number of instances the stores currently hold. Derived rather than tracked, so it cannot
36
+ * fall out of step with the array it describes.
37
+ */
38
+ get capacity(): number;
39
+ /** The number of active instances. */
40
+ get instancesCount(): number;
41
+ /**
42
+ * One past the highest live id — the bound every per-instance sweep iterates to. Larger than
43
+ * {@link instancesCount} whenever a removal left a hole, since only trailing dead instances pull
44
+ * it back in.
45
+ *
46
+ * **Pulled back on removal**, unlike the batched text and line classes, whose equivalent is a
47
+ * pure high-water mark. Both bound a sweep; only this one shrinks. It can afford to because the
48
+ * textures here are sized by {@link capacity} instead, which only ever grows.
49
+ * @see `InstancedMesh2._instancesArrayCount` upstream, which this renames: that name reads as a
50
+ * count of instances, which is exactly what it is not.
51
+ */
52
+ get addedInstancesCount(): number;
53
+ /**
54
+ * Adds `count` instances, reusing removed ids first, and reports each new id through `onCreation`
55
+ * so the caller can capture it. New instances start active and visible.
56
+ * @param count - How many to add.
57
+ * @param onCreation - Called with each new instance's id.
58
+ */
59
+ addInstances(count: number, onCreation?: (id: number) => void): void;
60
+ /**
61
+ * Removes instances by id, freeing them for reuse.
62
+ * @param ids - The instances to remove.
63
+ */
64
+ removeInstances(...ids: number[]): void;
65
+ /**
66
+ * Whether an instance draws — live, and not hidden. The test every per-instance sweep applies.
67
+ * @param id - The index of the instance.
68
+ * @returns Whether it is visible.
69
+ * @see `InstancedMesh2.getVisibilityAt` and `getActiveAndVisibilityAt` upstream, which are one
70
+ * predicate here: an instance cannot be visible without being live.
71
+ */
72
+ getVisibleAt(id: number): boolean;
73
+ /**
74
+ * Shows or hides an instance. A freed id stays freed: liveness and visibility are one value, so
75
+ * writing visibility here would otherwise hand a dead instance back to the draw list.
76
+ * @param id - The index of the instance.
77
+ * @param visible - Whether it should be visible.
78
+ */
79
+ setVisibleAt(id: number, visible: boolean): void;
80
+ /**
81
+ * Whether an instance is live — handed out and not yet removed, hidden or not. What the bounds
82
+ * sweep to, since a hidden instance still occupies space.
83
+ * @param id - The index of the instance.
84
+ * @returns Whether it is active (not deleted).
85
+ */
86
+ getActiveAt(id: number): boolean;
87
+ /**
88
+ * Adopts another store's instances, for cloning a mesh.
89
+ * @param source - The store to copy.
90
+ */
91
+ copyFrom(source: Instances): void;
92
+ /**
93
+ * Moves the live range's bound, growing every store when it would pass what they hold. Shrinking
94
+ * only moves the bound; the instances past it keep their data until something reuses them.
95
+ * @param count - One past the highest id the range should cover.
96
+ * @see `InstancedMesh2.setInstancesArrayCount` upstream, which is `@internal` there for the same
97
+ * reason it is private here: only {@link addInstances} has cause to move the bound.
98
+ */
99
+ private setAddedInstancesCount;
100
+ /**
101
+ * Adds one instance at an id: marks it live and visible, then hands the id to `onCreation` so the
102
+ * owner can reset what it holds and the caller can fill in the rest.
103
+ *
104
+ * One write, so a reused id cannot inherit the hidden state of whatever freed it — the id now
105
+ * belongs to a different instance.
106
+ * @param id - The instance.
107
+ * @param onCreation - Called with the id.
108
+ */
109
+ private addInstance;
110
+ /**
111
+ * Grows this store and everything the owner keeps per instance.
112
+ *
113
+ * Upstream's `resizeBuffers` grows the availability array itself; here the owner has its buffers
114
+ * and this has the instances, so each grows its own.
115
+ * @param newCapacity - The new capacity, always larger than the current one.
116
+ */
117
+ private resize;
118
+ }
119
+ //# sourceMappingURL=Instances.d.ts.map