@lovo/matter-react 0.2.0 → 0.4.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/CHANGELOG.md +46 -0
- package/README.md +9 -5
- package/dist/index.cjs +205 -160
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +93 -66
- package/dist/index.d.ts +93 -66
- package/dist/index.js +205 -161
- package/dist/index.js.map +1 -1
- package/package.json +29 -29
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# @lovo/matter-react
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1c69220: Rename public API symbols to domain-accurate names.
|
|
8
|
+
|
|
9
|
+
New primary names: `FrameScheduler`, `GpuRenderer`, `GpuBackend` (`@lovo/matter`); `ShaderScene`, `ShaderSceneProps`, `ShaderContext`, `ShaderContextValue`, `useShaderContext`, `ShaderMonitor`, `ShaderMonitorProps`, `AnimatableSignal` (`@lovo/matter-react`).
|
|
10
|
+
|
|
11
|
+
Old names (`MatterScheduler`, `MatterRenderer`, `MatterBackend`, `MatterScene`, `MatterSceneProps`, `MatterContext`, `MatterContextValue`, `useMatterContext`, `MatterMonitor`, `MatterMonitorProps`, `MatterSignal`, `MatterBackend`) are deprecated with `@deprecated` JSDoc and continue to work. They will be removed no earlier than 0.5.0.
|
|
12
|
+
|
|
13
|
+
**Migration:** Replace old names with new in your imports and JSX. A one-pass find-and-replace is sufficient — no behavioral changes.
|
|
14
|
+
|
|
15
|
+
## 0.3.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- c4cbb52: Add the overlay-component category. `MatterScene` now drives its render via `three/webgpu`'s `PostProcessing` pipeline so child components can register chained TSL transforms instead of each owning their own material draw.
|
|
20
|
+
|
|
21
|
+
**New: `useOverlayPass(transform, deps)` hook**
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { useOverlayPass, useAnimatableUniform } from "@lovo/matter-react";
|
|
25
|
+
|
|
26
|
+
export function MyOverlay({ intensity }) {
|
|
27
|
+
const intensityU = useAnimatableUniform(intensity);
|
|
28
|
+
useOverlayPass(
|
|
29
|
+
(input) => input.mul(intensityU), // takes upstream pixel, returns modified pixel
|
|
30
|
+
[intensityU]
|
|
31
|
+
);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Mount the component inside any `<MatterScene>` and it composes onto the pipeline; multiple overlays chain in mount order. Uniforms captured inside `transform` update in place and don't need to be in `deps` — only put structural changes (mode toggles, etc.) in `deps` so the transform gets re-registered.
|
|
37
|
+
|
|
38
|
+
**Registry-side ships (delivered via `@lovo/matter-cli` copy-paste):**
|
|
39
|
+
|
|
40
|
+
- `<FilmGrain>` — additive or subtractive grain overlay.
|
|
41
|
+
- `<Vignette>` — radial edge darkening, aspect-corrected so the mask is a circle on widescreen.
|
|
42
|
+
- **Breaking:** `<MeshGradient>` no longer accepts `grain` / `grainSpeed` props. Stack `<FilmGrain />` as a sibling inside `<MatterScene>` instead. Existing copies pulled before this release keep working; new pulls / CLI refreshes pick up the new shape. The MeshGradient docs page has the new pattern.
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- Updated dependencies [3856367]
|
|
47
|
+
- @lovo/matter@0.3.0
|
|
48
|
+
|
|
3
49
|
## 0.2.0
|
|
4
50
|
|
|
5
51
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
React binding for **Matter** — shader components on WebGPU + Three.js TSL.
|
|
4
4
|
|
|
5
|
-
This package wraps the engine ([`@lovo/matter`](https://www.npmjs.com/package/@lovo/matter)) with React-friendly primitives: a shared `<
|
|
5
|
+
This package wraps the engine ([`@lovo/matter`](https://www.npmjs.com/package/@lovo/matter)) with React-friendly primitives: a shared `<ShaderScene>` canvas, a `useShaderMaterial` hook for `@react-three/fiber` integration, and input hooks (`useCursor`, `useScroll`).
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -17,21 +17,21 @@ npm install @lovo/matter @lovo/matter-react react three
|
|
|
17
17
|
Matter components work in three configurations:
|
|
18
18
|
|
|
19
19
|
1. **Drop-in** — each component manages its own canvas. Simplest path; one canvas per effect.
|
|
20
|
-
2. **Shared scene** — wrap multiple Matter components in a single `<
|
|
20
|
+
2. **Shared scene** — wrap multiple Matter components in a single `<ShaderScene>` to share one canvas (faster, layered effects).
|
|
21
21
|
3. **Inside `@react-three/fiber`** — use `useShaderMaterial` directly inside a r3f `<Canvas>` you already own.
|
|
22
22
|
|
|
23
23
|
## Minimal usage (Mode 2: shared scene)
|
|
24
24
|
|
|
25
25
|
```tsx
|
|
26
|
-
import {
|
|
26
|
+
import { ShaderScene } from '@lovo/matter-react'
|
|
27
27
|
// LinearGradient is copy-pasted into your project via @lovo/matter-cli
|
|
28
28
|
import { LinearGradient } from '@/components/matter/linear-gradient'
|
|
29
29
|
|
|
30
30
|
export default function Hero() {
|
|
31
31
|
return (
|
|
32
|
-
<
|
|
32
|
+
<ShaderScene>
|
|
33
33
|
<LinearGradient colors={['#0b0c2a', '#1d1f57', '#7d2dff']} angle={120} />
|
|
34
|
-
</
|
|
34
|
+
</ShaderScene>
|
|
35
35
|
)
|
|
36
36
|
}
|
|
37
37
|
```
|
|
@@ -52,6 +52,10 @@ The component lands in `src/components/matter/linear-gradient.tsx` and is yours
|
|
|
52
52
|
|
|
53
53
|
<https://github.com/lovo-hq/matter>
|
|
54
54
|
|
|
55
|
+
## Migration from 0.3.x
|
|
56
|
+
|
|
57
|
+
`MatterScene`, `MatterMonitor`, `useMatterContext`, and related types have been renamed to `ShaderScene`, `ShaderMonitor`, `useShaderContext`, `ShaderContextValue`, etc. The old names are deprecated and still work — remove them at your leisure before 0.5.0.
|
|
58
|
+
|
|
55
59
|
## License
|
|
56
60
|
|
|
57
61
|
MIT — see [LICENSE](./LICENSE).
|
package/dist/index.cjs
CHANGED
|
@@ -21,29 +21,103 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
FallbackBoundary: () => FallbackBoundary,
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
ShaderMonitor: () => ShaderMonitor,
|
|
25
|
+
ShaderScene: () => ShaderScene,
|
|
26
26
|
useAnimatableUniform: () => useAnimatableUniform,
|
|
27
27
|
useCursor: () => useCursor,
|
|
28
|
-
|
|
28
|
+
useOverlayPass: () => useOverlayPass,
|
|
29
29
|
useResize: () => useResize,
|
|
30
30
|
useScroll: () => useScroll,
|
|
31
|
+
useShaderContext: () => useShaderContext,
|
|
31
32
|
useShaderMaterial: () => useShaderMaterial,
|
|
32
33
|
useStaticHint: () => useStaticHint
|
|
33
34
|
});
|
|
34
35
|
module.exports = __toCommonJS(index_exports);
|
|
35
36
|
|
|
36
|
-
// src/
|
|
37
|
+
// src/components/fallback-boundary/fallback-boundary.tsx
|
|
38
|
+
var import_react = require("react");
|
|
39
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
40
|
+
function FallbackBoundary({ fallback, children }) {
|
|
41
|
+
const [mounted, setMounted] = (0, import_react.useState)(false);
|
|
42
|
+
(0, import_react.useEffect)(() => {
|
|
43
|
+
setMounted(true);
|
|
44
|
+
}, []);
|
|
45
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: mounted ? children : fallback ?? null });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/components/shader-monitor/shader-monitor.tsx
|
|
49
|
+
var import_react3 = require("react");
|
|
50
|
+
|
|
51
|
+
// src/context/shader-context.ts
|
|
37
52
|
var import_react2 = require("react");
|
|
38
|
-
var
|
|
39
|
-
var import_matter = require("@lovo/matter");
|
|
53
|
+
var ShaderContext = (0, import_react2.createContext)(null);
|
|
40
54
|
|
|
41
|
-
// src/
|
|
42
|
-
var
|
|
43
|
-
var
|
|
55
|
+
// src/components/shader-monitor/shader-monitor.tsx
|
|
56
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
57
|
+
var anchorStyle = {
|
|
58
|
+
"top-left": { top: 8, left: 8 },
|
|
59
|
+
"top-right": { top: 8, right: 8 },
|
|
60
|
+
"bottom-left": { bottom: 8, left: 8 },
|
|
61
|
+
"bottom-right": { bottom: 8, right: 8 }
|
|
62
|
+
};
|
|
63
|
+
var baseStyle = {
|
|
64
|
+
position: "absolute",
|
|
65
|
+
zIndex: 10,
|
|
66
|
+
padding: "6px 8px",
|
|
67
|
+
borderRadius: 6,
|
|
68
|
+
background: "rgba(0, 0, 0, 0.6)",
|
|
69
|
+
color: "#fff",
|
|
70
|
+
font: "11px ui-monospace, monospace",
|
|
71
|
+
lineHeight: 1.4,
|
|
72
|
+
pointerEvents: "none",
|
|
73
|
+
whiteSpace: "pre"
|
|
74
|
+
};
|
|
75
|
+
function ShaderMonitor({ anchor = "top-right" }) {
|
|
76
|
+
const ctx = (0, import_react3.useContext)(ShaderContext);
|
|
77
|
+
const [stats, setStats] = (0, import_react3.useState)({ fps: 0, ticks: 0, frames: 0 });
|
|
78
|
+
const ticksRef = (0, import_react3.useRef)(0);
|
|
79
|
+
const fpsAccumRef = (0, import_react3.useRef)({ frames: 0, lastSampleAt: 0, fps: 0 });
|
|
80
|
+
(0, import_react3.useEffect)(() => {
|
|
81
|
+
if (!ctx) return;
|
|
82
|
+
const client = (tick) => {
|
|
83
|
+
ticksRef.current += 1;
|
|
84
|
+
const acc = fpsAccumRef.current;
|
|
85
|
+
acc.frames += 1;
|
|
86
|
+
if (acc.lastSampleAt === 0) acc.lastSampleAt = tick.now;
|
|
87
|
+
const dt = tick.now - acc.lastSampleAt;
|
|
88
|
+
if (dt >= 500) {
|
|
89
|
+
acc.fps = Math.round(acc.frames * 1e3 / dt);
|
|
90
|
+
acc.frames = 0;
|
|
91
|
+
acc.lastSampleAt = tick.now;
|
|
92
|
+
}
|
|
93
|
+
setStats({ fps: acc.fps, ticks: ticksRef.current, frames: acc.frames });
|
|
94
|
+
};
|
|
95
|
+
ctx.scheduler.add(client);
|
|
96
|
+
return () => ctx.scheduler.remove(client);
|
|
97
|
+
}, [ctx]);
|
|
98
|
+
if (!ctx) {
|
|
99
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: "no scene" });
|
|
100
|
+
}
|
|
101
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: [
|
|
102
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { "data-testid": "matter-monitor-fps", children: [
|
|
103
|
+
"fps: ",
|
|
104
|
+
stats.fps || "\u2014"
|
|
105
|
+
] }),
|
|
106
|
+
"\n",
|
|
107
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { "data-testid": "matter-monitor-ticks", children: [
|
|
108
|
+
"ticks: ",
|
|
109
|
+
stats.ticks
|
|
110
|
+
] })
|
|
111
|
+
] });
|
|
112
|
+
}
|
|
44
113
|
|
|
45
|
-
// src/
|
|
46
|
-
var
|
|
114
|
+
// src/components/shader-scene/shader-scene.tsx
|
|
115
|
+
var import_matter = require("@lovo/matter");
|
|
116
|
+
var import_react4 = require("react");
|
|
117
|
+
var import_three = require("three");
|
|
118
|
+
var import_tsl = require("three/tsl");
|
|
119
|
+
var import_webgpu = require("three/webgpu");
|
|
120
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
47
121
|
var defaultStyle = {
|
|
48
122
|
position: "absolute",
|
|
49
123
|
inset: 0,
|
|
@@ -51,12 +125,12 @@ var defaultStyle = {
|
|
|
51
125
|
width: "100%",
|
|
52
126
|
height: "100%"
|
|
53
127
|
};
|
|
54
|
-
function
|
|
128
|
+
function ShaderScene(props) {
|
|
55
129
|
const { children, fallback, className, style, maxDPR } = props;
|
|
56
|
-
const canvasRef = (0,
|
|
57
|
-
const [ctx, setCtx] = (0,
|
|
58
|
-
const [error, setError] = (0,
|
|
59
|
-
(0,
|
|
130
|
+
const canvasRef = (0, import_react4.useRef)(null);
|
|
131
|
+
const [ctx, setCtx] = (0, import_react4.useState)(null);
|
|
132
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
133
|
+
(0, import_react4.useEffect)(() => {
|
|
60
134
|
const canvas = canvasRef.current;
|
|
61
135
|
if (!canvas) return;
|
|
62
136
|
let cancelled = false;
|
|
@@ -71,8 +145,29 @@ function MatterScene(props) {
|
|
|
71
145
|
const scene = new import_three.Scene();
|
|
72
146
|
const camera = new import_three.OrthographicCamera(-1, 1, 1, -1, 0.1, 10);
|
|
73
147
|
camera.position.z = 1;
|
|
74
|
-
const
|
|
75
|
-
scheduler
|
|
148
|
+
const postProcessing = new import_webgpu.PostProcessing(renderer.three);
|
|
149
|
+
const scheduler = new import_matter.FrameScheduler();
|
|
150
|
+
const overlays = /* @__PURE__ */ new Map();
|
|
151
|
+
const basePass = (0, import_tsl.pass)(scene, camera);
|
|
152
|
+
const rebuildOutputNode = () => {
|
|
153
|
+
const seed = basePass;
|
|
154
|
+
postProcessing.outputNode = Array.from(overlays.values()).reduce(
|
|
155
|
+
(node, transform) => transform(node),
|
|
156
|
+
seed
|
|
157
|
+
);
|
|
158
|
+
postProcessing.needsUpdate = true;
|
|
159
|
+
};
|
|
160
|
+
rebuildOutputNode();
|
|
161
|
+
const registerOverlay = (transform) => {
|
|
162
|
+
const key = /* @__PURE__ */ Symbol("overlay");
|
|
163
|
+
overlays.set(key, transform);
|
|
164
|
+
rebuildOutputNode();
|
|
165
|
+
return () => {
|
|
166
|
+
overlays.delete(key);
|
|
167
|
+
rebuildOutputNode();
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
scheduler.add(() => postProcessing.render());
|
|
76
171
|
scheduler.start();
|
|
77
172
|
const visibility = (0, import_matter.createVisibilityWatcher)();
|
|
78
173
|
const intersection = (0, import_matter.createIntersectionWatcher)(canvas);
|
|
@@ -95,11 +190,11 @@ function MatterScene(props) {
|
|
|
95
190
|
scheduler.dispose();
|
|
96
191
|
renderer.dispose();
|
|
97
192
|
};
|
|
98
|
-
setCtx({ renderer, scene, camera, scheduler });
|
|
193
|
+
setCtx({ renderer, scene, camera, scheduler, registerOverlay });
|
|
99
194
|
} catch (err) {
|
|
100
195
|
if (cancelled) return;
|
|
101
196
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
102
|
-
console.error("[
|
|
197
|
+
console.error("[ShaderScene] renderer init failed:", e);
|
|
103
198
|
setError(e);
|
|
104
199
|
}
|
|
105
200
|
};
|
|
@@ -111,9 +206,9 @@ function MatterScene(props) {
|
|
|
111
206
|
setCtx(null);
|
|
112
207
|
};
|
|
113
208
|
}, [maxDPR]);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
209
|
+
let content;
|
|
210
|
+
if (error) {
|
|
211
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
117
212
|
"div",
|
|
118
213
|
{
|
|
119
214
|
style: {
|
|
@@ -130,47 +225,67 @@ function MatterScene(props) {
|
|
|
130
225
|
textAlign: "center"
|
|
131
226
|
},
|
|
132
227
|
children: [
|
|
133
|
-
"
|
|
228
|
+
"ShaderScene init failed:",
|
|
134
229
|
"\n",
|
|
135
230
|
error.message
|
|
136
231
|
]
|
|
137
232
|
}
|
|
138
|
-
)
|
|
233
|
+
);
|
|
234
|
+
} else if (ctx) {
|
|
235
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShaderContext.Provider, { value: ctx, children });
|
|
236
|
+
} else {
|
|
237
|
+
content = fallback ?? null;
|
|
238
|
+
}
|
|
239
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className, style: { ...defaultStyle, ...style }, children: [
|
|
240
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("canvas", { ref: canvasRef, style: { width: "100%", height: "100%", display: "block" } }),
|
|
241
|
+
content
|
|
139
242
|
] });
|
|
140
243
|
}
|
|
141
244
|
|
|
142
|
-
// src/
|
|
143
|
-
var
|
|
144
|
-
|
|
145
|
-
|
|
245
|
+
// src/hooks/use-animatable-uniform/use-animatable-uniform.ts
|
|
246
|
+
var import_react5 = require("react");
|
|
247
|
+
var import_tsl2 = require("three/tsl");
|
|
248
|
+
var isSignal = (value) => {
|
|
249
|
+
if (typeof value !== "object" || value === null) return false;
|
|
250
|
+
return "get" in value && typeof value.get === "function" && "on" in value && typeof value.on === "function";
|
|
251
|
+
};
|
|
252
|
+
function useAnimatableUniform(value) {
|
|
253
|
+
const uniformNode = (0, import_react5.useMemo)(() => {
|
|
254
|
+
const initial = isSignal(value) ? value.get() : value;
|
|
255
|
+
return (0, import_tsl2.uniform)(initial);
|
|
256
|
+
}, []);
|
|
257
|
+
(0, import_react5.useEffect)(() => {
|
|
258
|
+
if (isSignal(value)) {
|
|
259
|
+
const unsub = value.on("change", (next) => {
|
|
260
|
+
uniformNode.value = next;
|
|
261
|
+
});
|
|
262
|
+
return unsub;
|
|
263
|
+
}
|
|
264
|
+
uniformNode.value = value;
|
|
265
|
+
return void 0;
|
|
266
|
+
}, [value, uniformNode]);
|
|
267
|
+
return uniformNode;
|
|
146
268
|
}
|
|
147
269
|
|
|
148
|
-
// src/
|
|
149
|
-
var
|
|
150
|
-
var
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}, [build]);
|
|
157
|
-
(0, import_react4.useEffect)(() => {
|
|
158
|
-
return () => material.dispose();
|
|
159
|
-
}, [material]);
|
|
160
|
-
return material;
|
|
270
|
+
// src/hooks/use-cursor/use-cursor.ts
|
|
271
|
+
var import_matter2 = require("@lovo/matter");
|
|
272
|
+
var import_react7 = require("react");
|
|
273
|
+
|
|
274
|
+
// src/hooks/use-shader-context/use-shader-context.ts
|
|
275
|
+
var import_react6 = require("react");
|
|
276
|
+
function useShaderContext() {
|
|
277
|
+
return (0, import_react6.useContext)(ShaderContext);
|
|
161
278
|
}
|
|
162
279
|
|
|
163
|
-
// src/
|
|
164
|
-
var import_react5 = require("react");
|
|
165
|
-
var import_matter2 = require("@lovo/matter");
|
|
280
|
+
// src/hooks/use-cursor/use-cursor.ts
|
|
166
281
|
var STUB_SIGNAL = {
|
|
167
282
|
get: () => [0.5, 0.5],
|
|
168
283
|
on: () => () => void 0
|
|
169
284
|
};
|
|
170
285
|
function useCursor(opts = {}) {
|
|
171
|
-
const ctx =
|
|
172
|
-
const [input, setInput] = (0,
|
|
173
|
-
(0,
|
|
286
|
+
const ctx = useShaderContext();
|
|
287
|
+
const [input, setInput] = (0, import_react7.useState)(null);
|
|
288
|
+
(0, import_react7.useEffect)(() => {
|
|
174
289
|
const canvas = ctx?.renderer.three.domElement;
|
|
175
290
|
const elementOpt = opts.element ?? (canvas instanceof HTMLElement ? canvas : void 0);
|
|
176
291
|
const fresh = new import_matter2.CursorInput({ ...opts, element: elementOpt });
|
|
@@ -195,7 +310,7 @@ function useCursor(opts = {}) {
|
|
|
195
310
|
};
|
|
196
311
|
}
|
|
197
312
|
return () => {
|
|
198
|
-
detach
|
|
313
|
+
detach();
|
|
199
314
|
fresh.dispose();
|
|
200
315
|
setInput(null);
|
|
201
316
|
};
|
|
@@ -203,16 +318,27 @@ function useCursor(opts = {}) {
|
|
|
203
318
|
return input ?? STUB_SIGNAL;
|
|
204
319
|
}
|
|
205
320
|
|
|
206
|
-
// src/
|
|
207
|
-
var
|
|
321
|
+
// src/hooks/use-overlay-pass/use-overlay-pass.ts
|
|
322
|
+
var import_react8 = require("react");
|
|
323
|
+
function useOverlayPass(transform, deps) {
|
|
324
|
+
const ctx = useShaderContext();
|
|
325
|
+
(0, import_react8.useEffect)(() => {
|
|
326
|
+
if (!ctx) return;
|
|
327
|
+
const unregister = ctx.registerOverlay(transform);
|
|
328
|
+
return unregister;
|
|
329
|
+
}, [ctx, ...deps]);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/hooks/use-resize/use-resize.ts
|
|
333
|
+
var import_react9 = require("react");
|
|
208
334
|
var STUB_SIGNAL2 = {
|
|
209
335
|
get: () => [0, 0, 1],
|
|
210
336
|
on: () => () => void 0
|
|
211
337
|
};
|
|
212
338
|
function useResize() {
|
|
213
|
-
const ctx =
|
|
214
|
-
const [signal, setSignal] = (0,
|
|
215
|
-
(0,
|
|
339
|
+
const ctx = useShaderContext();
|
|
340
|
+
const [signal, setSignal] = (0, import_react9.useState)(null);
|
|
341
|
+
(0, import_react9.useEffect)(() => {
|
|
216
342
|
if (!ctx) return void 0;
|
|
217
343
|
const canvas = ctx.renderer.three.domElement;
|
|
218
344
|
if (!(canvas instanceof HTMLCanvasElement)) return void 0;
|
|
@@ -272,15 +398,15 @@ function useResize() {
|
|
|
272
398
|
return signal ?? STUB_SIGNAL2;
|
|
273
399
|
}
|
|
274
400
|
|
|
275
|
-
// src/
|
|
276
|
-
var
|
|
401
|
+
// src/hooks/use-scroll/use-scroll.ts
|
|
402
|
+
var import_react10 = require("react");
|
|
277
403
|
var STUB_SIGNAL3 = {
|
|
278
404
|
get: () => [0, 0],
|
|
279
405
|
on: () => () => void 0
|
|
280
406
|
};
|
|
281
407
|
function useScroll() {
|
|
282
|
-
const [signal, setSignal] = (0,
|
|
283
|
-
(0,
|
|
408
|
+
const [signal, setSignal] = (0, import_react10.useState)(null);
|
|
409
|
+
(0, import_react10.useEffect)(() => {
|
|
284
410
|
if (typeof window === "undefined") return void 0;
|
|
285
411
|
const compute = () => {
|
|
286
412
|
const y = window.scrollY;
|
|
@@ -322,123 +448,42 @@ function useScroll() {
|
|
|
322
448
|
return signal ?? STUB_SIGNAL3;
|
|
323
449
|
}
|
|
324
450
|
|
|
325
|
-
// src/
|
|
326
|
-
var
|
|
327
|
-
var
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const unsub = value.on("change", (next) => {
|
|
339
|
-
;
|
|
340
|
-
uniformNode.value = next;
|
|
341
|
-
});
|
|
342
|
-
return unsub;
|
|
343
|
-
}
|
|
344
|
-
;
|
|
345
|
-
uniformNode.value = value;
|
|
346
|
-
return void 0;
|
|
347
|
-
}, [value, uniformNode]);
|
|
348
|
-
return uniformNode;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// src/FallbackBoundary.tsx
|
|
352
|
-
var import_react9 = require("react");
|
|
353
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
354
|
-
function FallbackBoundary({ fallback, children }) {
|
|
355
|
-
const [mounted, setMounted] = (0, import_react9.useState)(false);
|
|
356
|
-
(0, import_react9.useEffect)(() => {
|
|
357
|
-
setMounted(true);
|
|
358
|
-
}, []);
|
|
359
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: mounted ? children : fallback ?? null });
|
|
451
|
+
// src/hooks/use-shader-material/use-shader-material.ts
|
|
452
|
+
var import_react11 = require("react");
|
|
453
|
+
var import_webgpu2 = require("three/webgpu");
|
|
454
|
+
function useShaderMaterial(build) {
|
|
455
|
+
const material = (0, import_react11.useMemo)(() => {
|
|
456
|
+
const m = new import_webgpu2.MeshBasicNodeMaterial();
|
|
457
|
+
m.colorNode = build();
|
|
458
|
+
return m;
|
|
459
|
+
}, [build]);
|
|
460
|
+
(0, import_react11.useEffect)(() => {
|
|
461
|
+
return () => material.dispose();
|
|
462
|
+
}, [material]);
|
|
463
|
+
return material;
|
|
360
464
|
}
|
|
361
465
|
|
|
362
|
-
// src/
|
|
363
|
-
var
|
|
466
|
+
// src/hooks/use-static-hint/use-static-hint.ts
|
|
467
|
+
var import_react12 = require("react");
|
|
364
468
|
function useStaticHint(hint) {
|
|
365
|
-
const ctx =
|
|
366
|
-
(0,
|
|
469
|
+
const ctx = useShaderContext();
|
|
470
|
+
(0, import_react12.useEffect)(() => {
|
|
367
471
|
if (!ctx) return;
|
|
368
472
|
ctx.scheduler.setIdle(hint);
|
|
369
473
|
return () => ctx.scheduler.setIdle(false);
|
|
370
474
|
}, [ctx, hint]);
|
|
371
475
|
}
|
|
372
|
-
|
|
373
|
-
// src/MatterMonitor.tsx
|
|
374
|
-
var import_react11 = require("react");
|
|
375
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
376
|
-
var anchorStyle = {
|
|
377
|
-
"top-left": { top: 8, left: 8 },
|
|
378
|
-
"top-right": { top: 8, right: 8 },
|
|
379
|
-
"bottom-left": { bottom: 8, left: 8 },
|
|
380
|
-
"bottom-right": { bottom: 8, right: 8 }
|
|
381
|
-
};
|
|
382
|
-
var baseStyle = {
|
|
383
|
-
position: "absolute",
|
|
384
|
-
zIndex: 10,
|
|
385
|
-
padding: "6px 8px",
|
|
386
|
-
borderRadius: 6,
|
|
387
|
-
background: "rgba(0, 0, 0, 0.6)",
|
|
388
|
-
color: "#fff",
|
|
389
|
-
font: "11px ui-monospace, monospace",
|
|
390
|
-
lineHeight: 1.4,
|
|
391
|
-
pointerEvents: "none",
|
|
392
|
-
whiteSpace: "pre"
|
|
393
|
-
};
|
|
394
|
-
function MatterMonitor({ anchor = "top-right" }) {
|
|
395
|
-
const ctx = (0, import_react11.useContext)(MatterContext);
|
|
396
|
-
const [stats, setStats] = (0, import_react11.useState)({ fps: 0, ticks: 0, frames: 0 });
|
|
397
|
-
const ticksRef = (0, import_react11.useRef)(0);
|
|
398
|
-
const fpsAccumRef = (0, import_react11.useRef)({ frames: 0, lastSampleAt: 0, fps: 0 });
|
|
399
|
-
(0, import_react11.useEffect)(() => {
|
|
400
|
-
if (!ctx) return;
|
|
401
|
-
const client = (tick) => {
|
|
402
|
-
ticksRef.current += 1;
|
|
403
|
-
const acc = fpsAccumRef.current;
|
|
404
|
-
acc.frames += 1;
|
|
405
|
-
if (acc.lastSampleAt === 0) acc.lastSampleAt = tick.now;
|
|
406
|
-
const dt = tick.now - acc.lastSampleAt;
|
|
407
|
-
if (dt >= 500) {
|
|
408
|
-
acc.fps = Math.round(acc.frames * 1e3 / dt);
|
|
409
|
-
acc.frames = 0;
|
|
410
|
-
acc.lastSampleAt = tick.now;
|
|
411
|
-
}
|
|
412
|
-
setStats({ fps: acc.fps, ticks: ticksRef.current, frames: acc.frames });
|
|
413
|
-
};
|
|
414
|
-
ctx.scheduler.add(client);
|
|
415
|
-
return () => ctx.scheduler.remove(client);
|
|
416
|
-
}, [ctx]);
|
|
417
|
-
if (!ctx) {
|
|
418
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: "no scene" });
|
|
419
|
-
}
|
|
420
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: [
|
|
421
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { "data-testid": "matter-monitor-fps", children: [
|
|
422
|
-
"fps: ",
|
|
423
|
-
stats.fps || "\u2014"
|
|
424
|
-
] }),
|
|
425
|
-
"\n",
|
|
426
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { "data-testid": "matter-monitor-ticks", children: [
|
|
427
|
-
"ticks: ",
|
|
428
|
-
stats.ticks
|
|
429
|
-
] })
|
|
430
|
-
] });
|
|
431
|
-
}
|
|
432
476
|
// Annotate the CommonJS export names for ESM import in node:
|
|
433
477
|
0 && (module.exports = {
|
|
434
478
|
FallbackBoundary,
|
|
435
|
-
|
|
436
|
-
|
|
479
|
+
ShaderMonitor,
|
|
480
|
+
ShaderScene,
|
|
437
481
|
useAnimatableUniform,
|
|
438
482
|
useCursor,
|
|
439
|
-
|
|
483
|
+
useOverlayPass,
|
|
440
484
|
useResize,
|
|
441
485
|
useScroll,
|
|
486
|
+
useShaderContext,
|
|
442
487
|
useShaderMaterial,
|
|
443
488
|
useStaticHint
|
|
444
489
|
});
|