From c77a176b38ee88c611b83d7af5a6d1e22d83b0c4 Mon Sep 17 00:00:00 2001 From: Joshua Koo Date: Tue, 25 Jan 2022 19:03:04 -0800 Subject: [PATCH] Improve vr haptics example (#23307) * Improve vr haptics example - make audio more pleasant a) changing frequency to a pentatonic b) changing oscillator from square to sine type - fix a bug so multiple boxes can simultaneously light up when more than 1 controllers is used * Make vr haptics example compatible with WebXR emulator * Make sure that we don't get array out of bounds for musical scale again --- examples/webxr_vr_haptics.html | 84 +++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/examples/webxr_vr_haptics.html b/examples/webxr_vr_haptics.html index 1ea2516ff79a9d..57e0a0c6cf4e91 100644 --- a/examples/webxr_vr_haptics.html +++ b/examples/webxr_vr_haptics.html @@ -1,7 +1,7 @@ - three.js vr - dragging + three.js vr - haptics @@ -42,6 +42,9 @@ let controls, group; let audioCtx = null; + // minor pentatonic scale, so whichever notes is striked would be more pleasant + const musicScale = [ 0, 3, 5, 7, 10, 12 ]; + init(); animate(); @@ -56,14 +59,9 @@ audioCtx = new ( window.AudioContext || window.webkitAudioContext )(); function createOscillator() { + // creates oscillator const oscillator = audioCtx.createOscillator(); - const real = Array.from( { length: 8192 }, ( _, n ) => ( - n === 0 ? - 0 : - 4 / ( n * Math.PI ) * Math.sin( Math.PI * n * 0.18 ) - ) ); - const imag = real.map( () => 0 ); - oscillator.setPeriodicWave( audioCtx.createPeriodicWave( Float32Array.from( real ), Float32Array.from( imag ) ) ); + oscillator.type = 'sine'; // possible values: sine, triangle, square oscillator.start(); return oscillator; @@ -116,10 +114,11 @@ group = new THREE.Group(); group.position.z = - 0.5; scene.add( group ); + const BOXES = 10; - for ( let i = 0; i < 10; i ++ ) { + for ( let i = 0; i < BOXES; i ++ ) { - const intensity = ( i + 1 ) / 10; + const intensity = ( i + 1 ) / BOXES; const w = 0.1; const h = 0.1; const minH = 1; @@ -230,56 +229,66 @@ function handleCollisions() { + for ( let i = 0; i < group.children.length; i ++ ) { + + group.children[ i ].collided = false; + + } + for ( let g = 0; g < controllers.length; g ++ ) { - controllers[ g ].colliding = false; + const controller = controllers[ g ]; + controller.colliding = false; - const { grip, gamepad } = controllers[ g ]; + const { grip, gamepad } = controller; const sphere = { radius: 0.03, center: grip.position }; - if ( 'hapticActuators' in gamepad && gamepad.hapticActuators != null && gamepad.hapticActuators.length > 0 ) { + 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 ]; - box.setFromObject( child ); - if ( box.intersectsSphere( sphere ) ) { + const child = group.children[ i ]; + box.setFromObject( child ); + if ( box.intersectsSphere( sphere ) ) { - child.material.emissive.b = 1; - const intensity = child.userData.index / group.children.length; - child.scale.setScalar( 1 + Math.random() * 0.1 * intensity ); - gamepad.hapticActuators[ 0 ].pulse( intensity, 100 ); - oscillators[ g ].frequency.value = 100 + intensity * 60; - controllers[ g ].colliding = true; + child.material.emissive.b = 1; + const intensity = child.userData.index / group.children.length; + child.scale.setScalar( 1 + Math.random() * 0.1 * intensity ); - } else { + if ( supportHaptic ) { - child.material.emissive.b = 0; - child.scale.setScalar( 1 ); + gamepad.hapticActuators[ 0 ].pulse( intensity, 100 ); } + 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; + } } - if ( controllers[ g ].colliding ) { - if ( ! controllers[ g ].playing ) { - controllers[ g ].playing = true; + if ( controller.colliding ) { + + if ( ! controller.playing ) { + + controller.playing = true; oscillators[ g ].connect( audioCtx.destination ); } } else { - if ( controllers[ g ].playing ) { + if ( controller.playing ) { - controllers[ g ].playing = false; + controller.playing = false; oscillators[ g ].disconnect( audioCtx.destination ); } @@ -288,6 +297,19 @@ } + for ( let i = 0; i < group.children.length; i ++ ) { + + let child = group.children[ i ]; + if ( ! child.collided ) { + + // reset uncollided boxes + child.material.emissive.b = 0; + child.scale.setScalar( 1 ); + + } + + } + } function render() {