diff --git a/examples/voxelDrumming.js b/examples/voxelDrumming.js new file mode 100644 index 0000000000..a2acb05958 --- /dev/null +++ b/examples/voxelDrumming.js @@ -0,0 +1,167 @@ +// +// voxelDrumming.js +// hifi +// +// Created by Brad Hefta-Gaub on 2/14/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Overlays, Controller, and Audio classes +// +// It adds Hydra controller "fingertip on voxels" drumming +// + +Menu.addMenuItem({ + menuName: "Developer > Hand Options", + menuItemName: "Voxel Drumming", + isCheckable: true, + isChecked: false + }); + +var collisionCenter = new Array(); +collisionCenter[0] = { x: 0, y: 0, z: 0}; +collisionCenter[1] = { x: 0, y: 0, z: 0}; + +var collisionAge = new Array(); +collisionAge[0] = 0; +collisionAge[1] = 0; + +var collisionDuration = new Array(); +collisionDuration[0] = 0; +collisionDuration[1] = 0; + +var isColliding = new Array(); +isColliding[0] = false; +isColliding[1] = false; + +var highlightVoxel = Overlays.addOverlay("cube", + { + position: { x: 0, y: 0, z: 0}, + size: 0, + color: { red: 0, green: 0, blue: 0 }, + visible: false, + lineWidth: 3, + solid: false + }); + +var collisionBubble = new Array(); +collisionBubble[0] = Overlays.addOverlay("sphere", + { + position: { x: 0, y: 0, z: 0}, + size: 0, + color: { red: 0, green: 0, blue: 0 }, + alpha: 0.5, + visible: false + }); +collisionBubble[1] = Overlays.addOverlay("sphere", + { + position: { x: 0, y: 0, z: 0}, + size: 0, + color: { red: 0, green: 0, blue: 0 }, + alpha: 0.5, + visible: false + }); + +var audioOptions = new AudioInjectionOptions();
 +audioOptions.position = { x: MyAvatar.position.x, y: MyAvatar.position.y + 1, z: MyAvatar.position.z }; +audioOptions.volume = 1; + + +function clamp(valueToClamp, minValue, maxValue) { + return Math.max(minValue, Math.min(maxValue, valueToClamp)); +} + +function produceCollisionSound(palm, voxelDetail) { + // Collision between finger and a voxel plays sound + + var palmVelocity = Controller.getSpatialControlVelocity(palm * 2); + var speed = Vec3.length(palmVelocity); + var fingerTipPosition = Controller.getSpatialControlPosition(palm * 2 + 1); + + var deltaTime = 1/60; //close enough + var LOWEST_FREQUENCY = 100.0; + var HERTZ_PER_RGB = 3.0; + var DECAY_PER_SAMPLE = 0.0005; + var DURATION_MAX = 2.0; + var MIN_VOLUME = 0.1; + var volume = MIN_VOLUME + clamp(speed, 0.0, (1.0 - MIN_VOLUME)); + var duration = volume; + + collisionCenter[palm] = fingerTipPosition; + collisionAge[palm] = deltaTime; + collisionDuration[palm] = duration; + + var voxelBrightness = voxelDetail.red + voxelDetail.green + voxelDetail.blue; + var frequency = LOWEST_FREQUENCY + (voxelBrightness * HERTZ_PER_RGB); + + audioOptions.position = fingerTipPosition; + Audio.startDrumSound(volume, frequency, DURATION_MAX, DECAY_PER_SAMPLE, audioOptions); +} + +function update() { + var deltaTime = 1/60; //close enough + + // Voxel Drumming with fingertips if enabled + if (Menu.isOptionChecked("Voxel Drumming")) { + + for (var palm = 0; palm < 2; palm++) { + var fingerTipPosition = Controller.getSpatialControlPosition(palm * 2 + 1); + + var voxel = Voxels.getVoxelEnclosingPoint(fingerTipPosition); + if (voxel.s > 0) { + if (!isColliding[palm]) { + // Collision has just started + isColliding[palm] = true; + produceCollisionSound(palm, voxel); + + // Set highlight voxel + Overlays.editOverlay(highlightVoxel, + { + position: { x: voxel.x, y: voxel.y, z: voxel.z}, + size: voxel.s + 0.002, + color: { red: voxel.red + 128, green: voxel.green + 128, blue: voxel.blue + 128 }, + visible: true + }); + } + } else { + if (isColliding[palm]) { + // Collision has just ended + isColliding[palm] = false; + Overlays.editOverlay(highlightVoxel, { visible: false }); + } + } + + if (collisionAge[palm] > 0) { + collisionAge[palm] += deltaTime; + } + + // If hand/voxel collision has happened, render a little expanding sphere + if (collisionAge[palm] > 0) { + var opacity = clamp(1 - (collisionAge[palm] / collisionDuration[palm]), 0, 1); + var size = collisionAge[palm] * 0.25; + + Overlays.editOverlay(collisionBubble[palm], + { + position: { x: collisionCenter[palm].x, y: collisionCenter[palm].y, z: collisionCenter[palm].z}, + size: size, + color: { red: 255, green: 0, blue: 0 }, + alpha: 0.5 * opacity, + visible: true + }); + + if (collisionAge[palm] > collisionDuration[palm]) { + collisionAge[palm] = 0; + Overlays.editOverlay(collisionBubble[palm], { visible: false }); + } + } + } // palm loop + } // menu item check +} +Script.willSendVisualDataCallback.connect(update); + +function scriptEnding() { + Overlays.deleteOverlay(highlightVoxel); + Overlays.deleteOverlay(collisionBubble[0]); + Overlays.deleteOverlay(collisionBubble[1]); + Menu.removeMenuItem("Developer > Hand Options","Voxel Drumming"); +} +Script.scriptEnding.connect(scriptEnding);