|
|
@@ -30,45 +30,17 @@
|
|
|
|
|
|
let container;
|
|
|
let camera, scene, renderer;
|
|
|
+
|
|
|
+ let listener;
|
|
|
let controller1, controller2;
|
|
|
let controllerGrip1, controllerGrip2;
|
|
|
+
|
|
|
const box = new THREE.Box3();
|
|
|
|
|
|
- const controllers = [];
|
|
|
- const oscillators = [];
|
|
|
let controls, group;
|
|
|
- let audioCtx = null;
|
|
|
-
|
|
|
- // minor pentatonic scale, so whichever notes is stricken would be more pleasant
|
|
|
- const musicScale = [ 0, 3, 5, 7, 10 ];
|
|
|
|
|
|
init();
|
|
|
|
|
|
- function initAudio() {
|
|
|
-
|
|
|
- if ( audioCtx !== null ) {
|
|
|
-
|
|
|
- return;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- audioCtx = new ( window.AudioContext || window.webkitAudioContext )();
|
|
|
- function createOscillator() {
|
|
|
-
|
|
|
- // creates oscillator
|
|
|
- const oscillator = audioCtx.createOscillator();
|
|
|
- oscillator.type = 'sine'; // possible values: sine, triangle, square
|
|
|
- oscillator.start();
|
|
|
- return oscillator;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- oscillators.push( createOscillator() );
|
|
|
- oscillators.push( createOscillator() );
|
|
|
- window.oscillators = oscillators;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
function init() {
|
|
|
|
|
|
container = document.createElement( 'div' );
|
|
|
@@ -80,6 +52,9 @@
|
|
|
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 10 );
|
|
|
camera.position.set( 0, 1.6, 3 );
|
|
|
|
|
|
+ listener = new THREE.AudioListener();
|
|
|
+ camera.add( listener );
|
|
|
+
|
|
|
controls = new OrbitControls( camera, container );
|
|
|
controls.target.set( 0, 1.6, 0 );
|
|
|
controls.update();
|
|
|
@@ -140,7 +115,6 @@
|
|
|
renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
renderer.setAnimationLoop( animate );
|
|
|
- renderer.xr.addEventListener( 'sessionstart', () => initAudio() );
|
|
|
renderer.shadowMap.enabled = true;
|
|
|
renderer.xr.enabled = true;
|
|
|
container.appendChild( renderer.domElement );
|
|
|
@@ -175,25 +149,31 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
- function controllerConnected( evt ) {
|
|
|
+ function controllerConnected( event ) {
|
|
|
|
|
|
- controllers.push( {
|
|
|
- gamepad: evt.data.gamepad,
|
|
|
- grip: evt.target,
|
|
|
- colliding: false,
|
|
|
- playing: false
|
|
|
- } );
|
|
|
+ const oscillator = listener.context.createOscillator();
|
|
|
+ oscillator.type = 'sine';
|
|
|
+ oscillator.start()
|
|
|
|
|
|
- }
|
|
|
+ const audio = new THREE.PositionalAudio( listener );
|
|
|
+ audio.setNodeSource( oscillator );
|
|
|
+ audio.setRefDistance( 20 );
|
|
|
+ audio.setVolume( 0 );
|
|
|
|
|
|
- function controllerDisconnected( evt ) {
|
|
|
+ this.userData.gamepad = event.data.gamepad;
|
|
|
+ this.userData.colliding = false;
|
|
|
+ this.userData.audio = audio;
|
|
|
|
|
|
- const index = controllers.findIndex( o => o.controller === evt.target );
|
|
|
- if ( index !== - 1 ) {
|
|
|
+ this.add( audio );
|
|
|
|
|
|
- controllers.splice( index, 1 );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ function controllerDisconnected( event ) {
|
|
|
+
|
|
|
+ const audio = this.userData.audio;
|
|
|
+ audio.source.stop();
|
|
|
+
|
|
|
+ this.remove( audio );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -206,9 +186,8 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
- //
|
|
|
|
|
|
- function handleCollisions() {
|
|
|
+ function handleCollisions( controller ) {
|
|
|
|
|
|
for ( let i = 0; i < group.children.length; i ++ ) {
|
|
|
|
|
|
@@ -216,78 +195,77 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
- for ( let g = 0; g < controllers.length; g ++ ) {
|
|
|
-
|
|
|
- const controller = controllers[ g ];
|
|
|
- controller.colliding = false;
|
|
|
-
|
|
|
- const { grip, gamepad } = controller;
|
|
|
- const sphere = {
|
|
|
- radius: 0.03,
|
|
|
- center: grip.position
|
|
|
- };
|
|
|
+ handleController( controllerGrip1 );
|
|
|
+ handleController( controllerGrip2 );
|
|
|
|
|
|
- const supportHaptic = 'hapticActuators' in gamepad && gamepad.hapticActuators != null && gamepad.hapticActuators.length > 0;
|
|
|
+ for ( let i = 0; i < group.children.length; i ++ ) {
|
|
|
|
|
|
- for ( let i = 0; i < group.children.length; i ++ ) {
|
|
|
+ const child = group.children[ i ];
|
|
|
+ if ( ! child.collided ) {
|
|
|
|
|
|
- const child = group.children[ i ];
|
|
|
- box.setFromObject( child );
|
|
|
- if ( box.intersectsSphere( sphere ) ) {
|
|
|
+ // reset uncollided boxes
|
|
|
+ child.material.emissive.b = 0;
|
|
|
+ child.scale.setScalar( 1 );
|
|
|
|
|
|
- child.material.emissive.b = 1;
|
|
|
- const intensity = child.userData.index / group.children.length;
|
|
|
- child.scale.setScalar( 1 + Math.random() * 0.1 * intensity );
|
|
|
+ }
|
|
|
|
|
|
- if ( supportHaptic ) {
|
|
|
+ }
|
|
|
|
|
|
- gamepad.hapticActuators[ 0 ].pulse( intensity, 100 );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ // minor pentatonic scale, so whichever notes is stricken would be more pleasant
|
|
|
+ const musicScale = [ 0, 3, 5, 7, 10 ];
|
|
|
|
|
|
- const musicInterval = musicScale[ child.userData.index % musicScale.length ] + 12 * Math.floor( child.userData.index / musicScale.length );
|
|
|
- oscillators[ g ].frequency.value = 110 * Math.pow( 2, musicInterval / 12 );
|
|
|
- controller.colliding = true;
|
|
|
- group.children[ i ].collided = true;
|
|
|
+ function handleController( controller ) {
|
|
|
|
|
|
- }
|
|
|
+ controller.userData.colliding = false;
|
|
|
|
|
|
- }
|
|
|
+ const audio = controller.userData.audio;
|
|
|
+ const gamepad = controller.userData.gamepad;
|
|
|
|
|
|
+ if ( audio === undefined || gamepad === undefined ) return;
|
|
|
|
|
|
+ const oscillator = audio.source;
|
|
|
+ const supportHaptic = 'hapticActuators' in gamepad && gamepad.hapticActuators != null && gamepad.hapticActuators.length > 0;
|
|
|
|
|
|
- if ( controller.colliding ) {
|
|
|
+ const sphere = {
|
|
|
+ radius: 0.03,
|
|
|
+ center: controller.position
|
|
|
+ };
|
|
|
|
|
|
- if ( ! controller.playing ) {
|
|
|
+ for ( let i = 0; i < group.children.length; i ++ ) {
|
|
|
|
|
|
- controller.playing = true;
|
|
|
- oscillators[ g ].connect( audioCtx.destination );
|
|
|
+ const child = group.children[ i ];
|
|
|
+ box.setFromObject( child );
|
|
|
|
|
|
- }
|
|
|
+ if ( box.intersectsSphere( sphere ) ) {
|
|
|
|
|
|
- } else {
|
|
|
+ child.material.emissive.b = 1;
|
|
|
+ const intensity = child.userData.index / group.children.length;
|
|
|
+ child.scale.setScalar( 1 + Math.random() * 0.1 * intensity );
|
|
|
|
|
|
- if ( controller.playing ) {
|
|
|
+ if ( supportHaptic ) {
|
|
|
|
|
|
- controller.playing = false;
|
|
|
- oscillators[ g ].disconnect( audioCtx.destination );
|
|
|
+ gamepad.hapticActuators[ 0 ].pulse( intensity, 100 );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ const musicInterval = musicScale[ child.userData.index % musicScale.length ] + 12 * Math.floor( child.userData.index / musicScale.length );
|
|
|
+ oscillator.frequency.value = 110 * Math.pow( 2, musicInterval / 12 );
|
|
|
+ controller.userData.colliding = true;
|
|
|
+ group.children[ i ].collided = true;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- for ( let i = 0; i < group.children.length; i ++ ) {
|
|
|
+ if ( controller.userData.colliding ) {
|
|
|
|
|
|
- const child = group.children[ i ];
|
|
|
- if ( ! child.collided ) {
|
|
|
+ audio.setVolume( 0.5 );
|
|
|
|
|
|
- // reset uncollided boxes
|
|
|
- child.material.emissive.b = 0;
|
|
|
- child.scale.setScalar( 1 );
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ audio.setVolume( 0 );
|
|
|
|
|
|
}
|
|
|
|