@expofp/renderer 0.0.0-experimental.cf063fc

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,155 @@
1
+ import { extend } from '@react-three/fiber';
2
+ import { BatchedMesh as BatchedMeshBase, } from 'three';
3
+ import { InstanceUniforms } from '../InstanceUniforms';
4
+ import { openMain } from '../shader-source';
5
+ /**
6
+ * Three's `BatchedMesh` with per-instance uniforms, backed by a {@link SquareDataTexture}.
7
+ *
8
+ * Ported from three.ez's batched-mesh-extensions, which supplies this as prototype patches over
9
+ * `BatchedMesh` rather than a subclass:
10
+ * {@link https://github.com/agargaro/batched-mesh-extensions/blob/master/src/core/feature/Uniforms.ts | Uniforms}
11
+ * and
12
+ * {@link https://github.com/agargaro/batched-mesh-extensions/blob/master/src/patch/PatchBatchedMeshMaterial.ts | PatchBatchedMeshMaterial}.
13
+ * Only the uniform feature is taken: BVH, raycasting, LOD and per-object frustum culling are left
14
+ * behind, since the 2D layers draw every instance and pick through their own tables.
15
+ *
16
+ * Two departures from upstream:
17
+ *
18
+ * - **Uniforms are added, not initialized once** — see {@link addUniforms} and
19
+ * {@link InstanceUniforms}.
20
+ * - **Bounds invalidate themselves.** Every mutation that can move the mesh's extents nulls the
21
+ * cached bounds, so callers cannot forget; three's frustum test recomputes a null one on demand.
22
+ */
23
+ export class BatchedMeshImpl extends BatchedMeshBase {
24
+ /** Per-instance uniform store, shared with {@link InstancedMeshImpl}. */
25
+ uniforms = new InstanceUniforms();
26
+ /** The material whose `onBeforeCompile` is already wrapped, so it is wrapped exactly once. */
27
+ patchedMaterial = null;
28
+ /**
29
+ * Declares per-instance uniforms, merging with any already declared and rebuilding the backing
30
+ * texture (existing values carried across). See {@link InstanceUniforms.add}.
31
+ * @param schema - The uniforms to add.
32
+ */
33
+ addUniforms(schema) {
34
+ this.uniforms.add(schema, this.maxInstanceCount);
35
+ // The generated GLSL changed shape, so whatever was compiled against the old layout is stale.
36
+ if (this.patchedMaterial)
37
+ this.patchedMaterial.needsUpdate = true;
38
+ }
39
+ /**
40
+ * Reads one instance's uniform.
41
+ * @param id - The instance to read.
42
+ * @param name - The uniform's name.
43
+ * @param target - Receives the result; unused for a `float`, which is returned directly.
44
+ * @returns The value.
45
+ */
46
+ getUniformAt(id, name, target) {
47
+ return this.uniforms.get(id, name, target);
48
+ }
49
+ /**
50
+ * Writes one instance's uniform and marks its row for upload.
51
+ * @param id - The instance to write.
52
+ * @param name - The uniform's name.
53
+ * @param value - The value to write.
54
+ */
55
+ setUniformAt(id, name, value) {
56
+ this.uniforms.set(id, name, value);
57
+ }
58
+ raycast(raycaster, intersects) {
59
+ const before = intersects.length;
60
+ super.raycast(raycaster, intersects);
61
+ // three names the instance it hit as `batchId` and leaves `instanceId` unset — but `instanceId`
62
+ // is what R3F keys hover state and hit dedup by, so without this every instance in a batch shares
63
+ // one identity: `onPointerOut` never fires between two touching shapes, and overlapping hits in
64
+ // one batch collapse to a single entry. Copying it costs nothing and makes a batch's members as
65
+ // distinguishable to R3F as an instanced mesh's already are.
66
+ for (let hit = before; hit < intersects.length; hit++) {
67
+ intersects[hit].instanceId = intersects[hit].batchId;
68
+ }
69
+ }
70
+ onBeforeRender(renderer, scene, camera, geometry, material, group) {
71
+ super.onBeforeRender(renderer, scene, camera, geometry, material, group);
72
+ // Only meshes that declared uniforms touch the material or a texture; a plain shape batch that
73
+ // never called `addUniforms` leaves its material exactly as it found it.
74
+ if (!this.uniforms.texture)
75
+ return;
76
+ // Both before the program is built for this draw: the patch supplies the GLSL the compile needs,
77
+ // and the texture flushes whatever changed since the last frame.
78
+ this.patchMaterial();
79
+ this.uniforms.texture.update(renderer);
80
+ }
81
+ addInstance(geometryId) {
82
+ const instanceId = super.addInstance(geometryId);
83
+ this.invalidateBounds();
84
+ return instanceId;
85
+ }
86
+ deleteInstance(instanceId) {
87
+ super.deleteInstance(instanceId);
88
+ this.invalidateBounds();
89
+ return this;
90
+ }
91
+ setMatrixAt(instanceId, matrix) {
92
+ super.setMatrixAt(instanceId, matrix);
93
+ this.invalidateBounds();
94
+ return this;
95
+ }
96
+ setInstanceCount(maxInstanceCount) {
97
+ super.setInstanceCount(maxInstanceCount);
98
+ // The uniform texture is sized per instance, so it has to grow with the mesh.
99
+ this.uniforms.resize(maxInstanceCount);
100
+ }
101
+ dispose() {
102
+ super.dispose();
103
+ this.uniforms.dispose();
104
+ }
105
+ /**
106
+ * Nulls the cached bounds so the next frustum test recomputes them. Kept here rather than at the
107
+ * call sites: every mutation that moves an instance goes through one of the overrides above.
108
+ */
109
+ invalidateBounds() {
110
+ this.boundingBox = null;
111
+ this.boundingSphere = null;
112
+ }
113
+ /**
114
+ * Wraps the current material's `onBeforeCompile` so compiled programs can read the uniform
115
+ * texture, once per material.
116
+ *
117
+ * Deferred to render time rather than done when uniforms are declared, because the material is
118
+ * usually attached after the mesh is constructed — R3F mounts it as a child element.
119
+ */
120
+ patchMaterial() {
121
+ const material = this.material;
122
+ if (Array.isArray(material) || material === this.patchedMaterial)
123
+ return;
124
+ // The shader identifier for the name of the index.
125
+ const batchIndex = 'batchIndex';
126
+ // GLSL declaring the per-instance index from three's batching. `getIndirectIndex` resolves the
127
+ // draw to an instance; the half-pixel offset keeps `int()` truncation on the right side of the
128
+ // boundary, because the varying carrying the index is interpolated rather than flat
129
+ // (see SquareDataTexture), so a value arriving a hair below its integer would otherwise
130
+ // truncate to the previous instance.
131
+ const batchIndexGlsl = /* glsl */ `float ${batchIndex} = getIndirectIndex( gl_DrawID ) + 0.5;`;
132
+ this.patchedMaterial = material;
133
+ const onBeforeCompileBase = material.onBeforeCompile.bind(material);
134
+ // three caches a compiled program under this key alone and compares no source. The prologue
135
+ // below changes the GLSL, and the schema decides how, so both have to reach the key. Without
136
+ // it, two batches whose schemas differ share one program and the second draws against the
137
+ // first's layout. Bound rather than saved raw, because this patch is one-shot and never undone.
138
+ const cacheKeyBase = material.customProgramCacheKey.bind(material);
139
+ material.customProgramCacheKey = () => `efpBatched_${this.uniforms.shaderKey}_${cacheKeyBase()}`;
140
+ material.onBeforeCompile = (shader, renderer) => {
141
+ if (this.uniforms.patchShader(shader, batchIndex)) {
142
+ // Declares the index the prologue just injected reads. Written after it precisely so it runs
143
+ // before it — see `openMain`.
144
+ shader.vertexShader = openMain(shader.vertexShader, batchIndexGlsl);
145
+ }
146
+ onBeforeCompileBase(shader, renderer);
147
+ };
148
+ material.needsUpdate = true;
149
+ }
150
+ }
151
+ /**
152
+ * Registers the class as an R3F element, by value so the generated key cannot collide with the
153
+ * `BatchedMesh` three already puts in the catalog when the canvas mounts.
154
+ */
155
+ export const BatchedMesh = extend(BatchedMeshImpl);
@@ -0,0 +1,141 @@
1
+ import { type Camera, type Material, type Mesh, Sphere } from 'three';
2
+ import { type CustomSortFn } from './InstancedMesh';
3
+ import { type Instances } from './Instances';
4
+ /**
5
+ * The instances to draw, in draw order. Reused across frames, so read it before the next pass rather
6
+ * than holding on to it.
7
+ */
8
+ export interface DrawList {
9
+ /** The instance each `gl_InstanceID` reads; only the first {@link count} entries are meaningful. */
10
+ ids: Float32Array;
11
+ /** How many entries the last pass wrote. */
12
+ count: number;
13
+ }
14
+ /**
15
+ * What {@link FrustumCulling} reads off the mesh it culls, beyond the `matrixWorld` every mesh has,
16
+ * which the camera and frustum are brought into.
17
+ */
18
+ export interface CullingHost extends Mesh {
19
+ /** Orders the render list, or `null` to sort by depth. */
20
+ customSort: CustomSortFn | null;
21
+ /** Whether each instance is frustum-culled individually. */
22
+ perObjectFrustumCulled: boolean;
23
+ /** Whether instances are depth-sorted before drawing. */
24
+ sortObjects: boolean;
25
+ /**
26
+ * Encloses one instance, in the mesh's own space — what the frustum tests, and what depth sorting
27
+ * measures from.
28
+ * @param id - The instance.
29
+ * @param target - Receives the result.
30
+ * @returns The bounds.
31
+ */
32
+ getBoundingSphereAt(id: number, target: Sphere): Sphere;
33
+ }
34
+ /**
35
+ * Builds the draw list for one draw: every drawable instance, culled and ordered per the host's
36
+ * {@link CullingHost.perObjectFrustumCulled} and {@link CullingHost.sortObjects}.
37
+ *
38
+ * Ported from three.ez's instanced-mesh `feature/FrustumCulling.ts`, with `utils/SortingUtils.ts`
39
+ * and `utils/InstancedRenderList.ts` folded in — the latter as a pair of module arrays rather than a
40
+ * class, since nothing outside this file pushes to it. BVH culling and every LOD path are dropped;
41
+ * `linearCulling` keeps the seam they sat beside, so porting them later is additive.
42
+ *
43
+ * Departures from upstream:
44
+ *
45
+ * - **It returns a list rather than writing the mesh's buffer.** Upstream fills the mesh's
46
+ * `instanceIndex` attribute in place and flags it for upload from here. This hands back a
47
+ * {@link DrawList} and leaves both the write-out and the upload to the caller, so no path can
48
+ * produce a list that never reaches the GPU.
49
+ * - **The host encloses its own instances.** Upstream moves the geometry's bounding sphere onto each
50
+ * instance's matrix, which assumes every instance is *placed* by one. A line batch is not: a
51
+ * segment is placed by its two endpoints, and no matrix maps the template onto it, because the
52
+ * quad is expanded after projection in screen space. Asking the host for the sphere costs the same
53
+ * and holds for both.
54
+ * - **The count always resets** (see {@link performFrustumCulling}).
55
+ * - **The material is passed in** rather than read off the mesh, so a mesh drawing an array of them
56
+ * orders each group by its own. Upstream reads `this.material`, which is the whole array.
57
+ */
58
+ export declare class FrustumCulling {
59
+ /** The draw list handed back each pass, reused so a frame allocates nothing. */
60
+ private list;
61
+ /**
62
+ * The camera and frustum for the pass in flight, in the mesh's own space — derived once by
63
+ * {@link frustumCulling} and read by the gather it delegates to.
64
+ *
65
+ * Held here rather than at module scope, which is where upstream keeps them, because upstream
66
+ * patches culling onto the prototype and has nowhere else to put them. They are state for the
67
+ * length of a pass, not scratch: what makes the module-level values below safe to share between
68
+ * meshes is that each is filled and consumed within a single statement, and these are not.
69
+ */
70
+ private readonly frustum;
71
+ private readonly cameraPos;
72
+ private readonly forward;
73
+ /**
74
+ * Rebuilds the draw list for one draw.
75
+ * @param host - The mesh being drawn.
76
+ * @param instances - The mesh's instance allocator, which says what is live and visible.
77
+ * @param camera - The camera to cull and sort against.
78
+ * @param material - The material about to draw, which picks the default sort direction.
79
+ * @returns The instances to draw, in draw order.
80
+ */
81
+ performFrustumCulling(host: CullingHost, instances: Instances, camera: Camera, material: Material): DrawList;
82
+ /**
83
+ * Picks the gather-and-order path the host's flags call for.
84
+ * @param host - The mesh being drawn.
85
+ * @param instances - The mesh's instance allocator, which says what is live and visible.
86
+ * @param camera - The camera to cull and sort against.
87
+ * @param material - The material about to draw.
88
+ */
89
+ private frustumCulling;
90
+ /**
91
+ * Lists every drawable instance in id order, for the path that neither culls nor sorts.
92
+ * @param instances - The mesh's instance allocator, which says what is live and visible.
93
+ */
94
+ private updateIndexArray;
95
+ /**
96
+ * Measures every drawable instance into the sort list, for the path that sorts without culling.
97
+ * @param host - The mesh being drawn.
98
+ * @param instances - The mesh's instance allocator, which says what is live and visible.
99
+ * @param camera - The camera being culled against, which {@link instanceSphere} may measure from.
100
+ */
101
+ private updateRenderList;
102
+ /**
103
+ * Frustum-tests every drawable instance one at a time, listing or measuring the survivors.
104
+ * @param host - The mesh being drawn.
105
+ * @param instances - The mesh's instance allocator, which says what is live and visible.
106
+ * @param camera - The camera being culled against, which {@link instanceSphere} may measure from.
107
+ */
108
+ private linearCulling;
109
+ /**
110
+ * What the frustum is tested against for one instance — the bounds the host encloses it with.
111
+ *
112
+ * A seam, because *what could be on screen* is not always the same question as *where the instance
113
+ * is*: a line is drawn as a ribbon expanded around its segment after projection, so a batch of them
114
+ * culls against something wider than the host's own bounds. That difference belongs to the pass —
115
+ * it depends on the camera, which the host's bounds do not know about — rather than to the host.
116
+ * @param host - The mesh being drawn.
117
+ * @param id - The instance.
118
+ * @param camera - The camera being culled against. Unused here, because the host's own bounds are
119
+ * the whole answer for an instance placed by a matrix; passed because a subclass measuring what
120
+ * an instance *covers* needs to know where it is being looked at from.
121
+ * @param target - Receives the result.
122
+ * @returns The bounds to test.
123
+ */
124
+ protected instanceSphere(host: CullingHost, id: number, camera: Camera, target: Sphere): Sphere;
125
+ /**
126
+ * How far along the camera's forward axis an instance sits — what the default sorts order by.
127
+ *
128
+ * Measured from the center of what encloses the instance, which is where the culled path always
129
+ * measured it; the non-culled path used to take the instance matrix's translation instead, so two
130
+ * meshes differing only in `perObjectFrustumCulled` could disagree about the order of a geometry
131
+ * whose origin is not its center.
132
+ *
133
+ * Reads the pass's camera basis, so it is a method: as a free function it would have looked like
134
+ * arithmetic over its argument while depending on two module variables some earlier call had to
135
+ * have filled.
136
+ * @param sphere - The instance's bounds, in the mesh's space.
137
+ * @returns The depth.
138
+ */
139
+ private depthOf;
140
+ }
141
+ //# sourceMappingURL=FrustumCulling.d.ts.map
@@ -0,0 +1,246 @@
1
+ import { Frustum, Matrix4, Sphere, Vector3 } from 'three';
2
+ /**
3
+ * Builds the draw list for one draw: every drawable instance, culled and ordered per the host's
4
+ * {@link CullingHost.perObjectFrustumCulled} and {@link CullingHost.sortObjects}.
5
+ *
6
+ * Ported from three.ez's instanced-mesh `feature/FrustumCulling.ts`, with `utils/SortingUtils.ts`
7
+ * and `utils/InstancedRenderList.ts` folded in — the latter as a pair of module arrays rather than a
8
+ * class, since nothing outside this file pushes to it. BVH culling and every LOD path are dropped;
9
+ * `linearCulling` keeps the seam they sat beside, so porting them later is additive.
10
+ *
11
+ * Departures from upstream:
12
+ *
13
+ * - **It returns a list rather than writing the mesh's buffer.** Upstream fills the mesh's
14
+ * `instanceIndex` attribute in place and flags it for upload from here. This hands back a
15
+ * {@link DrawList} and leaves both the write-out and the upload to the caller, so no path can
16
+ * produce a list that never reaches the GPU.
17
+ * - **The host encloses its own instances.** Upstream moves the geometry's bounding sphere onto each
18
+ * instance's matrix, which assumes every instance is *placed* by one. A line batch is not: a
19
+ * segment is placed by its two endpoints, and no matrix maps the template onto it, because the
20
+ * quad is expanded after projection in screen space. Asking the host for the sphere costs the same
21
+ * and holds for both.
22
+ * - **The count always resets** (see {@link performFrustumCulling}).
23
+ * - **The material is passed in** rather than read off the mesh, so a mesh drawing an array of them
24
+ * orders each group by its own. Upstream reads `this.material`, which is the whole array.
25
+ */
26
+ export class FrustumCulling {
27
+ /** The draw list handed back each pass, reused so a frame allocates nothing. */
28
+ list = { ids: new Float32Array(0), count: 0 };
29
+ /**
30
+ * The camera and frustum for the pass in flight, in the mesh's own space — derived once by
31
+ * {@link frustumCulling} and read by the gather it delegates to.
32
+ *
33
+ * Held here rather than at module scope, which is where upstream keeps them, because upstream
34
+ * patches culling onto the prototype and has nowhere else to put them. They are state for the
35
+ * length of a pass, not scratch: what makes the module-level values below safe to share between
36
+ * meshes is that each is filled and consumed within a single statement, and these are not.
37
+ */
38
+ frustum = new Frustum();
39
+ cameraPos = new Vector3();
40
+ forward = new Vector3();
41
+ /**
42
+ * Rebuilds the draw list for one draw.
43
+ * @param host - The mesh being drawn.
44
+ * @param instances - The mesh's instance allocator, which says what is live and visible.
45
+ * @param camera - The camera to cull and sort against.
46
+ * @param material - The material about to draw, which picks the default sort direction.
47
+ * @returns The instances to draw, in draw order.
48
+ */
49
+ performFrustumCulling(host, instances, camera, material) {
50
+ // The list can never hold more than one entry per live instance, so capacity bounds it.
51
+ if (this.list.ids.length < instances.capacity)
52
+ this.list.ids = new Float32Array(instances.capacity);
53
+ // Upstream resets only when culling or sorting, which leaves a mesh whose instances were all
54
+ // removed drawing the previous pass's list. Resetting unconditionally is a fix, not a port.
55
+ this.list.count = 0;
56
+ if (instances.addedInstancesCount === 0)
57
+ return this.list;
58
+ this.frustumCulling(host, instances, camera, material);
59
+ return this.list;
60
+ }
61
+ /**
62
+ * Picks the gather-and-order path the host's flags call for.
63
+ * @param host - The mesh being drawn.
64
+ * @param instances - The mesh's instance allocator, which says what is live and visible.
65
+ * @param camera - The camera to cull and sort against.
66
+ * @param material - The material about to draw.
67
+ */
68
+ frustumCulling(host, instances, camera, material) {
69
+ const sortObjects = host.sortObjects;
70
+ const perObjectFrustumCulled = host.perObjectFrustumCulled;
71
+ // Neither culling nor ordering: the list is every drawable instance, in id order.
72
+ if (!perObjectFrustumCulled && !sortObjects) {
73
+ this.updateIndexArray(instances);
74
+ return;
75
+ }
76
+ // Depth is measured in the mesh's own space, so bring the camera into it once up front.
77
+ if (sortObjects) {
78
+ _invMatrixWorld.copy(host.matrixWorld).invert();
79
+ this.cameraPos.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(_invMatrixWorld);
80
+ this.forward
81
+ .set(0, 0, -1)
82
+ .transformDirection(camera.matrixWorld)
83
+ .transformDirection(_invMatrixWorld);
84
+ }
85
+ // Gather the candidates: every drawable instance, or only those the frustum keeps.
86
+ if (!perObjectFrustumCulled) {
87
+ this.updateRenderList(host, instances, camera);
88
+ }
89
+ else {
90
+ _projScreenMatrix
91
+ .multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)
92
+ .multiply(host.matrixWorld);
93
+ this.frustum.setFromProjectionMatrix(_projScreenMatrix);
94
+ this.linearCulling(host, instances, camera);
95
+ }
96
+ // Order what was gathered, then flatten it into the ids the draw reads.
97
+ if (sortObjects) {
98
+ const customSort = host.customSort;
99
+ const list = _renderList;
100
+ if (customSort === null) {
101
+ list.sort(material.transparent ? sortTransparent : sortOpaque);
102
+ }
103
+ else {
104
+ customSort(list);
105
+ }
106
+ const ids = this.list.ids;
107
+ const count = list.length;
108
+ for (let i = 0; i < count; i++) {
109
+ ids[i] = list[i].index;
110
+ }
111
+ this.list.count = count;
112
+ list.length = 0;
113
+ }
114
+ }
115
+ /**
116
+ * Lists every drawable instance in id order, for the path that neither culls nor sorts.
117
+ * @param instances - The mesh's instance allocator, which says what is live and visible.
118
+ */
119
+ updateIndexArray(instances) {
120
+ const ids = this.list.ids;
121
+ const addedInstancesCount = instances.addedInstancesCount;
122
+ let count = 0;
123
+ for (let i = 0; i < addedInstancesCount; i++) {
124
+ if (instances.getVisibleAt(i)) {
125
+ ids[count++] = i;
126
+ }
127
+ }
128
+ this.list.count = count;
129
+ }
130
+ /**
131
+ * Measures every drawable instance into the sort list, for the path that sorts without culling.
132
+ * @param host - The mesh being drawn.
133
+ * @param instances - The mesh's instance allocator, which says what is live and visible.
134
+ * @param camera - The camera being culled against, which {@link instanceSphere} may measure from.
135
+ */
136
+ updateRenderList(host, instances, camera) {
137
+ const addedInstancesCount = instances.addedInstancesCount;
138
+ for (let i = 0; i < addedInstancesCount; i++) {
139
+ if (instances.getVisibleAt(i)) {
140
+ pushRenderItem(this.depthOf(this.instanceSphere(host, i, camera, _sphere)), i);
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * Frustum-tests every drawable instance one at a time, listing or measuring the survivors.
146
+ * @param host - The mesh being drawn.
147
+ * @param instances - The mesh's instance allocator, which says what is live and visible.
148
+ * @param camera - The camera being culled against, which {@link instanceSphere} may measure from.
149
+ */
150
+ linearCulling(host, instances, camera) {
151
+ const ids = this.list.ids;
152
+ const addedInstancesCount = instances.addedInstancesCount;
153
+ const sortObjects = host.sortObjects;
154
+ let count = 0;
155
+ for (let i = 0; i < addedInstancesCount; i++) {
156
+ if (!instances.getVisibleAt(i))
157
+ continue;
158
+ // What the frustum sees of this instance is whatever encloses it.
159
+ const sphere = this.instanceSphere(host, i, camera, _sphere);
160
+ if (this.frustum.intersectsSphere(sphere)) {
161
+ if (sortObjects) {
162
+ pushRenderItem(this.depthOf(sphere), i);
163
+ }
164
+ else {
165
+ ids[count++] = i;
166
+ }
167
+ }
168
+ }
169
+ // Only the unordered path finished the list here; the ordered one hands off to the sort.
170
+ if (!sortObjects)
171
+ this.list.count = count;
172
+ }
173
+ /**
174
+ * What the frustum is tested against for one instance — the bounds the host encloses it with.
175
+ *
176
+ * A seam, because *what could be on screen* is not always the same question as *where the instance
177
+ * is*: a line is drawn as a ribbon expanded around its segment after projection, so a batch of them
178
+ * culls against something wider than the host's own bounds. That difference belongs to the pass —
179
+ * it depends on the camera, which the host's bounds do not know about — rather than to the host.
180
+ * @param host - The mesh being drawn.
181
+ * @param id - The instance.
182
+ * @param camera - The camera being culled against. Unused here, because the host's own bounds are
183
+ * the whole answer for an instance placed by a matrix; passed because a subclass measuring what
184
+ * an instance *covers* needs to know where it is being looked at from.
185
+ * @param target - Receives the result.
186
+ * @returns The bounds to test.
187
+ */
188
+ instanceSphere(host, id, camera, target) {
189
+ return host.getBoundingSphereAt(id, target);
190
+ }
191
+ /**
192
+ * How far along the camera's forward axis an instance sits — what the default sorts order by.
193
+ *
194
+ * Measured from the center of what encloses the instance, which is where the culled path always
195
+ * measured it; the non-culled path used to take the instance matrix's translation instead, so two
196
+ * meshes differing only in `perObjectFrustumCulled` could disagree about the order of a geometry
197
+ * whose origin is not its center.
198
+ *
199
+ * Reads the pass's camera basis, so it is a method: as a free function it would have looked like
200
+ * arithmetic over its argument while depending on two module variables some earlier call had to
201
+ * have filled.
202
+ * @param sphere - The instance's bounds, in the mesh's space.
203
+ * @returns The depth.
204
+ */
205
+ depthOf(sphere) {
206
+ return _position.subVectors(sphere.center, this.cameraPos).dot(this.forward);
207
+ }
208
+ }
209
+ /**
210
+ * Adds an entry to the sort list, writing through the pooled object that sits at that position.
211
+ *
212
+ * The two arrays are what make the reuse work, and are why upstream keeps both: sorting reorders
213
+ * {@link _renderList}, so its own instances cannot be addressed by push order afterwards, while
214
+ * {@link _renderPool} is never reordered and always hands back the same object for a given
215
+ * position. Every field is overwritten here before the entry is pushed, so whatever the last pass
216
+ * left in it is irrelevant.
217
+ * @param depth - Depth along the camera's forward axis.
218
+ * @param index - The instance id.
219
+ */
220
+ function pushRenderItem(depth, index) {
221
+ const item = (_renderPool[_renderList.length] ??= { depth, index });
222
+ item.depth = depth;
223
+ item.index = index;
224
+ _renderList.push(item);
225
+ }
226
+ function sortOpaque(a, b) {
227
+ return a.depth - b.depth;
228
+ }
229
+ function sortTransparent(a, b) {
230
+ return b.depth - a.depth;
231
+ }
232
+ // Scratch shared by every mesh's culling pass. Each is filled and consumed within one statement or
233
+ // the next, which is what makes sharing them safe — anything a pass has to keep for longer than that
234
+ // is a field of the pass instead.
235
+ const _projScreenMatrix = new Matrix4();
236
+ const _invMatrixWorld = new Matrix4();
237
+ const _position = new Vector3();
238
+ const _sphere = new Sphere();
239
+ /** The entries one pass collected, truncated at the end of it and re-pushed by the next. */
240
+ const _renderList = [];
241
+ /**
242
+ * Every entry ever handed out, in push order and never truncated — so a sorted pass refills objects
243
+ * rather than allocating one per visible instance. It settles at the most instances ever sorted at
244
+ * once and stays there.
245
+ */
246
+ const _renderPool = [];