@heliguy-xyz/splat-viewer 1.0.0-rc.26 → 1.0.0-rc.28
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 +52 -336
- package/dist/web-component/splat-viewer.esm.js +88 -19
- package/dist/web-component/splat-viewer.esm.min.js +1 -1
- package/dist/web-component/splat-viewer.js +88 -19
- package/dist/web-component/splat-viewer.min.js +1 -1
- package/dist/web-component/supersplat-core/controllers.d.ts +5 -0
- package/dist/web-component/supersplat-core/controllers.d.ts.map +1 -1
- package/dist/web-component/supersplat-core/splat.d.ts.map +1 -1
- package/dist/web-component/types/supersplat-core/controllers.d.ts +5 -0
- package/dist/web-component/types/supersplat-core/controllers.d.ts.map +1 -1
- package/dist/web-component/types/supersplat-core/splat.d.ts.map +1 -1
- package/dist/web-component/types/web-component/OrbitCameraScript.d.ts.map +1 -1
- package/dist/web-component/types/web-component/SplatViewerCore.d.ts.map +1 -1
- package/dist/web-component/types/web-component/SupersplatAdapter.d.ts +9 -0
- package/dist/web-component/types/web-component/SupersplatAdapter.d.ts.map +1 -1
- package/dist/web-component/types/web-component/utils/config.d.ts +1 -0
- package/dist/web-component/types/web-component/utils/config.d.ts.map +1 -1
- package/dist/web-component/web-component/OrbitCameraScript.d.ts.map +1 -1
- package/dist/web-component/web-component/SplatViewerCore.d.ts.map +1 -1
- package/dist/web-component/web-component/SupersplatAdapter.d.ts +9 -0
- package/dist/web-component/web-component/SupersplatAdapter.d.ts.map +1 -1
- package/dist/web-component/web-component/utils/config.d.ts +1 -0
- package/dist/web-component/web-component/utils/config.d.ts.map +1 -1
- package/package.json +5 -12
- package/CHANGELOG.md +0 -78
package/README.md
CHANGED
|
@@ -1,382 +1,98 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @heliguy-xyz/splat-viewer
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
rendering of point cloud data. Framework-agnostic and embeddable in any web application with zero
|
|
5
|
-
external dependencies.
|
|
3
|
+
A standalone 3D Gaussian Splat viewer web component with multi-model support, transform system, fly camera, and scene configuration. Framework-agnostic and works with vanilla JavaScript, React, Vue, Angular, and any other framework.
|
|
6
4
|
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
- 🎯 **Gaussian Splat Rendering**: Native support for `.splat` files using PlayCanvas
|
|
10
|
-
- 📊 **PLY Support**: Traditional point cloud rendering for `.ply` and `.ply.compressed` formats
|
|
11
|
-
- 📦 **SOG Support**: Native support for `.sog` (Spatially Ordered Gaussians) files
|
|
12
|
-
- 🎮 **Interactive Controls**: Smooth camera navigation and manipulation
|
|
13
|
-
- 🧭 **Navigation Cube**: Interactive 3D cube for quick camera orientation control
|
|
14
|
-
- 📈 **Performance Monitoring**: Real-time rendering statistics
|
|
15
|
-
- ⚡ **High Performance**: WebGL2-powered rendering with PlayCanvas engine
|
|
16
|
-
- 📱 **Responsive Design**: Full-screen immersive viewing experience
|
|
17
|
-
- 🔧 **Self-Contained**: Single file with bundled PlayCanvas engine (2MB)
|
|
18
|
-
- 🎨 **Built-in Loading**: Professional loading spinner with orange/black theme
|
|
19
|
-
- 🚀 **Zero Dependencies**: No external scripts required
|
|
20
|
-
|
|
21
|
-
### Enhanced Features (v1.0.0)
|
|
22
|
-
|
|
23
|
-
- 🎭 **Multi-Model Support**: Load and manage multiple models in the same scene
|
|
24
|
-
- 🔄 **Transform System**: Programmatically position, rotate, and scale models
|
|
25
|
-
- 🎥 **Fly Camera Mode**: First-person WASD + mouse-look navigation with configurable controls
|
|
26
|
-
- 🎨 **Scene Configuration**: Adjust FOV and background color with smooth animations
|
|
27
|
-
- 👁️ **Preview Mode**: Disable camera controls for product previews with programmatic rotation
|
|
28
|
-
- 📡 **Comprehensive Events**: React to model lifecycle, transforms, camera changes, and more
|
|
29
|
-
- 📘 **TypeScript Support**: Full type definitions for all APIs
|
|
30
|
-
|
|
31
|
-
## Quick Start
|
|
32
|
-
|
|
33
|
-
### Installation
|
|
34
|
-
|
|
35
|
-
#### Via npm (Recommended)
|
|
5
|
+
## Installation
|
|
36
6
|
|
|
37
7
|
```bash
|
|
38
|
-
npm install @heliguy/
|
|
8
|
+
npm install @heliguy-xyz/splat-viewer
|
|
39
9
|
```
|
|
40
10
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
```html
|
|
44
|
-
<!-- UMD Bundle -->
|
|
45
|
-
<script src="https://unpkg.com/@heliguy/web-viewer@1.0.0/dist/web-component/splat-viewer.min.js"></script>
|
|
46
|
-
|
|
47
|
-
<!-- ESM Bundle -->
|
|
48
|
-
<script type="module">
|
|
49
|
-
import '@heliguy/web-viewer/esm'
|
|
50
|
-
</script>
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Prerequisites
|
|
54
|
-
|
|
55
|
-
- Modern browser with WebGL2 support
|
|
56
|
-
- Node.js ≥18.0.0 (for development only)
|
|
57
|
-
|
|
58
|
-
### Usage
|
|
11
|
+
## Quick Start
|
|
59
12
|
|
|
60
|
-
|
|
13
|
+
### Vanilla JavaScript / HTML
|
|
61
14
|
|
|
62
15
|
```html
|
|
63
16
|
<!DOCTYPE html>
|
|
64
17
|
<html>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<!-- Import from npm package -->
|
|
78
|
-
<script src="node_modules/@heliguy/web-viewer/dist/web-component/splat-viewer.min.js"></script>
|
|
79
|
-
|
|
80
|
-
<script>
|
|
81
|
-
const viewer = document.getElementById('viewer')
|
|
82
|
-
viewer.addEventListener('ready', () => {
|
|
83
|
-
viewer.load('path/to/model.splat')
|
|
84
|
-
})
|
|
85
|
-
</script>
|
|
86
|
-
</body>
|
|
18
|
+
<head>
|
|
19
|
+
<script type="module" src="node_modules/@heliguy-xyz/splat-viewer/dist/splat-viewer.esm.js"></script>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<splat-viewer
|
|
23
|
+
src="path/to/model.splat"
|
|
24
|
+
width="800"
|
|
25
|
+
height="600"
|
|
26
|
+
auto-focus
|
|
27
|
+
></splat-viewer>
|
|
28
|
+
</body>
|
|
87
29
|
</html>
|
|
88
30
|
```
|
|
89
31
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
Use the provided `viewer.html` page that reads query parameters and configures the web component.
|
|
93
|
-
Host `viewer.html` alongside `dist/web-component/splat-viewer.min.js`.
|
|
94
|
-
|
|
95
|
-
Supported query params:
|
|
96
|
-
|
|
97
|
-
- `src` (required): URL to your model (encode it!)
|
|
98
|
-
- `width` (optional): CSS width (e.g. `100%`, `800px`)
|
|
99
|
-
- `height` (optional): CSS height (e.g. `100vh`, `600px`)
|
|
100
|
-
- `autoFocus` (optional): `true` | `false` (default: `true`)
|
|
101
|
-
- `stats` (optional): `true` | `false`
|
|
102
|
-
|
|
103
|
-
Example:
|
|
104
|
-
|
|
105
|
-
```html
|
|
106
|
-
<iframe
|
|
107
|
-
src="https://your-domain.com/viewer.html?src=https%3A%2F%2Fcdn.yourdomain.com%2Fmodels%2FRyhope.ply&width=100%25&height=100vh&autoFocus=true"
|
|
108
|
-
width="100%"
|
|
109
|
-
height="600"
|
|
110
|
-
style="border:0; background:#000"
|
|
111
|
-
allowfullscreen
|
|
112
|
-
></iframe>
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Notes:
|
|
116
|
-
|
|
117
|
-
- Ensure CORS allows the viewer origin to fetch the model (`Access-Control-Allow-Origin`).
|
|
118
|
-
- Prefer hosting `viewer.html`, the bundle, and models on the same domain to avoid CORS.
|
|
119
|
-
- URL-encode the `src` value.
|
|
120
|
-
|
|
121
|
-
#### React / Next.js
|
|
32
|
+
### React
|
|
122
33
|
|
|
123
34
|
```tsx
|
|
124
|
-
import
|
|
125
|
-
import '
|
|
35
|
+
import '@heliguy-xyz/splat-viewer';
|
|
36
|
+
import { useRef, useEffect } from 'react';
|
|
126
37
|
|
|
127
38
|
function App() {
|
|
128
|
-
const viewerRef = useRef
|
|
39
|
+
const viewerRef = useRef(null);
|
|
129
40
|
|
|
130
41
|
useEffect(() => {
|
|
131
|
-
const viewer = viewerRef.current
|
|
132
|
-
if (!viewer) return
|
|
42
|
+
const viewer = viewerRef.current;
|
|
43
|
+
if (!viewer) return;
|
|
133
44
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
viewer.addEventListener('ready', handleReady)
|
|
139
|
-
return () => viewer.removeEventListener('ready', handleReady)
|
|
140
|
-
}, [])
|
|
45
|
+
viewer.addEventListener('loaded', (e) => {
|
|
46
|
+
console.log('Model loaded:', e.detail);
|
|
47
|
+
});
|
|
48
|
+
}, []);
|
|
141
49
|
|
|
142
50
|
return (
|
|
143
51
|
<splat-viewer
|
|
144
52
|
ref={viewerRef}
|
|
53
|
+
src="/model.splat"
|
|
145
54
|
width="100%"
|
|
146
55
|
height="600px"
|
|
147
56
|
auto-focus
|
|
148
57
|
/>
|
|
149
|
-
)
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
**TypeScript:** Add to your `global.d.ts`:
|
|
154
|
-
```typescript
|
|
155
|
-
declare namespace JSX {
|
|
156
|
-
interface IntrinsicElements {
|
|
157
|
-
'splat-viewer': React.DetailedHTMLProps<
|
|
158
|
-
React.HTMLAttributes<HTMLElement> & {
|
|
159
|
-
ref?: React.Ref<any>
|
|
160
|
-
src?: string
|
|
161
|
-
width?: string
|
|
162
|
-
height?: string
|
|
163
|
-
'auto-focus'?: boolean
|
|
164
|
-
'enable-stats'?: boolean
|
|
165
|
-
'preview-mode'?: boolean
|
|
166
|
-
},
|
|
167
|
-
HTMLElement
|
|
168
|
-
>
|
|
169
|
-
}
|
|
58
|
+
);
|
|
170
59
|
}
|
|
171
60
|
```
|
|
172
61
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
```vue
|
|
176
|
-
<template>
|
|
177
|
-
<splat-viewer
|
|
178
|
-
ref="viewerRef"
|
|
179
|
-
width="100%"
|
|
180
|
-
height="600px"
|
|
181
|
-
auto-focus
|
|
182
|
-
/>
|
|
183
|
-
</template>
|
|
184
|
-
|
|
185
|
-
<script setup>
|
|
186
|
-
import { ref, onMounted } from 'vue'
|
|
187
|
-
import '@heliguy/web-viewer'
|
|
188
|
-
|
|
189
|
-
const viewerRef = ref(null)
|
|
190
|
-
|
|
191
|
-
onMounted(() => {
|
|
192
|
-
const viewer = viewerRef.value
|
|
193
|
-
const handleReady = () => {
|
|
194
|
-
viewer.load('/models/scene.splat')
|
|
195
|
-
}
|
|
196
|
-
viewer.addEventListener('ready', handleReady)
|
|
197
|
-
})
|
|
198
|
-
</script>
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
## Commands
|
|
202
|
-
|
|
203
|
-
| Command | Description |
|
|
204
|
-
| ----------------------------- | ---------------------------------- |
|
|
205
|
-
| `npm run web-component:serve` | Start development server |
|
|
206
|
-
| `npm run web-component:build` | Build self-contained web component |
|
|
207
|
-
| `npm run web-component:clean` | Clean build artifacts |
|
|
208
|
-
| `npm run lint` | Check code quality with ESLint |
|
|
209
|
-
| `npm run lint:fix` | Fix linting issues automatically |
|
|
210
|
-
| `npm run format` | Format code with Prettier |
|
|
211
|
-
| `npm run format:check` | Check code formatting |
|
|
212
|
-
|
|
213
|
-
## Web Component API
|
|
214
|
-
|
|
215
|
-
### Attributes
|
|
216
|
-
|
|
217
|
-
#### Basic Configuration
|
|
218
|
-
|
|
219
|
-
- `src` - Model file path or URL
|
|
220
|
-
- `width` - Canvas width (default: "100%")
|
|
221
|
-
- `height` - Canvas height (default: "400px")
|
|
222
|
-
- `auto-focus` - Auto-focus on load (default: true)
|
|
223
|
-
- `enable-stats` - Show performance stats (default: false)
|
|
224
|
-
- `max-splats` - Maximum splat count (default: 2000000)
|
|
225
|
-
- `preview-mode` - Disable camera controls for preview use (default: false)
|
|
226
|
-
|
|
227
|
-
#### Camera Controls
|
|
228
|
-
|
|
229
|
-
- `orbit-sensitivity` - Mouse orbit sensitivity (default: 0.3)
|
|
230
|
-
- `pan-sensitivity` - Mouse pan sensitivity (default: 0.5)
|
|
231
|
-
- `zoom-sensitivity` - Mouse zoom sensitivity (default: 0.1)
|
|
232
|
-
- `min-distance` - Minimum camera distance (default: 1)
|
|
233
|
-
- `max-distance` - Maximum camera distance (default: 100)
|
|
234
|
-
|
|
235
|
-
#### Navigation Cube
|
|
236
|
-
|
|
237
|
-
- `enable-navigation-cube` - Show interactive navigation cube (default: false)
|
|
238
|
-
- `navigation-cube-size` - Cube size in pixels (default: 120)
|
|
239
|
-
- `navigation-cube-transition` - Camera transition duration in ms (default: 800)
|
|
240
|
-
|
|
241
|
-
### Preview Mode
|
|
242
|
-
|
|
243
|
-
Preview mode disables all camera controls (mouse/touch interactions) while maintaining programmatic rotation capabilities. This is ideal for:
|
|
244
|
-
|
|
245
|
-
- Product configurators with UI-controlled rotation
|
|
246
|
-
- Model previews with custom control interfaces
|
|
247
|
-
- Embedded viewers where you want to control all interactions
|
|
248
|
-
|
|
249
|
-
**Usage:**
|
|
250
|
-
|
|
251
|
-
```html
|
|
252
|
-
<splat-viewer src="model.splat" preview-mode></splat-viewer>
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
**Features in Preview Mode:**
|
|
256
|
-
- ✅ Camera controls are completely disabled
|
|
257
|
-
- ✅ Model is automatically centered and framed on load
|
|
258
|
-
- ✅ Programmatic rotation via `setModelRotation()` API still works
|
|
259
|
-
- ✅ All other APIs remain functional (visibility, transforms, etc.)
|
|
260
|
-
|
|
261
|
-
**Example with Rotation Controls:**
|
|
262
|
-
|
|
263
|
-
```javascript
|
|
264
|
-
const viewer = document.getElementById('viewer');
|
|
265
|
-
|
|
266
|
-
viewer.addEventListener('loaded', () => {
|
|
267
|
-
const models = viewer.getModels();
|
|
268
|
-
const modelId = models[0].id;
|
|
269
|
-
|
|
270
|
-
// Programmatically set rotation
|
|
271
|
-
viewer.setModelRotation(modelId, { x: 0, y: 45, z: 0 });
|
|
272
|
-
});
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
See `examples/preview-mode.html` for a complete working example.
|
|
276
|
-
|
|
277
|
-
### Events
|
|
278
|
-
|
|
279
|
-
#### Core Events
|
|
280
|
-
- `ready` - Component initialized
|
|
281
|
-
- `loading-start` - Model loading started
|
|
282
|
-
- `loading-progress` - Model loading progress update
|
|
283
|
-
- `loaded` - Model loaded successfully
|
|
284
|
-
- `error` - Loading error occurred
|
|
285
|
-
- `stats-update` - Performance statistics update
|
|
286
|
-
- `camera-change` - Camera position/target changed
|
|
287
|
-
- `interaction-start` / `interaction-end` - User interaction with orbit controls
|
|
288
|
-
|
|
289
|
-
#### Multi-Model Events (v1.0.0)
|
|
290
|
-
- `model-added` - Model added to scene
|
|
291
|
-
- `model-removed` - Model removed from scene
|
|
292
|
-
- `scene-cleared` - All models cleared
|
|
293
|
-
|
|
294
|
-
#### Transform Events (v1.0.0)
|
|
295
|
-
- `model-transform-changed` - Model transform updated
|
|
296
|
-
|
|
297
|
-
#### Camera Events (v1.0.0)
|
|
298
|
-
- `camera-mode-changed` - Camera mode switched (orbit/fly)
|
|
299
|
-
- `fly-camera-move` - Fly camera position changed
|
|
300
|
-
- `fly-camera-look` - Fly camera rotation changed
|
|
301
|
-
|
|
302
|
-
#### Scene Config Events (v1.0.0)
|
|
303
|
-
- `camera-fov-changed` - Camera FOV changed
|
|
304
|
-
- `background-color-changed` - Background color changed
|
|
305
|
-
|
|
306
|
-
## Project Structure
|
|
307
|
-
|
|
308
|
-
```
|
|
309
|
-
src/
|
|
310
|
-
├── web-component/ # Main web component implementation
|
|
311
|
-
│ ├── SplatViewerElement.ts # Web component class
|
|
312
|
-
│ ├── SplatViewerCore.ts # Core rendering engine
|
|
313
|
-
│ ├── NavigationCubeController.ts # Navigation cube controller
|
|
314
|
-
│ ├── OrbitCameraScript.ts # Camera controls
|
|
315
|
-
│ ├── navigation-cube/ # Navigation cube modules
|
|
316
|
-
│ │ ├── CubeRenderer.ts # 3D cube visual rendering
|
|
317
|
-
│ │ ├── ControlPointManager.ts # Control point UI elements
|
|
318
|
-
│ │ └── CameraTransitionController.ts # Camera animations
|
|
319
|
-
│ ├── types/ # TypeScript definitions
|
|
320
|
-
│ ├── loader/ # File loading system (.ply, .sog, .splat)
|
|
321
|
-
│ └── utils/ # Utility functions
|
|
322
|
-
├── build/ # Build configuration
|
|
323
|
-
├── dist/web-component/ # Built self-contained web component
|
|
324
|
-
│ └── splat-viewer.min.js # Single 2MB file with PlayCanvas
|
|
325
|
-
├── test-web-component.html # Basic test page
|
|
326
|
-
└── test-navigation-cube.html # Navigation cube test page
|
|
327
|
-
|
|
328
|
-
assets/ # 3D model assets for testing
|
|
329
|
-
docs/ # Documentation
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
## Supported File Formats
|
|
333
|
-
|
|
334
|
-
- **.sog** - Spatially Ordered Gaussians (PlayCanvas native support)
|
|
335
|
-
- **.splat** - Gaussian Splat files (PlayCanvas native support)
|
|
336
|
-
- **.ply** - Point cloud files (standard format)
|
|
337
|
-
- **.ply.compressed** - Compressed PLY files (gzip/deflate)
|
|
338
|
-
|
|
339
|
-
## Self-Contained Bundle
|
|
340
|
-
|
|
341
|
-
The web component is distributed as a single 2MB file (`splat-viewer.min.js`) that includes:
|
|
62
|
+
## Features
|
|
342
63
|
|
|
343
|
-
-
|
|
344
|
-
-
|
|
345
|
-
-
|
|
346
|
-
-
|
|
347
|
-
-
|
|
64
|
+
- 🎨 **Multi-Model Support** - Load and manage multiple models in one scene
|
|
65
|
+
- 🎥 **Dual Camera Modes** - Orbit and fly camera controls
|
|
66
|
+
- 🔧 **Transform System** - Position, rotate, and scale models programmatically
|
|
67
|
+
- 📦 **Multiple Formats** - Support for PLY, SPLAT, and SOG files
|
|
68
|
+
- ⚡ **High Performance** - Optimized rendering with PlayCanvas
|
|
69
|
+
- 🎯 **Selection Tools** - Box and sphere selection for splats
|
|
70
|
+
- 🎨 **Scene Configuration** - Background, grid, FOV, and more
|
|
71
|
+
- 📱 **Responsive** - Works on desktop, tablet, and mobile
|
|
348
72
|
|
|
349
|
-
|
|
73
|
+
## API
|
|
350
74
|
|
|
351
|
-
-
|
|
352
|
-
- Version consistency guaranteed
|
|
353
|
-
- Easy embedding in any web application
|
|
354
|
-
- Offline functionality
|
|
75
|
+
See the full [API documentation](https://github.com/Heliguy-com/3d-web-viewer/tree/main/packages/core/docs) for detailed information.
|
|
355
76
|
|
|
356
77
|
## Development
|
|
357
78
|
|
|
79
|
+
This package is part of the `@heliguy-xyz/splat-viewer` monorepo.
|
|
80
|
+
|
|
358
81
|
```bash
|
|
359
|
-
#
|
|
360
|
-
npm run
|
|
82
|
+
# Build
|
|
83
|
+
npm run build
|
|
361
84
|
|
|
362
|
-
#
|
|
363
|
-
npm run
|
|
85
|
+
# Development server
|
|
86
|
+
npm run dev
|
|
364
87
|
|
|
365
|
-
#
|
|
366
|
-
npm run
|
|
88
|
+
# Type check
|
|
89
|
+
npm run type-check
|
|
367
90
|
```
|
|
368
91
|
|
|
369
|
-
##
|
|
370
|
-
|
|
371
|
-
### Netlify
|
|
372
|
-
|
|
373
|
-
- Publish directory: `dist`
|
|
374
|
-
- Build command: `npm run build`
|
|
375
|
-
- Node version: 18 (configured in `netlify.toml`)
|
|
92
|
+
## License
|
|
376
93
|
|
|
377
|
-
|
|
378
|
-
(`build/copy-viewer-to-index.mjs`). Rollup uses an ESM config at `build/rollup.config.mjs`.
|
|
94
|
+
MIT © Heliguy
|
|
379
95
|
|
|
380
|
-
##
|
|
96
|
+
## Related Packages
|
|
381
97
|
|
|
382
|
-
|
|
98
|
+
- [@heliguy-xyz/splat-viewer-react-ui](https://www.npmjs.com/package/@heliguy-xyz/splat-viewer-react-ui) - Full-featured React UI component
|
|
@@ -139557,11 +139557,15 @@ function parseJsonSafely(jsonString) {
|
|
|
139557
139557
|
}
|
|
139558
139558
|
/**
|
|
139559
139559
|
* Convert a string to a boolean value
|
|
139560
|
+
* For HTML boolean attributes, an empty string means the attribute is present (true)
|
|
139560
139561
|
*/
|
|
139561
139562
|
function parseBoolean(value) {
|
|
139562
139563
|
if (typeof value === 'boolean')
|
|
139563
139564
|
return value;
|
|
139564
139565
|
if (typeof value === 'string') {
|
|
139566
|
+
// Empty string means attribute is present without value (should be true for boolean attributes)
|
|
139567
|
+
if (value === '')
|
|
139568
|
+
return true;
|
|
139565
139569
|
return value.toLowerCase() === 'true' || value === '1';
|
|
139566
139570
|
}
|
|
139567
139571
|
return false;
|
|
@@ -140452,6 +140456,10 @@ function registerOrbitCameraScript() {
|
|
|
140452
140456
|
this.isPanning = false;
|
|
140453
140457
|
// Allow host to temporarily disable camera input (e.g., while dragging gizmos)
|
|
140454
140458
|
this._inputEnabled = true;
|
|
140459
|
+
// Granular control over specific input types
|
|
140460
|
+
this._orbitEnabled = true;
|
|
140461
|
+
this._panEnabled = true;
|
|
140462
|
+
this._zoomEnabled = true;
|
|
140455
140463
|
// Setup context menu prevention
|
|
140456
140464
|
this.setupContextMenuPrevention();
|
|
140457
140465
|
// Use PlayCanvas mouse events for reliable input handling
|
|
@@ -140489,6 +140497,19 @@ function registerOrbitCameraScript() {
|
|
|
140489
140497
|
this.isPanning = false;
|
|
140490
140498
|
}
|
|
140491
140499
|
};
|
|
140500
|
+
OrbitCamera.prototype.setInputControls = function (options) {
|
|
140501
|
+
if (options.orbit !== undefined)
|
|
140502
|
+
this._orbitEnabled = !!options.orbit;
|
|
140503
|
+
if (options.pan !== undefined)
|
|
140504
|
+
this._panEnabled = !!options.pan;
|
|
140505
|
+
if (options.zoom !== undefined)
|
|
140506
|
+
this._zoomEnabled = !!options.zoom;
|
|
140507
|
+
// Clear any in-progress interactions if being disabled
|
|
140508
|
+
if (this._orbitEnabled === false)
|
|
140509
|
+
this.isOrbiting = false;
|
|
140510
|
+
if (this._panEnabled === false)
|
|
140511
|
+
this.isPanning = false;
|
|
140512
|
+
};
|
|
140492
140513
|
OrbitCamera.prototype.update = function (dt) {
|
|
140493
140514
|
// Update navigation cube if enabled
|
|
140494
140515
|
if (this.navigationCube &&
|
|
@@ -140505,12 +140526,13 @@ function registerOrbitCameraScript() {
|
|
|
140505
140526
|
typeof this.navigationCube.cancelTransition === 'function') {
|
|
140506
140527
|
this.navigationCube.cancelTransition('manual-interaction');
|
|
140507
140528
|
}
|
|
140508
|
-
if (event.button === MOUSEBUTTON_LEFT) {
|
|
140529
|
+
if (event.button === MOUSEBUTTON_LEFT && this._orbitEnabled !== false) {
|
|
140509
140530
|
this.isOrbiting = true;
|
|
140510
140531
|
this.emitInteractionEvent?.('interaction-start', 'rotate');
|
|
140511
140532
|
}
|
|
140512
|
-
else if (event.button === MOUSEBUTTON_RIGHT ||
|
|
140513
|
-
event.button === MOUSEBUTTON_MIDDLE)
|
|
140533
|
+
else if ((event.button === MOUSEBUTTON_RIGHT ||
|
|
140534
|
+
event.button === MOUSEBUTTON_MIDDLE) &&
|
|
140535
|
+
this._panEnabled !== false) {
|
|
140514
140536
|
this.isPanning = true;
|
|
140515
140537
|
this.emitInteractionEvent?.('interaction-start', 'pan');
|
|
140516
140538
|
}
|
|
@@ -140558,7 +140580,7 @@ function registerOrbitCameraScript() {
|
|
|
140558
140580
|
}
|
|
140559
140581
|
};
|
|
140560
140582
|
OrbitCamera.prototype.onMouseWheel = function (event) {
|
|
140561
|
-
if (this._inputEnabled === false)
|
|
140583
|
+
if (this._inputEnabled === false || this._zoomEnabled === false)
|
|
140562
140584
|
return;
|
|
140563
140585
|
// Cancel any ongoing navigation cube transition when manual zoom interaction starts
|
|
140564
140586
|
if (this.navigationCube &&
|
|
@@ -142694,6 +142716,10 @@ class Splat extends Element$1 {
|
|
|
142694
142716
|
this.entity.setEulerAngles(orientation);
|
|
142695
142717
|
this.entity.addComponent('gsplat', { asset });
|
|
142696
142718
|
const instance = this.entity.gsplat.instance;
|
|
142719
|
+
// Check if the gsplat component initialized properly
|
|
142720
|
+
if (!instance || !instance.meshInstance) {
|
|
142721
|
+
throw new Error('Failed to initialize gsplat component. The file may not contain valid Gaussian Splatting data.');
|
|
142722
|
+
}
|
|
142697
142723
|
// use custom render order distance calculation for splats
|
|
142698
142724
|
instance.meshInstance.calculateSortDistance = (meshInstance, pos, dir) => {
|
|
142699
142725
|
const bound = this.localBound;
|
|
@@ -143802,6 +143828,10 @@ class PointerController {
|
|
|
143802
143828
|
};
|
|
143803
143829
|
// Allow temporarily disabling camera controls (e.g. while dragging gizmos).
|
|
143804
143830
|
let enabled = true;
|
|
143831
|
+
// Granular control over specific input types
|
|
143832
|
+
let orbitEnabled = true;
|
|
143833
|
+
let panEnabled = true;
|
|
143834
|
+
let zoomEnabled = true;
|
|
143805
143835
|
const resetState = () => {
|
|
143806
143836
|
pressedButton = -1;
|
|
143807
143837
|
touches = [];
|
|
@@ -143908,13 +143938,13 @@ class PointerController {
|
|
|
143908
143938
|
(event.shiftKey || event.ctrlKey ? 'orbit' :
|
|
143909
143939
|
(event.altKey || event.metaKey ? 'zoom' : null)) :
|
|
143910
143940
|
null;
|
|
143911
|
-
if (mod === 'orbit' || (mod === null && pressedButton === 0)) {
|
|
143941
|
+
if ((mod === 'orbit' || (mod === null && pressedButton === 0)) && orbitEnabled) {
|
|
143912
143942
|
orbit(dx, dy);
|
|
143913
143943
|
}
|
|
143914
|
-
else if (mod === 'zoom' || (mod === null && pressedButton === 1)) {
|
|
143944
|
+
else if ((mod === 'zoom' || (mod === null && pressedButton === 1)) && zoomEnabled) {
|
|
143915
143945
|
zoom(dy * -0.02);
|
|
143916
143946
|
}
|
|
143917
|
-
else if (mod === 'pan' || (mod === null && pressedButton === 2)) {
|
|
143947
|
+
else if ((mod === 'pan' || (mod === null && pressedButton === 2)) && panEnabled) {
|
|
143918
143948
|
pan(x, y, dx, dy);
|
|
143919
143949
|
}
|
|
143920
143950
|
}
|
|
@@ -143925,7 +143955,9 @@ class PointerController {
|
|
|
143925
143955
|
const dy = event.offsetY - touch.y;
|
|
143926
143956
|
touch.x = event.offsetX;
|
|
143927
143957
|
touch.y = event.offsetY;
|
|
143928
|
-
|
|
143958
|
+
if (orbitEnabled) {
|
|
143959
|
+
orbit(dx, dy);
|
|
143960
|
+
}
|
|
143929
143961
|
}
|
|
143930
143962
|
else if (touches.length === 2) {
|
|
143931
143963
|
const touch = touches[touches.map(t => t.id).indexOf(event.pointerId)];
|
|
@@ -143934,8 +143966,12 @@ class PointerController {
|
|
|
143934
143966
|
const mx = (touches[0].x + touches[1].x) * 0.5;
|
|
143935
143967
|
const my = (touches[0].y + touches[1].y) * 0.5;
|
|
143936
143968
|
const ml = dist(touches[0].x, touches[0].y, touches[1].x, touches[1].y);
|
|
143937
|
-
|
|
143938
|
-
|
|
143969
|
+
if (panEnabled) {
|
|
143970
|
+
pan(mx, my, (mx - midx), (my - midy));
|
|
143971
|
+
}
|
|
143972
|
+
if (zoomEnabled) {
|
|
143973
|
+
zoom((ml - midlen) * 0.01);
|
|
143974
|
+
}
|
|
143939
143975
|
midx = mx;
|
|
143940
143976
|
midy = my;
|
|
143941
143977
|
midlen = ml;
|
|
@@ -143953,16 +143989,20 @@ class PointerController {
|
|
|
143953
143989
|
return;
|
|
143954
143990
|
const { deltaX, deltaY } = event;
|
|
143955
143991
|
if (isMouseEvent(deltaX, deltaY)) {
|
|
143956
|
-
|
|
143992
|
+
if (zoomEnabled)
|
|
143993
|
+
zoom(deltaY * -2e-3);
|
|
143957
143994
|
}
|
|
143958
143995
|
else if (event.ctrlKey || event.metaKey) {
|
|
143959
|
-
|
|
143996
|
+
if (zoomEnabled)
|
|
143997
|
+
zoom(deltaY * -0.02);
|
|
143960
143998
|
}
|
|
143961
143999
|
else if (event.shiftKey) {
|
|
143962
|
-
|
|
144000
|
+
if (panEnabled)
|
|
144001
|
+
pan(event.offsetX, event.offsetY, deltaX, deltaY);
|
|
143963
144002
|
}
|
|
143964
144003
|
else {
|
|
143965
|
-
|
|
144004
|
+
if (orbitEnabled)
|
|
144005
|
+
orbit(deltaX, deltaY);
|
|
143966
144006
|
}
|
|
143967
144007
|
event.preventDefault();
|
|
143968
144008
|
};
|
|
@@ -144030,6 +144070,14 @@ class PointerController {
|
|
|
144030
144070
|
resetState();
|
|
144031
144071
|
}
|
|
144032
144072
|
};
|
|
144073
|
+
this.setInputControls = (options) => {
|
|
144074
|
+
if (options.orbit !== undefined)
|
|
144075
|
+
orbitEnabled = !!options.orbit;
|
|
144076
|
+
if (options.pan !== undefined)
|
|
144077
|
+
panEnabled = !!options.pan;
|
|
144078
|
+
if (options.zoom !== undefined)
|
|
144079
|
+
zoomEnabled = !!options.zoom;
|
|
144080
|
+
};
|
|
144033
144081
|
}
|
|
144034
144082
|
}
|
|
144035
144083
|
|
|
@@ -147334,6 +147382,26 @@ class SupersplatAdapter {
|
|
|
147334
147382
|
console.warn('SupersplatAdapter: Failed to toggle camera controls', e);
|
|
147335
147383
|
}
|
|
147336
147384
|
}
|
|
147385
|
+
/**
|
|
147386
|
+
* Configure granular control over specific camera input types.
|
|
147387
|
+
* Allows enabling/disabling orbit, pan, and zoom independently.
|
|
147388
|
+
*/
|
|
147389
|
+
setCameraInputControls(options) {
|
|
147390
|
+
const controller = this.scene?.camera?.controller;
|
|
147391
|
+
if (!controller)
|
|
147392
|
+
return;
|
|
147393
|
+
try {
|
|
147394
|
+
if (typeof controller.setInputControls === 'function') {
|
|
147395
|
+
controller.setInputControls(options);
|
|
147396
|
+
}
|
|
147397
|
+
else {
|
|
147398
|
+
console.warn('SupersplatAdapter: Camera controller does not support setInputControls');
|
|
147399
|
+
}
|
|
147400
|
+
}
|
|
147401
|
+
catch (e) {
|
|
147402
|
+
console.warn('SupersplatAdapter: Failed to set camera input controls', e);
|
|
147403
|
+
}
|
|
147404
|
+
}
|
|
147337
147405
|
/**
|
|
147338
147406
|
* Enable/disable supersplat-core camera auto-update (orbit tweens).
|
|
147339
147407
|
* When enabled, the camera entity transform may be driven by an external controller (fly mode).
|
|
@@ -147792,9 +147860,9 @@ class SplatViewerCore {
|
|
|
147792
147860
|
this._supersplatReady = this._supersplat.init();
|
|
147793
147861
|
this._supersplatReady
|
|
147794
147862
|
?.then(() => {
|
|
147795
|
-
//
|
|
147863
|
+
// In preview mode, enable zoom only (disable orbit and pan)
|
|
147796
147864
|
if (this.previewMode && this._supersplat) {
|
|
147797
|
-
this._supersplat.
|
|
147865
|
+
this._supersplat.setCameraInputControls?.({ orbit: false, pan: false, zoom: true });
|
|
147798
147866
|
}
|
|
147799
147867
|
// Only set up fly camera if not in preview mode
|
|
147800
147868
|
if (!this.previewMode) {
|
|
@@ -150007,11 +150075,12 @@ class SplatViewerCore {
|
|
|
150007
150075
|
detail: { type: interactionType },
|
|
150008
150076
|
});
|
|
150009
150077
|
};
|
|
150010
|
-
//
|
|
150078
|
+
// In preview mode, enable zoom only (disable orbit and pan)
|
|
150011
150079
|
if (this.previewMode && this._orbit) {
|
|
150012
150080
|
const orbitAny = this._orbit;
|
|
150013
|
-
if (typeof orbitAny.
|
|
150014
|
-
|
|
150081
|
+
if (typeof orbitAny.setInputControls === 'function') {
|
|
150082
|
+
// Enable zoom, disable orbit and pan
|
|
150083
|
+
orbitAny.setInputControls({ orbit: false, pan: false, zoom: true });
|
|
150015
150084
|
}
|
|
150016
150085
|
}
|
|
150017
150086
|
this.entities.camera.setPosition(0, 0, 10);
|