|
30 | 30 |
|
31 | 31 | let container;
|
32 | 32 | let camera, scene, renderer;
|
| 33 | + |
| 34 | + let listener; |
33 | 35 | let controller1, controller2;
|
34 | 36 | let controllerGrip1, controllerGrip2;
|
| 37 | + |
35 | 38 | const box = new THREE.Box3();
|
36 | 39 |
|
37 |
| - const controllers = []; |
38 |
| - const oscillators = []; |
39 | 40 | let controls, group;
|
40 |
| - let audioCtx = null; |
41 |
| - |
42 |
| - // minor pentatonic scale, so whichever notes is stricken would be more pleasant |
43 |
| - const musicScale = [ 0, 3, 5, 7, 10 ]; |
44 | 41 |
|
45 | 42 | init();
|
46 | 43 |
|
47 |
| - function initAudio() { |
48 |
| - |
49 |
| - if ( audioCtx !== null ) { |
50 |
| - |
51 |
| - return; |
52 |
| - |
53 |
| - } |
54 |
| - |
55 |
| - audioCtx = new ( window.AudioContext || window.webkitAudioContext )(); |
56 |
| - function createOscillator() { |
57 |
| - |
58 |
| - // creates oscillator |
59 |
| - const oscillator = audioCtx.createOscillator(); |
60 |
| - oscillator.type = 'sine'; // possible values: sine, triangle, square |
61 |
| - oscillator.start(); |
62 |
| - return oscillator; |
63 |
| - |
64 |
| - } |
65 |
| - |
66 |
| - oscillators.push( createOscillator() ); |
67 |
| - oscillators.push( createOscillator() ); |
68 |
| - window.oscillators = oscillators; |
69 |
| - |
70 |
| - } |
71 |
| - |
72 | 44 | function init() {
|
73 | 45 |
|
74 | 46 | container = document.createElement( 'div' );
|
|
80 | 52 | camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 10 );
|
81 | 53 | camera.position.set( 0, 1.6, 3 );
|
82 | 54 |
|
| 55 | + listener = new THREE.AudioListener(); |
| 56 | + camera.add( listener ); |
| 57 | + |
83 | 58 | controls = new OrbitControls( camera, container );
|
84 | 59 | controls.target.set( 0, 1.6, 0 );
|
85 | 60 | controls.update();
|
|
140 | 115 | renderer.setPixelRatio( window.devicePixelRatio );
|
141 | 116 | renderer.setSize( window.innerWidth, window.innerHeight );
|
142 | 117 | renderer.setAnimationLoop( animate );
|
143 |
| - renderer.xr.addEventListener( 'sessionstart', () => initAudio() ); |
144 | 118 | renderer.shadowMap.enabled = true;
|
145 | 119 | renderer.xr.enabled = true;
|
146 | 120 | container.appendChild( renderer.domElement );
|
|
175 | 149 |
|
176 | 150 | }
|
177 | 151 |
|
178 |
| - function controllerConnected( evt ) { |
| 152 | + function controllerConnected( event ) { |
179 | 153 |
|
180 |
| - controllers.push( { |
181 |
| - gamepad: evt.data.gamepad, |
182 |
| - grip: evt.target, |
183 |
| - colliding: false, |
184 |
| - playing: false |
185 |
| - } ); |
| 154 | + const oscillator = listener.context.createOscillator(); |
| 155 | + oscillator.type = 'sine'; |
| 156 | + oscillator.start() |
186 | 157 |
|
187 |
| - } |
| 158 | + const audio = new THREE.PositionalAudio( listener ); |
| 159 | + audio.setNodeSource( oscillator ); |
| 160 | + audio.setRefDistance( 20 ); |
| 161 | + audio.setVolume( 0 ); |
188 | 162 |
|
189 |
| - function controllerDisconnected( evt ) { |
| 163 | + this.userData.gamepad = event.data.gamepad; |
| 164 | + this.userData.colliding = false; |
| 165 | + this.userData.audio = audio; |
190 | 166 |
|
191 |
| - const index = controllers.findIndex( o => o.controller === evt.target ); |
192 |
| - if ( index !== - 1 ) { |
| 167 | + this.add( audio ); |
193 | 168 |
|
194 |
| - controllers.splice( index, 1 ); |
| 169 | + } |
195 | 170 |
|
196 |
| - } |
| 171 | + function controllerDisconnected( event ) { |
| 172 | + |
| 173 | + const audio = this.userData.audio; |
| 174 | + audio.source.stop(); |
| 175 | + |
| 176 | + this.remove( audio ); |
197 | 177 |
|
198 | 178 | }
|
199 | 179 |
|
|
206 | 186 |
|
207 | 187 | }
|
208 | 188 |
|
209 |
| - // |
210 | 189 |
|
211 |
| - function handleCollisions() { |
| 190 | + function handleCollisions( controller ) { |
212 | 191 |
|
213 | 192 | for ( let i = 0; i < group.children.length; i ++ ) {
|
214 | 193 |
|
215 | 194 | group.children[ i ].collided = false;
|
216 | 195 |
|
217 | 196 | }
|
218 | 197 |
|
219 |
| - for ( let g = 0; g < controllers.length; g ++ ) { |
220 |
| - |
221 |
| - const controller = controllers[ g ]; |
222 |
| - controller.colliding = false; |
223 |
| - |
224 |
| - const { grip, gamepad } = controller; |
225 |
| - const sphere = { |
226 |
| - radius: 0.03, |
227 |
| - center: grip.position |
228 |
| - }; |
| 198 | + handleController( controllerGrip1 ); |
| 199 | + handleController( controllerGrip2 ); |
229 | 200 |
|
230 |
| - const supportHaptic = 'hapticActuators' in gamepad && gamepad.hapticActuators != null && gamepad.hapticActuators.length > 0; |
| 201 | + for ( let i = 0; i < group.children.length; i ++ ) { |
231 | 202 |
|
232 |
| - for ( let i = 0; i < group.children.length; i ++ ) { |
| 203 | + const child = group.children[ i ]; |
| 204 | + if ( ! child.collided ) { |
233 | 205 |
|
234 |
| - const child = group.children[ i ]; |
235 |
| - box.setFromObject( child ); |
236 |
| - if ( box.intersectsSphere( sphere ) ) { |
| 206 | + // reset uncollided boxes |
| 207 | + child.material.emissive.b = 0; |
| 208 | + child.scale.setScalar( 1 ); |
237 | 209 |
|
238 |
| - child.material.emissive.b = 1; |
239 |
| - const intensity = child.userData.index / group.children.length; |
240 |
| - child.scale.setScalar( 1 + Math.random() * 0.1 * intensity ); |
| 210 | + } |
241 | 211 |
|
242 |
| - if ( supportHaptic ) { |
| 212 | + } |
243 | 213 |
|
244 |
| - gamepad.hapticActuators[ 0 ].pulse( intensity, 100 ); |
| 214 | + } |
245 | 215 |
|
246 |
| - } |
| 216 | + // minor pentatonic scale, so whichever notes is stricken would be more pleasant |
| 217 | + const musicScale = [ 0, 3, 5, 7, 10 ]; |
247 | 218 |
|
248 |
| - const musicInterval = musicScale[ child.userData.index % musicScale.length ] + 12 * Math.floor( child.userData.index / musicScale.length ); |
249 |
| - oscillators[ g ].frequency.value = 110 * Math.pow( 2, musicInterval / 12 ); |
250 |
| - controller.colliding = true; |
251 |
| - group.children[ i ].collided = true; |
| 219 | + function handleController( controller ) { |
252 | 220 |
|
253 |
| - } |
| 221 | + controller.userData.colliding = false; |
254 | 222 |
|
255 |
| - } |
| 223 | + const audio = controller.userData.audio; |
| 224 | + const gamepad = controller.userData.gamepad; |
256 | 225 |
|
| 226 | + if ( audio === undefined || gamepad === undefined ) return; |
257 | 227 |
|
| 228 | + const oscillator = audio.source; |
| 229 | + const supportHaptic = 'hapticActuators' in gamepad && gamepad.hapticActuators != null && gamepad.hapticActuators.length > 0; |
258 | 230 |
|
259 |
| - if ( controller.colliding ) { |
| 231 | + const sphere = { |
| 232 | + radius: 0.03, |
| 233 | + center: controller.position |
| 234 | + }; |
260 | 235 |
|
261 |
| - if ( ! controller.playing ) { |
| 236 | + for ( let i = 0; i < group.children.length; i ++ ) { |
262 | 237 |
|
263 |
| - controller.playing = true; |
264 |
| - oscillators[ g ].connect( audioCtx.destination ); |
| 238 | + const child = group.children[ i ]; |
| 239 | + box.setFromObject( child ); |
265 | 240 |
|
266 |
| - } |
| 241 | + if ( box.intersectsSphere( sphere ) ) { |
267 | 242 |
|
268 |
| - } else { |
| 243 | + child.material.emissive.b = 1; |
| 244 | + const intensity = child.userData.index / group.children.length; |
| 245 | + child.scale.setScalar( 1 + Math.random() * 0.1 * intensity ); |
269 | 246 |
|
270 |
| - if ( controller.playing ) { |
| 247 | + if ( supportHaptic ) { |
271 | 248 |
|
272 |
| - controller.playing = false; |
273 |
| - oscillators[ g ].disconnect( audioCtx.destination ); |
| 249 | + gamepad.hapticActuators[ 0 ].pulse( intensity, 100 ); |
274 | 250 |
|
275 | 251 | }
|
276 | 252 |
|
| 253 | + const musicInterval = musicScale[ child.userData.index % musicScale.length ] + 12 * Math.floor( child.userData.index / musicScale.length ); |
| 254 | + oscillator.frequency.value = 110 * Math.pow( 2, musicInterval / 12 ); |
| 255 | + controller.userData.colliding = true; |
| 256 | + group.children[ i ].collided = true; |
| 257 | + |
277 | 258 | }
|
278 | 259 |
|
279 | 260 | }
|
280 | 261 |
|
281 |
| - for ( let i = 0; i < group.children.length; i ++ ) { |
| 262 | + if ( controller.userData.colliding ) { |
282 | 263 |
|
283 |
| - const child = group.children[ i ]; |
284 |
| - if ( ! child.collided ) { |
| 264 | + audio.setVolume( 0.5 ); |
285 | 265 |
|
286 |
| - // reset uncollided boxes |
287 |
| - child.material.emissive.b = 0; |
288 |
| - child.scale.setScalar( 1 ); |
| 266 | + } else { |
289 | 267 |
|
290 |
| - } |
| 268 | + audio.setVolume( 0 ); |
291 | 269 |
|
292 | 270 | }
|
293 | 271 |
|
|
0 commit comments