|
| 1 | +<!DOCTYPE html> |
| 2 | +<html lang="en"> |
| 3 | + <head> |
| 4 | + <meta charset="UTF-8"> |
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"> |
| 6 | + <title>PlayCanvas Web Components - GSplat Flipbook</title> |
| 7 | + <script type="importmap"> |
| 8 | + { |
| 9 | + "imports": { |
| 10 | + "playcanvas": "../node_modules/playcanvas/build/playcanvas.mjs" |
| 11 | + } |
| 12 | + } |
| 13 | + </script> |
| 14 | + <script type="module" src="../dist/pwc.mjs"></script> |
| 15 | + <link rel="stylesheet" href="css/example.css"> |
| 16 | + </head> |
| 17 | + <body> |
| 18 | + <pc-app antialias="false" depth="false" high-resolution="false" stencil="false"> |
| 19 | + <!-- Assets --> |
| 20 | + <pc-asset src="../node_modules/playcanvas/scripts/esm/camera-controls.mjs"></pc-asset> |
| 21 | + <pc-asset src="../node_modules/playcanvas/scripts/esm/gsplat/gsplat-flipbook.mjs"></pc-asset> |
| 22 | + <pc-asset src="../node_modules/playcanvas/scripts/esm/shadow-catcher.mjs"></pc-asset> |
| 23 | + <pc-asset src="../node_modules/playcanvas/scripts/esm/xr-controllers.mjs"></pc-asset> |
| 24 | + <pc-asset src="../node_modules/playcanvas/scripts/esm/xr-navigation.mjs"></pc-asset> |
| 25 | + <pc-asset src="../node_modules/playcanvas/scripts/esm/xr-session.mjs"></pc-asset> |
| 26 | + <pc-asset src="assets/skies/octagon-lamps-photo-studio-2k.hdr" id="studio"></pc-asset> |
| 27 | + <!-- Scene --> |
| 28 | + <pc-scene> |
| 29 | + <!-- Sky --> |
| 30 | + <pc-sky asset="studio" type="dome" center="0 0.3 0" scale="5 5 5" lighting></pc-sky> |
| 31 | + <!-- Camera (with XR support) --> |
| 32 | + <pc-entity name="camera root"> |
| 33 | + <pc-entity name="camera" position="0 1.25 2"> |
| 34 | + <pc-camera far-clip="1500" fov="50" tone-mapping="aces"></pc-camera> |
| 35 | + <pc-scripts> |
| 36 | + <pc-script name="cameraControls" attributes='{ |
| 37 | + "enableFly": false, |
| 38 | + "enablePan": false, |
| 39 | + "focusPoint": "vec3:0,1.25,0", |
| 40 | + "zoomRange": "vec2:0.2,2.5" |
| 41 | + }'></pc-script> |
| 42 | + </pc-scripts> |
| 43 | + </pc-entity> |
| 44 | + <pc-scripts> |
| 45 | + <pc-script name="xrControllers"></pc-script> |
| 46 | + <pc-script name="xrNavigation"></pc-script> |
| 47 | + <pc-script name="xrSession"></pc-script> |
| 48 | + </pc-scripts> |
| 49 | + </pc-entity> |
| 50 | + <!-- Player (Flipbook) --> |
| 51 | + <pc-entity name="player" rotation="0 -90 180"> |
| 52 | + <pc-splat unified cast-shadows></pc-splat> |
| 53 | + <pc-scripts> |
| 54 | + <pc-script name="gsplatFlipbook" attributes='{ |
| 55 | + "fps": 30, |
| 56 | + "folder": "https://code.playcanvas.com/examples_data/example_basketball_02", |
| 57 | + "filenamePattern": "{frame:03}.compressed.ply", |
| 58 | + "startFrame": 1, |
| 59 | + "endFrame": 149, |
| 60 | + "playMode": "loop", |
| 61 | + "playing": false, |
| 62 | + "preloadCount": 149 |
| 63 | + }'></pc-script> |
| 64 | + </pc-scripts> |
| 65 | + </pc-entity> |
| 66 | + <!-- Shadow Catcher --> |
| 67 | + <pc-entity name="shadow-catcher" position="0 0 0"> |
| 68 | + <pc-render type="plane" cast-shadows="false"></pc-render> |
| 69 | + <pc-scripts> |
| 70 | + <pc-script name="shadowCatcher" attributes='{ |
| 71 | + "scale": "vec3:10,10,10" |
| 72 | + }'></pc-script> |
| 73 | + </pc-scripts> |
| 74 | + </pc-entity> |
| 75 | + <!-- Light --> |
| 76 | + <pc-entity name="light" rotation="55 320 0"> |
| 77 | + <pc-light type="directional" color="1 1 1" intensity="1" |
| 78 | + cast-shadows shadow-bias="0.2" normal-offset-bias="0.05" |
| 79 | + shadow-distance="10" shadow-intensity="0.3" |
| 80 | + shadow-resolution="2048" shadow-type="pcss-32f" |
| 81 | + penumbra-size="10" penumbra-falloff="2" |
| 82 | + shadow-samples="16" shadow-blocker-samples="16"> |
| 83 | + </pc-light> |
| 84 | + </pc-entity> |
| 85 | + </pc-scene> |
| 86 | + </pc-app> |
| 87 | + <script type="module" src="js/example.mjs"></script> |
| 88 | + <script type="module"> |
| 89 | + // Wait for frames to preload before starting playback |
| 90 | + document.addEventListener('DOMContentLoaded', async () => { |
| 91 | + const appElement = await document.querySelector('pc-app').ready(); |
| 92 | + const app = appElement.app; |
| 93 | + |
| 94 | + // Wait for the flipbook script to be ready |
| 95 | + const waitForFlipbook = setInterval(() => { |
| 96 | + const player = app.root.findByName('player'); |
| 97 | + const flipbook = player?.script?.gsplatFlipbook; |
| 98 | + |
| 99 | + if (flipbook) { |
| 100 | + clearInterval(waitForFlipbook); |
| 101 | + console.log('Flipbook script found, waiting for frames to load...'); |
| 102 | + |
| 103 | + // Now wait for all preloaded frames to be loaded |
| 104 | + const checkReady = setInterval(() => { |
| 105 | + const preloaded = flipbook.preloadedFrames || []; |
| 106 | + const loadedCount = preloaded.filter(f => f.asset.loaded).length; |
| 107 | + console.log(`Preloaded: ${loadedCount}/${preloaded.length}`); |
| 108 | + |
| 109 | + // Start when we have enough frames loaded |
| 110 | + if (preloaded.length >= 100 && loadedCount >= preloaded.length) { |
| 111 | + clearInterval(checkReady); |
| 112 | + flipbook.playing = true; |
| 113 | + console.log('Frames loaded, starting playback'); |
| 114 | + } |
| 115 | + }, 500); |
| 116 | + } |
| 117 | + }, 100); |
| 118 | + }); |
| 119 | + </script> |
| 120 | + </body> |
| 121 | +</html> |
0 commit comments