UNPKG

@heliguy-xyz/splat-viewer

Version:

A standalone 3D Gaussian Splat viewer web component with multi-model support, transform system, fly camera, and scene configuration

344 lines (267 loc) • 10.2 kB
# Heliguy Web Viewer A self-contained 3D Gaussian Splat viewer web component built with PlayCanvas for high-performance rendering of point cloud data. Framework-agnostic and embeddable in any web application with zero external dependencies. ## Features - šŸŽÆ **Gaussian Splat Rendering**: Native support for `.splat` files using PlayCanvas - šŸ“Š **PLY Support**: Traditional point cloud rendering for `.ply` and `.ply.compressed` formats - šŸ“¦ **SOG Support**: Native support for `.sog` (Spatially Ordered Gaussians) files - šŸŽ® **Interactive Controls**: Smooth camera navigation and manipulation - 🧭 **Navigation Cube**: Interactive 3D cube for quick camera orientation control - šŸ“ˆ **Performance Monitoring**: Real-time rendering statistics - ⚔ **High Performance**: WebGL2-powered rendering with PlayCanvas engine - šŸ“± **Responsive Design**: Full-screen immersive viewing experience - šŸ”§ **Self-Contained**: Single file with bundled PlayCanvas engine (2MB) - šŸŽØ **Built-in Loading**: Professional loading spinner with orange/black theme - šŸš€ **Zero Dependencies**: No external scripts required ### Enhanced Features (v1.0.0) - šŸŽ­ **Multi-Model Support**: Load and manage multiple models in the same scene - šŸ”„ **Transform System**: Programmatically position, rotate, and scale models - šŸŽ„ **Fly Camera Mode**: First-person WASD + mouse-look navigation with configurable controls - šŸŽØ **Scene Configuration**: Adjust FOV and background color with smooth animations - šŸ“” **Comprehensive Events**: React to model lifecycle, transforms, camera changes, and more - šŸ“˜ **TypeScript Support**: Full type definitions for all APIs ## Quick Start ### Installation #### Via npm (Recommended) ```bash npm install @heliguy/web-viewer ``` #### Via CDN ```html <!-- UMD Bundle --> <script src="https://unpkg.com/@heliguy/web-viewer@1.0.0/dist/web-component/splat-viewer.min.js"></script> <!-- ESM Bundle --> <script type="module"> import '@heliguy/web-viewer/esm' </script> ``` ### Prerequisites - Modern browser with WebGL2 support - Node.js ≄18.0.0 (for development only) ### Usage #### Vanilla HTML ```html <!DOCTYPE html> <html> <head> <title>3D Splat Viewer</title> </head> <body> <splat-viewer id="viewer" width="100%" height="600px" auto-focus enable-navigation-cube ></splat-viewer> <!-- Import from npm package --> <script src="node_modules/@heliguy/web-viewer/dist/web-component/splat-viewer.min.js"></script> <script> const viewer = document.getElementById('viewer') viewer.addEventListener('ready', () => { viewer.load('path/to/model.splat') }) </script> </body> </html> ``` #### Embed via iframe Use the provided `viewer.html` page that reads query parameters and configures the web component. Host `viewer.html` alongside `dist/web-component/splat-viewer.min.js`. Supported query params: - `src` (required): URL to your model (encode it!) - `width` (optional): CSS width (e.g. `100%`, `800px`) - `height` (optional): CSS height (e.g. `100vh`, `600px`) - `autoFocus` (optional): `true` | `false` (default: `true`) - `stats` (optional): `true` | `false` Example: ```html <iframe src="https://your-domain.com/viewer.html?src=https%3A%2F%2Fcdn.yourdomain.com%2Fmodels%2FRyhope.ply&width=100%25&height=100vh&autoFocus=true" width="100%" height="600" style="border:0; background:#000" allowfullscreen ></iframe> ``` Notes: - Ensure CORS allows the viewer origin to fetch the model (`Access-Control-Allow-Origin`). - Prefer hosting `viewer.html`, the bundle, and models on the same domain to avoid CORS. - URL-encode the `src` value. #### React / Next.js ```tsx import { useEffect, useRef } from 'react' import '@heliguy/web-viewer' function App() { const viewerRef = useRef<any>(null) useEffect(() => { const viewer = viewerRef.current if (!viewer) return const handleReady = () => { viewer.load('/models/scene.splat') } viewer.addEventListener('ready', handleReady) return () => viewer.removeEventListener('ready', handleReady) }, []) return ( <splat-viewer ref={viewerRef} width="100%" height="600px" auto-focus /> ) } ``` **TypeScript:** Add to your `global.d.ts`: ```typescript declare namespace JSX { interface IntrinsicElements { 'splat-viewer': React.DetailedHTMLProps< React.HTMLAttributes<HTMLElement> & { ref?: React.Ref<any> src?: string width?: string height?: string 'auto-focus'?: boolean 'enable-stats'?: boolean }, HTMLElement > } } ``` #### Vue 3 ```vue <template> <splat-viewer ref="viewerRef" width="100%" height="600px" auto-focus /> </template> <script setup> import { ref, onMounted } from 'vue' import '@heliguy/web-viewer' const viewerRef = ref(null) onMounted(() => { const viewer = viewerRef.value const handleReady = () => { viewer.load('/models/scene.splat') } viewer.addEventListener('ready', handleReady) }) </script> ``` ## Commands | Command | Description | | ----------------------------- | ---------------------------------- | | `npm run web-component:serve` | Start development server | | `npm run web-component:build` | Build self-contained web component | | `npm run web-component:clean` | Clean build artifacts | | `npm run lint` | Check code quality with ESLint | | `npm run lint:fix` | Fix linting issues automatically | | `npm run format` | Format code with Prettier | | `npm run format:check` | Check code formatting | ## Web Component API ### Attributes #### Basic Configuration - `src` - Model file path or URL - `width` - Canvas width (default: "100%") - `height` - Canvas height (default: "400px") - `auto-focus` - Auto-focus on load (default: true) - `enable-stats` - Show performance stats (default: false) - `max-splats` - Maximum splat count (default: 2000000) #### Camera Controls - `orbit-sensitivity` - Mouse orbit sensitivity (default: 0.3) - `pan-sensitivity` - Mouse pan sensitivity (default: 0.5) - `zoom-sensitivity` - Mouse zoom sensitivity (default: 0.1) - `min-distance` - Minimum camera distance (default: 1) - `max-distance` - Maximum camera distance (default: 100) #### Navigation Cube - `enable-navigation-cube` - Show interactive navigation cube (default: false) - `navigation-cube-size` - Cube size in pixels (default: 120) - `navigation-cube-transition` - Camera transition duration in ms (default: 800) ### Events #### Core Events - `ready` - Component initialized - `loading-start` - Model loading started - `loading-progress` - Model loading progress update - `loaded` - Model loaded successfully - `error` - Loading error occurred - `stats-update` - Performance statistics update - `camera-change` - Camera position/target changed - `interaction-start` / `interaction-end` - User interaction with orbit controls #### Multi-Model Events (v1.0.0) - `model-added` - Model added to scene - `model-removed` - Model removed from scene - `scene-cleared` - All models cleared #### Transform Events (v1.0.0) - `model-transform-changed` - Model transform updated #### Camera Events (v1.0.0) - `camera-mode-changed` - Camera mode switched (orbit/fly) - `fly-camera-move` - Fly camera position changed - `fly-camera-look` - Fly camera rotation changed #### Scene Config Events (v1.0.0) - `camera-fov-changed` - Camera FOV changed - `background-color-changed` - Background color changed ## Project Structure ``` src/ ā”œā”€ā”€ web-component/ # Main web component implementation │ ā”œā”€ā”€ SplatViewerElement.ts # Web component class │ ā”œā”€ā”€ SplatViewerCore.ts # Core rendering engine │ ā”œā”€ā”€ NavigationCubeController.ts # Navigation cube controller │ ā”œā”€ā”€ OrbitCameraScript.ts # Camera controls │ ā”œā”€ā”€ navigation-cube/ # Navigation cube modules │ │ ā”œā”€ā”€ CubeRenderer.ts # 3D cube visual rendering │ │ ā”œā”€ā”€ ControlPointManager.ts # Control point UI elements │ │ └── CameraTransitionController.ts # Camera animations │ ā”œā”€ā”€ types/ # TypeScript definitions │ ā”œā”€ā”€ loader/ # File loading system (.ply, .sog, .splat) │ └── utils/ # Utility functions ā”œā”€ā”€ build/ # Build configuration ā”œā”€ā”€ dist/web-component/ # Built self-contained web component │ └── splat-viewer.min.js # Single 2MB file with PlayCanvas ā”œā”€ā”€ test-web-component.html # Basic test page └── test-navigation-cube.html # Navigation cube test page assets/ # 3D model assets for testing docs/ # Documentation ``` ## Supported File Formats - **.sog** - Spatially Ordered Gaussians (PlayCanvas native support) - **.splat** - Gaussian Splat files (PlayCanvas native support) - **.ply** - Point cloud files (standard format) - **.ply.compressed** - Compressed PLY files (gzip/deflate) ## Self-Contained Bundle The web component is distributed as a single 2MB file (`splat-viewer.min.js`) that includes: - Complete PlayCanvas engine (v2.11.8) - Web component implementation - File format loaders - Performance monitoring - Interactive controls **Benefits:** - Zero external dependencies - Version consistency guaranteed - Easy embedding in any web application - Offline functionality ## Development ```bash # Start development server npm run web-component:serve # Build for production npm run web-component:build # Clean build artifacts npm run web-component:clean ``` ## Deployment ### Netlify - Publish directory: `dist` - Build command: `npm run build` - Node version: 18 (configured in `netlify.toml`) The build emits `dist/index.html` from `viewer.html` using a small zero-dependency Node script (`build/copy-viewer-to-index.mjs`). Rollup uses an ESM config at `build/rollup.config.mjs`. ## License MIT License - see LICENSE file for details.