@vibecook/ice 0.1.0 → 0.3.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.
- package/README.md +37 -2
- package/dist/chunk-2GUYATUM.js +643 -0
- package/dist/chunk-2GUYATUM.js.map +1 -0
- package/dist/{chunk-5S7GSXJG.js → chunk-5QY7HK6G.js} +3145 -1306
- package/dist/chunk-5QY7HK6G.js.map +1 -0
- package/dist/{chunk-BCJ47SX7.js → chunk-OR5FNVUE.js} +460 -1
- package/dist/chunk-OR5FNVUE.js.map +1 -0
- package/dist/{chunk-FIAXPNNY.js → chunk-PKK6GLSA.js} +143 -551
- package/dist/chunk-PKK6GLSA.js.map +1 -0
- package/dist/devtools.js +508 -372
- package/dist/devtools.js.map +1 -1
- package/dist/dom.js +3 -11
- package/dist/ground.js +715 -0
- package/dist/ground.js.map +1 -0
- package/dist/index.js +80 -2
- package/dist/kernel.js +31 -1
- package/dist/r3f.js +605 -62
- package/dist/r3f.js.map +1 -1
- package/dist/react.js +36 -418
- package/dist/react.js.map +1 -1
- package/dist/types/packages/core/src/catalog/camera-derived.d.ts +65 -3
- package/dist/types/packages/core/src/catalog/gesture.d.ts +40 -4
- package/dist/types/packages/core/src/catalog/graph.d.ts +7 -6
- package/dist/types/packages/core/src/catalog/index.d.ts +1 -0
- package/dist/types/packages/core/src/catalog/insert.d.ts +31 -0
- package/dist/types/packages/core/src/catalog/pointer.d.ts +11 -0
- package/dist/types/packages/core/src/catalog/scene.d.ts +37 -4
- package/dist/types/packages/core/src/catalog/selection-presence.d.ts +7 -0
- package/dist/types/packages/core/src/catalog/settings-resources.d.ts +21 -0
- package/dist/types/packages/core/src/doc/autosave.d.ts +49 -2
- package/dist/types/packages/core/src/doc/bootstrap.d.ts +29 -0
- package/dist/types/packages/core/src/doc/channels.d.ts +9 -0
- package/dist/types/packages/core/src/doc/doc-commit-sink.d.ts +2 -1
- package/dist/types/packages/core/src/doc/doc-kit.d.ts +16 -1
- package/dist/types/packages/core/src/doc/envelope.d.ts +2 -1
- package/dist/types/packages/core/src/doc/migrate.d.ts +8 -6
- package/dist/types/packages/core/src/doc/schema-migrate.d.ts +50 -0
- package/dist/types/packages/core/src/doc/version-gate.d.ts +3 -0
- package/dist/types/packages/core/src/engine/commit-sink.d.ts +24 -2
- package/dist/types/packages/core/src/engine/engine.d.ts +11 -0
- package/dist/types/packages/core/src/engine/frame-control.d.ts +71 -0
- package/dist/types/packages/core/src/engine/frame-info.d.ts +16 -1
- package/dist/types/packages/core/src/facade/create-canvas-engine.d.ts +79 -6
- package/dist/types/packages/core/src/guards/guarded-tx.d.ts +4 -2
- package/dist/types/packages/core/src/helpers/churn-guard.d.ts +40 -0
- package/dist/types/packages/core/src/index.d.ts +15 -7
- package/dist/types/packages/core/src/input/queue.d.ts +11 -0
- package/dist/types/packages/core/src/interaction/gesture-status.d.ts +13 -0
- package/dist/types/packages/core/src/nav/nested-canvas.d.ts +47 -11
- package/dist/types/packages/core/src/ops/arrange.d.ts +15 -0
- package/dist/types/packages/core/src/ops/insert.d.ts +62 -0
- package/dist/types/packages/core/src/ops/point-pick.d.ts +11 -7
- package/dist/types/packages/core/src/ops/sibling-order.d.ts +70 -0
- package/dist/types/packages/core/src/schema/meta.d.ts +1 -0
- package/dist/types/packages/core/src/settings/defaults.d.ts +45 -0
- package/dist/types/packages/core/src/settings/ground-config.d.ts +55 -0
- package/dist/types/packages/core/src/systems/chrome.d.ts +6 -2
- package/dist/types/packages/core/src/systems/insert-ghost.d.ts +25 -0
- package/dist/types/packages/core/src/systems/l1-pick.d.ts +25 -11
- package/dist/types/packages/core/src/systems/l2-recognize.d.ts +10 -4
- package/dist/types/packages/core/src/systems/l3-behave.d.ts +6 -4
- package/dist/types/packages/core/src/systems/l3-claim.d.ts +7 -3
- package/dist/types/packages/core/src/systems/l3-drop.d.ts +3 -1
- package/dist/types/packages/core/src/systems/l3-resize.d.ts +3 -1
- package/dist/types/packages/core/src/systems/l3-snap.d.ts +25 -7
- package/dist/types/packages/core/src/systems/nav-flight.d.ts +95 -0
- package/dist/types/packages/core/src/widget/define-widget.d.ts +39 -0
- package/dist/types/packages/core/src/widget/mount-store.d.ts +11 -3
- package/dist/types/packages/core/src/widget/set-props.d.ts +16 -0
- package/dist/types/packages/core/src/widget/spawn.d.ts +21 -1
- package/dist/types/packages/devtools/src/attach.d.ts +77 -0
- package/dist/types/packages/devtools/src/dock.d.ts +38 -0
- package/dist/types/packages/devtools/src/gl-panel.d.ts +69 -0
- package/dist/types/packages/devtools/src/index.d.ts +8 -6
- package/dist/types/packages/dom/src/index.d.ts +1 -3
- package/dist/types/packages/dom/src/loop.d.ts +11 -0
- package/dist/types/packages/dom/src/measure-wiring.d.ts +2 -2
- package/dist/types/packages/dom/src/pointer-adapter.d.ts +21 -1
- package/dist/types/packages/dom/src/reflectors/dom-widgets.d.ts +5 -0
- package/dist/types/packages/ground/src/index.d.ts +52 -0
- package/dist/types/packages/ground/src/layer.d.ts +40 -0
- package/dist/types/packages/ground/src/pass.d.ts +41 -0
- package/dist/types/packages/ground/src/passes/grid.d.ts +21 -0
- package/dist/types/packages/ground/src/passes/guides-collect.d.ts +14 -0
- package/dist/types/packages/ground/src/passes/guides.d.ts +3 -0
- package/dist/types/packages/ground/src/passes/soup-collect.d.ts +35 -0
- package/dist/types/packages/ground/src/passes/soup-mesh.d.ts +8 -0
- package/dist/types/packages/ground/src/passes/wires-collect.d.ts +31 -0
- package/dist/types/packages/ground/src/passes/wires.d.ts +10 -0
- package/dist/types/packages/ground/src/renderer.d.ts +15 -0
- package/dist/types/packages/ice/src/ground.d.ts +1 -0
- package/dist/types/packages/kernel/src/bezier.d.ts +15 -0
- package/dist/types/packages/kernel/src/coords.d.ts +15 -0
- package/dist/types/packages/kernel/src/easing.d.ts +11 -0
- package/dist/types/packages/kernel/src/index.d.ts +3 -0
- package/dist/types/packages/kernel/src/layout.d.ts +118 -0
- package/dist/types/packages/kernel/src/nav-flight.d.ts +93 -0
- package/dist/types/packages/r3f/src/bridge.d.ts +73 -12
- package/dist/types/packages/r3f/src/composite-material.d.ts +9 -0
- package/dist/types/packages/r3f/src/compositor-pass.d.ts +22 -0
- package/dist/types/packages/r3f/src/gl-root.d.ts +76 -1
- package/dist/types/packages/r3f/src/gl-router.d.ts +10 -0
- package/dist/types/packages/r3f/src/index.d.ts +3 -2
- package/dist/types/packages/r3f/src/island-state.d.ts +44 -0
- package/dist/types/packages/r3f/src/preview-capture.d.ts +19 -0
- package/dist/types/packages/r3f/src/self-sustain.d.ts +39 -0
- package/dist/types/packages/r3f/src/use-island-frame.d.ts +16 -1
- package/dist/types/packages/react/src/engine-context.d.ts +21 -0
- package/dist/types/packages/react/src/index.d.ts +4 -2
- package/dist/types/packages/react/src/infinite-canvas.d.ts +40 -6
- package/dist/types/packages/react/src/preview-snapshots.d.ts +17 -0
- package/dist/types/packages/react/src/widget-preview.d.ts +14 -0
- package/package.json +32 -61
- package/dist/chunk-5S7GSXJG.js.map +0 -1
- package/dist/chunk-BCJ47SX7.js.map +0 -1
- package/dist/chunk-FIAXPNNY.js.map +0 -1
- package/dist/types/packages/devtools/src/panel.d.ts +0 -48
- package/dist/types/packages/dom/src/reflectors/grid.d.ts +0 -63
- package/dist/types/packages/dom/src/reflectors/wires.d.ts +0 -88
package/dist/r3f.js
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
import {
|
|
2
|
+
EngineProvider,
|
|
3
|
+
hasPreviewSnapshot,
|
|
4
|
+
setPreviewSnapshot
|
|
5
|
+
} from "./chunk-2GUYATUM.js";
|
|
6
|
+
import "./chunk-PKK6GLSA.js";
|
|
7
|
+
import {
|
|
8
|
+
Active,
|
|
2
9
|
Camera,
|
|
10
|
+
Culled,
|
|
11
|
+
GESTURE_DEFAULTS,
|
|
12
|
+
GestureSettings,
|
|
3
13
|
Grab,
|
|
14
|
+
Opacity,
|
|
4
15
|
Position,
|
|
5
16
|
PrefabId,
|
|
6
17
|
RUNTIME_BUDGETS,
|
|
7
18
|
Size,
|
|
8
19
|
StackZ,
|
|
20
|
+
StageMode,
|
|
9
21
|
Viewport,
|
|
10
22
|
Visible,
|
|
23
|
+
createCanvasEngine,
|
|
24
|
+
createSiblingOrderIndex,
|
|
25
|
+
defineQuery,
|
|
26
|
+
navFlightActive,
|
|
11
27
|
pickTopAt,
|
|
12
28
|
widgets
|
|
13
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-5QY7HK6G.js";
|
|
14
30
|
import {
|
|
15
31
|
compositeCameraFrustum,
|
|
16
32
|
computeIslandPhase,
|
|
33
|
+
cubicBezierEase,
|
|
17
34
|
fboPixelSize,
|
|
18
35
|
isOutOfBand,
|
|
19
36
|
screenToWorld,
|
|
@@ -21,9 +38,10 @@ import {
|
|
|
21
38
|
selectEvictions,
|
|
22
39
|
worldRectToComposite,
|
|
23
40
|
worldToIsland
|
|
24
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-OR5FNVUE.js";
|
|
25
42
|
|
|
26
43
|
// ../r3f/src/island-state.ts
|
|
44
|
+
var LIFT_DEFAULT_MS = 180;
|
|
27
45
|
var FRESH_PAINTED_AT = { w: 0, h: 0, dpr: 1, band: 0 };
|
|
28
46
|
function createIslandStateStore() {
|
|
29
47
|
const entries = /* @__PURE__ */ new Map();
|
|
@@ -39,7 +57,17 @@ function createIslandStateStore() {
|
|
|
39
57
|
animRefs: 0,
|
|
40
58
|
animatedDecl: false,
|
|
41
59
|
lastPaintSeq: 0,
|
|
42
|
-
|
|
60
|
+
pendingDtMs: 0,
|
|
61
|
+
compositeScale: 1,
|
|
62
|
+
liftTarget: 1,
|
|
63
|
+
liftFrom: 1,
|
|
64
|
+
liftElapsedMs: 0,
|
|
65
|
+
liftMs: 0,
|
|
66
|
+
compositeOpacity: 1,
|
|
67
|
+
opacityTarget: 1,
|
|
68
|
+
opacityFrom: 1,
|
|
69
|
+
opacityElapsedMs: 0,
|
|
70
|
+
opacityMs: 0
|
|
43
71
|
};
|
|
44
72
|
entries.set(key, s);
|
|
45
73
|
}
|
|
@@ -122,17 +150,45 @@ function createGLBridge(engine, opts = {}) {
|
|
|
122
150
|
const islands = /* @__PURE__ */ new Map();
|
|
123
151
|
const frameCbs = /* @__PURE__ */ new Map();
|
|
124
152
|
const renderAssert = createRenderWriteTrap(world, opts.devAssertRenderWrites === true);
|
|
125
|
-
|
|
126
|
-
let
|
|
153
|
+
const now = opts.now ?? (() => performance.now());
|
|
154
|
+
let renderNow = null;
|
|
155
|
+
let wantsFrame = false;
|
|
156
|
+
let animDueAt = null;
|
|
157
|
+
let rendering = false;
|
|
127
158
|
const requestFrame = () => {
|
|
128
|
-
|
|
129
|
-
else pending = true;
|
|
159
|
+
wantsFrame = true;
|
|
130
160
|
};
|
|
131
161
|
const removeReflector = engine.registerReflector({
|
|
132
162
|
name: "r3fInvalidator",
|
|
133
|
-
|
|
163
|
+
// StageMode: a background-hold flip must re-fire the pass — freezing on
|
|
164
|
+
// take, and (critically) REPAINTING Hot islands on release. The observe
|
|
165
|
+
// list also keeps strata enforcement armed (a rig must observe — the
|
|
166
|
+
// always-flush advance reflector below deliberately observes nothing).
|
|
167
|
+
observe: { resources: [Camera, Viewport, StageMode] },
|
|
134
168
|
flush: () => requestFrame()
|
|
135
169
|
});
|
|
170
|
+
const removeAdvanceReflector = engine.registerReflector({
|
|
171
|
+
name: "r3fAdvance",
|
|
172
|
+
always: true,
|
|
173
|
+
flush: () => {
|
|
174
|
+
if (rendering) return;
|
|
175
|
+
const t = now();
|
|
176
|
+
if (!wantsFrame && (animDueAt === null || t < animDueAt)) return;
|
|
177
|
+
if (renderNow === null) return;
|
|
178
|
+
wantsFrame = false;
|
|
179
|
+
animDueAt = null;
|
|
180
|
+
rendering = true;
|
|
181
|
+
try {
|
|
182
|
+
renderNow(t);
|
|
183
|
+
} finally {
|
|
184
|
+
rendering = false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
const order = createSiblingOrderIndex(world);
|
|
189
|
+
const removeOrderObserver = order.observe(() => {
|
|
190
|
+
if (order.stale()) requestFrame();
|
|
191
|
+
});
|
|
136
192
|
const removeWorldObserver = world.observe({
|
|
137
193
|
onDestroy: (e) => {
|
|
138
194
|
if (state.get(e) !== void 0) {
|
|
@@ -151,6 +207,7 @@ function createGLBridge(engine, opts = {}) {
|
|
|
151
207
|
engine,
|
|
152
208
|
state,
|
|
153
209
|
renderAssert,
|
|
210
|
+
order,
|
|
154
211
|
registerIsland(entity, handle) {
|
|
155
212
|
islands.set(entity, handle);
|
|
156
213
|
const s = state.ensure(entity);
|
|
@@ -163,9 +220,12 @@ function createGLBridge(engine, opts = {}) {
|
|
|
163
220
|
state.bumpPaint(entity);
|
|
164
221
|
requestFrame();
|
|
165
222
|
}),
|
|
166
|
-
// composite dirt — quad transform
|
|
223
|
+
// composite dirt — quad transform changed, texture still good (order
|
|
224
|
+
// dirt is bridge-scoped: the ChildOf reorder wake above):
|
|
167
225
|
world.reactive.observeValue(entity, Position, () => requestFrame()),
|
|
168
|
-
|
|
226
|
+
// composite dirt — the durable Opacity cell rides the quad's uOpacity,
|
|
227
|
+
// never the island's FBO (a fade must not repaint the content):
|
|
228
|
+
world.reactive.observeValue(entity, Opacity, () => requestFrame())
|
|
169
229
|
];
|
|
170
230
|
for (const group of widget?.groups ?? []) {
|
|
171
231
|
unsubs.push(
|
|
@@ -208,63 +268,115 @@ function createGLBridge(engine, opts = {}) {
|
|
|
208
268
|
state.bumpPaint(entity);
|
|
209
269
|
requestFrame();
|
|
210
270
|
},
|
|
211
|
-
setCompositeScale(entity, scale) {
|
|
271
|
+
setCompositeScale(entity, scale, durationMs = LIFT_DEFAULT_MS) {
|
|
212
272
|
const s = state.ensure(entity);
|
|
213
|
-
if (s.
|
|
214
|
-
s.
|
|
273
|
+
if (s.liftTarget === scale) return;
|
|
274
|
+
s.liftTarget = scale;
|
|
275
|
+
if (durationMs <= 0) {
|
|
276
|
+
s.compositeScale = scale;
|
|
277
|
+
s.liftMs = 0;
|
|
278
|
+
s.liftElapsedMs = 0;
|
|
279
|
+
} else {
|
|
280
|
+
s.liftFrom = s.compositeScale;
|
|
281
|
+
s.liftMs = durationMs;
|
|
282
|
+
s.liftElapsedMs = 0;
|
|
283
|
+
}
|
|
215
284
|
requestFrame();
|
|
216
285
|
},
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
286
|
+
setCompositeOpacity(entity, opacity, durationMs = LIFT_DEFAULT_MS) {
|
|
287
|
+
const s = state.ensure(entity);
|
|
288
|
+
if (s.opacityTarget === opacity) return;
|
|
289
|
+
s.opacityTarget = opacity;
|
|
290
|
+
if (durationMs <= 0) {
|
|
291
|
+
s.compositeOpacity = opacity;
|
|
292
|
+
s.opacityMs = 0;
|
|
293
|
+
s.opacityElapsedMs = 0;
|
|
294
|
+
} else {
|
|
295
|
+
s.opacityFrom = s.compositeOpacity;
|
|
296
|
+
s.opacityMs = durationMs;
|
|
297
|
+
s.opacityElapsedMs = 0;
|
|
298
|
+
}
|
|
299
|
+
requestFrame();
|
|
300
|
+
},
|
|
301
|
+
setRenderNow(fn) {
|
|
302
|
+
renderNow = fn;
|
|
303
|
+
},
|
|
304
|
+
schedulePass(plan) {
|
|
305
|
+
if (plan === "now") {
|
|
306
|
+
wantsFrame = true;
|
|
307
|
+
animDueAt = null;
|
|
308
|
+
} else if (plan === "none") {
|
|
309
|
+
animDueAt = null;
|
|
310
|
+
} else {
|
|
311
|
+
animDueAt = now() + plan;
|
|
222
312
|
}
|
|
223
313
|
},
|
|
224
314
|
uninstall() {
|
|
225
315
|
removeReflector();
|
|
316
|
+
removeAdvanceReflector();
|
|
317
|
+
removeOrderObserver();
|
|
226
318
|
removeWorldObserver();
|
|
227
319
|
renderAssert.dispose();
|
|
228
320
|
islands.clear();
|
|
229
321
|
frameCbs.clear();
|
|
230
322
|
state.clear();
|
|
231
|
-
|
|
323
|
+
renderNow = null;
|
|
324
|
+
wantsFrame = false;
|
|
325
|
+
animDueAt = null;
|
|
232
326
|
}
|
|
233
327
|
};
|
|
234
328
|
}
|
|
235
329
|
|
|
236
330
|
// ../r3f/src/compositor-pass.ts
|
|
237
331
|
var GRABBED_RENDER_ORDER = 1e9;
|
|
332
|
+
var LIFT_EASE = cubicBezierEase(0.2, 0.9, 0.3, 1.2);
|
|
333
|
+
var OPACITY_EASE = cubicBezierEase(0.25, 0.1, 0.25, 1);
|
|
238
334
|
function runCompositorPass(ctx) {
|
|
239
335
|
const { world, bridge, pool, quads, gl } = ctx;
|
|
240
|
-
const stats = {
|
|
336
|
+
const stats = {
|
|
337
|
+
repainted: 0,
|
|
338
|
+
pendingPaints: 0,
|
|
339
|
+
evicted: 0,
|
|
340
|
+
fboBytes: 0,
|
|
341
|
+
anyHot: false,
|
|
342
|
+
liftAnimating: false
|
|
343
|
+
};
|
|
241
344
|
const cam = world.getResource(Camera);
|
|
242
345
|
const vp = world.getResource(Viewport);
|
|
243
346
|
if (cam === void 0 || vp === void 0 || vp.w === 0) return stats;
|
|
347
|
+
const inFlight2 = navFlightActive(world);
|
|
348
|
+
const inMotion = cam.gesturing || inFlight2;
|
|
349
|
+
const backgrounded = (world.getResource(StageMode)?.backgroundHolds ?? 0) > 0;
|
|
350
|
+
const frozen = inFlight2 || backgrounded;
|
|
244
351
|
ctx.compCamera.setFrustum(compositeCameraFrustum(cam, vp.w, vp.h));
|
|
245
352
|
const idleDpr = vp.dpr > 0 ? vp.dpr : 1;
|
|
246
|
-
const targetDpr =
|
|
353
|
+
const targetDpr = inMotion ? Math.min(idleDpr, 1) : idleDpr;
|
|
247
354
|
if (gl.getPixelRatio() !== targetDpr) gl.setPixelRatio(targetDpr);
|
|
248
355
|
const phaseOf = (e) => {
|
|
249
356
|
const alive = world.isAlive(e);
|
|
357
|
+
const active = alive && world.hasTag(e, Active);
|
|
250
358
|
const visible = alive && world.hasTag(e, Visible) && bridge.islandFor(e) !== void 0;
|
|
251
359
|
const s = bridge.state.get(e);
|
|
252
360
|
const hasFbo = s !== void 0 && s.fboGeneration >= 0 && pool.get(e) !== null;
|
|
253
|
-
return computeIslandPhase(
|
|
361
|
+
return computeIslandPhase(active, visible, bridge.state.isAnimating(e), hasFbo);
|
|
254
362
|
};
|
|
255
363
|
for (const [e, s] of bridge.state.all()) {
|
|
256
364
|
s.phase = phaseOf(e);
|
|
257
365
|
if (s.phase === "Hot") stats.anyHot = true;
|
|
258
366
|
}
|
|
259
367
|
const band = selectBand(cam.zoom);
|
|
260
|
-
const effectiveDpr = gl.getPixelRatio() * band;
|
|
368
|
+
const effectiveDpr = Math.min(gl.getPixelRatio(), ctx.maxPaintDpr) * band;
|
|
261
369
|
const toPaint = [];
|
|
262
370
|
for (const [e, s] of bridge.state.all()) {
|
|
263
371
|
if (bridge.islandFor(e) === void 0) continue;
|
|
264
372
|
const wantsPhase = s.phase === "Hot" || s.phase === "Waking";
|
|
265
373
|
const genDirty = s.paintGeneration > s.fboGeneration;
|
|
266
|
-
const bandStale = !
|
|
267
|
-
|
|
374
|
+
const bandStale = !inMotion && s.fboGeneration >= 0 && isOutOfBand(cam.zoom, s.paintedAt.band);
|
|
375
|
+
const paintable = frozen ? wantsPhase && s.fboGeneration < 0 : wantsPhase || genDirty || bandStale;
|
|
376
|
+
if (paintable) {
|
|
377
|
+
toPaint.push(e);
|
|
378
|
+
if (s.phase === "Hot") s.pendingDtMs += ctx.dtMs;
|
|
379
|
+
}
|
|
268
380
|
}
|
|
269
381
|
toPaint.sort((a, b) => {
|
|
270
382
|
const sa = bridge.state.get(a);
|
|
@@ -285,7 +397,9 @@ function runCompositorPass(ctx) {
|
|
|
285
397
|
const size = world.isAlive(e) ? world.get(e, Size) : void 0;
|
|
286
398
|
if (handle === void 0 || s === void 0 || size === void 0 || size.w <= 0 || size.h <= 0) continue;
|
|
287
399
|
if (s.phase === "Hot") {
|
|
288
|
-
|
|
400
|
+
const owedMs = s.pendingDtMs;
|
|
401
|
+
s.pendingDtMs = 0;
|
|
402
|
+
for (const cb of bridge.frameCallbacksFor(e)) cb(owedMs);
|
|
289
403
|
}
|
|
290
404
|
ctx.islandCamera(e)?.setFrustum(size.w / 2, size.h / 2);
|
|
291
405
|
const fbo = pool.acquire(e, size.w, size.h, effectiveDpr);
|
|
@@ -330,29 +444,63 @@ function runCompositorPass(ctx) {
|
|
|
330
444
|
for (const key of quads.keys()) {
|
|
331
445
|
if (!liveKeys.has(key)) quads.remove(key);
|
|
332
446
|
}
|
|
447
|
+
const hasChrome = (e) => {
|
|
448
|
+
const type = world.get(e, PrefabId)?.id;
|
|
449
|
+
return typeof type === "string" && widgets.get(type)?.chrome != null;
|
|
450
|
+
};
|
|
451
|
+
const composited = [];
|
|
452
|
+
let clip = null;
|
|
453
|
+
const ordinals = bridge.order.ordinals();
|
|
333
454
|
for (const [e, s] of bridge.state.all()) {
|
|
334
455
|
const quad = quads.ensure(e);
|
|
335
456
|
const alive = world.isAlive(e);
|
|
336
457
|
const fbo = pool.get(e);
|
|
337
458
|
const pos = alive ? world.get(e, Position) : void 0;
|
|
338
459
|
const size = alive ? world.get(e, Size) : void 0;
|
|
339
|
-
if (fbo === null || s.fboGeneration < 0 || pos === void 0 || size === void 0) {
|
|
460
|
+
if (s.phase === "Dormant" || fbo === null || s.fboGeneration < 0 || pos === void 0 || size === void 0) {
|
|
340
461
|
quad.setVisible(false);
|
|
341
462
|
continue;
|
|
342
463
|
}
|
|
343
464
|
const q = worldRectToComposite({ x: pos.x, y: pos.y, width: size.w, height: size.h });
|
|
465
|
+
if (s.liftElapsedMs < s.liftMs) {
|
|
466
|
+
s.liftElapsedMs += ctx.dtMs;
|
|
467
|
+
const t = Math.min(1, s.liftElapsedMs / s.liftMs);
|
|
468
|
+
s.compositeScale = t >= 1 ? s.liftTarget : s.liftFrom + (s.liftTarget - s.liftFrom) * LIFT_EASE(t);
|
|
469
|
+
if (t < 1) stats.liftAnimating = true;
|
|
470
|
+
}
|
|
471
|
+
if (s.opacityElapsedMs < s.opacityMs) {
|
|
472
|
+
s.opacityElapsedMs += ctx.dtMs;
|
|
473
|
+
const t = Math.min(1, s.opacityElapsedMs / s.opacityMs);
|
|
474
|
+
s.compositeOpacity = t >= 1 ? s.opacityTarget : s.opacityFrom + (s.opacityTarget - s.opacityFrom) * OPACITY_EASE(t);
|
|
475
|
+
if (t < 1) stats.liftAnimating = true;
|
|
476
|
+
}
|
|
344
477
|
const lift = s.compositeScale;
|
|
345
478
|
quad.setTransform(q.x, q.y, q.sx * lift, q.sy * lift);
|
|
346
479
|
quad.setTexture(fbo.texture);
|
|
480
|
+
const baseOpacity = world.get(e, Opacity)?.a ?? 1;
|
|
481
|
+
quad.setOpacity(Math.min(1, Math.max(0, baseOpacity * s.compositeOpacity)));
|
|
347
482
|
const grabbed = world.get(e, Grab) !== void 0;
|
|
348
|
-
quad.setRenderOrder(
|
|
483
|
+
quad.setRenderOrder(
|
|
484
|
+
grabbed || lift !== 1 ? GRABBED_RENDER_ORDER : ordinals.get(e) ?? world.get(e, StackZ)?.z ?? 0
|
|
485
|
+
);
|
|
349
486
|
quad.setVisible(true);
|
|
350
487
|
pool.touch(e);
|
|
488
|
+
composited.push({ key: e, quad });
|
|
489
|
+
if (clip === null && grabbed && hasChrome(e)) {
|
|
490
|
+
const hw = q.sx * lift / 2;
|
|
491
|
+
const hh = q.sy * lift / 2;
|
|
492
|
+
clip = { key: e, minX: q.x - hw, minY: q.y - hh, maxX: q.x + hw, maxY: q.y + hh };
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
for (const { key, quad } of composited) {
|
|
496
|
+
if (clip === null) quad.setDragClip(0, 0, 0, 0, false);
|
|
497
|
+
else quad.setDragClip(clip.minX, clip.minY, clip.maxX, clip.maxY, key === clip.key);
|
|
351
498
|
}
|
|
352
499
|
gl.setRenderTarget(null);
|
|
353
500
|
gl.clear();
|
|
354
501
|
gl.render(ctx.compositeScene, ctx.compCamera.raw);
|
|
355
502
|
stats.fboBytes = pool.bytesUsed();
|
|
503
|
+
if (backgrounded) stats.anyHot = false;
|
|
356
504
|
return stats;
|
|
357
505
|
}
|
|
358
506
|
|
|
@@ -373,8 +521,10 @@ var VERTEX_SHADER = (
|
|
|
373
521
|
/* glsl */
|
|
374
522
|
`
|
|
375
523
|
varying vec2 vUv;
|
|
524
|
+
varying vec2 vWorld;
|
|
376
525
|
void main() {
|
|
377
526
|
vUv = uv;
|
|
527
|
+
vWorld = (modelMatrix * vec4(position, 1.0)).xy;
|
|
378
528
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
379
529
|
}
|
|
380
530
|
`
|
|
@@ -384,9 +534,20 @@ var FRAGMENT_SHADER = (
|
|
|
384
534
|
`
|
|
385
535
|
uniform sampler2D map;
|
|
386
536
|
uniform float uOpacity;
|
|
537
|
+
uniform vec4 uDraggedRect;
|
|
538
|
+
uniform float uIsDragged;
|
|
387
539
|
varying vec2 vUv;
|
|
540
|
+
varying vec2 vWorld;
|
|
388
541
|
|
|
389
542
|
void main() {
|
|
543
|
+
// Drag clip (v1 RFC-003, composite-space): while a chrome-carrying GL card
|
|
544
|
+
// is dragged, every OTHER quad discards fragments inside its rect so the
|
|
545
|
+
// card's P1 DOM chrome shows through. (0,0,0,0) rect \u21D2 measure-zero, off.
|
|
546
|
+
if (uIsDragged < 0.5 &&
|
|
547
|
+
vWorld.x >= uDraggedRect.x && vWorld.x <= uDraggedRect.z &&
|
|
548
|
+
vWorld.y >= uDraggedRect.y && vWorld.y <= uDraggedRect.w) {
|
|
549
|
+
discard;
|
|
550
|
+
}
|
|
390
551
|
vec4 c = texture2D(map, vUv);
|
|
391
552
|
if (c.a < 0.001) discard;
|
|
392
553
|
c.a *= uOpacity;
|
|
@@ -402,10 +563,15 @@ var CompositeMaterial = class extends ShaderMaterial {
|
|
|
402
563
|
// the setters allocation-free.
|
|
403
564
|
mapUniform;
|
|
404
565
|
opacityUniform;
|
|
566
|
+
draggedRectUniform;
|
|
567
|
+
isDraggedUniform;
|
|
405
568
|
constructor() {
|
|
406
569
|
const uniforms = {
|
|
407
570
|
map: { value: null },
|
|
408
|
-
uOpacity: { value: 1 }
|
|
571
|
+
uOpacity: { value: 1 },
|
|
572
|
+
// three uploads number[] as vec4 — no Vector4 allocation needed.
|
|
573
|
+
uDraggedRect: { value: [0, 0, 0, 0] },
|
|
574
|
+
uIsDragged: { value: 0 }
|
|
409
575
|
};
|
|
410
576
|
super({
|
|
411
577
|
vertexShader: VERTEX_SHADER,
|
|
@@ -422,6 +588,8 @@ var CompositeMaterial = class extends ShaderMaterial {
|
|
|
422
588
|
});
|
|
423
589
|
this.mapUniform = uniforms.map;
|
|
424
590
|
this.opacityUniform = uniforms.uOpacity;
|
|
591
|
+
this.draggedRectUniform = uniforms.uDraggedRect;
|
|
592
|
+
this.isDraggedUniform = uniforms.uIsDragged;
|
|
425
593
|
}
|
|
426
594
|
/** Bind the island's FBO texture (null clears). */
|
|
427
595
|
setMap(map) {
|
|
@@ -431,6 +599,21 @@ var CompositeMaterial = class extends ShaderMaterial {
|
|
|
431
599
|
setOpacity(opacity) {
|
|
432
600
|
this.opacityUniform.value = opacity;
|
|
433
601
|
}
|
|
602
|
+
/**
|
|
603
|
+
* Drag clip, composite-space min/max (v1's uDraggedRect re-cut from screen
|
|
604
|
+
* space). (0,0,0,0) = inactive. Mutates the vec4 in place — no allocation.
|
|
605
|
+
*/
|
|
606
|
+
setDraggedRect(minX, minY, maxX, maxY) {
|
|
607
|
+
const r = this.draggedRectUniform.value;
|
|
608
|
+
r[0] = minX;
|
|
609
|
+
r[1] = minY;
|
|
610
|
+
r[2] = maxX;
|
|
611
|
+
r[3] = maxY;
|
|
612
|
+
}
|
|
613
|
+
/** True on the dragged card's own quad — exempt from the clip. */
|
|
614
|
+
setIsDragged(isDragged) {
|
|
615
|
+
this.isDraggedUniform.value = isDragged ? 1 : 0;
|
|
616
|
+
}
|
|
434
617
|
// dispose() is inherited from ShaderMaterial — it fires the dispose event that
|
|
435
618
|
// frees the compiled program; no override needed.
|
|
436
619
|
};
|
|
@@ -460,13 +643,20 @@ function useIslandInvalidate() {
|
|
|
460
643
|
const { bridge, entity } = useIslandContext();
|
|
461
644
|
return useCallback(() => bridge.bumpPaint(entity), [bridge, entity]);
|
|
462
645
|
}
|
|
463
|
-
function useIslandLift(scale) {
|
|
646
|
+
function useIslandLift(scale, durationMs) {
|
|
464
647
|
const { bridge, entity } = useIslandContext();
|
|
465
648
|
useEffect(() => {
|
|
466
|
-
bridge.setCompositeScale(entity, scale);
|
|
467
|
-
}, [bridge, entity, scale]);
|
|
649
|
+
bridge.setCompositeScale(entity, scale, durationMs);
|
|
650
|
+
}, [bridge, entity, scale, durationMs]);
|
|
468
651
|
useEffect(() => () => bridge.setCompositeScale(entity, 1), [bridge, entity]);
|
|
469
652
|
}
|
|
653
|
+
function useIslandOpacity(opacity, durationMs) {
|
|
654
|
+
const { bridge, entity } = useIslandContext();
|
|
655
|
+
useEffect(() => {
|
|
656
|
+
bridge.setCompositeOpacity(entity, opacity, durationMs);
|
|
657
|
+
}, [bridge, entity, opacity, durationMs]);
|
|
658
|
+
useEffect(() => () => bridge.setCompositeOpacity(entity, 1), [bridge, entity]);
|
|
659
|
+
}
|
|
470
660
|
|
|
471
661
|
// ../r3f/src/island.tsx
|
|
472
662
|
function Island({ bridge, world, entity, widget, environment }) {
|
|
@@ -500,9 +690,13 @@ function Island({ bridge, world, entity, widget, environment }) {
|
|
|
500
690
|
// ../r3f/src/pool.ts
|
|
501
691
|
import { SRGBColorSpace, WebGLRenderTarget } from "three";
|
|
502
692
|
var COLOR_BYTES_PER_PIXEL = 4;
|
|
693
|
+
var DEPTH_BYTES_PER_PIXEL = 4;
|
|
503
694
|
var SAMPLES = 4;
|
|
504
695
|
function targetBytes(pixelWidth, pixelHeight) {
|
|
505
|
-
|
|
696
|
+
const msaa = SAMPLES > 1;
|
|
697
|
+
const colorBytes = COLOR_BYTES_PER_PIXEL * (msaa ? 1 + SAMPLES : 1);
|
|
698
|
+
const depthBytes = DEPTH_BYTES_PER_PIXEL * (msaa ? SAMPLES : 1);
|
|
699
|
+
return pixelWidth * pixelHeight * (colorBytes + depthBytes);
|
|
506
700
|
}
|
|
507
701
|
var RenderTargetPool = class {
|
|
508
702
|
entries = /* @__PURE__ */ new Map();
|
|
@@ -612,20 +806,37 @@ var RenderTargetPool = class {
|
|
|
612
806
|
}
|
|
613
807
|
};
|
|
614
808
|
|
|
809
|
+
// ../r3f/src/self-sustain.ts
|
|
810
|
+
var RATE_CAP_SLACK_MS = 4;
|
|
811
|
+
function selfSustainPlan(stats, elapsedMs, maxFps) {
|
|
812
|
+
if (stats.liftAnimating) return "now";
|
|
813
|
+
if (!stats.anyHot && stats.pendingPaints <= 0) return "none";
|
|
814
|
+
const interval = 1e3 / Math.max(1, maxFps);
|
|
815
|
+
const delay = interval - elapsedMs - RATE_CAP_SLACK_MS;
|
|
816
|
+
return delay <= 0 ? "now" : delay;
|
|
817
|
+
}
|
|
818
|
+
|
|
615
819
|
// ../r3f/src/gl-root.tsx
|
|
820
|
+
var visibleWidgetsQ = defineQuery([Visible]);
|
|
821
|
+
var culledWidgetsQ = defineQuery([Culled]);
|
|
616
822
|
function GLViews({
|
|
617
823
|
engine,
|
|
618
824
|
bridge,
|
|
619
825
|
store,
|
|
620
826
|
maxFboBytes = RUNTIME_BUDGETS.fboBytes,
|
|
621
827
|
maxRepaintsPerFrame = 4,
|
|
622
|
-
|
|
828
|
+
maxAnimationFps = 60,
|
|
829
|
+
maxPaintDpr = 1.5,
|
|
830
|
+
environment,
|
|
831
|
+
onFrameStats
|
|
623
832
|
}) {
|
|
624
833
|
const world = engine.world;
|
|
625
834
|
const gl = useThree((s) => s.gl);
|
|
626
835
|
const scene = useThree((s) => s.scene);
|
|
627
836
|
const set = useThree((s) => s.set);
|
|
628
|
-
const
|
|
837
|
+
const advance2 = useThree((s) => s.advance);
|
|
838
|
+
const frameloop = useThree((s) => s.frameloop);
|
|
839
|
+
const clock = useThree((s) => s.clock);
|
|
629
840
|
const poolRef = useRef2(null);
|
|
630
841
|
if (poolRef.current === null || poolRef.current.isDisposed()) {
|
|
631
842
|
poolRef.current = new RenderTargetPool();
|
|
@@ -660,6 +871,12 @@ function GLViews({
|
|
|
660
871
|
},
|
|
661
872
|
setRenderOrder: (n) => {
|
|
662
873
|
m.renderOrder = n;
|
|
874
|
+
},
|
|
875
|
+
setOpacity: (o) => m.material.setOpacity(o),
|
|
876
|
+
setDragClip: (minX, minY, maxX, maxY, exempt) => {
|
|
877
|
+
const mat = m.material;
|
|
878
|
+
mat.setDraggedRect(minX, minY, maxX, maxY);
|
|
879
|
+
mat.setIsDragged(exempt);
|
|
663
880
|
}
|
|
664
881
|
};
|
|
665
882
|
},
|
|
@@ -689,9 +906,30 @@ function GLViews({
|
|
|
689
906
|
[gl]
|
|
690
907
|
);
|
|
691
908
|
useEffect3(() => {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
909
|
+
clock.elapsedTime = performance.now() / 1e3;
|
|
910
|
+
bridge.setRenderNow((nowMs) => advance2(nowMs / 1e3, true));
|
|
911
|
+
return () => bridge.setRenderNow(null);
|
|
912
|
+
}, [bridge, advance2, clock]);
|
|
913
|
+
useEffect3(() => {
|
|
914
|
+
if (frameloop !== "never") {
|
|
915
|
+
console.warn(
|
|
916
|
+
`[ice/r3f] GLViews: <Canvas frameloop="${frameloop}"> \u2014 use frameloop="never". The engine drives frames (reflect-phase advance); a self-scheduling loop renders out of phase with the DOM planes.`
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
}, [frameloop]);
|
|
920
|
+
const statsCbRef = useRef2(onFrameStats);
|
|
921
|
+
statsCbRef.current = onFrameStats;
|
|
922
|
+
const paintsOwedRef = useRef2(false);
|
|
923
|
+
const gpuProfilerRef = useRef2(null);
|
|
924
|
+
const gpuProfilerState = useRef2("idle");
|
|
925
|
+
useEffect3(
|
|
926
|
+
() => () => {
|
|
927
|
+
gpuProfilerRef.current?.dispose();
|
|
928
|
+
gpuProfilerRef.current = null;
|
|
929
|
+
gpuProfilerState.current = "idle";
|
|
930
|
+
},
|
|
931
|
+
[]
|
|
932
|
+
);
|
|
695
933
|
useEffect3(() => {
|
|
696
934
|
const quadMap = quadsRef.current;
|
|
697
935
|
return () => {
|
|
@@ -705,6 +943,25 @@ function GLViews({
|
|
|
705
943
|
};
|
|
706
944
|
}, [scene, pool, bridge]);
|
|
707
945
|
useFrame((_, delta) => {
|
|
946
|
+
const profiling = statsCbRef.current !== void 0;
|
|
947
|
+
if (profiling) {
|
|
948
|
+
if (gpuProfilerState.current === "idle") {
|
|
949
|
+
gpuProfilerState.current = "loading";
|
|
950
|
+
void import("stats-gl").then(async ({ StatsProfiler }) => {
|
|
951
|
+
const p = new StatsProfiler({ trackGPU: true });
|
|
952
|
+
await p.init(gl);
|
|
953
|
+
gpuProfilerRef.current = p;
|
|
954
|
+
gpuProfilerState.current = "ready";
|
|
955
|
+
}).catch(() => {
|
|
956
|
+
gpuProfilerState.current = "failed";
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
gl.info.autoReset = false;
|
|
960
|
+
gl.info.reset();
|
|
961
|
+
} else if (!gl.info.autoReset) {
|
|
962
|
+
gl.info.autoReset = true;
|
|
963
|
+
}
|
|
964
|
+
const passStart = performance.now();
|
|
708
965
|
bridge.renderAssert.begin();
|
|
709
966
|
let stats;
|
|
710
967
|
try {
|
|
@@ -742,13 +999,83 @@ function GLViews({
|
|
|
742
999
|
compositeScene: scene,
|
|
743
1000
|
maxFboBytes,
|
|
744
1001
|
maxRepaintsPerFrame,
|
|
1002
|
+
maxPaintDpr,
|
|
745
1003
|
dtMs: delta * 1e3
|
|
746
1004
|
});
|
|
747
1005
|
} finally {
|
|
748
1006
|
bridge.renderAssert.end();
|
|
749
1007
|
}
|
|
750
|
-
|
|
1008
|
+
const cb = statsCbRef.current;
|
|
1009
|
+
if (cb !== void 0) {
|
|
1010
|
+
const gpu = gpuProfilerRef.current;
|
|
1011
|
+
gpu?.update();
|
|
1012
|
+
const d = gpu?.getData();
|
|
1013
|
+
const info = gl.info;
|
|
1014
|
+
let total = 0;
|
|
1015
|
+
let hot = 0;
|
|
1016
|
+
let warm = 0;
|
|
1017
|
+
let waking = 0;
|
|
1018
|
+
let cold = 0;
|
|
1019
|
+
let dormant = 0;
|
|
1020
|
+
let pixels = 0;
|
|
1021
|
+
const bandHistogram = {};
|
|
1022
|
+
for (const [e, s] of bridge.state.all()) {
|
|
1023
|
+
total++;
|
|
1024
|
+
if (s.phase === "Hot") hot++;
|
|
1025
|
+
else if (s.phase === "Warm") warm++;
|
|
1026
|
+
else if (s.phase === "Waking") waking++;
|
|
1027
|
+
else if (s.phase === "Dormant") dormant++;
|
|
1028
|
+
else cold++;
|
|
1029
|
+
if (s.fboGeneration >= 0 && pool.get(e) !== null) {
|
|
1030
|
+
pixels += s.paintedAt.w * s.paintedAt.h;
|
|
1031
|
+
const key = `\xD7${s.paintedAt.band}`;
|
|
1032
|
+
bandHistogram[key] = (bandHistogram[key] ?? 0) + 1;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
let visibleWidgets = 0;
|
|
1036
|
+
let culledWidgets = 0;
|
|
1037
|
+
world.query(visibleWidgetsQ).each((b) => {
|
|
1038
|
+
visibleWidgets += b.count;
|
|
1039
|
+
});
|
|
1040
|
+
world.query(culledWidgetsQ).each((b) => {
|
|
1041
|
+
culledWidgets += b.count;
|
|
1042
|
+
});
|
|
1043
|
+
const zoom = world.getResource(Camera)?.zoom ?? 1;
|
|
1044
|
+
const band = selectBand(zoom);
|
|
1045
|
+
cb({
|
|
1046
|
+
cpuMs: performance.now() - passStart,
|
|
1047
|
+
gpuMs: d?.gpu ?? 0,
|
|
1048
|
+
fps: d?.fps ?? 0,
|
|
1049
|
+
drawCalls: info.render.calls,
|
|
1050
|
+
triangles: info.render.triangles,
|
|
1051
|
+
points: info.render.points,
|
|
1052
|
+
lines: info.render.lines,
|
|
1053
|
+
programs: info.programs?.length ?? 0,
|
|
1054
|
+
geometries: info.memory.geometries,
|
|
1055
|
+
textures: info.memory.textures,
|
|
1056
|
+
renderTargets: pool.size(),
|
|
1057
|
+
renderMegaPixels: pixels / 1e6,
|
|
1058
|
+
fboBytes: pool.bytesUsed(),
|
|
1059
|
+
fboBudgetBytes: maxFboBytes,
|
|
1060
|
+
islands: { total, hot, warm, waking, cold, dormant },
|
|
1061
|
+
bandHistogram,
|
|
1062
|
+
repainted: stats.repainted,
|
|
1063
|
+
pendingPaints: stats.pendingPaints,
|
|
1064
|
+
evicted: stats.evicted,
|
|
1065
|
+
zoom,
|
|
1066
|
+
band,
|
|
1067
|
+
effectiveDpr: Math.min(gl.getPixelRatio(), maxPaintDpr) * band,
|
|
1068
|
+
visibleWidgets,
|
|
1069
|
+
culledWidgets
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
bridge.schedulePass(selfSustainPlan(stats, performance.now() - passStart, maxAnimationFps));
|
|
1073
|
+
paintsOwedRef.current = stats.pendingPaints > 0 || stats.liftAnimating;
|
|
751
1074
|
}, 1);
|
|
1075
|
+
useEffect3(
|
|
1076
|
+
() => engine.frame.settleWhile("gl-paints", () => paintsOwedRef.current),
|
|
1077
|
+
[engine]
|
|
1078
|
+
);
|
|
752
1079
|
const entries = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
753
1080
|
const islands = [];
|
|
754
1081
|
for (const entry of entries) {
|
|
@@ -778,6 +1105,9 @@ var ResourceRegistry = class {
|
|
|
778
1105
|
textures = /* @__PURE__ */ new Map();
|
|
779
1106
|
disposed = false;
|
|
780
1107
|
acquireGeometry(key, factory) {
|
|
1108
|
+
if (this.disposed) {
|
|
1109
|
+
throw new Error("ResourceRegistry: cannot acquire after dispose");
|
|
1110
|
+
}
|
|
781
1111
|
const existing = this.geometries.get(key);
|
|
782
1112
|
if (existing) {
|
|
783
1113
|
existing.refCount++;
|
|
@@ -791,6 +1121,9 @@ var ResourceRegistry = class {
|
|
|
791
1121
|
this.releaseFrom(this.geometries, key);
|
|
792
1122
|
}
|
|
793
1123
|
acquireMaterial(key, factory) {
|
|
1124
|
+
if (this.disposed) {
|
|
1125
|
+
throw new Error("ResourceRegistry: cannot acquire after dispose");
|
|
1126
|
+
}
|
|
794
1127
|
const existing = this.materials.get(key);
|
|
795
1128
|
if (existing) {
|
|
796
1129
|
existing.refCount++;
|
|
@@ -804,6 +1137,9 @@ var ResourceRegistry = class {
|
|
|
804
1137
|
this.releaseFrom(this.materials, key);
|
|
805
1138
|
}
|
|
806
1139
|
acquireTexture(key, factory) {
|
|
1140
|
+
if (this.disposed) {
|
|
1141
|
+
throw new Error("ResourceRegistry: cannot acquire after dispose");
|
|
1142
|
+
}
|
|
807
1143
|
const existing = this.textures.get(key);
|
|
808
1144
|
if (existing) {
|
|
809
1145
|
existing.refCount++;
|
|
@@ -888,14 +1224,53 @@ function handlersOf(obj) {
|
|
|
888
1224
|
function createGLPointerRouter({ world, bridge, index }) {
|
|
889
1225
|
const raycaster = new Raycaster();
|
|
890
1226
|
const ndc = new Vector2();
|
|
1227
|
+
const routerOrder = createSiblingOrderIndex(world);
|
|
891
1228
|
const captures = /* @__PURE__ */ new Map();
|
|
892
1229
|
const hovered = /* @__PURE__ */ new Map();
|
|
893
1230
|
const unclaimedDowns = /* @__PURE__ */ new Map();
|
|
1231
|
+
let hoverInteractive = false;
|
|
1232
|
+
const chainInteractive = (hits) => {
|
|
1233
|
+
for (const h of hits) {
|
|
1234
|
+
for (let obj = h.object; obj !== null; obj = obj.parent) {
|
|
1235
|
+
const handlers = handlersOf(obj);
|
|
1236
|
+
if (handlers !== void 0 && (handlers.onPointerDown !== void 0 || handlers.onClick !== void 0)) {
|
|
1237
|
+
return true;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return false;
|
|
1242
|
+
};
|
|
1243
|
+
const withinTapSlop = (downX, downY, upX, upY) => {
|
|
1244
|
+
const slop = world.getResource(GestureSettings)?.tapSlopPx ?? GESTURE_DEFAULTS.tapSlopPx;
|
|
1245
|
+
const dx = upX - downX;
|
|
1246
|
+
const dy = upY - downY;
|
|
1247
|
+
return dx * dx + dy * dy <= slop * slop;
|
|
1248
|
+
};
|
|
1249
|
+
const chainHas = (hits, target) => {
|
|
1250
|
+
for (const h of hits) {
|
|
1251
|
+
for (let obj = h.object; obj !== null; obj = obj.parent) {
|
|
1252
|
+
if (obj === target) return true;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
return false;
|
|
1256
|
+
};
|
|
1257
|
+
const filterToChain = (hits, target) => {
|
|
1258
|
+
const kept = [];
|
|
1259
|
+
for (const h of hits) {
|
|
1260
|
+
for (let obj = h.object; obj !== null; obj = obj.parent) {
|
|
1261
|
+
if (obj === target) {
|
|
1262
|
+
kept.push(h);
|
|
1263
|
+
break;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return kept;
|
|
1268
|
+
};
|
|
894
1269
|
const castAt = (screenX, screenY) => {
|
|
895
1270
|
const cam = world.getResource(Camera);
|
|
896
1271
|
if (cam === void 0) return void 0;
|
|
897
1272
|
const w = screenToWorld(screenX, screenY, cam);
|
|
898
|
-
const hit = pickTopAt(world, index, w.x, w.y, 0);
|
|
1273
|
+
const hit = pickTopAt(world, index, w.x, w.y, 0, void 0, routerOrder.ordinals());
|
|
899
1274
|
if (hit === void 0) return void 0;
|
|
900
1275
|
const type = world.get(hit, PrefabId)?.id;
|
|
901
1276
|
const widget = typeof type === "string" ? widgets.get(type) : void 0;
|
|
@@ -919,6 +1294,11 @@ function createGLPointerRouter({ world, bridge, index }) {
|
|
|
919
1294
|
raycaster.setFromCamera(ndc, cam3);
|
|
920
1295
|
return { entity: hit, hits: raycaster.intersectObjects(handle.scene.children, true) };
|
|
921
1296
|
};
|
|
1297
|
+
const captureHits = (x, y, capture) => {
|
|
1298
|
+
const cast = castAt(x, y);
|
|
1299
|
+
if (cast === void 0 || cast.entity !== capture.entity) return [];
|
|
1300
|
+
return capture.downObject === void 0 ? cast.hits : filterToChain(cast.hits, capture.downObject);
|
|
1301
|
+
};
|
|
922
1302
|
const dispatch = (type, eventType, entity, hits, native, only) => {
|
|
923
1303
|
const visited = /* @__PURE__ */ new Set();
|
|
924
1304
|
let stopped = false;
|
|
@@ -1022,34 +1402,48 @@ function createGLPointerRouter({ world, bridge, index }) {
|
|
|
1022
1402
|
return {
|
|
1023
1403
|
route(kind, screenX, screenY, native) {
|
|
1024
1404
|
if (kind === "down") {
|
|
1025
|
-
const
|
|
1026
|
-
if (
|
|
1405
|
+
const cast = castAt(screenX, screenY);
|
|
1406
|
+
if (cast === void 0) {
|
|
1027
1407
|
unclaimedDowns.delete(native.pointerId);
|
|
1028
1408
|
return false;
|
|
1029
1409
|
}
|
|
1030
|
-
const { stopped, claimedBy } = dispatch("onPointerDown", "pointerdown",
|
|
1410
|
+
const { stopped, claimedBy } = dispatch("onPointerDown", "pointerdown", cast.entity, cast.hits, native);
|
|
1031
1411
|
if (stopped) {
|
|
1032
1412
|
unclaimedDowns.delete(native.pointerId);
|
|
1033
|
-
captures.set(native.pointerId, {
|
|
1413
|
+
captures.set(native.pointerId, {
|
|
1414
|
+
entity: cast.entity,
|
|
1415
|
+
downObject: claimedBy,
|
|
1416
|
+
downX: screenX,
|
|
1417
|
+
downY: screenY
|
|
1418
|
+
});
|
|
1034
1419
|
return true;
|
|
1035
1420
|
}
|
|
1036
|
-
const top =
|
|
1037
|
-
if (top !== void 0)
|
|
1421
|
+
const top = cast.hits[0]?.object;
|
|
1422
|
+
if (top !== void 0) {
|
|
1423
|
+
unclaimedDowns.set(native.pointerId, {
|
|
1424
|
+
entity: cast.entity,
|
|
1425
|
+
object: top,
|
|
1426
|
+
downX: screenX,
|
|
1427
|
+
downY: screenY
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1038
1430
|
return false;
|
|
1039
1431
|
}
|
|
1040
1432
|
const capture = captures.get(native.pointerId);
|
|
1041
1433
|
if (kind === "move") {
|
|
1042
1434
|
if (capture !== void 0) {
|
|
1043
|
-
|
|
1044
|
-
const hits2 =
|
|
1435
|
+
hoverInteractive = true;
|
|
1436
|
+
const hits2 = captureHits(screenX, screenY, capture);
|
|
1045
1437
|
dispatch("onPointerMove", "pointermove", capture.entity, hits2, native, capture.downObject);
|
|
1046
1438
|
return true;
|
|
1047
1439
|
}
|
|
1440
|
+
hoverInteractive = false;
|
|
1048
1441
|
if (native.buttons === 0) {
|
|
1049
|
-
const
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1442
|
+
const cast = castAt(screenX, screenY);
|
|
1443
|
+
hoverInteractive = cast !== void 0 && chainInteractive(cast.hits);
|
|
1444
|
+
syncHover(cast, native);
|
|
1445
|
+
if (cast !== void 0) {
|
|
1446
|
+
dispatch("onPointerMove", "pointermove", cast.entity, cast.hits, native);
|
|
1053
1447
|
}
|
|
1054
1448
|
}
|
|
1055
1449
|
return false;
|
|
@@ -1057,29 +1451,176 @@ function createGLPointerRouter({ world, bridge, index }) {
|
|
|
1057
1451
|
if (capture === void 0) {
|
|
1058
1452
|
const pending = unclaimedDowns.get(native.pointerId);
|
|
1059
1453
|
unclaimedDowns.delete(native.pointerId);
|
|
1060
|
-
if (kind === "up" && pending !== void 0) {
|
|
1061
|
-
const
|
|
1062
|
-
if (
|
|
1063
|
-
dispatch("onClick", "click",
|
|
1454
|
+
if (kind === "up" && pending !== void 0 && withinTapSlop(pending.downX, pending.downY, screenX, screenY)) {
|
|
1455
|
+
const cast = castAt(screenX, screenY);
|
|
1456
|
+
if (cast !== void 0 && cast.entity === pending.entity && cast.hits[0]?.object === pending.object) {
|
|
1457
|
+
dispatch("onClick", "click", cast.entity, cast.hits, native, pending.object);
|
|
1064
1458
|
}
|
|
1065
1459
|
}
|
|
1066
1460
|
return false;
|
|
1067
1461
|
}
|
|
1068
1462
|
captures.delete(native.pointerId);
|
|
1069
|
-
const
|
|
1070
|
-
const hits = cast !== void 0 && cast.entity === capture.entity ? cast.hits : [];
|
|
1463
|
+
const hits = captureHits(screenX, screenY, capture);
|
|
1071
1464
|
if (kind === "up") {
|
|
1072
1465
|
const { claimedBy } = dispatch("onPointerUp", "pointerup", capture.entity, hits, native, capture.downObject);
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
dispatch("onClick", "click", capture.entity, hits, native, clickTarget);
|
|
1466
|
+
if (capture.downObject !== void 0 && (claimedBy === void 0 || claimedBy === capture.downObject) && chainHas(hits, capture.downObject) && withinTapSlop(capture.downX, capture.downY, screenX, screenY)) {
|
|
1467
|
+
dispatch("onClick", "click", capture.entity, hits, native, capture.downObject);
|
|
1076
1468
|
}
|
|
1077
1469
|
} else {
|
|
1078
1470
|
dispatch("onPointerCancel", "pointercancel", capture.entity, hits, native, capture.downObject);
|
|
1079
1471
|
}
|
|
1080
1472
|
return true;
|
|
1473
|
+
},
|
|
1474
|
+
overInteractive: () => hoverInteractive
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// ../r3f/src/preview-capture.tsx
|
|
1479
|
+
import { advance, createRoot } from "@react-three/fiber";
|
|
1480
|
+
import { createElement as createElement3 } from "react";
|
|
1481
|
+
var sandbox = null;
|
|
1482
|
+
function ensureEntity(def) {
|
|
1483
|
+
if (sandbox === null) {
|
|
1484
|
+
const ce = createCanvasEngine();
|
|
1485
|
+
ce.docs.create();
|
|
1486
|
+
ce.world.sync();
|
|
1487
|
+
ce.step(16);
|
|
1488
|
+
sandbox = { ce, entities: /* @__PURE__ */ new Map() };
|
|
1489
|
+
}
|
|
1490
|
+
const hit = sandbox.entities.get(def.type);
|
|
1491
|
+
if (hit !== void 0) return hit;
|
|
1492
|
+
try {
|
|
1493
|
+
const e = sandbox.ce.ops.spawnWidget(def.type, {
|
|
1494
|
+
x: 0,
|
|
1495
|
+
y: 0,
|
|
1496
|
+
undoable: false,
|
|
1497
|
+
...def.previewProps !== void 0 ? { props: def.previewProps } : {}
|
|
1498
|
+
});
|
|
1499
|
+
sandbox.ce.world.sync();
|
|
1500
|
+
sandbox.ce.step(32);
|
|
1501
|
+
sandbox.entities.set(def.type, e);
|
|
1502
|
+
return e;
|
|
1503
|
+
} catch (err) {
|
|
1504
|
+
console.warn(`ice: preview capture \u2014 sandbox spawn failed for "${def.type}".`, err);
|
|
1505
|
+
return void 0;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
function stubBridge(callbacks) {
|
|
1509
|
+
const stub = {
|
|
1510
|
+
addFrameCallback: (_e, cb) => {
|
|
1511
|
+
callbacks.add(cb);
|
|
1512
|
+
return () => callbacks.delete(cb);
|
|
1513
|
+
},
|
|
1514
|
+
bumpPaint: () => {
|
|
1515
|
+
},
|
|
1516
|
+
setCompositeScale: () => {
|
|
1517
|
+
},
|
|
1518
|
+
setCompositeOpacity: () => {
|
|
1081
1519
|
}
|
|
1082
1520
|
};
|
|
1521
|
+
return stub;
|
|
1522
|
+
}
|
|
1523
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1524
|
+
var inFlight = null;
|
|
1525
|
+
function captureWidgetPreviews(opts = {}) {
|
|
1526
|
+
if (inFlight !== null) return inFlight;
|
|
1527
|
+
const run = doCapture(opts).finally(() => {
|
|
1528
|
+
inFlight = null;
|
|
1529
|
+
});
|
|
1530
|
+
inFlight = run;
|
|
1531
|
+
return run;
|
|
1532
|
+
}
|
|
1533
|
+
async function doCapture(opts) {
|
|
1534
|
+
const wanted = opts.types ?? widgets.all().filter((d) => d.surface === "gl").map((d) => d.type);
|
|
1535
|
+
const defs = wanted.map((t) => widgets.get(t)).filter((d) => d !== void 0 && d.surface === "gl" && d.component !== null && d.component !== void 0).filter((d) => !hasPreviewSnapshot(d.type));
|
|
1536
|
+
if (defs.length === 0) return [];
|
|
1537
|
+
const canvas = document.createElement("canvas");
|
|
1538
|
+
const root = createRoot(canvas);
|
|
1539
|
+
const captured = [];
|
|
1540
|
+
let resolvedEnv;
|
|
1541
|
+
try {
|
|
1542
|
+
for (const def of defs) {
|
|
1543
|
+
try {
|
|
1544
|
+
const entity = ensureEntity(def);
|
|
1545
|
+
if (entity === void 0 || sandbox === null) continue;
|
|
1546
|
+
const { w, h } = def.defaultSize;
|
|
1547
|
+
const scale = opts.scale ?? Math.min(2, 512 / Math.max(w, h));
|
|
1548
|
+
const callbacks = /* @__PURE__ */ new Set();
|
|
1549
|
+
root.configure({
|
|
1550
|
+
frameloop: "never",
|
|
1551
|
+
gl: { alpha: true, antialias: true, preserveDrawingBuffer: true },
|
|
1552
|
+
orthographic: true,
|
|
1553
|
+
// The island frustum verbatim (island.tsx / the paint pass): center
|
|
1554
|
+
// origin, Y-up, camera on +Z looking at the content plane; `manual`
|
|
1555
|
+
// pins it against R3F's resize auto-frustum.
|
|
1556
|
+
camera: {
|
|
1557
|
+
left: -w / 2,
|
|
1558
|
+
right: w / 2,
|
|
1559
|
+
top: h / 2,
|
|
1560
|
+
bottom: -h / 2,
|
|
1561
|
+
near: 0.1,
|
|
1562
|
+
far: 2e3,
|
|
1563
|
+
position: [0, 0, 500],
|
|
1564
|
+
manual: true
|
|
1565
|
+
},
|
|
1566
|
+
size: { width: w, height: h, top: 0, left: 0 },
|
|
1567
|
+
dpr: scale
|
|
1568
|
+
});
|
|
1569
|
+
const View = def.component;
|
|
1570
|
+
const store = root.render(
|
|
1571
|
+
createElement3(
|
|
1572
|
+
EngineProvider,
|
|
1573
|
+
{ engine: sandbox.ce },
|
|
1574
|
+
createElement3(
|
|
1575
|
+
IslandContext.Provider,
|
|
1576
|
+
{ value: { bridge: stubBridge(callbacks), entity } },
|
|
1577
|
+
createElement3(View, { entity, world: sandbox.ce.world })
|
|
1578
|
+
)
|
|
1579
|
+
)
|
|
1580
|
+
);
|
|
1581
|
+
if (resolvedEnv === void 0) {
|
|
1582
|
+
resolvedEnv = typeof opts.environment === "function" ? opts.environment(store.getState().gl) : opts.environment ?? null;
|
|
1583
|
+
}
|
|
1584
|
+
store.getState().scene.environment = resolvedEnv;
|
|
1585
|
+
const cam = store.getState().camera;
|
|
1586
|
+
cam.left = -w / 2;
|
|
1587
|
+
cam.right = w / 2;
|
|
1588
|
+
cam.top = h / 2;
|
|
1589
|
+
cam.bottom = -h / 2;
|
|
1590
|
+
cam.near = 0.1;
|
|
1591
|
+
cam.far = 2e3;
|
|
1592
|
+
cam.position.set(0, 0, 500);
|
|
1593
|
+
cam.lookAt(0, 0, 0);
|
|
1594
|
+
cam.updateProjectionMatrix();
|
|
1595
|
+
cam.updateMatrixWorld(true);
|
|
1596
|
+
await sleep(30);
|
|
1597
|
+
const frames = opts.settleFrames ?? 12;
|
|
1598
|
+
for (let i = 0; i < frames; i++) {
|
|
1599
|
+
for (const cb of callbacks) cb(16.7);
|
|
1600
|
+
advance(i * 16.7 / 1e3, false, store.getState());
|
|
1601
|
+
await sleep(16);
|
|
1602
|
+
}
|
|
1603
|
+
for (const cb of callbacks) cb(16.7);
|
|
1604
|
+
advance(frames * 16.7 / 1e3, false, store.getState());
|
|
1605
|
+
const snap = document.createElement("canvas");
|
|
1606
|
+
snap.width = canvas.width;
|
|
1607
|
+
snap.height = canvas.height;
|
|
1608
|
+
snap.getContext("2d")?.drawImage(canvas, 0, 0);
|
|
1609
|
+
setPreviewSnapshot(def.type, snap);
|
|
1610
|
+
captured.push(def.type);
|
|
1611
|
+
} catch (err) {
|
|
1612
|
+
console.warn(`ice: preview capture failed for "${def.type}" \u2014 its tile keeps the fallback.`, err);
|
|
1613
|
+
}
|
|
1614
|
+
await new Promise((r) => {
|
|
1615
|
+
const w = window;
|
|
1616
|
+
if (w.requestIdleCallback !== void 0) w.requestIdleCallback(() => r(), { timeout: 500 });
|
|
1617
|
+
else setTimeout(r, 50);
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
} finally {
|
|
1621
|
+
root.unmount();
|
|
1622
|
+
}
|
|
1623
|
+
return captured;
|
|
1083
1624
|
}
|
|
1084
1625
|
export {
|
|
1085
1626
|
CompositeMaterial,
|
|
@@ -1088,6 +1629,7 @@ export {
|
|
|
1088
1629
|
IslandContext,
|
|
1089
1630
|
RenderTargetPool,
|
|
1090
1631
|
ResourceRegistry,
|
|
1632
|
+
captureWidgetPreviews,
|
|
1091
1633
|
createGLBridge,
|
|
1092
1634
|
createGLPointerRouter,
|
|
1093
1635
|
createIslandStateStore,
|
|
@@ -1096,6 +1638,7 @@ export {
|
|
|
1096
1638
|
useIslandContext,
|
|
1097
1639
|
useIslandFrame,
|
|
1098
1640
|
useIslandInvalidate,
|
|
1099
|
-
useIslandLift
|
|
1641
|
+
useIslandLift,
|
|
1642
|
+
useIslandOpacity
|
|
1100
1643
|
};
|
|
1101
1644
|
//# sourceMappingURL=r3f.js.map
|