@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,289 @@
1
+ import { Color, DataTexture, FloatType, Frustum, Matrix4, RGBAFormat, Sphere, Vector3, } from 'three';
2
+ import { Y_DOWN_FLIP } from '../../space/Space';
3
+ import { aimDepth } from '../../utils/math';
4
+ /**
5
+ * The data texture a batch's shader reads each member's transform and style from: one 32-float row
6
+ * per member, refilled once per draw with frustum culling folded in.
7
+ *
8
+ * Troika's `BatchedText.prepareForRender`, lifted out of the class and given ownership of the
9
+ * textures it fills — a pair, since the outline pass writes different fields into the same layout.
10
+ * Rows are addressed by id, so the texture is sized to the batch's high-water mark rather than to
11
+ * its live member count.
12
+ *
13
+ * Departures from upstream:
14
+ *
15
+ * - **The frustum is built against the batch**, not the scene root. See
16
+ * {@link MemberTextureHost.matrixWorld}.
17
+ * - **Hidden and culled members are collapsed, not repacked out** — a zero matrix takes every glyph
18
+ * of the member to a point, so it costs vertex shading but no fragments, and no repack.
19
+ * - **The upload is conditional.** Writes are compared against what the texture already holds, so a
20
+ * frame that changed nothing sends nothing.
21
+ * - **`setMatrixTexture` is inlined**, so a material that lost the method still draws. See
22
+ * {@link publish}.
23
+ */
24
+ export class MemberTexture {
25
+ /** One texture per pass, each allocated on the first draw that needs it. */
26
+ textures = {};
27
+ /** Whether this pass wrote a value differing from what the texture already held. */
28
+ needsUpdate = false;
29
+ /**
30
+ * Refills the texture for one draw and points the material at it.
31
+ * @param host - The batch being drawn.
32
+ * @param material - The text material being drawn, main or outline.
33
+ * @param camera - The camera to cull against.
34
+ */
35
+ prepareForRender(host, material, camera) {
36
+ const isOutline = material.isTextOutlineMaterial;
37
+ material.uniforms.uTroikaIsOutline.value = isOutline;
38
+ const rowCount = host.addedMembersCount;
39
+ // Floored at one member: `Math.log2(0)` is `-Infinity`, which upstream turns into a zero-width,
40
+ // `NaN`-height texture.
41
+ const texture = this.textureFor(isOutline ? 'outline' : 'main', Math.max(1, rowCount));
42
+ const texData = texture.image.data;
43
+ this.needsUpdate = false;
44
+ _matrix
45
+ .multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)
46
+ .multiply(host.matrixWorld);
47
+ _frustum.setFromProjectionMatrix(_matrix, camera.coordinateSystem);
48
+ // The camera's rotation, brought into the batch's own space, because that is where member
49
+ // matrices live. Computed once per draw, not once per member. `Y_DOWN_FLIP` plays the camera's
50
+ // own rest pose here, so `faceCamera` does not bake that pose in twice: once here and once more
51
+ // in the ordinary view transform at draw time. See `Y_DOWN_FLIP` in `space/Space.ts` for why
52
+ // this role holds regardless of a member's own scale or rotation.
53
+ _basis
54
+ .extractRotation(host.matrixWorld)
55
+ .transpose()
56
+ .multiply(_cameraBasis.extractRotation(camera.matrixWorld))
57
+ .multiply(Y_DOWN_FLIP);
58
+ // Hoisted out of the loop: `Text`'s `material` getter calls `hasOutline()`, which walks every
59
+ // member, so reading it per member made this loop quadratic — and it returns an *array* once
60
+ // outlines are on, whose `.color` is undefined. The material being drawn is already in hand.
61
+ const batchColor = host.color ?? material.color ?? 0xffffff;
62
+ // What a screen-space member's size is exact at, and where the camera looks from. Zero turns the
63
+ // correction off, for an orthographic camera (no divide to cancel) and for one that never meets
64
+ // the plan. See `depthScaleOf`.
65
+ const reference = camera.isPerspectiveCamera
66
+ ? aimDepth(camera, host.matrixWorld)
67
+ : 0;
68
+ _eye.setFromMatrixPosition(camera.matrixWorld);
69
+ _forward.setFromMatrixColumn(camera.matrixWorld, 2).normalize().negate();
70
+ for (let memberId = 0; memberId < rowCount; memberId++) {
71
+ const text = host.getText(memberId);
72
+ // An unsynced member has no layout to write and draws nothing either.
73
+ if (!text?.textRenderInfo)
74
+ continue;
75
+ faceCamera(text, _basis, reference > 0 ? depthScaleOf(text, host.matrixWorld, reference) : 1);
76
+ const start = memberId * FLOATS_PER_MEMBER;
77
+ if (isDrawn(text)) {
78
+ this.writeMember(texData, start, text, material, batchColor);
79
+ }
80
+ else {
81
+ this.collapse(texData, start);
82
+ }
83
+ }
84
+ // Assigning `false` is a no-op in three — the setter only bumps the version for `true` — so this
85
+ // requests an upload when something changed and leaves the texture alone when nothing did.
86
+ texture.needsUpdate = this.needsUpdate;
87
+ publish(material, texture);
88
+ }
89
+ /** Frees both passes' textures. */
90
+ dispose() {
91
+ for (const pass of Object.keys(this.textures)) {
92
+ this.textures[pass]?.dispose();
93
+ delete this.textures[pass];
94
+ }
95
+ }
96
+ /**
97
+ * The pass's texture, sized to hold every row, reallocated when the row count outgrows it.
98
+ * @param pass - Which pass is being drawn.
99
+ * @param rowCount - Rows the texture must hold, at least one.
100
+ * @returns The texture.
101
+ */
102
+ textureFor(pass, rowCount) {
103
+ let texture = this.textures[pass];
104
+ const dataLength = Math.pow(2, Math.ceil(Math.log2(rowCount * FLOATS_PER_MEMBER)));
105
+ if (texture && dataLength === texture.image.data.length)
106
+ return texture;
107
+ texture?.dispose();
108
+ const width = Math.min(dataLength / 4, 1024);
109
+ texture = new DataTexture(new Float32Array(dataLength), width, dataLength / 4 / width, RGBAFormat, FloatType);
110
+ this.textures[pass] = texture;
111
+ return texture;
112
+ }
113
+ /**
114
+ * Writes one member's row: its transform, then the style fields the pass being drawn reads.
115
+ *
116
+ * The member populates the material's uniforms itself, since that is where the defaults and the
117
+ * percentage parsing live; this reads the results straight back out of them.
118
+ * @param texData - The texture's backing array.
119
+ * @param start - The row's first float.
120
+ * @param text - The member to write.
121
+ * @param material - The material being drawn, main or outline.
122
+ * @param batchColor - The color a member with none of its own draws in.
123
+ */
124
+ writeMember(texData, start, text, material, batchColor) {
125
+ const isOutline = material.isTextOutlineMaterial;
126
+ const matrix = text.matrix.elements;
127
+ for (let i = 0; i < 16; i++) {
128
+ this.setTexData(texData, start + i, matrix[i]);
129
+ }
130
+ text._prepareForRender(material);
131
+ const { uTroikaTotalBounds, uTroikaClipRect, uTroikaPositionOffset, uTroikaEdgeOffset, uTroikaBlurRadius, uTroikaStrokeWidth, uTroikaStrokeColor, uTroikaStrokeOpacity, uTroikaFillOpacity, uTroikaCurveRadius, } = material.uniforms;
132
+ // Total bounds for uv
133
+ for (let i = 0; i < 4; i++) {
134
+ this.setTexData(texData, start + 16 + i, uTroikaTotalBounds.value.getComponent(i));
135
+ }
136
+ // Clip rect
137
+ for (let i = 0; i < 4; i++) {
138
+ this.setTexData(texData, start + 20 + i, uTroikaClipRect.value.getComponent(i));
139
+ }
140
+ // Color
141
+ const color = (isOutline ? text.outlineColor || 0 : text.color) ?? batchColor;
142
+ this.setTexData(texData, start + 24, _color.set(color).getHex());
143
+ // Fill opacity / outline opacity
144
+ this.setTexData(texData, start + 25, uTroikaFillOpacity.value);
145
+ // Curve radius
146
+ this.setTexData(texData, start + 26, uTroikaCurveRadius.value);
147
+ if (isOutline) {
148
+ // Outline properties
149
+ this.setTexData(texData, start + 28, uTroikaPositionOffset.value.x);
150
+ this.setTexData(texData, start + 29, uTroikaPositionOffset.value.y);
151
+ this.setTexData(texData, start + 30, uTroikaEdgeOffset.value);
152
+ this.setTexData(texData, start + 31, uTroikaBlurRadius.value);
153
+ }
154
+ else {
155
+ // Stroke properties
156
+ this.setTexData(texData, start + 28, uTroikaStrokeWidth.value);
157
+ this.setTexData(texData, start + 29, _color.set(uTroikaStrokeColor.value).getHex());
158
+ this.setTexData(texData, start + 30, uTroikaStrokeOpacity.value);
159
+ }
160
+ }
161
+ /**
162
+ * Zeroes a member's matrix, taking every glyph of it to a point.
163
+ * @param texData - The texture's backing array.
164
+ * @param start - The row's first float.
165
+ */
166
+ collapse(texData, start) {
167
+ for (let i = 0; i < 16; i++) {
168
+ this.setTexData(texData, start + i, 0);
169
+ }
170
+ }
171
+ /**
172
+ * Writes one float, noting whether it changed.
173
+ * @param texData - The texture's backing array.
174
+ * @param index - The float's index.
175
+ * @param value - The value to write.
176
+ */
177
+ setTexData(texData, index, value) {
178
+ if (value !== texData[index]) {
179
+ texData[index] = value;
180
+ this.needsUpdate = true;
181
+ }
182
+ }
183
+ }
184
+ /**
185
+ * How much a screen-space member is scaled so that it keeps the on-screen size its plan-unit size
186
+ * asks for wherever it sits in a perspective frame.
187
+ *
188
+ * The same correction a sprite's shader applies to an image (`spriteDepthScale` in
189
+ * `sprites/sprite-material.ts`), and for the same reason: a member sized in plan units shrinks by
190
+ * the projection's divide, and a pitched camera spreads the plan across a range of depths, so one
191
+ * pixel size is right only where the camera's axis crosses the plan. Without it an image beside its
192
+ * text is constant in pixels while the text is not, and anything laid out inside the image's
193
+ * pixels (a count in a counter circle) slides off it as the pin moves up or down the screen.
194
+ * @param text - The member, with its sprite anchor set.
195
+ * @param matrixWorld - The batch's world matrix.
196
+ * @param reference - The depth the plan-unit size is exact at. Must be positive.
197
+ * @returns The scale, one at the reference depth. One as well for an anchor at or behind the eye.
198
+ */
199
+ function depthScaleOf(text, matrixWorld, reference) {
200
+ const anchor = text.spriteAnchor;
201
+ if (!anchor)
202
+ return 1;
203
+ const depth = _offset
204
+ .set(anchor.x, anchor.y, text.position.z)
205
+ .applyMatrix4(matrixWorld)
206
+ .sub(_eye)
207
+ .dot(_forward);
208
+ return depth > 0 ? depth / reference : 1;
209
+ }
210
+ /**
211
+ * Turns a screen-space member to face the camera, and leaves a flat one alone.
212
+ *
213
+ * Composed on the CPU, on a loop that already runs once per member per draw, rather than in the
214
+ * shader. The shader path costs twelve bytes per member of GPU memory and needs a seam on both
215
+ * `BatchedTextImpl.raycast` and `isDrawn`. This path costs neither, because both of those read the
216
+ * member's matrix already. Write the sprite matrix there and both follow.
217
+ *
218
+ * The composition is
219
+ * `T(anchor) . basis . S(depth) . T(position - anchor) . Rz(rotation.z) . S(scale)`. The basis is
220
+ * the camera's rotation brought into the batch's own space, corrected by `Y_DOWN_FLIP` for the
221
+ * camera's own rest pose (see {@link Y_DOWN_FLIP} in `space/Space.ts`). `S(depth)` is
222
+ * {@link depthScaleOf}, which scales the offset from the anchor along with the glyphs. Everything
223
+ * else is read off the member exactly as `updateMatrix` reads it. Straight overhead with the
224
+ * anchor at the reference depth the basis is the identity and the depth scale is one, so this
225
+ * reduces to the flat matrix, which is the equality the top-down baseline asserts on screen.
226
+ *
227
+ * Written through `setMatrix` only when it changed, so a still camera costs sixteen comparisons and
228
+ * no bounds invalidation. A pan or a zoom costs the same, because neither moves the camera's
229
+ * rotation. Only a roll or a pitch dirties a row.
230
+ * @param text - The member to turn.
231
+ * @param basis - The camera basis for this draw, in the batch's own space.
232
+ * @param depthScale - The member's perspective correction. One for none.
233
+ */
234
+ function faceCamera(text, basis, depthScale) {
235
+ const anchor = text.spriteAnchor;
236
+ if (!anchor)
237
+ return;
238
+ // `spriteAnchor` carries no z. The offset's z is zero by construction, and the pivot's z is read
239
+ // off the member's own position instead.
240
+ _offset.set(text.position.x - anchor.x, text.position.y - anchor.y, 0);
241
+ _spriteMatrix
242
+ .copy(basis)
243
+ .multiply(_step.makeScale(depthScale, depthScale, depthScale))
244
+ .multiply(_step.makeTranslation(_offset.x, _offset.y, _offset.z))
245
+ .multiply(_step.makeRotationZ(text.rotation.z))
246
+ .scale(text.scale)
247
+ .premultiply(_step.makeTranslation(anchor.x, anchor.y, text.position.z));
248
+ if (!text.matrix.equals(_spriteMatrix))
249
+ text.setMatrix(_spriteMatrix);
250
+ }
251
+ /**
252
+ * Whether a member draws this frame: shown, and inside the frustum this pass was set up with.
253
+ * @param text - The member to test.
254
+ * @returns Whether it draws.
255
+ */
256
+ function isDrawn(text) {
257
+ if (!text.visible)
258
+ return false;
259
+ return _frustum.intersectsSphere(_sphere.copy(text.geometry.boundingSphere).applyMatrix4(text.matrix));
260
+ }
261
+ /**
262
+ * Points a material at the texture it should read members from, sizing it in the same write.
263
+ *
264
+ * Troika's `setMatrixTexture`, inlined — this is the whole of it. Calling the method instead would
265
+ * throw on any material that lost it, which is every copy built by calling `material.constructor`;
266
+ * the uniforms it writes are inherited by all of them, so writing them directly leaves such a copy
267
+ * drawing the batch where the batch actually is.
268
+ * @param material - The material being drawn.
269
+ * @param texture - The texture it should read.
270
+ */
271
+ function publish(material, texture) {
272
+ const { uTroikaMatricesTexture, uTroikaMatricesTextureSize } = material.uniforms;
273
+ uTroikaMatricesTexture.value = texture;
274
+ uTroikaMatricesTextureSize.value.set(texture.image.width, texture.image.height);
275
+ }
276
+ /** Floats each member occupies; troika's packing, unchanged. */
277
+ const FLOATS_PER_MEMBER = 32;
278
+ // Scratch reused across calls, each filled and consumed within a single synchronous block.
279
+ const _color = new Color();
280
+ const _matrix = new Matrix4();
281
+ const _frustum = new Frustum();
282
+ const _sphere = new Sphere();
283
+ const _basis = new Matrix4();
284
+ const _cameraBasis = new Matrix4();
285
+ const _spriteMatrix = new Matrix4();
286
+ const _step = new Matrix4();
287
+ const _offset = new Vector3();
288
+ const _eye = new Vector3();
289
+ const _forward = new Vector3();
@@ -0,0 +1,88 @@
1
+ import { Matrix4, type Vector2 } from 'three';
2
+ import { Text as TroikaText, type TextMaterial } from 'troika-three-text';
3
+ import { type BatchedTextImpl } from './BatchedText';
4
+ /**
5
+ * troika's `Text` with the deviations a batched, 2D-painter renderer needs.
6
+ *
7
+ * Four departures from upstream, each load-bearing:
8
+ *
9
+ * 1. **The clip rect is not applied to the geometry when batched.** Upstream ends
10
+ * `_prepareForRender` with `geometry.applyClipRect(…)`, but a member's geometry *is* the batch's
11
+ * shared `GlyphsGeometry` — applying one member's rect would clip the whole batch, and dirty a
12
+ * buffer every member then re-dirties. The batch reads the rect out of the uniforms instead.
13
+ * 2. **`depthOffset` is unsupported.** Upstream writes `material.polygonOffset` and friends from it
14
+ * on every render, so a base material that set them itself is overwritten every frame — with
15
+ * `false`, since `depthOffset` defaults to `0`. Set polygon offset on the base material directly;
16
+ * this class never touches it.
17
+ * 3. **The `color` shortcut is skipped when batched.** It mutates the *shared* derived material once
18
+ * per member per frame, and the batch ignores `material.color` anyway — it reads `text.color`
19
+ * straight into the data texture.
20
+ * 4. **`strokeOpacity` defaults to `1` rather than staying `undefined`.** Upstream leaves it unset
21
+ * when `strokeWidth` is `0`; the batch writes that uniform into a `Float32Array`, where
22
+ * `undefined` becomes `NaN`, and `NaN !== NaN` makes its change-detection report a write every
23
+ * frame — re-uploading the whole texture forever.
24
+ *
25
+ * Members are **not in the scene graph**, so nothing calls `updateMatrixWorld` for them: their local
26
+ * `matrix` is the transform the batch draws with, and the batch's bounds pass is what recomposes it.
27
+ * That pass runs only when something reports a move, and three gives `position` and `scale` no change
28
+ * callback — so after moving a member, call {@link updateMatrix}, or write the matrix wholesale with
29
+ * {@link setMatrix}. Both report back through {@link _batchParent}.
30
+ *
31
+ * Render it with {@link Text}.
32
+ */
33
+ export declare class TextImpl extends TroikaText {
34
+ /**
35
+ * The batch this text belongs to, set by {@link BatchedTextImpl.addText}. It is how a member
36
+ * reaches its batch without the batch scanning for changes: property writes invalidate the sync
37
+ * through it, and transform writes invalidate the bounds.
38
+ */
39
+ _batchParent: BatchedTextImpl | undefined;
40
+ /**
41
+ * Backing store for the `_needsSync` accessor installed on the prototype below.
42
+ *
43
+ * Initialized `true`, which is both what a fresh member wants — it has never been laid out — and
44
+ * exactly what the base constructor leaves behind: its own property writes set the flag before
45
+ * this initializer runs, so seeding `false` here would quietly undo them.
46
+ */
47
+ _syncFlag: boolean;
48
+ /**
49
+ * Where this member pivots when it faces the camera, in the plan's x and y. `null` draws it flat
50
+ * on the plan.
51
+ *
52
+ * One object, set and cleared together, so the two halves cannot disagree. Deliberately not named
53
+ * `anchorX` or `anchorY`, which troika owns and lists in its syncable properties. Assigning those
54
+ * would re-run layout and repack the batch on every write. `position`, `quaternion`, `scale` and
55
+ * `matrix` are all absent from that list, which is what makes the per-draw compose free of layout.
56
+ *
57
+ * Two dimensional on purpose. Every line of a def sits at the def's own elevation, so the anchor's
58
+ * z always equals the member's own `position.z`. There is no separate z to store.
59
+ *
60
+ * Derived, never authored. `applyPlacement` sets it from the def's bounds.
61
+ */
62
+ spriteAnchor: Vector2 | null;
63
+ /**
64
+ * Writes the local transform directly and tells the batch its bounds moved.
65
+ *
66
+ * Turns off `matrixAutoUpdate`, so the batch's bounds pass leaves the matrix alone — the caller
67
+ * owns it from here.
68
+ * @param matrix - The transform to draw this text with.
69
+ */
70
+ setMatrix(matrix: Matrix4): void;
71
+ /**
72
+ * Recomposes the local matrix from `position`/`quaternion`/`scale`, and tells the batch its bounds
73
+ * moved.
74
+ *
75
+ * Members are not in the scene graph, so three never calls this; a caller that moved one should,
76
+ * and the report is what schedules the batch's bounds pass. That pass recomposes the matrix again
77
+ * — harmless, and it is what leaves a caller who reaches for `invalidateBounds` instead equally
78
+ * correct.
79
+ */
80
+ updateMatrix(): void;
81
+ _prepareForRender(material: TextMaterial): void;
82
+ }
83
+ /**
84
+ * Registers the class as an R3F element, by value so the generated key cannot collide with anything
85
+ * three puts in the catalog when the canvas mounts.
86
+ */
87
+ export declare const Text: import("react").ExoticComponent<import("@react-three/fiber/dist/declarations/src/core/utils").Mutable<import("@react-three/fiber/dist/declarations/src/core/utils").Overwrite<Partial<import("@react-three/fiber/dist/declarations/src/core/utils").Overwrite<TextImpl, import("@react-three/fiber").MathProps<TextImpl> & import("@react-three/fiber").ReactProps<TextImpl> & Partial<import("@react-three/fiber").EventHandlers>>>, Omit<import("@react-three/fiber").InstanceProps<TextImpl, typeof TextImpl>, "object">>>>;
88
+ //# sourceMappingURL=Text.d.ts.map
@@ -0,0 +1,230 @@
1
+ import { extend } from '@react-three/fiber';
2
+ import { Color, Matrix4, Vector3 } from 'three';
3
+ import { Text as TroikaText } from 'troika-three-text';
4
+ /**
5
+ * troika's `Text` with the deviations a batched, 2D-painter renderer needs.
6
+ *
7
+ * Four departures from upstream, each load-bearing:
8
+ *
9
+ * 1. **The clip rect is not applied to the geometry when batched.** Upstream ends
10
+ * `_prepareForRender` with `geometry.applyClipRect(…)`, but a member's geometry *is* the batch's
11
+ * shared `GlyphsGeometry` — applying one member's rect would clip the whole batch, and dirty a
12
+ * buffer every member then re-dirties. The batch reads the rect out of the uniforms instead.
13
+ * 2. **`depthOffset` is unsupported.** Upstream writes `material.polygonOffset` and friends from it
14
+ * on every render, so a base material that set them itself is overwritten every frame — with
15
+ * `false`, since `depthOffset` defaults to `0`. Set polygon offset on the base material directly;
16
+ * this class never touches it.
17
+ * 3. **The `color` shortcut is skipped when batched.** It mutates the *shared* derived material once
18
+ * per member per frame, and the batch ignores `material.color` anyway — it reads `text.color`
19
+ * straight into the data texture.
20
+ * 4. **`strokeOpacity` defaults to `1` rather than staying `undefined`.** Upstream leaves it unset
21
+ * when `strokeWidth` is `0`; the batch writes that uniform into a `Float32Array`, where
22
+ * `undefined` becomes `NaN`, and `NaN !== NaN` makes its change-detection report a write every
23
+ * frame — re-uploading the whole texture forever.
24
+ *
25
+ * Members are **not in the scene graph**, so nothing calls `updateMatrixWorld` for them: their local
26
+ * `matrix` is the transform the batch draws with, and the batch's bounds pass is what recomposes it.
27
+ * That pass runs only when something reports a move, and three gives `position` and `scale` no change
28
+ * callback — so after moving a member, call {@link updateMatrix}, or write the matrix wholesale with
29
+ * {@link setMatrix}. Both report back through {@link _batchParent}.
30
+ *
31
+ * Render it with {@link Text}.
32
+ */
33
+ export class TextImpl extends TroikaText {
34
+ /**
35
+ * The batch this text belongs to, set by {@link BatchedTextImpl.addText}. It is how a member
36
+ * reaches its batch without the batch scanning for changes: property writes invalidate the sync
37
+ * through it, and transform writes invalidate the bounds.
38
+ */
39
+ _batchParent = undefined;
40
+ /**
41
+ * Backing store for the `_needsSync` accessor installed on the prototype below.
42
+ *
43
+ * Initialized `true`, which is both what a fresh member wants — it has never been laid out — and
44
+ * exactly what the base constructor leaves behind: its own property writes set the flag before
45
+ * this initializer runs, so seeding `false` here would quietly undo them.
46
+ */
47
+ _syncFlag = true;
48
+ /**
49
+ * Where this member pivots when it faces the camera, in the plan's x and y. `null` draws it flat
50
+ * on the plan.
51
+ *
52
+ * One object, set and cleared together, so the two halves cannot disagree. Deliberately not named
53
+ * `anchorX` or `anchorY`, which troika owns and lists in its syncable properties. Assigning those
54
+ * would re-run layout and repack the batch on every write. `position`, `quaternion`, `scale` and
55
+ * `matrix` are all absent from that list, which is what makes the per-draw compose free of layout.
56
+ *
57
+ * Two dimensional on purpose. Every line of a def sits at the def's own elevation, so the anchor's
58
+ * z always equals the member's own `position.z`. There is no separate z to store.
59
+ *
60
+ * Derived, never authored. `applyPlacement` sets it from the def's bounds.
61
+ */
62
+ spriteAnchor = null;
63
+ /**
64
+ * Writes the local transform directly and tells the batch its bounds moved.
65
+ *
66
+ * Turns off `matrixAutoUpdate`, so the batch's bounds pass leaves the matrix alone — the caller
67
+ * owns it from here.
68
+ * @param matrix - The transform to draw this text with.
69
+ */
70
+ setMatrix(matrix) {
71
+ this.matrixAutoUpdate = false;
72
+ this.matrix.copy(matrix);
73
+ this._batchParent?.invalidateBounds();
74
+ }
75
+ /**
76
+ * Recomposes the local matrix from `position`/`quaternion`/`scale`, and tells the batch its bounds
77
+ * moved.
78
+ *
79
+ * Members are not in the scene graph, so three never calls this; a caller that moved one should,
80
+ * and the report is what schedules the batch's bounds pass. That pass recomposes the matrix again
81
+ * — harmless, and it is what leaves a caller who reaches for `invalidateBounds` instead equally
82
+ * correct.
83
+ */
84
+ updateMatrix() {
85
+ super.updateMatrix();
86
+ this._batchParent?.invalidateBounds();
87
+ }
88
+ _prepareForRender(material) {
89
+ const defaultStrokeColor = 0x808080;
90
+ const defaultOrient = '+x+y';
91
+ const isOutline = material.isTextOutlineMaterial;
92
+ const uniforms = material.uniforms;
93
+ const textInfo = this.textRenderInfo;
94
+ const batched = this._batchParent !== undefined;
95
+ if (textInfo) {
96
+ const { sdfTexture, blockBounds } = textInfo;
97
+ const { width, height } = sdfTexture.image;
98
+ uniforms.uTroikaSDFTexture.value = sdfTexture;
99
+ uniforms.uTroikaSDFTextureSize.value.set(width, height);
100
+ uniforms.uTroikaSDFGlyphSize.value = textInfo.sdfGlyphSize;
101
+ uniforms.uTroikaSDFExponent.value = textInfo.sdfExponent;
102
+ uniforms.uTroikaTotalBounds.value.fromArray(blockBounds);
103
+ uniforms.uTroikaUseGlyphColors.value = !isOutline && !!textInfo.glyphColors;
104
+ let distanceOffset = 0;
105
+ let blurRadius = 0;
106
+ let strokeWidth = 0;
107
+ let fillOpacity;
108
+ // Deviation 4 (see class docs): initialized, not left undefined, so the batch never writes
109
+ // `NaN` into its data texture and re-uploads it on every frame thereafter.
110
+ let strokeOpacity = 1;
111
+ let strokeColor;
112
+ let offsetX = 0;
113
+ let offsetY = 0;
114
+ if (isOutline) {
115
+ const { outlineWidth, outlineOffsetX, outlineOffsetY, outlineBlur, outlineOpacity } = this;
116
+ distanceOffset = this._parsePercent(outlineWidth) || 0;
117
+ blurRadius = Math.max(0, this._parsePercent(outlineBlur) || 0);
118
+ fillOpacity = outlineOpacity;
119
+ offsetX = this._parsePercent(outlineOffsetX) || 0;
120
+ offsetY = this._parsePercent(outlineOffsetY) || 0;
121
+ }
122
+ else {
123
+ strokeWidth = Math.max(0, this._parsePercent(this.strokeWidth) || 0);
124
+ if (strokeWidth) {
125
+ strokeColor = this.strokeColor;
126
+ uniforms.uTroikaStrokeColor.value.set(strokeColor ?? defaultStrokeColor);
127
+ strokeOpacity = this.strokeOpacity ?? 1;
128
+ }
129
+ fillOpacity = this.fillOpacity;
130
+ }
131
+ uniforms.uTroikaEdgeOffset.value = distanceOffset;
132
+ uniforms.uTroikaPositionOffset.value.set(offsetX, offsetY);
133
+ uniforms.uTroikaBlurRadius.value = blurRadius;
134
+ uniforms.uTroikaStrokeWidth.value = strokeWidth;
135
+ uniforms.uTroikaStrokeOpacity.value = strokeOpacity;
136
+ uniforms.uTroikaFillOpacity.value = fillOpacity ?? 1;
137
+ uniforms.uTroikaCurveRadius.value = this.curveRadius || 0;
138
+ const clipRect = this.clipRect;
139
+ if (clipRect && Array.isArray(clipRect) && clipRect.length === 4) {
140
+ uniforms.uTroikaClipRect.value.fromArray(clipRect);
141
+ }
142
+ else {
143
+ // no clipping - choose a finite rect that shouldn't ever be reached by overflowing glyphs or outlines
144
+ const pad = (this.fontSize || 0.1) * 100;
145
+ uniforms.uTroikaClipRect.value.set(blockBounds[0] - pad, blockBounds[1] - pad, blockBounds[2] + pad, blockBounds[3] + pad);
146
+ }
147
+ // Deviation 1 (see class docs): the geometry is the batch's, shared by every member.
148
+ if (!batched)
149
+ this.geometry.applyClipRect(uniforms.uTroikaClipRect.value);
150
+ }
151
+ uniforms.uTroikaSDFDebug.value = !!this.debugSDF;
152
+ // Deviation 2 (see class docs): upstream wrote `material.polygonOffset`,
153
+ // `polygonOffsetFactor` and `polygonOffsetUnits` from `this.depthOffset` here.
154
+ // Deviation 3 (see class docs): the shortcut writes to the shared derived material, and the
155
+ // batch reads `text.color` directly, so batched members skip it entirely.
156
+ if (!batched) {
157
+ // Shortcut for setting material color via `color` prop on the mesh; this is
158
+ // applied only to the derived material to avoid mutating a shared base material.
159
+ const color = isOutline ? this.outlineColor || 0 : this.color;
160
+ if (color == null) {
161
+ // @ts-expect-error - Vendored
162
+ delete material.color; //inherit from base
163
+ }
164
+ else {
165
+ // eslint-disable-next-line no-prototype-builtins
166
+ const colorObj = material.hasOwnProperty('color')
167
+ ? material.color
168
+ : (material.color = new Color());
169
+ if (color !== colorObj._input || typeof color === 'object') {
170
+ colorObj.set((colorObj._input = color));
171
+ }
172
+ }
173
+ }
174
+ // base orientation
175
+ let orient = this.orientation || defaultOrient;
176
+ if (orient !== material._orientation) {
177
+ const rotMat = uniforms.uTroikaOrient.value;
178
+ orient = orient.replace(/[^-+xyz]/g, '');
179
+ const match = orient !== defaultOrient && /^([-+])([xyz])([-+])([xyz])$/.exec(orient);
180
+ if (match) {
181
+ const [, hSign, hAxis, vSign, vAxis] = match;
182
+ // @ts-expect-error - Vendored
183
+ _horizontal.set(0, 0, 0)[hAxis] = hSign === '-' ? 1 : -1;
184
+ // @ts-expect-error - Vendored
185
+ _vertical.set(0, 0, 0)[vAxis] = vSign === '-' ? -1 : 1;
186
+ _matrix.lookAt(ORIGIN, _horizontal.cross(_vertical), _vertical);
187
+ rotMat.setFromMatrix4(_matrix);
188
+ }
189
+ else {
190
+ rotMat.identity();
191
+ }
192
+ material._orientation = orient;
193
+ }
194
+ }
195
+ }
196
+ /**
197
+ * Routes every write of troika's own dirty flag through the batch.
198
+ *
199
+ * Upstream re-declares all eighteen syncable properties (`text`, `fontSize`, `anchorX`, …) as
200
+ * accessors that set `_needsSync`; hooking the flag itself instead of copying that list covers every
201
+ * one of them, plus the queued-resync path inside `Text.sync`, and cannot drift when troika adds a
202
+ * nineteenth.
203
+ *
204
+ * Runs during `super()` — the base constructor seeds the syncable properties — at which point the
205
+ * `_batchParent` field initializer has not run yet, so the optional call is doing real work.
206
+ */
207
+ Object.defineProperty(TextImpl.prototype, '_needsSync', {
208
+ configurable: true,
209
+ get() {
210
+ return this._syncFlag;
211
+ },
212
+ set(value) {
213
+ this._syncFlag = value;
214
+ // Undefined while the base constructor runs, which is fine: a member is always constructed
215
+ // before it joins a batch, so there is no parent to tell yet.
216
+ if (value)
217
+ this._batchParent?.requestSync();
218
+ },
219
+ });
220
+ /**
221
+ * Registers the class as an R3F element, by value so the generated key cannot collide with anything
222
+ * three puts in the catalog when the canvas mounts.
223
+ */
224
+ export const Text = extend(TextImpl);
225
+ // Scratch reused across calls, each filled and consumed within a single synchronous block.
226
+ const _matrix = new Matrix4();
227
+ const _horizontal = new Vector3();
228
+ const _vertical = new Vector3();
229
+ /** The eye a base-orientation `lookAt` turns about — always the local origin. */
230
+ const ORIGIN = new Vector3();
@@ -0,0 +1,56 @@
1
+ import { type Material } from 'three';
2
+ import { type BatchTextMaterial, type TextMaterial } from 'troika-three-text';
3
+ import { type InstanceUniforms } from '../InstanceUniforms';
4
+ /**
5
+ * The material side of a text batch: what troika's derivation has to become before it can draw a
6
+ * batch's members, and where the batch's per-instance uniforms are declared.
7
+ *
8
+ * Troika builds that material in two moments with its own derivation between them, so the work
9
+ * splits the same way — {@link patchBaseMaterial} runs on the base material *before*
10
+ * `createDerivedMaterial`, {@link configureDerivedMaterial} on the result *after*. Each order is
11
+ * load-bearing; see the two.
12
+ *
13
+ * Free functions holding no state, as `instanced-material.ts` is. What is per-batch — which material
14
+ * has already been wrapped — stays on the batch.
15
+ */
16
+ /** The per-member index attribute troika's batch material declares, and the uniforms texture indexes by. */
17
+ export declare const MEMBER_INDEX_ATTRIBUTE = "aTroikaTextBatchMemberIndex";
18
+ /**
19
+ * Wraps a base material's `onBeforeCompile` so a batch's per-instance uniforms are declared ahead of
20
+ * anything the material itself patches in.
21
+ *
22
+ * The prologue goes on the **base**, ahead of troika's rewrite: troika calls the base material's
23
+ * handler first and then rebuilds `main` around it, renaming the original `troikaOrigMain`.
24
+ * Injecting here puts the prologue and whatever the material patches in on the same side of that
25
+ * rename, so a material patch reading a per-instance uniform finds it in scope; injecting from the
26
+ * derived handler instead would strand the two in different functions.
27
+ *
28
+ * Call once per material — a second wrap would nest, and the caller holds the memo that prevents it.
29
+ * @param material - The base material troika derives from.
30
+ * @param uniforms - The batch's per-instance uniforms.
31
+ */
32
+ export declare function patchBaseMaterial(material: Material, uniforms: InstanceUniforms): void;
33
+ /**
34
+ * Finishes troika's derivation: reverts its two impositions on the base, and installs the one shader
35
+ * hook a batch needs.
36
+ * @param derived - What `createDerivedMaterial` returned.
37
+ * @param base - The material it was derived from.
38
+ * @param uniforms - The batch's per-instance uniforms, whose presence the program is cached by.
39
+ * @returns `derived`.
40
+ */
41
+ export declare function configureDerivedMaterial(derived: TextMaterial, base: Material, uniforms: InstanceUniforms): TextMaterial;
42
+ /**
43
+ * Whether a material draws through the batched text shader — the only thing a batch's per-draw
44
+ * texture fill asks of the material it is handed.
45
+ *
46
+ * `isTroikaTextMaterial`, which upstream tests instead, does not establish that. Troika sets it on
47
+ * the text derivation, which the batch's material only sits on top of — so a plain unbatched `Text`
48
+ * material carries the flag too, and filling one would write matrices its shader never reads. The
49
+ * uniforms are the honest test: a batch derives over a link that declares them, an unbatched `Text`
50
+ * never does, and every derivation merges its base's uniform map forward, so they survive into any
51
+ * sibling built by calling `material.constructor`.
52
+ * @param material - The material three is about to draw with.
53
+ * @returns Whether it reads its transforms from a matrix texture.
54
+ */
55
+ export declare function readsMemberMatrices(material: Material): material is BatchTextMaterial;
56
+ //# sourceMappingURL=batched-text-material.d.ts.map