@@ -44,6 +44,7 @@ <h1>SceneView Web</h1>
4444
4545< div class ="badge "> Powered by SceneView + Filament.js</ div >
4646
47+ < script src ="https://cdn.jsdelivr.net/npm/filament@1.52.3/filament.js "> </ script >
4748< script src ="js/sceneview.js "> </ script >
4849< script >
4950const MODELS = {
@@ -56,27 +57,111 @@ <h1>SceneView Web</h1>
5657const status = document . getElementById ( 'status' ) ;
5758let viewer = null ;
5859
59- // One liner!
60- SceneView . modelViewer ( 'viewer' , MODELS [ 'DamagedHelmet' ] ) . then ( v => {
61- viewer = v ;
62- status . textContent = 'Drag to rotate · Scroll to zoom' ;
63- status . style . color = '#34a853' ;
64- } ) . catch ( e => {
65- status . textContent = 'Error: ' + e . message ;
66- status . style . color = '#ea4335' ;
60+ // Wait for Filament WASM to be ready, then use one-liner
61+ const DEFAULT_MODEL = MODELS [ 'DamagedHelmet' ] ;
62+ Filament . init ( [ DEFAULT_MODEL ] , function ( ) {
63+ status . textContent = 'Creating viewer...' ;
64+ try {
65+ const canvas = document . getElementById ( 'viewer' ) ;
66+ canvas . width = canvas . clientWidth * devicePixelRatio ;
67+ canvas . height = canvas . clientHeight * devicePixelRatio ;
68+
69+ const engine = Filament . Engine . create ( canvas ) ;
70+ const scene = engine . createScene ( ) ;
71+ const renderer = engine . createRenderer ( ) ;
72+ const cam = engine . createCamera ( Filament . EntityManager . get ( ) . create ( ) ) ;
73+ const view = engine . createView ( ) ;
74+ const sc = engine . createSwapChain ( ) ;
75+
76+ view . setCamera ( cam ) ;
77+ view . setScene ( scene ) ;
78+ view . setViewport ( [ 0 , 0 , canvas . width , canvas . height ] ) ;
79+ renderer . setClearOptions ( { clearColor : [ 0.05 , 0.06 , 0.1 , 1.0 ] , clear : true } ) ;
80+ cam . setProjectionFov ( 45 , canvas . width / canvas . height , 0.1 , 100 , Filament . Camera$Fov . VERTICAL ) ;
81+
82+ // Lights
83+ const sun = Filament . EntityManager . get ( ) . create ( ) ;
84+ Filament . LightManager . Builder ( Filament . LightManager$Type . SUN )
85+ . color ( [ 0.98 , 0.92 , 0.89 ] ) . intensity ( 110000 ) . direction ( [ 0.6 , - 1 , - 0.8 ] )
86+ . sunAngularRadius ( 1.9 ) . sunHaloSize ( 10 ) . sunHaloFalloff ( 80 )
87+ . build ( engine , sun ) ;
88+ scene . addEntity ( sun ) ;
89+
90+ const fill = Filament . EntityManager . get ( ) . create ( ) ;
91+ Filament . LightManager . Builder ( Filament . LightManager$Type . DIRECTIONAL )
92+ . color ( [ 0.6 , 0.65 , 0.8 ] ) . intensity ( 30000 ) . direction ( [ - 0.5 , 0.5 , 1 ] )
93+ . build ( engine , fill ) ;
94+ scene . addEntity ( fill ) ;
95+
96+ // Load default model
97+ const loader = engine . createAssetLoader ( ) ;
98+ let currentAsset = null ;
99+
100+ function loadModel ( url ) {
101+ if ( Filament . assets [ url ] ) {
102+ showModel ( url ) ;
103+ } else {
104+ status . textContent = 'Downloading model...' ;
105+ status . style . color = '#8ab4f8' ;
106+ Filament . init ( [ url ] , function ( ) { showModel ( url ) ; } ) ;
107+ }
108+ }
109+
110+ function showModel ( url ) {
111+ // Remove old
112+ if ( currentAsset ) {
113+ scene . remove ( currentAsset . getRoot ( ) ) ;
114+ currentAsset . getRenderableEntities ( ) . forEach ( e => scene . remove ( e ) ) ;
115+ }
116+ const asset = loader . createAsset ( Filament . assets [ url ] ) ;
117+ if ( asset ) {
118+ asset . loadResources ( ) ;
119+ scene . addEntity ( asset . getRoot ( ) ) ;
120+ scene . addEntities ( asset . getRenderableEntities ( ) ) ;
121+ currentAsset = asset ;
122+ status . textContent = 'Drag to rotate · Scroll to zoom' ;
123+ status . style . color = '#34a853' ;
124+ }
125+ }
126+
127+ showModel ( DEFAULT_MODEL ) ;
128+
129+ // Orbit controls
130+ let angle = 0 , orbitR = 3.5 , orbitH = 0.8 , dragging = false , lastX = 0 , lastY = 0 , autoRotate = true ;
131+
132+ canvas . addEventListener ( 'mousedown' , e => { dragging = true ; lastX = e . clientX ; lastY = e . clientY ; autoRotate = false ; } ) ;
133+ canvas . addEventListener ( 'mousemove' , e => { if ( ! dragging ) return ; angle += ( e . clientX - lastX ) * 0.005 ; orbitH += ( e . clientY - lastY ) * 0.01 ; lastX = e . clientX ; lastY = e . clientY ; } ) ;
134+ canvas . addEventListener ( 'mouseup' , ( ) => dragging = false ) ;
135+ canvas . addEventListener ( 'mouseleave' , ( ) => dragging = false ) ;
136+ canvas . addEventListener ( 'wheel' , e => { e . preventDefault ( ) ; orbitR *= ( 1 + e . deltaY * 0.001 ) ; orbitR = Math . max ( 0.5 , Math . min ( 20 , orbitR ) ) ; } , { passive : false } ) ;
137+ canvas . addEventListener ( 'touchstart' , e => { if ( e . touches . length === 1 ) { dragging = true ; lastX = e . touches [ 0 ] . clientX ; lastY = e . touches [ 0 ] . clientY ; autoRotate = false ; } } ) ;
138+ canvas . addEventListener ( 'touchmove' , e => { if ( ! dragging ) return ; e . preventDefault ( ) ; angle += ( e . touches [ 0 ] . clientX - lastX ) * 0.005 ; orbitH += ( e . touches [ 0 ] . clientY - lastY ) * 0.01 ; lastX = e . touches [ 0 ] . clientX ; lastY = e . touches [ 0 ] . clientY ; } , { passive : false } ) ;
139+ canvas . addEventListener ( 'touchend' , ( ) => dragging = false ) ;
140+
141+ // Render loop
142+ function render ( ) {
143+ if ( autoRotate ) angle += 0.006 ;
144+ cam . lookAt ( [ Math . sin ( angle ) * orbitR , orbitH , Math . cos ( angle ) * orbitR ] , [ 0 , 0 , 0 ] , [ 0 , 1 , 0 ] ) ;
145+ if ( renderer . beginFrame ( sc ) ) { renderer . render ( sc , view ) ; renderer . endFrame ( ) ; }
146+ engine . execute ( ) ;
147+ requestAnimationFrame ( render ) ;
148+ }
149+ render ( ) ;
150+
151+ // Expose for model switching buttons
152+ window . loadModel = loadModel ;
153+
154+ } catch ( e ) {
155+ status . textContent = 'Error: ' + e . message ;
156+ status . style . color = '#ea4335' ;
157+ console . error ( e ) ;
158+ }
67159} ) ;
68160
69161function load ( btn , name ) {
70162 document . querySelectorAll ( '.models button' ) . forEach ( b => b . classList . remove ( 'active' ) ) ;
71163 btn . classList . add ( 'active' ) ;
72- if ( viewer ) {
73- status . textContent = 'Loading ' + name + '...' ;
74- status . style . color = '#8ab4f8' ;
75- viewer . loadModel ( MODELS [ name ] ) . then ( ( ) => {
76- status . textContent = 'Drag to rotate · Scroll to zoom' ;
77- status . style . color = '#34a853' ;
78- } ) ;
79- }
164+ if ( window . loadModel ) window . loadModel ( MODELS [ name ] ) ;
80165}
81166</ script >
82167</ body >
0 commit comments