|
@@ -1,7 +1,7 @@
|
|
<!DOCTYPE html>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<html lang="en">
|
|
<head>
|
|
<head>
|
|
- <title>three.js vr - dragging</title>
|
|
|
|
|
|
+ <title>three.js vr - haptics</title>
|
|
<meta charset="utf-8">
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
<link type="text/css" rel="stylesheet" href="main.css">
|
|
<link type="text/css" rel="stylesheet" href="main.css">
|
|
@@ -42,6 +42,9 @@
|
|
let controls, group;
|
|
let controls, group;
|
|
let audioCtx = null;
|
|
let audioCtx = null;
|
|
|
|
|
|
|
|
+ // minor pentatonic scale, so whichever notes is striked would be more pleasant
|
|
|
|
+ const musicScale = [ 0, 3, 5, 7, 10, 12 ];
|
|
|
|
+
|
|
init();
|
|
init();
|
|
animate();
|
|
animate();
|
|
|
|
|
|
@@ -56,14 +59,9 @@
|
|
audioCtx = new ( window.AudioContext || window.webkitAudioContext )();
|
|
audioCtx = new ( window.AudioContext || window.webkitAudioContext )();
|
|
function createOscillator() {
|
|
function createOscillator() {
|
|
|
|
|
|
|
|
+ // creates oscillator
|
|
const oscillator = audioCtx.createOscillator();
|
|
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();
|
|
oscillator.start();
|
|
return oscillator;
|
|
return oscillator;
|
|
|
|
|
|
@@ -116,10 +114,11 @@
|
|
group = new THREE.Group();
|
|
group = new THREE.Group();
|
|
group.position.z = - 0.5;
|
|
group.position.z = - 0.5;
|
|
scene.add( group );
|
|
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 w = 0.1;
|
|
const h = 0.1;
|
|
const h = 0.1;
|
|
const minH = 1;
|
|
const minH = 1;
|
|
@@ -230,56 +229,66 @@
|
|
|
|
|
|
function handleCollisions() {
|
|
function handleCollisions() {
|
|
|
|
|
|
|
|
+ for ( let i = 0; i < group.children.length; i ++ ) {
|
|
|
|
+
|
|
|
|
+ group.children[ i ].collided = false;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
for ( let g = 0; g < controllers.length; g ++ ) {
|
|
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 = {
|
|
const sphere = {
|
|
radius: 0.03,
|
|
radius: 0.03,
|
|
center: grip.position
|
|
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 );
|
|
oscillators[ g ].connect( audioCtx.destination );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
} else {
|
|
} else {
|
|
|
|
|
|
- if ( controllers[ g ].playing ) {
|
|
|
|
|
|
+ if ( controller.playing ) {
|
|
|
|
|
|
- controllers[ g ].playing = false;
|
|
|
|
|
|
+ controller.playing = false;
|
|
oscillators[ g ].disconnect( audioCtx.destination );
|
|
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() {
|
|
function render() {
|