@particle-academy/fancy-3d-three 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.
- package/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/engine.cjs +80 -0
- package/dist/engine.cjs.map +1 -0
- package/dist/engine.d.cts +5 -0
- package/dist/engine.d.ts +5 -0
- package/dist/engine.js +58 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.cjs +788 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +283 -0
- package/dist/index.d.ts +283 -0
- package/dist/index.js +744 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +333 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +64 -0
- package/dist/react.d.ts +64 -0
- package/dist/react.js +309 -0
- package/dist/react.js.map +1 -0
- package/package.json +94 -0
package/dist/react.d.cts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, CSSProperties } from 'react';
|
|
3
|
+
import * as THREE from 'three';
|
|
4
|
+
|
|
5
|
+
interface StageContextValue {
|
|
6
|
+
renderer: THREE.WebGLRenderer;
|
|
7
|
+
scene: THREE.Scene;
|
|
8
|
+
camera: THREE.PerspectiveCamera;
|
|
9
|
+
/** The HTML <canvas> the renderer draws into — its rect anchors overlays. */
|
|
10
|
+
canvas: HTMLCanvasElement;
|
|
11
|
+
/** Subscribe to per-frame ticks (after matrices update, before render). */
|
|
12
|
+
onFrame(cb: () => void): () => void;
|
|
13
|
+
/** The DOM container that screen overlays mount into. */
|
|
14
|
+
overlayRoot: HTMLDivElement;
|
|
15
|
+
}
|
|
16
|
+
declare function useStage(): StageContextValue;
|
|
17
|
+
interface StageProps {
|
|
18
|
+
children?: ReactNode;
|
|
19
|
+
/** Initial camera radius. Defaults to 10. */
|
|
20
|
+
cameraRadius?: number;
|
|
21
|
+
/** Initial camera target. Defaults to (0, 1.5, 0). */
|
|
22
|
+
cameraTarget?: [number, number, number];
|
|
23
|
+
/** Initial horizontal angle (radians). Defaults to π/2. */
|
|
24
|
+
cameraAlpha?: number;
|
|
25
|
+
/** Initial vertical angle from +Y (radians). Defaults to π/2.4. */
|
|
26
|
+
cameraBeta?: number;
|
|
27
|
+
/** Background clear color. Defaults to a near-black. */
|
|
28
|
+
clearColor?: string;
|
|
29
|
+
/** Disable the default lights / camera if you want to set your own. */
|
|
30
|
+
bare?: boolean;
|
|
31
|
+
/** Callback after the scene is created — set up custom cameras, lights, meshes. */
|
|
32
|
+
onReady?: (scene: THREE.Scene, ctx: {
|
|
33
|
+
camera: THREE.PerspectiveCamera;
|
|
34
|
+
renderer: THREE.WebGLRenderer;
|
|
35
|
+
}) => void;
|
|
36
|
+
className?: string;
|
|
37
|
+
style?: CSSProperties;
|
|
38
|
+
}
|
|
39
|
+
declare function Stage({ children, cameraRadius, cameraTarget, cameraAlpha, cameraBeta, clearColor, bare, onReady, className, style, }: StageProps): react.JSX.Element;
|
|
40
|
+
interface MonitorProps {
|
|
41
|
+
children?: ReactNode;
|
|
42
|
+
/** World-space center of the screen. */
|
|
43
|
+
position: [number, number, number];
|
|
44
|
+
/** Mesh dimensions in world units. */
|
|
45
|
+
width: number;
|
|
46
|
+
height: number;
|
|
47
|
+
/** Y-axis rotation in radians. */
|
|
48
|
+
rotationY?: number;
|
|
49
|
+
/** Bezel color. Defaults to a near-black frame. */
|
|
50
|
+
bezel?: string;
|
|
51
|
+
/** Bezel thickness in world units. Defaults to 0.06. */
|
|
52
|
+
bezelThickness?: number;
|
|
53
|
+
/** Background color visible behind the React content. */
|
|
54
|
+
background?: string;
|
|
55
|
+
/** Design-time pixel width for the children. Defaults to `width * 360`. */
|
|
56
|
+
pixelWidth?: number;
|
|
57
|
+
/** Design-time pixel height for the children. Defaults to `height * 360`. */
|
|
58
|
+
pixelHeight?: number;
|
|
59
|
+
/** Optional name on the mesh. */
|
|
60
|
+
name?: string;
|
|
61
|
+
}
|
|
62
|
+
declare function Monitor({ children, position, width, height, rotationY, bezel, bezelThickness, background, pixelWidth, pixelHeight, name, }: MonitorProps): react.JSX.Element;
|
|
63
|
+
|
|
64
|
+
export { Monitor, type MonitorProps, Stage, type StageProps, useStage };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, CSSProperties } from 'react';
|
|
3
|
+
import * as THREE from 'three';
|
|
4
|
+
|
|
5
|
+
interface StageContextValue {
|
|
6
|
+
renderer: THREE.WebGLRenderer;
|
|
7
|
+
scene: THREE.Scene;
|
|
8
|
+
camera: THREE.PerspectiveCamera;
|
|
9
|
+
/** The HTML <canvas> the renderer draws into — its rect anchors overlays. */
|
|
10
|
+
canvas: HTMLCanvasElement;
|
|
11
|
+
/** Subscribe to per-frame ticks (after matrices update, before render). */
|
|
12
|
+
onFrame(cb: () => void): () => void;
|
|
13
|
+
/** The DOM container that screen overlays mount into. */
|
|
14
|
+
overlayRoot: HTMLDivElement;
|
|
15
|
+
}
|
|
16
|
+
declare function useStage(): StageContextValue;
|
|
17
|
+
interface StageProps {
|
|
18
|
+
children?: ReactNode;
|
|
19
|
+
/** Initial camera radius. Defaults to 10. */
|
|
20
|
+
cameraRadius?: number;
|
|
21
|
+
/** Initial camera target. Defaults to (0, 1.5, 0). */
|
|
22
|
+
cameraTarget?: [number, number, number];
|
|
23
|
+
/** Initial horizontal angle (radians). Defaults to π/2. */
|
|
24
|
+
cameraAlpha?: number;
|
|
25
|
+
/** Initial vertical angle from +Y (radians). Defaults to π/2.4. */
|
|
26
|
+
cameraBeta?: number;
|
|
27
|
+
/** Background clear color. Defaults to a near-black. */
|
|
28
|
+
clearColor?: string;
|
|
29
|
+
/** Disable the default lights / camera if you want to set your own. */
|
|
30
|
+
bare?: boolean;
|
|
31
|
+
/** Callback after the scene is created — set up custom cameras, lights, meshes. */
|
|
32
|
+
onReady?: (scene: THREE.Scene, ctx: {
|
|
33
|
+
camera: THREE.PerspectiveCamera;
|
|
34
|
+
renderer: THREE.WebGLRenderer;
|
|
35
|
+
}) => void;
|
|
36
|
+
className?: string;
|
|
37
|
+
style?: CSSProperties;
|
|
38
|
+
}
|
|
39
|
+
declare function Stage({ children, cameraRadius, cameraTarget, cameraAlpha, cameraBeta, clearColor, bare, onReady, className, style, }: StageProps): react.JSX.Element;
|
|
40
|
+
interface MonitorProps {
|
|
41
|
+
children?: ReactNode;
|
|
42
|
+
/** World-space center of the screen. */
|
|
43
|
+
position: [number, number, number];
|
|
44
|
+
/** Mesh dimensions in world units. */
|
|
45
|
+
width: number;
|
|
46
|
+
height: number;
|
|
47
|
+
/** Y-axis rotation in radians. */
|
|
48
|
+
rotationY?: number;
|
|
49
|
+
/** Bezel color. Defaults to a near-black frame. */
|
|
50
|
+
bezel?: string;
|
|
51
|
+
/** Bezel thickness in world units. Defaults to 0.06. */
|
|
52
|
+
bezelThickness?: number;
|
|
53
|
+
/** Background color visible behind the React content. */
|
|
54
|
+
background?: string;
|
|
55
|
+
/** Design-time pixel width for the children. Defaults to `width * 360`. */
|
|
56
|
+
pixelWidth?: number;
|
|
57
|
+
/** Design-time pixel height for the children. Defaults to `height * 360`. */
|
|
58
|
+
pixelHeight?: number;
|
|
59
|
+
/** Optional name on the mesh. */
|
|
60
|
+
name?: string;
|
|
61
|
+
}
|
|
62
|
+
declare function Monitor({ children, position, width, height, rotationY, bezel, bezelThickness, background, pixelWidth, pixelHeight, name, }: MonitorProps): react.JSX.Element;
|
|
63
|
+
|
|
64
|
+
export { Monitor, type MonitorProps, Stage, type StageProps, useStage };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
3
|
+
import { createContext, useContext, useRef, useState, useEffect, useLayoutEffect } from 'react';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/react.tsx
|
|
7
|
+
function adj3(m) {
|
|
8
|
+
return [
|
|
9
|
+
m[4] * m[8] - m[5] * m[7],
|
|
10
|
+
m[2] * m[7] - m[1] * m[8],
|
|
11
|
+
m[1] * m[5] - m[2] * m[4],
|
|
12
|
+
m[5] * m[6] - m[3] * m[8],
|
|
13
|
+
m[0] * m[8] - m[2] * m[6],
|
|
14
|
+
m[2] * m[3] - m[0] * m[5],
|
|
15
|
+
m[3] * m[7] - m[4] * m[6],
|
|
16
|
+
m[1] * m[6] - m[0] * m[7],
|
|
17
|
+
m[0] * m[4] - m[1] * m[3]
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
function multmm(a, b) {
|
|
21
|
+
const c = new Array(9);
|
|
22
|
+
for (let i = 0; i < 3; i++) {
|
|
23
|
+
for (let j = 0; j < 3; j++) {
|
|
24
|
+
let cij = 0;
|
|
25
|
+
for (let k = 0; k < 3; k++) cij += a[3 * i + k] * b[3 * k + j];
|
|
26
|
+
c[3 * i + j] = cij;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return c;
|
|
30
|
+
}
|
|
31
|
+
function multmv(m, v) {
|
|
32
|
+
return [
|
|
33
|
+
m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
|
|
34
|
+
m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
|
|
35
|
+
m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
function basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4) {
|
|
39
|
+
const m = [x1, x2, x3, y1, y2, y3, 1, 1, 1];
|
|
40
|
+
const v = multmv(adj3(m), [x4, y4, 1]);
|
|
41
|
+
return multmm(m, [v[0], 0, 0, 0, v[1], 0, 0, 0, v[2]]);
|
|
42
|
+
}
|
|
43
|
+
function quadToQuad(w, h, x1, y1, x2, y2, x3, y3, x4, y4) {
|
|
44
|
+
const s = basisToPoints(0, 0, w, 0, w, h, 0, h);
|
|
45
|
+
const d = basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4);
|
|
46
|
+
const t = multmm(d, adj3(s));
|
|
47
|
+
const n = t[8];
|
|
48
|
+
for (let i = 0; i < 9; i++) t[i] /= n;
|
|
49
|
+
return t;
|
|
50
|
+
}
|
|
51
|
+
function matrix3dString(t) {
|
|
52
|
+
return `matrix3d(${t[0]}, ${t[3]}, 0, ${t[6]}, ${t[1]}, ${t[4]}, 0, ${t[7]}, 0, 0, 1, 0, ${t[2]}, ${t[5]}, 0, ${t[8]})`;
|
|
53
|
+
}
|
|
54
|
+
var StageContext = createContext(null);
|
|
55
|
+
function useStage() {
|
|
56
|
+
const ctx = useContext(StageContext);
|
|
57
|
+
if (!ctx) throw new Error("useStage() must be called inside <Stage>");
|
|
58
|
+
return ctx;
|
|
59
|
+
}
|
|
60
|
+
function Stage({
|
|
61
|
+
children,
|
|
62
|
+
cameraRadius = 10,
|
|
63
|
+
cameraTarget = [0, 1.5, 0],
|
|
64
|
+
cameraAlpha = Math.PI / 2,
|
|
65
|
+
cameraBeta = Math.PI / 2.4,
|
|
66
|
+
clearColor = "#06080f",
|
|
67
|
+
bare = false,
|
|
68
|
+
onReady,
|
|
69
|
+
className,
|
|
70
|
+
style
|
|
71
|
+
}) {
|
|
72
|
+
const containerRef = useRef(null);
|
|
73
|
+
const canvasRef = useRef(null);
|
|
74
|
+
const overlayRef = useRef(null);
|
|
75
|
+
const [stage, setStage] = useState(null);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const canvas = canvasRef.current;
|
|
78
|
+
const container = containerRef.current;
|
|
79
|
+
const overlay = overlayRef.current;
|
|
80
|
+
if (!canvas || !container || !overlay) return;
|
|
81
|
+
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true, preserveDrawingBuffer: true, stencil: true });
|
|
82
|
+
renderer.setPixelRatio(window.devicePixelRatio || 1);
|
|
83
|
+
renderer.setClearColor(new THREE.Color(clearColor), 1);
|
|
84
|
+
const scene = new THREE.Scene();
|
|
85
|
+
const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1e3);
|
|
86
|
+
const target = new THREE.Vector3(...cameraTarget);
|
|
87
|
+
const controls = new OrbitControls(camera, canvas);
|
|
88
|
+
controls.target.copy(target);
|
|
89
|
+
controls.enableDamping = true;
|
|
90
|
+
controls.dampingFactor = 0.08;
|
|
91
|
+
if (!bare) {
|
|
92
|
+
camera.position.set(
|
|
93
|
+
target.x + cameraRadius * Math.sin(cameraBeta) * Math.cos(cameraAlpha),
|
|
94
|
+
target.y + cameraRadius * Math.cos(cameraBeta),
|
|
95
|
+
target.z + cameraRadius * Math.sin(cameraBeta) * Math.sin(cameraAlpha)
|
|
96
|
+
);
|
|
97
|
+
controls.minDistance = 1;
|
|
98
|
+
controls.maxDistance = 80;
|
|
99
|
+
controls.maxPolarAngle = Math.PI / 2 - 0.05;
|
|
100
|
+
const hemi = new THREE.HemisphereLight(11781887, 1710638, 0.9);
|
|
101
|
+
scene.add(hemi);
|
|
102
|
+
const dir = new THREE.DirectionalLight(16777215, 0.8);
|
|
103
|
+
dir.position.set(-0.4, 1, 0.6).multiplyScalar(10);
|
|
104
|
+
scene.add(dir);
|
|
105
|
+
} else {
|
|
106
|
+
camera.position.set(0, 0, cameraRadius);
|
|
107
|
+
}
|
|
108
|
+
camera.lookAt(target);
|
|
109
|
+
controls.update();
|
|
110
|
+
onReady?.(scene, { camera, renderer });
|
|
111
|
+
const frameSubs = /* @__PURE__ */ new Set();
|
|
112
|
+
let raf = 0;
|
|
113
|
+
const loop = () => {
|
|
114
|
+
raf = requestAnimationFrame(loop);
|
|
115
|
+
controls.update();
|
|
116
|
+
camera.updateMatrixWorld();
|
|
117
|
+
camera.matrixWorldInverse.copy(camera.matrixWorld).invert();
|
|
118
|
+
scene.traverse((o) => {
|
|
119
|
+
if (o.userData && o.userData.faceCamera) o.lookAt(camera.position);
|
|
120
|
+
});
|
|
121
|
+
frameSubs.forEach((cb) => cb());
|
|
122
|
+
renderer.render(scene, camera);
|
|
123
|
+
};
|
|
124
|
+
const resize = () => {
|
|
125
|
+
const w = container.clientWidth || 1;
|
|
126
|
+
const h = container.clientHeight || 1;
|
|
127
|
+
renderer.setSize(w, h, false);
|
|
128
|
+
camera.aspect = w / h;
|
|
129
|
+
camera.updateProjectionMatrix();
|
|
130
|
+
};
|
|
131
|
+
resize();
|
|
132
|
+
const ro = new ResizeObserver(resize);
|
|
133
|
+
ro.observe(container);
|
|
134
|
+
window.addEventListener("resize", resize);
|
|
135
|
+
loop();
|
|
136
|
+
setStage({
|
|
137
|
+
renderer,
|
|
138
|
+
scene,
|
|
139
|
+
camera,
|
|
140
|
+
canvas,
|
|
141
|
+
overlayRoot: overlay,
|
|
142
|
+
onFrame(cb) {
|
|
143
|
+
frameSubs.add(cb);
|
|
144
|
+
return () => frameSubs.delete(cb);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return () => {
|
|
148
|
+
cancelAnimationFrame(raf);
|
|
149
|
+
window.removeEventListener("resize", resize);
|
|
150
|
+
ro.disconnect();
|
|
151
|
+
controls.dispose();
|
|
152
|
+
renderer.dispose();
|
|
153
|
+
setStage(null);
|
|
154
|
+
};
|
|
155
|
+
}, []);
|
|
156
|
+
return /* @__PURE__ */ jsxs(
|
|
157
|
+
"div",
|
|
158
|
+
{
|
|
159
|
+
ref: containerRef,
|
|
160
|
+
className,
|
|
161
|
+
style: { position: "relative", overflow: "hidden", ...style },
|
|
162
|
+
"data-fancy-3d-stage": "",
|
|
163
|
+
children: [
|
|
164
|
+
/* @__PURE__ */ jsx("canvas", { ref: canvasRef, style: { display: "block", width: "100%", height: "100%", outline: "none" }, tabIndex: 0 }),
|
|
165
|
+
/* @__PURE__ */ jsx("div", { ref: overlayRef, style: { position: "absolute", inset: 0, pointerEvents: "none" }, "data-fancy-3d-overlay-root": "" }),
|
|
166
|
+
stage && /* @__PURE__ */ jsx(StageContext.Provider, { value: stage, children })
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
function Monitor({
|
|
172
|
+
children,
|
|
173
|
+
position,
|
|
174
|
+
width,
|
|
175
|
+
height,
|
|
176
|
+
rotationY = 0,
|
|
177
|
+
bezel = "#0b0f17",
|
|
178
|
+
bezelThickness = 0.06,
|
|
179
|
+
background = "#0b1220",
|
|
180
|
+
pixelWidth,
|
|
181
|
+
pixelHeight,
|
|
182
|
+
name = "screen"
|
|
183
|
+
}) {
|
|
184
|
+
const stage = useStage();
|
|
185
|
+
const meshRef = useRef(null);
|
|
186
|
+
const overlayRef = useRef(null);
|
|
187
|
+
const innerSize = useRef({
|
|
188
|
+
w: pixelWidth ?? Math.max(120, Math.round(width * 360)),
|
|
189
|
+
h: pixelHeight ?? Math.max(80, Math.round(height * 360))
|
|
190
|
+
});
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
const { scene } = stage;
|
|
193
|
+
const bezelMesh = new THREE.Mesh(
|
|
194
|
+
new THREE.BoxGeometry(width + bezelThickness * 2, height + bezelThickness * 2, bezelThickness),
|
|
195
|
+
new THREE.MeshStandardMaterial({ color: new THREE.Color(bezel), roughness: 0.5, metalness: 0.1 })
|
|
196
|
+
);
|
|
197
|
+
bezelMesh.position.set(position[0], position[1], position[2]);
|
|
198
|
+
bezelMesh.rotation.y = rotationY;
|
|
199
|
+
scene.add(bezelMesh);
|
|
200
|
+
const pane = new THREE.Mesh(
|
|
201
|
+
new THREE.PlaneGeometry(width, height),
|
|
202
|
+
new THREE.MeshBasicMaterial({ color: new THREE.Color(background), side: THREE.DoubleSide, toneMapped: false })
|
|
203
|
+
);
|
|
204
|
+
pane.rotation.y = rotationY;
|
|
205
|
+
const forward = new THREE.Vector3(0, 0, 1).applyAxisAngle(new THREE.Vector3(0, 1, 0), rotationY).multiplyScalar(bezelThickness / 2 + 1e-3);
|
|
206
|
+
pane.position.set(position[0] + forward.x, position[1] + forward.y, position[2] + forward.z);
|
|
207
|
+
pane.userData = { fancy3d: "screen", screenName: name };
|
|
208
|
+
scene.add(pane);
|
|
209
|
+
meshRef.current = pane;
|
|
210
|
+
return () => {
|
|
211
|
+
scene.remove(pane);
|
|
212
|
+
scene.remove(bezelMesh);
|
|
213
|
+
pane.geometry.dispose();
|
|
214
|
+
pane.material.dispose();
|
|
215
|
+
bezelMesh.geometry.dispose();
|
|
216
|
+
bezelMesh.material.dispose();
|
|
217
|
+
meshRef.current = null;
|
|
218
|
+
};
|
|
219
|
+
}, [stage, name, width, height, bezel, bezelThickness, background, position, rotationY]);
|
|
220
|
+
useEffect(() => {
|
|
221
|
+
const unsub = stage.onFrame(() => {
|
|
222
|
+
const mesh = meshRef.current;
|
|
223
|
+
const overlay = overlayRef.current;
|
|
224
|
+
if (!mesh || !overlay) return;
|
|
225
|
+
const { camera, canvas } = stage;
|
|
226
|
+
mesh.updateWorldMatrix(true, false);
|
|
227
|
+
const cw = canvas.clientWidth;
|
|
228
|
+
const ch = canvas.clientHeight;
|
|
229
|
+
const halfW = width / 2;
|
|
230
|
+
const halfH = height / 2;
|
|
231
|
+
const local = [
|
|
232
|
+
new THREE.Vector3(-halfW, halfH, 0),
|
|
233
|
+
new THREE.Vector3(halfW, halfH, 0),
|
|
234
|
+
new THREE.Vector3(halfW, -halfH, 0),
|
|
235
|
+
new THREE.Vector3(-halfW, -halfH, 0)
|
|
236
|
+
];
|
|
237
|
+
const world = local.map((v) => v.applyMatrix4(mesh.matrixWorld));
|
|
238
|
+
const inFront = world.every((c) => c.clone().applyMatrix4(camera.matrixWorldInverse).z < -1e-3);
|
|
239
|
+
const right = world[1].clone().sub(world[0]);
|
|
240
|
+
const up = world[0].clone().sub(world[3]);
|
|
241
|
+
const normal = new THREE.Vector3().crossVectors(up, right).normalize();
|
|
242
|
+
const center = world[0].clone().add(world[2]).multiplyScalar(0.5);
|
|
243
|
+
const toCam = camera.position.clone().sub(center).normalize();
|
|
244
|
+
const facing = Math.abs(normal.dot(toCam));
|
|
245
|
+
if (!inFront || facing <= 0.05) {
|
|
246
|
+
if (overlay.style.display !== "none") overlay.style.display = "none";
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const projected = world.map((c) => {
|
|
250
|
+
const ndc = c.clone().project(camera);
|
|
251
|
+
return { x: (ndc.x * 0.5 + 0.5) * cw, y: (-ndc.y * 0.5 + 0.5) * ch, z: ndc.z };
|
|
252
|
+
});
|
|
253
|
+
const avgZ = (projected[0].z + projected[1].z + projected[2].z + projected[3].z) / 4;
|
|
254
|
+
const depth01 = Math.max(0, Math.min(1, avgZ * 0.5 + 0.5));
|
|
255
|
+
const zStr = String(Math.round((1 - depth01) * 1e5));
|
|
256
|
+
if (overlay.style.zIndex !== zStr) overlay.style.zIndex = zStr;
|
|
257
|
+
const w0 = innerSize.current.w;
|
|
258
|
+
const h0 = innerSize.current.h;
|
|
259
|
+
const t = quadToQuad(
|
|
260
|
+
w0,
|
|
261
|
+
h0,
|
|
262
|
+
projected[0].x,
|
|
263
|
+
projected[0].y,
|
|
264
|
+
projected[1].x,
|
|
265
|
+
projected[1].y,
|
|
266
|
+
projected[2].x,
|
|
267
|
+
projected[2].y,
|
|
268
|
+
projected[3].x,
|
|
269
|
+
projected[3].y
|
|
270
|
+
);
|
|
271
|
+
if (overlay.style.display !== "block") overlay.style.display = "block";
|
|
272
|
+
overlay.style.transform = matrix3dString(t);
|
|
273
|
+
});
|
|
274
|
+
return unsub;
|
|
275
|
+
}, [stage, width, height]);
|
|
276
|
+
useLayoutEffect(() => {
|
|
277
|
+
const el = overlayRef.current;
|
|
278
|
+
if (!el) return;
|
|
279
|
+
if (el.parentElement !== stage.overlayRoot) stage.overlayRoot.appendChild(el);
|
|
280
|
+
return () => {
|
|
281
|
+
if (el.parentElement === stage.overlayRoot) stage.overlayRoot.removeChild(el);
|
|
282
|
+
};
|
|
283
|
+
}, [stage]);
|
|
284
|
+
return /* @__PURE__ */ jsx(
|
|
285
|
+
"div",
|
|
286
|
+
{
|
|
287
|
+
ref: overlayRef,
|
|
288
|
+
"data-fancy-3d-screen": name,
|
|
289
|
+
style: {
|
|
290
|
+
position: "absolute",
|
|
291
|
+
left: 0,
|
|
292
|
+
top: 0,
|
|
293
|
+
width: innerSize.current.w,
|
|
294
|
+
height: innerSize.current.h,
|
|
295
|
+
transformOrigin: "0 0",
|
|
296
|
+
pointerEvents: "auto",
|
|
297
|
+
overflow: "hidden",
|
|
298
|
+
background,
|
|
299
|
+
borderRadius: 4,
|
|
300
|
+
display: "none"
|
|
301
|
+
},
|
|
302
|
+
children
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export { Monitor, Stage, useStage };
|
|
308
|
+
//# sourceMappingURL=react.js.map
|
|
309
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx"],"names":[],"mappings":"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","file":"react.js","sourcesContent":["/**\n * `<Stage>` and `<Monitor>` — declarative React API for fancy-3d (three.js).\n *\n * <Stage>\n * <Monitor position={[0, 2, 0]} width={4} height={2.5}>\n * <Card>...real react-fancy components...</Card>\n * </Monitor>\n * </Stage>\n *\n * `<Stage>` owns the three renderer + scene + camera (OrbitControls) + render\n * loop and exposes them via context. `<Monitor>` builds a bezel mesh, projects\n * its world-space corners to screen space each frame, and renders its children\n * into a positioned DOM overlay — real, interactive React, not a rasterized\n * snapshot. Mirrors `@particle-academy/fancy-3d-babylon`'s React API.\n */\nimport * as THREE from \"three\";\nimport { OrbitControls } from \"three/examples/jsm/controls/OrbitControls.js\";\nimport {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\n\n/* ------------------------------------------------------------------ */\n/* 2D perspective transform — maps a unit rectangle to a 4-point quad */\n/* via a 3x3 homography, packed into a CSS matrix3d for transform. */\n/* ------------------------------------------------------------------ */\n\ntype Mat3 = [number, number, number, number, number, number, number, number, number];\n\nfunction adj3(m: Mat3): Mat3 {\n return [\n m[4] * m[8] - m[5] * m[7], m[2] * m[7] - m[1] * m[8], m[1] * m[5] - m[2] * m[4],\n m[5] * m[6] - m[3] * m[8], m[0] * m[8] - m[2] * m[6], m[2] * m[3] - m[0] * m[5],\n m[3] * m[7] - m[4] * m[6], m[1] * m[6] - m[0] * m[7], m[0] * m[4] - m[1] * m[3],\n ];\n}\n\nfunction multmm(a: Mat3, b: Mat3): Mat3 {\n const c: number[] = new Array(9);\n for (let i = 0; i < 3; i++) {\n for (let j = 0; j < 3; j++) {\n let cij = 0;\n for (let k = 0; k < 3; k++) cij += a[3 * i + k] * b[3 * k + j];\n c[3 * i + j] = cij;\n }\n }\n return c as Mat3;\n}\n\nfunction multmv(m: Mat3, v: [number, number, number]): [number, number, number] {\n return [\n m[0] * v[0] + m[1] * v[1] + m[2] * v[2],\n m[3] * v[0] + m[4] * v[1] + m[5] * v[2],\n m[6] * v[0] + m[7] * v[1] + m[8] * v[2],\n ];\n}\n\nfunction basisToPoints(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number): Mat3 {\n const m: Mat3 = [x1, x2, x3, y1, y2, y3, 1, 1, 1];\n const v = multmv(adj3(m), [x4, y4, 1]);\n return multmm(m, [v[0], 0, 0, 0, v[1], 0, 0, 0, v[2]]);\n}\n\nfunction quadToQuad(\n w: number, h: number,\n x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number,\n): Mat3 {\n const s = basisToPoints(0, 0, w, 0, w, h, 0, h);\n const d = basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4);\n const t = multmm(d, adj3(s));\n const n = t[8];\n for (let i = 0; i < 9; i++) t[i] /= n;\n return t;\n}\n\nfunction matrix3dString(t: Mat3): string {\n return `matrix3d(${t[0]}, ${t[3]}, 0, ${t[6]}, ${t[1]}, ${t[4]}, 0, ${t[7]}, 0, 0, 1, 0, ${t[2]}, ${t[5]}, 0, ${t[8]})`;\n}\n\n/* ------------------------------------------------------------------ */\n/* Stage context */\n/* ------------------------------------------------------------------ */\n\ninterface StageContextValue {\n renderer: THREE.WebGLRenderer;\n scene: THREE.Scene;\n camera: THREE.PerspectiveCamera;\n /** The HTML <canvas> the renderer draws into — its rect anchors overlays. */\n canvas: HTMLCanvasElement;\n /** Subscribe to per-frame ticks (after matrices update, before render). */\n onFrame(cb: () => void): () => void;\n /** The DOM container that screen overlays mount into. */\n overlayRoot: HTMLDivElement;\n}\n\nconst StageContext = createContext<StageContextValue | null>(null);\n\nexport function useStage(): StageContextValue {\n const ctx = useContext(StageContext);\n if (!ctx) throw new Error(\"useStage() must be called inside <Stage>\");\n return ctx;\n}\n\n/* ------------------------------------------------------------------ */\n/* Stage */\n/* ------------------------------------------------------------------ */\n\nexport interface StageProps {\n children?: ReactNode;\n /** Initial camera radius. Defaults to 10. */\n cameraRadius?: number;\n /** Initial camera target. Defaults to (0, 1.5, 0). */\n cameraTarget?: [number, number, number];\n /** Initial horizontal angle (radians). Defaults to π/2. */\n cameraAlpha?: number;\n /** Initial vertical angle from +Y (radians). Defaults to π/2.4. */\n cameraBeta?: number;\n /** Background clear color. Defaults to a near-black. */\n clearColor?: string;\n /** Disable the default lights / camera if you want to set your own. */\n bare?: boolean;\n /** Callback after the scene is created — set up custom cameras, lights, meshes. */\n onReady?: (scene: THREE.Scene, ctx: { camera: THREE.PerspectiveCamera; renderer: THREE.WebGLRenderer }) => void;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function Stage({\n children,\n cameraRadius = 10,\n cameraTarget = [0, 1.5, 0],\n cameraAlpha = Math.PI / 2,\n cameraBeta = Math.PI / 2.4,\n clearColor = \"#06080f\",\n bare = false,\n onReady,\n className,\n style,\n}: StageProps) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const overlayRef = useRef<HTMLDivElement | null>(null);\n const [stage, setStage] = useState<StageContextValue | null>(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n const container = containerRef.current;\n const overlay = overlayRef.current;\n if (!canvas || !container || !overlay) return;\n\n const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true, preserveDrawingBuffer: true, stencil: true });\n renderer.setPixelRatio(window.devicePixelRatio || 1);\n renderer.setClearColor(new THREE.Color(clearColor), 1);\n\n const scene = new THREE.Scene();\n const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000);\n const target = new THREE.Vector3(...cameraTarget);\n\n const controls = new OrbitControls(camera, canvas);\n controls.target.copy(target);\n controls.enableDamping = true;\n controls.dampingFactor = 0.08;\n\n if (!bare) {\n // Spherical → cartesian (Babylon ArcRotateCamera alpha/beta convention).\n camera.position.set(\n target.x + cameraRadius * Math.sin(cameraBeta) * Math.cos(cameraAlpha),\n target.y + cameraRadius * Math.cos(cameraBeta),\n target.z + cameraRadius * Math.sin(cameraBeta) * Math.sin(cameraAlpha),\n );\n controls.minDistance = 1;\n controls.maxDistance = 80;\n controls.maxPolarAngle = Math.PI / 2 - 0.05; // don't dip below the horizon\n\n const hemi = new THREE.HemisphereLight(0xb3c6ff, 0x1a1a2e, 0.9);\n scene.add(hemi);\n const dir = new THREE.DirectionalLight(0xffffff, 0.8);\n dir.position.set(-0.4, 1, 0.6).multiplyScalar(10);\n scene.add(dir);\n } else {\n camera.position.set(0, 0, cameraRadius);\n }\n camera.lookAt(target);\n controls.update();\n\n onReady?.(scene, { camera, renderer });\n\n const frameSubs = new Set<() => void>();\n let raf = 0;\n const loop = () => {\n raf = requestAnimationFrame(loop);\n controls.update();\n camera.updateMatrixWorld();\n camera.matrixWorldInverse.copy(camera.matrixWorld).invert();\n scene.traverse((o) => {\n if (o.userData && o.userData.faceCamera) o.lookAt(camera.position);\n });\n frameSubs.forEach((cb) => cb());\n renderer.render(scene, camera);\n };\n\n const resize = () => {\n const w = container.clientWidth || 1;\n const h = container.clientHeight || 1;\n renderer.setSize(w, h, false);\n camera.aspect = w / h;\n camera.updateProjectionMatrix();\n };\n resize();\n const ro = new ResizeObserver(resize);\n ro.observe(container);\n window.addEventListener(\"resize\", resize);\n loop();\n\n setStage({\n renderer,\n scene,\n camera,\n canvas,\n overlayRoot: overlay,\n onFrame(cb) {\n frameSubs.add(cb);\n return () => frameSubs.delete(cb);\n },\n });\n\n return () => {\n cancelAnimationFrame(raf);\n window.removeEventListener(\"resize\", resize);\n ro.disconnect();\n controls.dispose();\n renderer.dispose();\n setStage(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ position: \"relative\", overflow: \"hidden\", ...style }}\n data-fancy-3d-stage=\"\"\n >\n <canvas ref={canvasRef} style={{ display: \"block\", width: \"100%\", height: \"100%\", outline: \"none\" }} tabIndex={0} />\n <div ref={overlayRef} style={{ position: \"absolute\", inset: 0, pointerEvents: \"none\" }} data-fancy-3d-overlay-root=\"\" />\n {stage && <StageContext.Provider value={stage}>{children}</StageContext.Provider>}\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Monitor — interactive mount point on a 3D mesh */\n/* ------------------------------------------------------------------ */\n\nexport interface MonitorProps {\n children?: ReactNode;\n /** World-space center of the screen. */\n position: [number, number, number];\n /** Mesh dimensions in world units. */\n width: number;\n height: number;\n /** Y-axis rotation in radians. */\n rotationY?: number;\n /** Bezel color. Defaults to a near-black frame. */\n bezel?: string;\n /** Bezel thickness in world units. Defaults to 0.06. */\n bezelThickness?: number;\n /** Background color visible behind the React content. */\n background?: string;\n /** Design-time pixel width for the children. Defaults to `width * 360`. */\n pixelWidth?: number;\n /** Design-time pixel height for the children. Defaults to `height * 360`. */\n pixelHeight?: number;\n /** Optional name on the mesh. */\n name?: string;\n}\n\nexport function Monitor({\n children,\n position,\n width,\n height,\n rotationY = 0,\n bezel = \"#0b0f17\",\n bezelThickness = 0.06,\n background = \"#0b1220\",\n pixelWidth,\n pixelHeight,\n name = \"screen\",\n}: MonitorProps) {\n const stage = useStage();\n const meshRef = useRef<THREE.Mesh | null>(null);\n const overlayRef = useRef<HTMLDivElement | null>(null);\n const innerSize = useRef({\n w: pixelWidth ?? Math.max(120, Math.round(width * 360)),\n h: pixelHeight ?? Math.max(80, Math.round(height * 360)),\n });\n\n // Build the bezel + projection-target pane.\n useEffect(() => {\n const { scene } = stage;\n const bezelMesh = new THREE.Mesh(\n new THREE.BoxGeometry(width + bezelThickness * 2, height + bezelThickness * 2, bezelThickness),\n new THREE.MeshStandardMaterial({ color: new THREE.Color(bezel), roughness: 0.5, metalness: 0.1 }),\n );\n bezelMesh.position.set(position[0], position[1], position[2]);\n bezelMesh.rotation.y = rotationY;\n scene.add(bezelMesh);\n\n const pane = new THREE.Mesh(\n new THREE.PlaneGeometry(width, height),\n new THREE.MeshBasicMaterial({ color: new THREE.Color(background), side: THREE.DoubleSide, toneMapped: false }),\n );\n pane.rotation.y = rotationY;\n // Offset the pane forward of the bezel's +Z front so the HTML overlay lines\n // up with the visible face.\n const forward = new THREE.Vector3(0, 0, 1).applyAxisAngle(new THREE.Vector3(0, 1, 0), rotationY).multiplyScalar(bezelThickness / 2 + 0.001);\n pane.position.set(position[0] + forward.x, position[1] + forward.y, position[2] + forward.z);\n pane.userData = { fancy3d: \"screen\", screenName: name };\n scene.add(pane);\n meshRef.current = pane;\n\n return () => {\n scene.remove(pane);\n scene.remove(bezelMesh);\n pane.geometry.dispose();\n (pane.material as THREE.Material).dispose();\n bezelMesh.geometry.dispose();\n (bezelMesh.material as THREE.Material).dispose();\n meshRef.current = null;\n };\n }, [stage, name, width, height, bezel, bezelThickness, background, position, rotationY]);\n\n // Each frame: project the four corners and apply the matrix3d transform.\n useEffect(() => {\n const unsub = stage.onFrame(() => {\n const mesh = meshRef.current;\n const overlay = overlayRef.current;\n if (!mesh || !overlay) return;\n const { camera, canvas } = stage;\n mesh.updateWorldMatrix(true, false);\n\n const cw = canvas.clientWidth;\n const ch = canvas.clientHeight;\n const halfW = width / 2;\n const halfH = height / 2;\n // Mesh-local corners: top-left, top-right, bottom-right, bottom-left.\n const local = [\n new THREE.Vector3(-halfW, halfH, 0),\n new THREE.Vector3(halfW, halfH, 0),\n new THREE.Vector3(halfW, -halfH, 0),\n new THREE.Vector3(-halfW, -halfH, 0),\n ];\n const world = local.map((v) => v.applyMatrix4(mesh.matrixWorld));\n\n // Behind-camera cull (view-space z >= 0 means behind, since the camera\n // looks down -Z).\n const inFront = world.every((c) => c.clone().applyMatrix4(camera.matrixWorldInverse).z < -0.001);\n // Edge-on cull — hide when nearly perpendicular (projected quad collapses).\n const right = world[1].clone().sub(world[0]);\n const up = world[0].clone().sub(world[3]);\n const normal = new THREE.Vector3().crossVectors(up, right).normalize();\n const center = world[0].clone().add(world[2]).multiplyScalar(0.5);\n const toCam = camera.position.clone().sub(center).normalize();\n const facing = Math.abs(normal.dot(toCam));\n\n if (!inFront || facing <= 0.05) {\n if (overlay.style.display !== \"none\") overlay.style.display = \"none\";\n return;\n }\n\n const projected = world.map((c) => {\n const ndc = c.clone().project(camera);\n return { x: (ndc.x * 0.5 + 0.5) * cw, y: (-ndc.y * 0.5 + 0.5) * ch, z: ndc.z };\n });\n\n // Depth-sort overlays: closer-to-camera (smaller NDC z) stacks above.\n const avgZ = (projected[0].z + projected[1].z + projected[2].z + projected[3].z) / 4;\n const depth01 = Math.max(0, Math.min(1, avgZ * 0.5 + 0.5));\n const zStr = String(Math.round((1 - depth01) * 100000));\n if (overlay.style.zIndex !== zStr) overlay.style.zIndex = zStr;\n\n const w0 = innerSize.current.w;\n const h0 = innerSize.current.h;\n const t = quadToQuad(\n w0, h0,\n projected[0].x, projected[0].y,\n projected[1].x, projected[1].y,\n projected[2].x, projected[2].y,\n projected[3].x, projected[3].y,\n );\n\n if (overlay.style.display !== \"block\") overlay.style.display = \"block\";\n overlay.style.transform = matrix3dString(t);\n });\n return unsub;\n }, [stage, width, height]);\n\n // Mount the overlay into the stage's overlay root.\n useLayoutEffect(() => {\n const el = overlayRef.current;\n if (!el) return;\n if (el.parentElement !== stage.overlayRoot) stage.overlayRoot.appendChild(el);\n return () => {\n if (el.parentElement === stage.overlayRoot) stage.overlayRoot.removeChild(el);\n };\n }, [stage]);\n\n return (\n <div\n ref={overlayRef}\n data-fancy-3d-screen={name}\n style={{\n position: \"absolute\",\n left: 0,\n top: 0,\n width: innerSize.current.w,\n height: innerSize.current.h,\n transformOrigin: \"0 0\",\n pointerEvents: \"auto\",\n overflow: \"hidden\",\n background,\n borderRadius: 4,\n display: \"none\",\n }}\n >\n {children}\n </div>\n );\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@particle-academy/fancy-3d-three",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "three.js adapter for @particle-academy/fancy-3d — WebGL renderer, primitives, React components (Stage, Monitor, Card3D, Screen) that mount onto a three.js Scene.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/Particle-Academy/fancy-3d-three.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/Particle-Academy/fancy-3d-three#readme",
|
|
10
|
+
"bugs": "https://github.com/Particle-Academy/fancy-3d-three/issues",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.cjs",
|
|
13
|
+
"module": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"require": {
|
|
22
|
+
"types": "./dist/index.d.cts",
|
|
23
|
+
"default": "./dist/index.cjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"./react": {
|
|
27
|
+
"import": {
|
|
28
|
+
"types": "./dist/react.d.ts",
|
|
29
|
+
"default": "./dist/react.js"
|
|
30
|
+
},
|
|
31
|
+
"require": {
|
|
32
|
+
"types": "./dist/react.d.cts",
|
|
33
|
+
"default": "./dist/react.cjs"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"./engine": {
|
|
37
|
+
"import": {
|
|
38
|
+
"types": "./dist/engine.d.ts",
|
|
39
|
+
"default": "./dist/engine.js"
|
|
40
|
+
},
|
|
41
|
+
"require": {
|
|
42
|
+
"types": "./dist/engine.d.cts",
|
|
43
|
+
"default": "./dist/engine.cjs"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"README.md"
|
|
50
|
+
],
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"dev": "tsup --watch",
|
|
54
|
+
"lint": "tsc --noEmit",
|
|
55
|
+
"clean": "rm -rf dist",
|
|
56
|
+
"prepublishOnly": "tsup"
|
|
57
|
+
},
|
|
58
|
+
"keywords": [
|
|
59
|
+
"react",
|
|
60
|
+
"3d",
|
|
61
|
+
"three",
|
|
62
|
+
"threejs",
|
|
63
|
+
"webgl",
|
|
64
|
+
"fancy-3d"
|
|
65
|
+
],
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"@particle-academy/fancy-3d": "^0.4.0",
|
|
68
|
+
"@particle-academy/react-fancy": ">=3",
|
|
69
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
70
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
71
|
+
"three": ">=0.160.0"
|
|
72
|
+
},
|
|
73
|
+
"peerDependenciesMeta": {
|
|
74
|
+
"@particle-academy/react-fancy": {
|
|
75
|
+
"optional": true
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@particle-academy/fancy-3d": "^0.4.0",
|
|
80
|
+
"@particle-academy/react-fancy": "^3.0.0",
|
|
81
|
+
"@types/react": "^19.0.0",
|
|
82
|
+
"@types/react-dom": "^19.0.0",
|
|
83
|
+
"@types/three": "^0.184.1",
|
|
84
|
+
"react": "^19.0.0",
|
|
85
|
+
"react-dom": "^19.0.0",
|
|
86
|
+
"three": "^0.180.0",
|
|
87
|
+
"tsup": "^8.5.0",
|
|
88
|
+
"typescript": "^5.8.0"
|
|
89
|
+
},
|
|
90
|
+
"publishConfig": {
|
|
91
|
+
"access": "public"
|
|
92
|
+
},
|
|
93
|
+
"license": "MIT"
|
|
94
|
+
}
|