@opsydyn/effect-three 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/LICENSE +21 -0
  3. package/README.md +191 -2
  4. package/dist/index-4045wme0.js +129 -0
  5. package/dist/index-4045wme0.js.map +10 -0
  6. package/dist/index-ef8pbv9g.js +5118 -0
  7. package/dist/index-ef8pbv9g.js.map +40 -0
  8. package/dist/index.d.ts +12 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +1361 -0
  11. package/dist/index.js.map +14 -0
  12. package/dist/kernel/animation.d.ts +14 -0
  13. package/dist/kernel/animation.d.ts.map +1 -0
  14. package/dist/kernel/behaviours.d.ts +16 -0
  15. package/dist/kernel/behaviours.d.ts.map +1 -0
  16. package/dist/kernel/bodies.d.ts +60 -0
  17. package/dist/kernel/bodies.d.ts.map +1 -0
  18. package/dist/kernel/bodySources.d.ts +47 -0
  19. package/dist/kernel/bodySources.d.ts.map +1 -0
  20. package/dist/kernel/build.d.ts +564 -0
  21. package/dist/kernel/build.d.ts.map +1 -0
  22. package/dist/kernel/cameraMotion.d.ts +39 -0
  23. package/dist/kernel/cameraMotion.d.ts.map +1 -0
  24. package/dist/kernel/clock.d.ts +17 -0
  25. package/dist/kernel/clock.d.ts.map +1 -0
  26. package/dist/kernel/contactShadowPasses.d.ts +47 -0
  27. package/dist/kernel/contactShadowPasses.d.ts.map +1 -0
  28. package/dist/kernel/description.d.ts +1120 -0
  29. package/dist/kernel/description.d.ts.map +1 -0
  30. package/dist/kernel/diff.d.ts +90 -0
  31. package/dist/kernel/diff.d.ts.map +1 -0
  32. package/dist/kernel/environmentPresets.d.ts +30 -0
  33. package/dist/kernel/environmentPresets.d.ts.map +1 -0
  34. package/dist/kernel/equality.d.ts +64 -0
  35. package/dist/kernel/equality.d.ts.map +1 -0
  36. package/dist/kernel/frames.d.ts +55 -0
  37. package/dist/kernel/frames.d.ts.map +1 -0
  38. package/dist/kernel/index.d.ts +37 -0
  39. package/dist/kernel/index.d.ts.map +1 -0
  40. package/dist/kernel/index.js +335 -0
  41. package/dist/kernel/index.js.map +9 -0
  42. package/dist/kernel/instances.d.ts +26 -0
  43. package/dist/kernel/instances.d.ts.map +1 -0
  44. package/dist/kernel/labels.d.ts +45 -0
  45. package/dist/kernel/labels.d.ts.map +1 -0
  46. package/dist/kernel/loop.d.ts +49 -0
  47. package/dist/kernel/loop.d.ts.map +1 -0
  48. package/dist/kernel/materials.d.ts +24 -0
  49. package/dist/kernel/materials.d.ts.map +1 -0
  50. package/dist/kernel/motion.d.ts +36 -0
  51. package/dist/kernel/motion.d.ts.map +1 -0
  52. package/dist/kernel/orbit.d.ts +127 -0
  53. package/dist/kernel/orbit.d.ts.map +1 -0
  54. package/dist/kernel/overlays.d.ts +16 -0
  55. package/dist/kernel/overlays.d.ts.map +1 -0
  56. package/dist/kernel/performance.d.ts +41 -0
  57. package/dist/kernel/performance.d.ts.map +1 -0
  58. package/dist/kernel/physics.d.ts +166 -0
  59. package/dist/kernel/physics.d.ts.map +1 -0
  60. package/dist/kernel/physicsDebug.d.ts +18 -0
  61. package/dist/kernel/physicsDebug.d.ts.map +1 -0
  62. package/dist/kernel/pointer.d.ts +357 -0
  63. package/dist/kernel/pointer.d.ts.map +1 -0
  64. package/dist/kernel/renderKit.d.ts +48 -0
  65. package/dist/kernel/renderKit.d.ts.map +1 -0
  66. package/dist/kernel/renderer.d.ts +83 -0
  67. package/dist/kernel/renderer.d.ts.map +1 -0
  68. package/dist/kernel/resources.d.ts +112 -0
  69. package/dist/kernel/resources.d.ts.map +1 -0
  70. package/dist/kernel/sceneGraph.d.ts +155 -0
  71. package/dist/kernel/sceneGraph.d.ts.map +1 -0
  72. package/dist/kernel/sceneIndex.d.ts +90 -0
  73. package/dist/kernel/sceneIndex.d.ts.map +1 -0
  74. package/dist/kernel/stage.d.ts +87 -0
  75. package/dist/kernel/stage.d.ts.map +1 -0
  76. package/dist/kernel/textLayout.d.ts +29 -0
  77. package/dist/kernel/textLayout.d.ts.map +1 -0
  78. package/dist/kernel/tween.d.ts +35 -0
  79. package/dist/kernel/tween.d.ts.map +1 -0
  80. package/dist/kernel/xrInput.d.ts +107 -0
  81. package/dist/kernel/xrInput.d.ts.map +1 -0
  82. package/dist/kernel/xrLocomotion.d.ts +38 -0
  83. package/dist/kernel/xrLocomotion.d.ts.map +1 -0
  84. package/dist/runtime/FrameSource.d.ts +29 -0
  85. package/dist/runtime/FrameSource.d.ts.map +1 -0
  86. package/dist/runtime/ResourceRegistry.d.ts +18 -0
  87. package/dist/runtime/ResourceRegistry.d.ts.map +1 -0
  88. package/dist/runtime/SceneRuntime.d.ts +68 -0
  89. package/dist/runtime/SceneRuntime.d.ts.map +1 -0
  90. package/dist/runtime/SceneSchema.d.ts +2536 -0
  91. package/dist/runtime/SceneSchema.d.ts.map +1 -0
  92. package/dist/runtime/errors.d.ts +19 -0
  93. package/dist/runtime/errors.d.ts.map +1 -0
  94. package/dist/runtime/index.d.ts +11 -0
  95. package/dist/runtime/index.d.ts.map +1 -0
  96. package/dist/webgl/contactShadows.d.ts +5 -0
  97. package/dist/webgl/contactShadows.d.ts.map +1 -0
  98. package/dist/webgl/index.d.ts +11 -0
  99. package/dist/webgl/index.d.ts.map +1 -0
  100. package/dist/webgl/index.js +239 -0
  101. package/dist/webgl/index.js.map +13 -0
  102. package/dist/webgl/shaderGrid.d.ts +27 -0
  103. package/dist/webgl/shaderGrid.d.ts.map +1 -0
  104. package/dist/webgl/sky.d.ts +4 -0
  105. package/dist/webgl/sky.d.ts.map +1 -0
  106. package/dist/webgpu/contactShadows.d.ts +5 -0
  107. package/dist/webgpu/contactShadows.d.ts.map +1 -0
  108. package/dist/webgpu/index.d.ts +10 -0
  109. package/dist/webgpu/index.d.ts.map +1 -0
  110. package/dist/webgpu/index.js +216 -0
  111. package/dist/webgpu/index.js.map +13 -0
  112. package/dist/webgpu/shaderGrid.d.ts +5 -0
  113. package/dist/webgpu/shaderGrid.d.ts.map +1 -0
  114. package/dist/webgpu/sky.d.ts +4 -0
  115. package/dist/webgpu/sky.d.ts.map +1 -0
  116. package/package.json +82 -4
  117. package/src/index.ts +11 -0
  118. package/src/kernel/animation.ts +24 -0
  119. package/src/kernel/behaviours.ts +36 -0
  120. package/src/kernel/bodies.ts +338 -0
  121. package/src/kernel/bodySources.ts +203 -0
  122. package/src/kernel/build.ts +1325 -0
  123. package/src/kernel/cameraMotion.ts +202 -0
  124. package/src/kernel/clock.ts +35 -0
  125. package/src/kernel/contactShadowPasses.ts +207 -0
  126. package/src/kernel/description.ts +1197 -0
  127. package/src/kernel/diff.ts +383 -0
  128. package/src/kernel/environmentPresets.ts +38 -0
  129. package/src/kernel/equality.ts +604 -0
  130. package/src/kernel/frames.ts +124 -0
  131. package/src/kernel/index.ts +274 -0
  132. package/src/kernel/instances.ts +97 -0
  133. package/src/kernel/labels.ts +48 -0
  134. package/src/kernel/loop.ts +248 -0
  135. package/src/kernel/materials.ts +258 -0
  136. package/src/kernel/motion.ts +75 -0
  137. package/src/kernel/orbit.ts +554 -0
  138. package/src/kernel/overlays.ts +130 -0
  139. package/src/kernel/performance.ts +101 -0
  140. package/src/kernel/physics.ts +361 -0
  141. package/src/kernel/physicsDebug.ts +68 -0
  142. package/src/kernel/pointer.ts +785 -0
  143. package/src/kernel/renderKit.ts +53 -0
  144. package/src/kernel/renderer.ts +136 -0
  145. package/src/kernel/resources.ts +504 -0
  146. package/src/kernel/sceneGraph.ts +1440 -0
  147. package/src/kernel/sceneIndex.ts +219 -0
  148. package/src/kernel/stage.ts +157 -0
  149. package/src/kernel/textLayout.ts +102 -0
  150. package/src/kernel/tween.ts +80 -0
  151. package/src/kernel/xrInput.ts +230 -0
  152. package/src/kernel/xrLocomotion.ts +72 -0
  153. package/src/runtime/FrameSource.ts +41 -0
  154. package/src/runtime/ResourceRegistry.ts +24 -0
  155. package/src/runtime/SceneRuntime.ts +229 -0
  156. package/src/runtime/SceneSchema.ts +983 -0
  157. package/src/runtime/errors.ts +18 -0
  158. package/src/runtime/index.ts +10 -0
  159. package/src/webgl/contactShadows.ts +67 -0
  160. package/src/webgl/index.ts +19 -0
  161. package/src/webgl/shaderGrid.ts +196 -0
  162. package/src/webgl/sky.ts +42 -0
  163. package/src/webgpu/contactShadows.ts +83 -0
  164. package/src/webgpu/index.ts +19 -0
  165. package/src/webgpu/shaderGrid.ts +158 -0
  166. package/src/webgpu/sky.ts +35 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Frame scheduling contracts. A scheduler calls registered callbacks once per frame:
3
+ * `requestAnimationFrame` in a browser, three's `setAnimationLoop` for WebGPU and XR, or
4
+ * a manual scheduler in tests.
5
+ *
6
+ * @module
7
+ */
8
+
9
+ /** Registers a per-frame callback and returns a function that unregisters it. */
10
+ export type FrameScheduler = {
11
+ readonly subscribe: (onFrame: () => void) => () => void;
12
+ };
13
+
14
+ /** A scheduler driven by hand. `step` runs one frame for every subscriber. */
15
+ export type ManualFrames = FrameScheduler & {
16
+ readonly step: () => void;
17
+ readonly subscribers: () => number;
18
+ };
19
+
20
+ /** Creates a manual frame scheduler for deterministic tests. */
21
+ export const createManualFrames = (): ManualFrames => {
22
+ const callbacks = new Set<() => void>();
23
+ return {
24
+ subscribe: (onFrame) => {
25
+ callbacks.add(onFrame);
26
+ return () => {
27
+ callbacks.delete(onFrame);
28
+ };
29
+ },
30
+ step: () => {
31
+ // Snapshot: callbacks subscribed during this frame first run on the next one.
32
+ for (const onFrame of Array.from(callbacks)) onFrame();
33
+ },
34
+ subscribers: () => callbacks.size,
35
+ };
36
+ };
37
+
38
+ /** A scheduler that forwards frames only while open. */
39
+ export type GatedFrames = FrameScheduler & {
40
+ readonly setOpen: (open: boolean) => void;
41
+ };
42
+
43
+ /**
44
+ * Wraps `base` so frames pass only while open: for example, only while the canvas is on
45
+ * screen, or only while a scene has something to draw. A closed gate unsubscribes from
46
+ * `base`, so a scheduler that stops when nobody listens (such as the browser's animation
47
+ * frames) really stops.
48
+ */
49
+ export const createGatedFrames = (base: FrameScheduler): GatedFrames => {
50
+ let open = true;
51
+ const callbacks = new Set<() => void>();
52
+ let unsubscribe: (() => void) | undefined;
53
+ const forward = (): void => {
54
+ // Snapshot: a callback may close the gate or unsubscribe while running.
55
+ for (const onFrame of Array.from(callbacks)) onFrame();
56
+ };
57
+ const sync = (): void => {
58
+ const wanted = open && callbacks.size > 0;
59
+ if (wanted && unsubscribe === undefined) unsubscribe = base.subscribe(forward);
60
+ else if (!wanted && unsubscribe !== undefined) {
61
+ unsubscribe();
62
+ unsubscribe = undefined;
63
+ }
64
+ };
65
+ return {
66
+ subscribe: (onFrame) => {
67
+ callbacks.add(onFrame);
68
+ sync();
69
+ return () => {
70
+ callbacks.delete(onFrame);
71
+ sync();
72
+ };
73
+ },
74
+ setOpen: (next) => {
75
+ open = next;
76
+ sync();
77
+ },
78
+ };
79
+ };
80
+
81
+ /** Turns a scene's frames on and off. Off unsubscribes, so an idle scheduler can stop. */
82
+ export type FrameControl = {
83
+ readonly wake: () => void;
84
+ readonly sleep: () => void;
85
+ };
86
+
87
+ /** The part of a scene loop that pacing needs. */
88
+ export type Paced = {
89
+ readonly frame: () => void;
90
+ readonly wantsFrames: () => boolean;
91
+ };
92
+
93
+ export type FramePacer = {
94
+ /** The per-frame callback: draws, then sleeps when no further frame is wanted. */
95
+ readonly frame: () => void;
96
+ /** Connects the subscription's control, putting it to sleep if nothing is wanted yet. */
97
+ readonly attach: (control: FrameControl) => void;
98
+ /** Wakes the subscription if a frame is now wanted. Call on every invalidation. */
99
+ readonly wake: () => void;
100
+ /** Draws one frame now, whatever the mode: React Three Fiber's `advance`. */
101
+ readonly advance: () => void;
102
+ };
103
+
104
+ /**
105
+ * Paces a scene's frames: subscribed while frames are wanted, asleep otherwise, so an
106
+ * idle scene costs no frames at all.
107
+ */
108
+ export const createFramePacer = (loop: Paced): FramePacer => {
109
+ let control: FrameControl | undefined;
110
+ return {
111
+ frame: () => {
112
+ loop.frame();
113
+ if (!loop.wantsFrames()) control?.sleep();
114
+ },
115
+ attach: (next) => {
116
+ control = next;
117
+ if (!loop.wantsFrames()) next.sleep();
118
+ },
119
+ wake: () => {
120
+ if (loop.wantsFrames()) control?.wake();
121
+ },
122
+ advance: loop.frame,
123
+ };
124
+ };
@@ -0,0 +1,274 @@
1
+ /**
2
+ * The non-Effect kernel (ADR-003): descriptions, keyed diff, applier, behaviours and
3
+ * resource ownership. Synchronous, allocation-conscious and DOM-free.
4
+ *
5
+ * @module
6
+ */
7
+ export type * from './description.ts';
8
+ export { handJoints } from './description.ts';
9
+ export { clipRunning, clipTime } from './animation.ts';
10
+ export {
11
+ type Behaviour,
12
+ type BehaviourPose,
13
+ builtinBehaviours,
14
+ createPose,
15
+ float,
16
+ resetPose,
17
+ } from './motion.ts';
18
+ export { spinAngle, writeRotation } from './behaviours.ts';
19
+ export { buildMaterial, type HostMaterial, type Textures, updateMaterial } from './materials.ts';
20
+ export { capacityFor, type HostInstances } from './instances.ts';
21
+ export {
22
+ createPerformanceMonitor,
23
+ type PerformanceChange,
24
+ type PerformanceMonitor,
25
+ type PerformanceOptions,
26
+ } from './performance.ts';
27
+ export { applyTween, easeProgress, startTween, type Tween } from './tween.ts';
28
+ export {
29
+ ambientLight,
30
+ basic,
31
+ behave,
32
+ bloom,
33
+ borrowed,
34
+ box,
35
+ capsule,
36
+ body,
37
+ circle,
38
+ colorRange,
39
+ xr,
40
+ glide,
41
+ xrAnchor,
42
+ headSpace,
43
+ jointSpace,
44
+ controllerSpace,
45
+ anchorSpace,
46
+ planeSpace,
47
+ meshSpace,
48
+ xrMesh,
49
+ xrMeshGeometryId,
50
+ physics,
51
+ cuboidCollider,
52
+ ballCollider,
53
+ capsuleCollider,
54
+ cylinderCollider,
55
+ coneCollider,
56
+ velocityMotor,
57
+ interactionGroups,
58
+ positionMotor,
59
+ shaderGrid,
60
+ label,
61
+ bodyRef,
62
+ joint,
63
+ fixedJoint,
64
+ sphericalJoint,
65
+ revoluteJoint,
66
+ prismaticJoint,
67
+ ropeJoint,
68
+ springJoint,
69
+ contactShadows,
70
+ grid,
71
+ sky,
72
+ sunPositionFromAngles,
73
+ environment,
74
+ text,
75
+ cone,
76
+ cylinder,
77
+ directionalLight,
78
+ ease,
79
+ film,
80
+ fixed,
81
+ group,
82
+ hemisphereLight,
83
+ instance,
84
+ instances,
85
+ instant,
86
+ lambert,
87
+ mesh,
88
+ normalMaterial,
89
+ shadowMaterial,
90
+ orthographicCamera,
91
+ perspectiveCamera,
92
+ phong,
93
+ play,
94
+ physical,
95
+ plane,
96
+ pointLight,
97
+ renderSettings,
98
+ ring,
99
+ scene,
100
+ sharedGeometry,
101
+ lentGeometry,
102
+ sharedMaterial,
103
+ sphere,
104
+ spin,
105
+ spotLight,
106
+ standard,
107
+ overlay,
108
+ renderTexture,
109
+ torus,
110
+ type TransformOptions,
111
+ view,
112
+ vignette,
113
+ } from './build.ts';
114
+ export { changedFields, diff, type DiffResult, type Field, type Op } from './diff.ts';
115
+ export {
116
+ cameraEquals,
117
+ animationEquals,
118
+ behaviourEquals,
119
+ effectsEqual,
120
+ geometryRefEquals,
121
+ instanceEquals,
122
+ instancesEqual,
123
+ materialRefEquals,
124
+ resourcesEqual,
125
+ renderSettingsEquals,
126
+ rotationEquals,
127
+ shapeEquals,
128
+ transitionEquals,
129
+ environmentEquals,
130
+ contactShadowsEquals,
131
+ bodyEquals,
132
+ overlaysEqual,
133
+ xrEquals,
134
+ physicsEquals,
135
+ skyEquals,
136
+ viewsEqual,
137
+ vec3Equals,
138
+ } from './equality.ts';
139
+ export {
140
+ createResourceStore,
141
+ type GeometryReplaced,
142
+ type Loan,
143
+ type MaterialReplaced,
144
+ type ResourceCounts,
145
+ type ResourceStats,
146
+ type ResourceStore,
147
+ } from './resources.ts';
148
+ export { createSceneClock, type SceneClock } from './clock.ts';
149
+ export {
150
+ createFramePacer,
151
+ createGatedFrames,
152
+ type FrameControl,
153
+ type FramePacer,
154
+ createManualFrames,
155
+ type FrameScheduler,
156
+ type GatedFrames,
157
+ type ManualFrames,
158
+ } from './frames.ts';
159
+ export {
160
+ autoRotated,
161
+ type Box,
162
+ clipCamera,
163
+ type Cursor,
164
+ defaultOrbit,
165
+ dollyCamera,
166
+ dollyCameraToward,
167
+ fitCamera,
168
+ orbit,
169
+ type OrbitKey,
170
+ orbitKey,
171
+ type OrbitOptions,
172
+ orbitOptions,
173
+ panCamera,
174
+ rotateCamera,
175
+ } from './orbit.ts';
176
+ export { type OverlayPlacement, placeOverlays } from './overlays.ts';
177
+ export {
178
+ type BodyPose,
179
+ type Collider,
180
+ type ColliderShape,
181
+ colliderFor,
182
+ collidersFor,
183
+ colliderSignature,
184
+ type Collision,
185
+ type ContactForce,
186
+ type DebugLines,
187
+ describedColliders,
188
+ interactionBits,
189
+ type PhysicsEngine,
190
+ type PhysicsJoint,
191
+ type PhysicsWorld,
192
+ type Quat,
193
+ } from './physics.ts';
194
+ export type { BodyCollision, BodyContactForce } from './bodies.ts';
195
+ export type {
196
+ ContactShadowPasses,
197
+ HostedShaderGrid,
198
+ HostedSky,
199
+ RenderKit,
200
+ ShadowedScene,
201
+ } from './renderKit.ts';
202
+ export { glided, type LocomotionOptions, locomote, teleport } from './xrLocomotion.ts';
203
+ export {
204
+ createXrTracker,
205
+ type XrButtonName,
206
+ type XrHand,
207
+ type XrInput,
208
+ type XrMesh,
209
+ type XrPlane,
210
+ type XrStickDirection,
211
+ } from './xrInput.ts';
212
+ export type { HostedLabel, LabelEngine, LabelLayout } from './labels.ts';
213
+ export {
214
+ type EnvironmentPreset,
215
+ environmentPresets,
216
+ environmentPresetUrl,
217
+ } from './environmentPresets.ts';
218
+ export {
219
+ type CenterAlign,
220
+ centerOffset,
221
+ type Stage,
222
+ stage,
223
+ stageBounds,
224
+ type StageLighting,
225
+ type StagePreset,
226
+ stagePresets,
227
+ } from './stage.ts';
228
+ export { createSceneLoop, type SceneLoop, type SceneLoopOptions } from './loop.ts';
229
+ export {
230
+ createPicker,
231
+ createPointerTracker,
232
+ type Gesture,
233
+ type Hit,
234
+ type Modifiers,
235
+ noModifiers,
236
+ type Picker,
237
+ type PlaneEquation,
238
+ pointOnPlane,
239
+ type PointerInput,
240
+ type PointerTracker,
241
+ type Ray,
242
+ type Region,
243
+ type SceneEvent,
244
+ } from './pointer.ts';
245
+ export {
246
+ recordingRendererFactory,
247
+ type RecordingRenderer,
248
+ type RenderArea,
249
+ type RenderTarget,
250
+ type RecordingRendererFactory,
251
+ type RendererFactory,
252
+ type RendererMemory,
253
+ type SceneRenderer,
254
+ type Viewport,
255
+ } from './renderer.ts';
256
+ export {
257
+ createSceneGraph,
258
+ type HostCamera,
259
+ type HostedTexture,
260
+ type HostedView,
261
+ type HostMesh,
262
+ type ReconcileResult,
263
+ type SceneGraph,
264
+ type SceneGraphOptions,
265
+ } from './sceneGraph.ts';
266
+ export {
267
+ type DescriptionError,
268
+ emptyIndex,
269
+ type IndexedNode,
270
+ indexScene,
271
+ type IndexResult,
272
+ type ParentKey,
273
+ type SceneIndex,
274
+ } from './sceneIndex.ts';
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Writing instance descriptions into three.js's `InstancedMesh` buffers.
3
+ *
4
+ * Slots are positional: instance `i` of the description lives in slot `i`. An update
5
+ * writes only the slots whose instance changed, so moving one of 10,000 instances writes
6
+ * one matrix. Capacity grows by doubling, so adding instances rarely reallocates.
7
+ *
8
+ * @module
9
+ */
10
+ import {
11
+ type BufferGeometry,
12
+ Color,
13
+ DynamicDrawUsage,
14
+ Euler,
15
+ InstancedMesh,
16
+ Matrix4,
17
+ Quaternion,
18
+ Vector3,
19
+ } from 'three';
20
+
21
+ import type { Instance } from './description.ts';
22
+ import { instanceEquals } from './equality.ts';
23
+ import type { HostMaterial } from './materials.ts';
24
+
25
+ /** The three.js object an `instances` node is hosted as. */
26
+ export type HostInstances = InstancedMesh<BufferGeometry, HostMaterial>;
27
+
28
+ const minimumCapacity = 16;
29
+
30
+ /** Slots to allocate for `count` instances: the next power of two, at least 16. */
31
+ export const capacityFor = (count: number): number =>
32
+ Math.max(minimumCapacity, 2 ** Math.ceil(Math.log2(Math.max(1, count))));
33
+
34
+ // Scratch values, so writing instances allocates nothing.
35
+ const matrix = new Matrix4();
36
+ const position = new Vector3();
37
+ const euler = new Euler();
38
+ const quaternion = new Quaternion();
39
+ const scale = new Vector3();
40
+ const color = new Color();
41
+
42
+ const writeSlot = (mesh: HostInstances, slot: number, instance: Instance): void => {
43
+ position.set(instance.position[0], instance.position[1], instance.position[2]);
44
+ euler.set(instance.rotation[0], instance.rotation[1], instance.rotation[2]);
45
+ quaternion.setFromEuler(euler);
46
+ scale.set(instance.scale[0], instance.scale[1], instance.scale[2]);
47
+ mesh.setMatrixAt(slot, matrix.compose(position, quaternion, scale));
48
+ mesh.setColorAt(slot, color.setHex(instance.color));
49
+ };
50
+
51
+ /** Creates an instanced mesh with room for at least `count` instances. */
52
+ export const createInstances = (
53
+ geometry: BufferGeometry,
54
+ material: HostMaterial,
55
+ count: number,
56
+ ): HostInstances => {
57
+ const mesh: HostInstances = new InstancedMesh(geometry, material, capacityFor(count));
58
+ mesh.instanceMatrix.setUsage(DynamicDrawUsage);
59
+ // Allocate per-instance colours up front so the shader never changes as colours come and go.
60
+ mesh.setColorAt(0, color.setHex(0xffffff));
61
+ mesh.instanceColor?.setUsage(DynamicDrawUsage);
62
+ mesh.count = 0;
63
+ return mesh;
64
+ };
65
+
66
+ /** Whether `count` instances fit without reallocating. */
67
+ export const fits = (mesh: HostInstances, count: number): boolean =>
68
+ count <= mesh.instanceMatrix.count;
69
+
70
+ /**
71
+ * Writes `next` into `mesh`, skipping slots whose instance is unchanged from `previous`
72
+ * (pass an empty list to write every slot). `next` must fit. Returns the slots written.
73
+ */
74
+ export const writeInstances = (
75
+ mesh: HostInstances,
76
+ previous: ReadonlyArray<Instance>,
77
+ next: ReadonlyArray<Instance>,
78
+ ): number => {
79
+ let written = 0;
80
+ for (let slot = 0; slot < next.length; slot++) {
81
+ const instance = next[slot];
82
+ if (instance === undefined) continue;
83
+ const before = previous[slot];
84
+ if (before !== undefined && instanceEquals(before, instance)) continue;
85
+ writeSlot(mesh, slot, instance);
86
+ written++;
87
+ }
88
+ if (written > 0 || mesh.count !== next.length) {
89
+ mesh.count = next.length;
90
+ mesh.instanceMatrix.needsUpdate = true;
91
+ if (mesh.instanceColor !== null) mesh.instanceColor.needsUpdate = true;
92
+ // Picking and frustum culling recompute these lazily from the new instances.
93
+ mesh.boundingSphere = null;
94
+ mesh.boundingBox = null;
95
+ }
96
+ return written;
97
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The kernel's side of labels (distance-field text): an engine-neutral contract. An
3
+ * engine package (troika, in `@opsydyn/effect-three-troika`) implements `LabelEngine`; the
4
+ * scene graph hosts what it makes inside each label's wrapper.
5
+ *
6
+ * Behavioural reference: Drei's `Text` at `bf6f4ad` (`src/core/Text.tsx`): a troika mesh
7
+ * per label, its properties set from props, `sync` run after each change and the frame
8
+ * invalidated when it finishes, and the mesh disposed on unmount.
9
+ *
10
+ * @module
11
+ */
12
+ import type { Material, Object3D } from 'three';
13
+
14
+ import type { LabelNode } from './description.ts';
15
+
16
+ /**
17
+ * A label's laid-out size in its own units (before its scale), each as
18
+ * `[minX, minY, maxX, maxY]`: `block` the whole text block, line height included, and
19
+ * `visible` tight round the glyphs (troika's `blockBounds` and `visibleBounds`).
20
+ */
21
+ export type LabelLayout = {
22
+ readonly block: readonly [number, number, number, number];
23
+ readonly visible: readonly [number, number, number, number];
24
+ };
25
+
26
+ /** One label as an engine draws it. */
27
+ export type HostedLabel = {
28
+ /** What to show inside the label's wrapper. */
29
+ readonly object: Object3D;
30
+ /** Applies new text or type settings; `ready` is called again when they are drawn. */
31
+ readonly update: (label: LabelNode) => void;
32
+ /**
33
+ * Draws the glyphs with a material the scene lends (`label({ material })`), or the
34
+ * engine's own with `undefined`. The scene owns it: the engine never disposes it.
35
+ */
36
+ readonly setMaterial: (material: Material | undefined) => void;
37
+ /** The text's laid-out size, once it has been laid out. */
38
+ readonly layout: () => LabelLayout | undefined;
39
+ readonly dispose: () => void;
40
+ };
41
+
42
+ /**
43
+ * Draws labels. `ready` is called whenever a label's glyphs have been built (fonts load
44
+ * and glyphs build in the background), so the scene can draw again.
45
+ */
46
+ export type LabelEngine = {
47
+ readonly create: (label: LabelNode, ready: () => void) => HostedLabel;
48
+ };