softbox 4.0.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 +270 -0
- package/dist/ThreePathTracingService-Bv1CmdCK.cjs +3977 -0
- package/dist/ThreePathTracingService-CRv4tDno.js +6287 -0
- package/dist/index-1KtcaoH6.js +11920 -0
- package/dist/index-C4Chx9Wl.cjs +11 -0
- package/dist/index.d.cts +621 -0
- package/dist/index.d.ts +621 -0
- package/dist/simple-viewer.cjs +1 -0
- package/dist/simple-viewer.es.js +12 -0
- package/package.json +102 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 LEMing
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# softbox
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/softbox)
|
|
4
|
+
[](https://bundlephobia.com/package/softbox)
|
|
5
|
+
[](https://github.com/LEMing/softbox/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/LEMing/softbox/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
**The batteries-included React 3D viewer — any GLB looks studio-shot in one line.**
|
|
9
|
+
|
|
10
|
+
> Formerly published as [`threedviewer`](https://www.npmjs.com/package/threedviewer) — renamed in 4.0.0, same API. See [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md).
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
<SimpleViewer object="/model.glb" />
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Balanced studio lighting, a glossy glass floor with a contact shadow, auto-framing, compressed-asset decoders — all on by default, zero configuration, zero external CDN requests. The core is ~105 kB gzip; the path tracer and the decoders live in lazy chunks that load only when actually used. Every release is gated by 470+ unit tests plus a Playwright suite that asserts **real WebGL pixels** in CI.
|
|
17
|
+
|
|
18
|
+
[](https://leming.github.io/softbox/)
|
|
19
|
+
|
|
20
|
+
**▶ [Live playground](https://leming.github.io/softbox/)** — drag & drop your own `.glb`, switch presets live, click the model to pin hotspots, download a still.
|
|
21
|
+
|
|
22
|
+
## What you get
|
|
23
|
+
|
|
24
|
+
- **[Visual presets](#visual-presets)** — `studio · product · neutral · dark · outdoor`, one word, switched live
|
|
25
|
+
- **[Turntable](#turntable)** — one-word showcase auto-rotate that pauses while the user drags
|
|
26
|
+
- **[Animations](#animations)** — GLTF clips autoplay with one word; play/pause/clip-picker API
|
|
27
|
+
- **[Photoreal mode](#photoreal-mode--stills)** — progressive path tracing + `captureStill()` PNG export
|
|
28
|
+
- **[Video capture](#video-capture)** — `captureVideo()` records the canvas to WebM/MP4, zero dependencies
|
|
29
|
+
- **[Hotspots & click selection](#hotspot-annotations--click-selection)** — DOM pins on world-space points, BVH-accelerated picking
|
|
30
|
+
- **Compressed assets** — DRACO, KTX2/Basis and Meshopt decoders wired in, fetched lazily
|
|
31
|
+
- **Built-in chrome** — loading overlay, preset picker, viewport gizmo — all optional, all off-or-subtle by default
|
|
32
|
+
- **Typed end to end** — every option, event payload and handle method ships TypeScript types
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install softbox
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Peer dependencies: `react` / `react-dom` `>=18 <20`, `three` `>=0.177 <0.186`.
|
|
41
|
+
|
|
42
|
+
## Quick start
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { SimpleViewer } from 'softbox';
|
|
46
|
+
|
|
47
|
+
function App() {
|
|
48
|
+
return (
|
|
49
|
+
<div style={{ width: '100%', height: '400px' }}>
|
|
50
|
+
<SimpleViewer object="https://modelviewer.dev/shared-assets/models/RobotExpressive.glb" />
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
A `THREE.Object3D` works too:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<SimpleViewer object={new THREE.Mesh(geometry, material)} />
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Visual presets
|
|
63
|
+
|
|
64
|
+
One word sets a cohesive look — background, tone mapping, environment intensity. Presets switch **live**: no rebuild, no model reload.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
<SimpleViewer object={url} preset="product" />
|
|
68
|
+
// studio | product | neutral | dark | outdoor
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Let your users switch presets with the built-in picker (off by default):
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<SimpleViewer object={url} options={{ ui: { presets: true } }} />
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Turntable
|
|
78
|
+
|
|
79
|
+
One word puts the model on a slowly rotating showcase turntable. It pauses while the user drags and resumes on release; toggling is live, like presets.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<SimpleViewer object={url} turntable />
|
|
83
|
+
// speed: options.controls.autoRotateSpeed (2 ≈ one orbit / 30 s)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Animations
|
|
87
|
+
|
|
88
|
+
One word plays everything the GLTF carries — all clips, looped, from the moment the model loads.
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
<SimpleViewer object={url} animations />
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Pick one clip, tune the speed, or build your own play/pause UI via the handle:
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
<SimpleViewer object={url} options={{ animations: { autoplay: 'Walk', speed: 1.5 } }} />
|
|
98
|
+
|
|
99
|
+
viewerRef.current.getAnimationNames(); // ['Walk', 'Idle', ...]
|
|
100
|
+
viewerRef.current.pauseAnimations();
|
|
101
|
+
viewerRef.current.playAnimations('Idle');
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Turntable and animations compose — a spinning, walking robot is two words.
|
|
105
|
+
|
|
106
|
+
## Photoreal mode & stills
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<SimpleViewer object={url} pathTraced />
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`pathTraced` turns on progressive path tracing (a construction-time render mode). Capture a PNG programmatically — in raster mode at any resolution, in path-traced mode once the accumulation completes:
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
const handle = viewerRef.current;
|
|
116
|
+
const dataUrl = await handle.captureStill({ width: 1920 }); // PNG data URL
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Video capture
|
|
120
|
+
|
|
121
|
+
Record the live canvas into a clip — no dependencies, straight through `MediaRecorder` (WebM in Chromium/Firefox, MP4 in Safari). Motion is captured as it happens, so `turntable` + `captureVideo` is a ready-made product-card orbit:
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
<SimpleViewer ref={viewerRef} object={url} turntable />
|
|
125
|
+
|
|
126
|
+
const blob = await viewerRef.current.captureVideo({ duration: 5 }); // Blob
|
|
127
|
+
// also: fps, mimeType, videoBitsPerSecond
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Hotspot annotations & click selection
|
|
131
|
+
|
|
132
|
+
Pin DOM content to a world-space point — it tracks orbiting, zooming and resizes:
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { SimpleViewer, Hotspot } from 'softbox';
|
|
136
|
+
|
|
137
|
+
<SimpleViewer object="/model.glb">
|
|
138
|
+
<Hotspot position={[0, 1.2, 0]}>
|
|
139
|
+
<div className="pin">Engine</div>
|
|
140
|
+
</Hotspot>
|
|
141
|
+
<Hotspot position={[0.4, 0.2, 0.8]} occlude /> {/* hidden when the model covers it */}
|
|
142
|
+
</SimpleViewer>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Without children a built-in dot pin is rendered. `occlude` hides the hotspot while the model blocks the line of sight to its anchor.
|
|
146
|
+
|
|
147
|
+
A click on the model (drags and pinches are ignored) emits `object:selected` with the hit object and world-space point — feed it straight into a `<Hotspot>`:
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
handle.events.on('object:selected', ({ object, point }) => {
|
|
151
|
+
addPin([point.x, point.y, point.z]);
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Raycasts are BVH-accelerated per loaded model (logarithmic on high-poly meshes); opt out with `options.selection = { bvh: false }`.
|
|
156
|
+
|
|
157
|
+
## Events
|
|
158
|
+
|
|
159
|
+
Subscribe through the imperative handle:
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
const { events } = viewerRef.current;
|
|
163
|
+
events.on('model:loaded', ({ model, loadTime }) => console.log('loaded in', loadTime, 'ms'));
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
| Event | Fires |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `model:loading` / `model:loaded` / `model:error` | around every model load |
|
|
169
|
+
| `render:complete` | after each rendered frame |
|
|
170
|
+
| `pathtracing:complete` | once the sample cap is reached |
|
|
171
|
+
| `controls:change` | when the camera controls move |
|
|
172
|
+
| `object:selected` | when a click hits the loaded model (`{ object, point }`) |
|
|
173
|
+
| `error` | any viewer error |
|
|
174
|
+
|
|
175
|
+
## Imperative handle
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
const viewerRef = useRef<SimpleViewerHandle>(null);
|
|
179
|
+
<SimpleViewer ref={viewerRef} object={url} />
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The handle exposes `scene`, `camera`, `renderer`, `controls`, `events`, `loadModel(source)`, `captureStill(options?)`, `captureVideo(options?)`, `getAnimationNames()`, `playAnimations(name?)`, `pauseAnimations()` and `dispose()`.
|
|
183
|
+
|
|
184
|
+
## Configuration
|
|
185
|
+
|
|
186
|
+
Everything is optional — the defaults are the point. Pass `options` to override any part:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import { ControlType, defaultOptions } from 'softbox';
|
|
190
|
+
|
|
191
|
+
const options: SimpleViewerOptions = {
|
|
192
|
+
preset: 'studio', // one-word look; explicit options below win over it
|
|
193
|
+
backgroundColor: '#f0f0f7',
|
|
194
|
+
staticScene: false, // stop the render loop when nothing moves
|
|
195
|
+
|
|
196
|
+
camera: { position: [60, 60, 60], fov: 45, autoFitToObject: true },
|
|
197
|
+
|
|
198
|
+
// The procedural studio environment lights the scene by default (zero network
|
|
199
|
+
// requests). Point environment.url at an HDR/EXR/image to use your own.
|
|
200
|
+
environment: { environmentIntensity: 0.7 },
|
|
201
|
+
|
|
202
|
+
renderer: { antialias: true, toneMappingExposure: 1.1 },
|
|
203
|
+
|
|
204
|
+
controls: { type: ControlType.OrbitControls, enableDamping: true, autoRotate: false },
|
|
205
|
+
|
|
206
|
+
animations: { autoplay: true, speed: 1 }, // GLTF clip playback
|
|
207
|
+
|
|
208
|
+
helpers: {
|
|
209
|
+
grid: { type: 'hexagonal_glass' }, // glossy glass floor; also: hexagonal_wire, square_wire, stone_tiles
|
|
210
|
+
gizmo: false, // optional viewport orientation gizmo
|
|
211
|
+
studioEnvironment: true,
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
pathTracing: { enabled: false, maxSamples: 16 },
|
|
215
|
+
|
|
216
|
+
ui: { presets: false }, // built-in preset picker chips
|
|
217
|
+
selection: { bvh: true }, // BVH-accelerated raycasts for picking/occlusion
|
|
218
|
+
loadingIndicator: true, // built-in loading overlay (object form customizes it)
|
|
219
|
+
loaders: {}, // DRACO/KTX2/Meshopt decoder config (self-host paths, toggles)
|
|
220
|
+
};
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
See [`defaultOptions`](https://github.com/LEMing/softbox/blob/main/src/defaultOptions.ts) for the full annotated set and the typed option interfaces exported from the package root.
|
|
224
|
+
|
|
225
|
+
### Loading indicator
|
|
226
|
+
|
|
227
|
+
A built-in overlay shows while a model loads (and an error state if it fails) — the scene is never blank. Customize or disable it:
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
<SimpleViewer object={url} options={{ loadingIndicator: { label: 'Loading…' } }} />
|
|
231
|
+
<SimpleViewer object={url} options={{ loadingIndicator: false }} /> // drive your own via events
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Viewport gizmo
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
helpers: { gizmo: { placement: 'top-right', size: 128 } }
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Interactive orientation cube: click to snap the camera to axis views, synchronized with the controls.
|
|
241
|
+
|
|
242
|
+
### Path tracing options
|
|
243
|
+
|
|
244
|
+
`pathTracing`: `enabled`, `maxSamples` (completion cap), `bounces`, `transmissiveBounces`, `renderScale`, `lowResScale`, `dynamicLowRes`. With `replaceWithScreenshotOnComplete: true` (default) the finished frame replaces the live canvas until the user interacts.
|
|
245
|
+
|
|
246
|
+
## Next.js / SSR
|
|
247
|
+
|
|
248
|
+
The viewer renders into a WebGL canvas, so it is client-only. In Next.js, load it dynamically:
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
'use client';
|
|
252
|
+
import dynamic from 'next/dynamic';
|
|
253
|
+
|
|
254
|
+
const SimpleViewer = dynamic(
|
|
255
|
+
() => import('softbox').then((m) => m.SimpleViewer),
|
|
256
|
+
{ ssr: false }
|
|
257
|
+
);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Upgrading
|
|
261
|
+
|
|
262
|
+
- **3.x → 4.0**: the package was renamed — `threedviewer` is now **`softbox`**. Swap the dependency and the import specifier; the API is unchanged. See [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md).
|
|
263
|
+
- **3.0 → 3.x**: additive all the way — presets, `ui`, `pathTraced`, `captureStill`, `Hotspot`, `object:selected`, `selection`, `turntable`, `animations` are new surface on top of 3.0; existing options keep working. React peer is `>=18 <20`.
|
|
264
|
+
- **2.x → 3.0**: breaking release (removed no-op APIs, ESM+CJS packaging). See [MIGRATION_GUIDE.md](https://github.com/LEMing/softbox/blob/main/MIGRATION_GUIDE.md).
|
|
265
|
+
|
|
266
|
+
Full history: [CHANGELOG.md](https://github.com/LEMing/softbox/blob/main/CHANGELOG.md)
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
MIT
|