From 80c18c227e3874a8dd0d37b9aab232e75e8f3b01 Mon Sep 17 00:00:00 2001 From: James Pollack Date: Thu, 3 Sep 2015 16:42:14 -0700 Subject: [PATCH 1/5] Attach the wand.js script to an entity and then move it around to make bubbles. Customize bubble behavior with bubble.js - make sure to update the path reference in wand.js --- examples/toys/bubblewand/bubble.js | 39 ++++ examples/toys/bubblewand/wand.js | 343 +++++++++++++++++++++++++++++ 2 files changed, 382 insertions(+) create mode 100644 examples/toys/bubblewand/bubble.js create mode 100644 examples/toys/bubblewand/wand.js diff --git a/examples/toys/bubblewand/bubble.js b/examples/toys/bubblewand/bubble.js new file mode 100644 index 0000000000..ae673b994e --- /dev/null +++ b/examples/toys/bubblewand/bubble.js @@ -0,0 +1,39 @@ +(function() { + + var popSound; + this.preload = function(entityID) { + // console.log('bubble preload') + this.entityID = entityID; + popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav"); + + } + + this.collisionWithEntity = function(myID, otherID, collision) { + + Entities.deleteEntity(myID); + this.burstBubbleSound(collision.contactPoint) + + }; + + this.unload = function(entityID) { + // console.log('bubble unload') + // this.properties = Entities.getEntityProperties(entityID); + //var location = this.properties.position; + //this.burstBubbleSound(); + }; + + + + this.burstBubbleSound = function(location) { + + var audioOptions = { + volume: 0.5, + position: location + } + + //Audio.playSound(popSound, audioOptions); + + } + + +}) \ No newline at end of file diff --git a/examples/toys/bubblewand/wand.js b/examples/toys/bubblewand/wand.js new file mode 100644 index 0000000000..3e16fa5e08 --- /dev/null +++ b/examples/toys/bubblewand/wand.js @@ -0,0 +1,343 @@ +// wand.js +// part of bubblewand +// +// Created by James B. Pollack @imgntn -- 09/03/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Makes bubbles when you wave the object around, or hold it near your mouth and make noise into the microphone. +// +// For the example, it's attached to a wand -- but you can attach it to whatever entity you want. I dream of BubbleBees :) bzzzz...pop! +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +function randInt(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + + +function convertRange(value, r1, r2) { + return (value - r1[0]) * (r2[1] - r2[0]) / (r1[1] - r1[0]) + r2[0]; +} + +// helpers -- @zappoman +// Computes the penetration between a point and a sphere (centered at the origin) +// if point is inside sphere: returns true and stores the result in 'penetration' +// (the vector that would move the point outside the sphere) +// otherwise returns false +function findSphereHit(point, sphereRadius) { + var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations + var vectorLength = Vec3.length(point); + if (vectorLength < EPSILON) { + return true; + } + var distance = vectorLength - sphereRadius; + if (distance < 0.0) { + return true; + } + return false; +} + +function findSpherePointHit(sphereCenter, sphereRadius, point) { + return findSphereHit(Vec3.subtract(point, sphereCenter), sphereRadius); +} + +function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadius) { + return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter); +} + +(function() { + var console = {}; + console.log = function(p) { + if (arguments.length > 1) { + + for (var i = 1; i < arguments.length; i++) { + print(arguments[i]) + } + + } else { + print(p) + } + + } + + var bubbleModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx"; + var bubbleScript = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/bubble.js?' + randInt(2, 5096); + var popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav"); + var wandModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx"; + + + + var targetSize = 0.4; + var targetColor = { + red: 128, + green: 128, + blue: 128 + }; + var targetColorHit = { + red: 0, + green: 255, + blue: 0 + }; + var moveCycleColor = { + red: 255, + green: 255, + blue: 0 + }; + + var handSize = 0.25; + var leftCubePosition = MyAvatar.getLeftPalmPosition(); + var rightCubePosition = MyAvatar.getRightPalmPosition(); + + var RIGHT_FRONT = 512; + + var leftHand = Overlays.addOverlay("cube", { + position: leftCubePosition, + size: handSize, + color: { + red: 0, + green: 0, + blue: 255 + }, + alpha: 1, + solid: false + }); + + var rightHand = Overlays.addOverlay("cube", { + position: rightCubePosition, + size: handSize, + color: { + red: 255, + green: 0, + blue: 0 + }, + alpha: 1, + solid: false + }); + + var gustZoneOverlay = Overlays.addOverlay("cube", { + position: getGustDetectorPosition(), + size: targetSize, + color: targetColor, + alpha: 1, + solid: false + }); + + + function getGustDetectorPosition() { + var DISTANCE_IN_FRONT = 0.2; + var DISTANCE_UP = 0.5; + var DISTANCE_TO_SIDE = 0.0; + + var up = Quat.getUp(MyAvatar.orientation); + var front = Quat.getFront(MyAvatar.orientation); + var right = Quat.getRight(MyAvatar.orientation); + + var upOffset = Vec3.multiply(up, DISTANCE_UP); + var rightOffset = Vec3.multiply(right, DISTANCE_TO_SIDE); + var frontOffset = Vec3.multiply(front, DISTANCE_IN_FRONT); + + var offset = Vec3.sum(Vec3.sum(rightOffset, frontOffset), upOffset); + var position = Vec3.sum(MyAvatar.position, offset); + return position; + } + + + var BUBBLE_GRAVITY = { + x: 0, + y: -0.05, + z: 0 + } + + + var thisEntity = this; + + this.preload = function(entityID) { + print('PRELOAD') + this.entityID = entityID; + this.properties = Entities.getEntityProperties(this.entityID); + BubbleWand.lastPosition = this.properties.position; + } + + this.unload = function(entityID) { + Overlays.deleteOverlay(leftHand); + Overlays.deleteOverlay(rightHand); + Overlays.deleteOverlay(rightFront) + Entities.editEntity(entityID, { + name: "" + }); + Script.update.disconnect(BubbleWand.update); + collectGarbage(); + }; + + + var BubbleWand = { + bubbles: [], + currentBubble: null, + update: function() { + BubbleWand.updateControllerState(); + }, + updateControllerState: function() { + var _t = this; + var properties = Entities.getEntityProperties(thisEntity.entityID); + var wandPosition = properties.position; + + var leftHandPos = MyAvatar.getLeftPalmPosition(); + var rightHandPos = MyAvatar.getRightPalmPosition(); + + Overlays.editOverlay(leftHand, { + position: leftHandPos + }); + Overlays.editOverlay(rightHand, { + position: rightHandPos + }); + + //not really a sphere... + var hitTargetWithWand = findSphereSphereHit(wandPosition, handSize / 2, getGustDetectorPosition(), targetSize / 2) + + var mouthMode; + if (hitTargetWithWand) { + Overlays.editOverlay(gustZoneOverlay, { + position: getGustDetectorPosition(), + color: targetColorHit + }) + mouthMode = true; + + } else { + Overlays.editOverlay(gustZoneOverlay, { + position: getGustDetectorPosition(), + color: targetColor + }) + mouthMode=false; + } + + var velocity = Vec3.subtract(wandPosition,BubbleWand.lastPosition) + + + + _t.lastPosition = wandPosition; + + //print('VELOCITY:::'+JSON.stringify(velocity)) + var velocityStrength = Vec3.length(velocity) *100; + print('velocityStrength::' + velocityStrength); + //todo: angular velocity without the controller + // var angularVelocity = Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip); + var dimensions = Entities.getEntityProperties(_t.currentBubble).dimensions; + + var volumeLevel = MyAvatar.audioAverageLoudness; + var convertedVolume = convertRange(volumeLevel, [0, 5000], [0, 10]); + // print('CONVERTED VOLUME:' + convertedVolume); + + var growthFactor = convertedVolume + velocityStrength; + // print('growthFactor::' + growthFactor); + if (velocityStrength > 1 || convertedVolume > 1) { + var bubbleSize = randInt(1, 5) + bubbleSize = bubbleSize / 10; + if (dimensions.x > bubbleSize) { + console.log('RELEASE BUBBLE') + var lifetime = randInt(3, 8); + //sound is somewhat unstable at the moment + // Script.setTimeout(function() { + // _t.burstBubbleSound(_t.currentBubble) + // }, lifetime * 1000) + //need to add some kind of forward velocity for bubble that you blow + Entities.editEntity(_t.currentBubble, { + velocity: Vec3.normalize(velocity), + // angularVelocity: Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip), + lifetime: lifetime + }); + + _t.spawnBubble(); + return + } else { + if (mouthMode) { + dimensions.x += 0.015 * convertedVolume; + dimensions.y += 0.015 * convertedVolume; + dimensions.z += 0.015 * convertedVolume; + + } else { + dimensions.x += 0.015 * velocityStrength; + dimensions.y += 0.015 * velocityStrength; + dimensions.z += 0.015 * velocityStrength; + } + + } + } else { + if (dimensions.x >= 0.02) { + dimensions.x -= 0.001; + dimensions.y -= 0.001; + dimensions.z -= 0.001; + } + + } + + Entities.editEntity(_t.currentBubble, { + position: wandPosition, + dimensions: dimensions + }); + + }, + burstBubbleSound: function(bubble) { + var position = Entities.getEntityProperties(bubble).position; + var orientation = Entities.getEntityProperties(bubble).orientation; + console.log('bubble position at pop: ' + JSON.stringify(position)); + var audioOptions = { + volume: 0.5, + position: position, + orientation: orientation + } + + //Audio.playSound(popSound, audioOptions); + + //remove this bubble from the array + var i = BubbleWand.bubbles.indexOf(bubble); + + if (i != -1) { + BubbleWand.bubbles.splice(i, 1); + } + + }, + spawnBubble: function() { + console.log('spawning bubble') + var _t = this; + var properties = Entities.getEntityProperties(thisEntity.entityID); + var wandPosition = properties.position; + + + _t.currentBubble = Entities.addEntity({ + type: 'Model', + modelURL: bubbleModel, + position: wandPosition, + dimensions: { + x: 0.01, + y: 0.01, + z: 0.01 + }, + ignoreForCollisions: true, + gravity: BUBBLE_GRAVITY, + // collisionSoundURL:popSound, + shapeType: "sphere", + script: bubbleScript, + }); + _t.bubbles.push(_t.currentBubble) + }, + init: function() { + var _t = this; + _t.spawnBubble(); + Script.update.connect(BubbleWand.update); + } + } + + function collectGarbage() { + console.log('COLLECTING GARBAGE!!!') + Entities.deleteEntity(BubbleWand.currentBubble); + while (BubbleWand.bubbles.length > 0) { + Entities.deleteEntity(BubbleWand.bubbles.pop()); + } + } + + BubbleWand.init(); + + + +}) \ No newline at end of file From cf0d2361747fad92fdc06dfdbc51b12d45179608 Mon Sep 17 00:00:00 2001 From: James Pollack Date: Thu, 3 Sep 2015 16:50:37 -0700 Subject: [PATCH 2/5] silence logging statements for now --- examples/toys/bubblewand/wand.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/toys/bubblewand/wand.js b/examples/toys/bubblewand/wand.js index 3e16fa5e08..c49ba8d97d 100644 --- a/examples/toys/bubblewand/wand.js +++ b/examples/toys/bubblewand/wand.js @@ -153,7 +153,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu var thisEntity = this; this.preload = function(entityID) { - print('PRELOAD') + // print('PRELOAD') this.entityID = entityID; this.properties = Entities.getEntityProperties(this.entityID); BubbleWand.lastPosition = this.properties.position; @@ -219,7 +219,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu //print('VELOCITY:::'+JSON.stringify(velocity)) var velocityStrength = Vec3.length(velocity) *100; - print('velocityStrength::' + velocityStrength); + // print('velocityStrength::' + velocityStrength); //todo: angular velocity without the controller // var angularVelocity = Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip); var dimensions = Entities.getEntityProperties(_t.currentBubble).dimensions; @@ -234,7 +234,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu var bubbleSize = randInt(1, 5) bubbleSize = bubbleSize / 10; if (dimensions.x > bubbleSize) { - console.log('RELEASE BUBBLE') + // console.log('RELEASE BUBBLE') var lifetime = randInt(3, 8); //sound is somewhat unstable at the moment // Script.setTimeout(function() { @@ -280,7 +280,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu burstBubbleSound: function(bubble) { var position = Entities.getEntityProperties(bubble).position; var orientation = Entities.getEntityProperties(bubble).orientation; - console.log('bubble position at pop: ' + JSON.stringify(position)); + //console.log('bubble position at pop: ' + JSON.stringify(position)); var audioOptions = { volume: 0.5, position: position, @@ -298,7 +298,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu }, spawnBubble: function() { - console.log('spawning bubble') + // console.log('spawning bubble') var _t = this; var properties = Entities.getEntityProperties(thisEntity.entityID); var wandPosition = properties.position; @@ -329,7 +329,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu } function collectGarbage() { - console.log('COLLECTING GARBAGE!!!') + // console.log('COLLECTING GARBAGE!!!') Entities.deleteEntity(BubbleWand.currentBubble); while (BubbleWand.bubbles.length > 0) { Entities.deleteEntity(BubbleWand.bubbles.pop()); From 1e9bac8dee37d5b0c52480cdc11f639bdfbc0a5e Mon Sep 17 00:00:00 2001 From: James Pollack Date: Thu, 3 Sep 2015 17:42:41 -0700 Subject: [PATCH 3/5] add create wand script, add offset to bubble spawn point so that it is at the end of the wand --- examples/toys/bubblewand/createWand.js | 27 ++++++++++++++++++++++++++ examples/toys/bubblewand/wand.js | 16 ++++++++++----- 2 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 examples/toys/bubblewand/createWand.js diff --git a/examples/toys/bubblewand/createWand.js b/examples/toys/bubblewand/createWand.js new file mode 100644 index 0000000000..065ec0a189 --- /dev/null +++ b/examples/toys/bubblewand/createWand.js @@ -0,0 +1,27 @@ +function randInt(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +var wandModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx?" + randInt(0, 10000); +var scriptURL = "http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/wand.js" + randInt(1, 10048) +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); +var paintGun = Entities.addEntity({ + type: "Model", + modelURL: wandModel, + position: center, + dimensions: { + x: 0.1, + y: 1, + z: 0.1 + }, + collisionsWillMove: true, + shapeType: 'box', + script: scriptURL +}); + +function cleanup() { + Entities.deleteEntity(paintGun); +} + + +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/toys/bubblewand/wand.js b/examples/toys/bubblewand/wand.js index c49ba8d97d..4cd6c3850e 100644 --- a/examples/toys/bubblewand/wand.js +++ b/examples/toys/bubblewand/wand.js @@ -64,7 +64,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu var bubbleModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx"; var bubbleScript = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/bubble.js?' + randInt(2, 5096); var popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav"); - var wandModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx"; + @@ -272,7 +272,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu } Entities.editEntity(_t.currentBubble, { - position: wandPosition, + position: _t.wandTipPosition, dimensions: dimensions }); @@ -302,17 +302,23 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu var _t = this; var properties = Entities.getEntityProperties(thisEntity.entityID); var wandPosition = properties.position; - - + var upVector = Quat.getUp(properties.rotation); + var frontVector = Quat.getFront(properties.rotation); + var upOffset = Vec3.multiply(upVector,0.5); + var forwardOffset =Vec3.multiply(frontVector,0.1); + var offsetVector = Vec3.sum(upOffset,forwardOffset); + var wandTipPosition = Vec3.sum(wandPosition,offsetVector); + _t.wandTipPosition=wandTipPosition; _t.currentBubble = Entities.addEntity({ type: 'Model', modelURL: bubbleModel, - position: wandPosition, + position: wandTipPosition, dimensions: { x: 0.01, y: 0.01, z: 0.01 }, + collisionsWillMove:false, ignoreForCollisions: true, gravity: BUBBLE_GRAVITY, // collisionSoundURL:popSound, From a9ad18ec80c179f0b62943e170a912720e669860 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 3 Sep 2015 20:42:56 -0700 Subject: [PATCH 4/5] cleanup, comments, and consolidation. --- examples/toys/bubblewand/bubble.js | 8 +-- examples/toys/bubblewand/createWand.js | 2 +- examples/toys/bubblewand/wand.js | 71 +++++++++----------------- 3 files changed, 28 insertions(+), 53 deletions(-) diff --git a/examples/toys/bubblewand/bubble.js b/examples/toys/bubblewand/bubble.js index ae673b994e..7076430eaa 100644 --- a/examples/toys/bubblewand/bubble.js +++ b/examples/toys/bubblewand/bubble.js @@ -1,22 +1,22 @@ (function() { - +//example of a nested entity. it doesn't do much now besides delete itself if it collides with something (bubbles are fragile! it would be cool if it sometimes merged with other bubbbles it hit) +//todo: play bubble sounds from the bubble itself instead of the wand. needs some sound fixes and a way to find its own position before unload for spatialization var popSound; this.preload = function(entityID) { - // console.log('bubble preload') + // print('bubble preload') this.entityID = entityID; popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav"); } this.collisionWithEntity = function(myID, otherID, collision) { - + //if(Entites.getEntityProperties(otherID).userData.objectType==='') { merge bubbles?} Entities.deleteEntity(myID); this.burstBubbleSound(collision.contactPoint) }; this.unload = function(entityID) { - // console.log('bubble unload') // this.properties = Entities.getEntityProperties(entityID); //var location = this.properties.position; //this.burstBubbleSound(); diff --git a/examples/toys/bubblewand/createWand.js b/examples/toys/bubblewand/createWand.js index 065ec0a189..065a93e84e 100644 --- a/examples/toys/bubblewand/createWand.js +++ b/examples/toys/bubblewand/createWand.js @@ -3,7 +3,7 @@ function randInt(min, max) { } var wandModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx?" + randInt(0, 10000); -var scriptURL = "http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/wand.js" + randInt(1, 10048) +var scriptURL = "http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/wand.js?" + randInt(1, 10048) var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); var paintGun = Entities.addEntity({ type: "Model", diff --git a/examples/toys/bubblewand/wand.js b/examples/toys/bubblewand/wand.js index 4cd6c3850e..3bc67537ff 100644 --- a/examples/toys/bubblewand/wand.js +++ b/examples/toys/bubblewand/wand.js @@ -47,26 +47,10 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu } (function() { - var console = {}; - console.log = function(p) { - if (arguments.length > 1) { - - for (var i = 1; i < arguments.length; i++) { - print(arguments[i]) - } - - } else { - print(p) - } - - } var bubbleModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx"; var bubbleScript = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/bubble.js?' + randInt(2, 5096); var popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav"); - - - var targetSize = 0.4; var targetColor = { @@ -79,18 +63,11 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu green: 255, blue: 0 }; - var moveCycleColor = { - red: 255, - green: 255, - blue: 0 - }; var handSize = 0.25; var leftCubePosition = MyAvatar.getLeftPalmPosition(); var rightCubePosition = MyAvatar.getRightPalmPosition(); - var RIGHT_FRONT = 512; - var leftHand = Overlays.addOverlay("cube", { position: leftCubePosition, size: handSize, @@ -153,7 +130,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu var thisEntity = this; this.preload = function(entityID) { - // print('PRELOAD') + // print('PRELOAD') this.entityID = entityID; this.properties = Entities.getEntityProperties(this.entityID); BubbleWand.lastPosition = this.properties.position; @@ -175,9 +152,6 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu bubbles: [], currentBubble: null, update: function() { - BubbleWand.updateControllerState(); - }, - updateControllerState: function() { var _t = this; var properties = Entities.getEntityProperties(thisEntity.entityID); var wandPosition = properties.position; @@ -194,7 +168,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu //not really a sphere... var hitTargetWithWand = findSphereSphereHit(wandPosition, handSize / 2, getGustDetectorPosition(), targetSize / 2) - + var mouthMode; if (hitTargetWithWand) { Overlays.editOverlay(gustZoneOverlay, { @@ -208,18 +182,18 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu position: getGustDetectorPosition(), color: targetColor }) - mouthMode=false; + mouthMode = false; } - var velocity = Vec3.subtract(wandPosition,BubbleWand.lastPosition) + var velocity = Vec3.subtract(wandPosition, BubbleWand.lastPosition) - - _t.lastPosition = wandPosition; - //print('VELOCITY:::'+JSON.stringify(velocity)) - var velocityStrength = Vec3.length(velocity) *100; - // print('velocityStrength::' + velocityStrength); + _t.lastPosition = wandPosition; + + //print('VELOCITY:::'+JSON.stringify(velocity)) + var velocityStrength = Vec3.length(velocity) * 100; + // print('velocityStrength::' + velocityStrength); //todo: angular velocity without the controller // var angularVelocity = Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip); var dimensions = Entities.getEntityProperties(_t.currentBubble).dimensions; @@ -227,14 +201,14 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu var volumeLevel = MyAvatar.audioAverageLoudness; var convertedVolume = convertRange(volumeLevel, [0, 5000], [0, 10]); // print('CONVERTED VOLUME:' + convertedVolume); - + var growthFactor = convertedVolume + velocityStrength; - // print('growthFactor::' + growthFactor); + // print('growthFactor::' + growthFactor); if (velocityStrength > 1 || convertedVolume > 1) { var bubbleSize = randInt(1, 5) bubbleSize = bubbleSize / 10; if (dimensions.x > bubbleSize) { - // console.log('RELEASE BUBBLE') + // print('RELEASE BUBBLE') var lifetime = randInt(3, 8); //sound is somewhat unstable at the moment // Script.setTimeout(function() { @@ -262,6 +236,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu } } + } else { } else { if (dimensions.x >= 0.02) { dimensions.x -= 0.001; @@ -280,7 +255,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu burstBubbleSound: function(bubble) { var position = Entities.getEntityProperties(bubble).position; var orientation = Entities.getEntityProperties(bubble).orientation; - //console.log('bubble position at pop: ' + JSON.stringify(position)); + //print('bubble position at pop: ' + JSON.stringify(position)); var audioOptions = { volume: 0.5, position: position, @@ -298,17 +273,17 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu }, spawnBubble: function() { - // console.log('spawning bubble') + // print('spawning bubble') var _t = this; var properties = Entities.getEntityProperties(thisEntity.entityID); var wandPosition = properties.position; var upVector = Quat.getUp(properties.rotation); - var frontVector = Quat.getFront(properties.rotation); - var upOffset = Vec3.multiply(upVector,0.5); - var forwardOffset =Vec3.multiply(frontVector,0.1); - var offsetVector = Vec3.sum(upOffset,forwardOffset); - var wandTipPosition = Vec3.sum(wandPosition,offsetVector); - _t.wandTipPosition=wandTipPosition; + var frontVector = Quat.getFront(properties.rotation); + var upOffset = Vec3.multiply(upVector, 0.5); + var forwardOffset = Vec3.multiply(frontVector, 0.1); + var offsetVector = Vec3.sum(upOffset, forwardOffset); + var wandTipPosition = Vec3.sum(wandPosition, offsetVector); + _t.wandTipPosition = wandTipPosition; _t.currentBubble = Entities.addEntity({ type: 'Model', modelURL: bubbleModel, @@ -318,7 +293,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu y: 0.01, z: 0.01 }, - collisionsWillMove:false, + collisionsWillMove: false, ignoreForCollisions: true, gravity: BUBBLE_GRAVITY, // collisionSoundURL:popSound, @@ -335,7 +310,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu } function collectGarbage() { - // console.log('COLLECTING GARBAGE!!!') + // print('COLLECTING GARBAGE!!!') Entities.deleteEntity(BubbleWand.currentBubble); while (BubbleWand.bubbles.length > 0) { Entities.deleteEntity(BubbleWand.bubbles.pop()); From 5684e13400bffee13ca6152e4a46d28fcea8efc1 Mon Sep 17 00:00:00 2001 From: James Pollack Date: Fri, 4 Sep 2015 15:49:12 -0700 Subject: [PATCH 5/5] updates for cleaner PR --- examples/toys/bubblewand/bubble.js | 33 +++-- examples/toys/bubblewand/createWand.js | 27 +++- examples/toys/bubblewand/wand.js | 163 ++++++++++++------------- 3 files changed, 123 insertions(+), 100 deletions(-) diff --git a/examples/toys/bubblewand/bubble.js b/examples/toys/bubblewand/bubble.js index 7076430eaa..05b625c124 100644 --- a/examples/toys/bubblewand/bubble.js +++ b/examples/toys/bubblewand/bubble.js @@ -1,18 +1,33 @@ +// bubble.js +// part of bubblewand +// +// Created by James B. Pollack @imgntn -- 09/03/2015 +// Copyright 2015 High Fidelity, Inc. +// +// example of a nested entity. it doesn't do much now besides delete itself if it collides with something (bubbles are fragile! it would be cool if it sometimes merged with other bubbbles it hit) +// todo: play bubble sounds from the bubble itself instead of the wand. +// blocker: needs some sound fixes and a way to find its own position before unload for spatialization +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + (function() { -//example of a nested entity. it doesn't do much now besides delete itself if it collides with something (bubbles are fragile! it would be cool if it sometimes merged with other bubbbles it hit) -//todo: play bubble sounds from the bubble itself instead of the wand. needs some sound fixes and a way to find its own position before unload for spatialization - var popSound; + // Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js"); + // Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js"); + + //var popSound; this.preload = function(entityID) { // print('bubble preload') this.entityID = entityID; - popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav"); + // popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav"); } this.collisionWithEntity = function(myID, otherID, collision) { //if(Entites.getEntityProperties(otherID).userData.objectType==='') { merge bubbles?} Entities.deleteEntity(myID); - this.burstBubbleSound(collision.contactPoint) + // this.burstBubbleSound(collision.contactPoint) }; @@ -26,10 +41,10 @@ this.burstBubbleSound = function(location) { - var audioOptions = { - volume: 0.5, - position: location - } + // var audioOptions = { + // volume: 0.5, + // position: location + // } //Audio.playSound(popSound, audioOptions); diff --git a/examples/toys/bubblewand/createWand.js b/examples/toys/bubblewand/createWand.js index 065a93e84e..d5204c8075 100644 --- a/examples/toys/bubblewand/createWand.js +++ b/examples/toys/bubblewand/createWand.js @@ -1,11 +1,25 @@ -function randInt(min, max) { - return Math.floor(Math.random() * (max - min)) + min; -} +// createWand.js +// part of bubblewand +// +// Created by James B. Pollack @imgntn -- 09/03/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Loads a wand model and attaches the bubble wand behavior. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + + +Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js"); +Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js"); var wandModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx?" + randInt(0, 10000); -var scriptURL = "http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/wand.js?" + randInt(1, 10048) +var scriptURL = "http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/wand.js?" + randInt(1, 100500) + + +//create the wand in front of the avatar var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); -var paintGun = Entities.addEntity({ +var wand = Entities.addEntity({ type: "Model", modelURL: wandModel, position: center, @@ -14,13 +28,14 @@ var paintGun = Entities.addEntity({ y: 1, z: 0.1 }, + //must be enabled to be grabbable in the physics engine collisionsWillMove: true, shapeType: 'box', script: scriptURL }); function cleanup() { - Entities.deleteEntity(paintGun); + //Entities.deleteEntity(wand); } diff --git a/examples/toys/bubblewand/wand.js b/examples/toys/bubblewand/wand.js index 3bc67537ff..be1042ab79 100644 --- a/examples/toys/bubblewand/wand.js +++ b/examples/toys/bubblewand/wand.js @@ -11,66 +11,37 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -function randInt(min, max) { - return Math.floor(Math.random() * (max - min)) + min; -} - - function convertRange(value, r1, r2) { return (value - r1[0]) * (r2[1] - r2[0]) / (r1[1] - r1[0]) + r2[0]; } -// helpers -- @zappoman -// Computes the penetration between a point and a sphere (centered at the origin) -// if point is inside sphere: returns true and stores the result in 'penetration' -// (the vector that would move the point outside the sphere) -// otherwise returns false -function findSphereHit(point, sphereRadius) { - var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations - var vectorLength = Vec3.length(point); - if (vectorLength < EPSILON) { - return true; - } - var distance = vectorLength - sphereRadius; - if (distance < 0.0) { - return true; - } - return false; -} - -function findSpherePointHit(sphereCenter, sphereRadius, point) { - return findSphereHit(Vec3.subtract(point, sphereCenter), sphereRadius); -} - -function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadius) { - return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter); -} - (function() { + Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js"); + Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js"); var bubbleModel = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx"; - var bubbleScript = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/bubble.js?' + randInt(2, 5096); + var bubbleScript = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/bubble.js?' + randInt(1, 10000); var popSound = SoundCache.getSound("http://hifi-public.s3.amazonaws.com/james/bubblewand/sounds/pop.wav"); - var targetSize = 0.4; - var targetColor = { + var TARGET_SIZE = 0.4; + var TARGET_COLOR = { red: 128, green: 128, blue: 128 }; - var targetColorHit = { + var TARGET_COLOR_HIT = { red: 0, green: 255, blue: 0 }; - var handSize = 0.25; + var HAND_SIZE = 0.25; var leftCubePosition = MyAvatar.getLeftPalmPosition(); var rightCubePosition = MyAvatar.getRightPalmPosition(); var leftHand = Overlays.addOverlay("cube", { position: leftCubePosition, - size: handSize, + size: HAND_SIZE, color: { red: 0, green: 0, @@ -82,7 +53,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu var rightHand = Overlays.addOverlay("cube", { position: rightCubePosition, - size: handSize, + size: HAND_SIZE, color: { red: 255, green: 0, @@ -94,14 +65,15 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu var gustZoneOverlay = Overlays.addOverlay("cube", { position: getGustDetectorPosition(), - size: targetSize, - color: targetColor, + size: TARGET_SIZE, + color: TARGET_COLOR, alpha: 1, solid: false }); function getGustDetectorPosition() { + //put the zone in front of your avatar's face var DISTANCE_IN_FRONT = 0.2; var DISTANCE_UP = 0.5; var DISTANCE_TO_SIDE = 0.0; @@ -127,24 +99,27 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu } - var thisEntity = this; + var wandEntity = this; this.preload = function(entityID) { // print('PRELOAD') this.entityID = entityID; this.properties = Entities.getEntityProperties(this.entityID); - BubbleWand.lastPosition = this.properties.position; } this.unload = function(entityID) { Overlays.deleteOverlay(leftHand); Overlays.deleteOverlay(rightHand); - Overlays.deleteOverlay(rightFront) + Overlays.deleteOverlay(gustZoneOverlay) Entities.editEntity(entityID, { name: "" }); Script.update.disconnect(BubbleWand.update); - collectGarbage(); + Entities.deleteEntity(BubbleWand.currentBubble); + while (BubbleWand.bubbles.length > 0) { + Entities.deleteEntity(BubbleWand.bubbles.pop()); + } + }; @@ -152,10 +127,15 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu bubbles: [], currentBubble: null, update: function() { + BubbleWand.internalUpdate(); + }, + internalUpdate: function() { var _t = this; - var properties = Entities.getEntityProperties(thisEntity.entityID); + //get the current position of the wand + var properties = Entities.getEntityProperties(wandEntity.entityID); var wandPosition = properties.position; + //debug overlays for mouth mode var leftHandPos = MyAvatar.getLeftPalmPosition(); var rightHandPos = MyAvatar.getRightPalmPosition(); @@ -166,62 +146,74 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu position: rightHandPos }); - //not really a sphere... - var hitTargetWithWand = findSphereSphereHit(wandPosition, handSize / 2, getGustDetectorPosition(), targetSize / 2) + //if the wand is in the gust detector, activate mouth mode and change the overlay color + var hitTargetWithWand = findSphereSphereHit(wandPosition, HAND_SIZE / 2, getGustDetectorPosition(), TARGET_SIZE / 2) var mouthMode; if (hitTargetWithWand) { Overlays.editOverlay(gustZoneOverlay, { position: getGustDetectorPosition(), - color: targetColorHit + color: TARGET_COLOR_HIT }) mouthMode = true; } else { Overlays.editOverlay(gustZoneOverlay, { position: getGustDetectorPosition(), - color: targetColor + color: TARGET_COLOR }) mouthMode = false; } + var volumeLevel = MyAvatar.audioAverageLoudness; + //volume numbers are pretty large, so lets scale them down. + var convertedVolume = convertRange(volumeLevel, [0, 5000], [0, 10]); + + // default is 'wave mode', where waving the object around grows the bubbles var velocity = Vec3.subtract(wandPosition, BubbleWand.lastPosition) - - + //store the last position of the wand for velocity calculations _t.lastPosition = wandPosition; - //print('VELOCITY:::'+JSON.stringify(velocity)) + // velocity numbers are pretty small, so lets make them a bit bigger var velocityStrength = Vec3.length(velocity) * 100; - // print('velocityStrength::' + velocityStrength); - //todo: angular velocity without the controller - // var angularVelocity = Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip); + + if (velocityStrength > 10) { + velocityStrength = 10 + } + + //actually grow the bubble var dimensions = Entities.getEntityProperties(_t.currentBubble).dimensions; - var volumeLevel = MyAvatar.audioAverageLoudness; - var convertedVolume = convertRange(volumeLevel, [0, 5000], [0, 10]); - // print('CONVERTED VOLUME:' + convertedVolume); - - var growthFactor = convertedVolume + velocityStrength; - // print('growthFactor::' + growthFactor); if (velocityStrength > 1 || convertedVolume > 1) { - var bubbleSize = randInt(1, 5) + + //add some variation in bubble sizes + var bubbleSize = randInt(1, 5); bubbleSize = bubbleSize / 10; + + //release the bubble if its dimensions are bigger than the bubble size if (dimensions.x > bubbleSize) { - // print('RELEASE BUBBLE') + //bubbles pop after existing for a bit -- so set a random lifetime var lifetime = randInt(3, 8); - //sound is somewhat unstable at the moment + + //sound is somewhat unstable at the moment so this is commented out. really audio should be played by the bubbles, but there's a blocker. // Script.setTimeout(function() { // _t.burstBubbleSound(_t.currentBubble) // }, lifetime * 1000) - //need to add some kind of forward velocity for bubble that you blow + + + //todo: angular velocity without the controller -- forward velocity for mouth mode bubbles + // var angularVelocity = Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip); + Entities.editEntity(_t.currentBubble, { velocity: Vec3.normalize(velocity), // angularVelocity: Controller.getSpatialControlRawAngularVelocity(hands.leftHand.tip), lifetime: lifetime }); - _t.spawnBubble(); + //release the bubble -- when we create a new bubble, it will carry on and this update loop will affect the new bubble + BubbleWand.spawnBubble(); + return } else { if (mouthMode) { @@ -236,7 +228,6 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu } } - } else { } else { if (dimensions.x >= 0.02) { dimensions.x -= 0.001; @@ -246,6 +237,7 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu } + //update the bubble to stay with the wand tip Entities.editEntity(_t.currentBubble, { position: _t.wandTipPosition, dimensions: dimensions @@ -253,29 +245,33 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu }, burstBubbleSound: function(bubble) { + //we want to play the sound at the same location and orientation as the bubble var position = Entities.getEntityProperties(bubble).position; var orientation = Entities.getEntityProperties(bubble).orientation; - //print('bubble position at pop: ' + JSON.stringify(position)); + + //set the options for the audio injector var audioOptions = { volume: 0.5, position: position, orientation: orientation } - //Audio.playSound(popSound, audioOptions); - //remove this bubble from the array + //var audioInjector = Audio.playSound(popSound, audioOptions); + + //remove this bubble from the array to keep things clean var i = BubbleWand.bubbles.indexOf(bubble); - if (i != -1) { BubbleWand.bubbles.splice(i, 1); } }, spawnBubble: function() { - // print('spawning bubble') var _t = this; - var properties = Entities.getEntityProperties(thisEntity.entityID); + //create a new bubble at the tip of the wand + //the tip of the wand is going to be in a different place than the center, so we move in space relative to the model to find that position + + var properties = Entities.getEntityProperties(wandEntity.entityID); var wandPosition = properties.position; var upVector = Quat.getUp(properties.rotation); var frontVector = Quat.getFront(properties.rotation); @@ -284,6 +280,11 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu var offsetVector = Vec3.sum(upOffset, forwardOffset); var wandTipPosition = Vec3.sum(wandPosition, offsetVector); _t.wandTipPosition = wandTipPosition; + + //store the position of the tip on spawn for use in velocity calculations + _t.lastPosition = wandTipPosition; + + //create a bubble at the wand tip _t.currentBubble = Entities.addEntity({ type: 'Model', modelURL: bubbleModel, @@ -300,25 +301,17 @@ function findSphereSphereHit(firstCenter, firstRadius, secondCenter, secondRadiu shapeType: "sphere", script: bubbleScript, }); + + //add this bubble to an array of bubbles so we can keep track of them _t.bubbles.push(_t.currentBubble) + }, init: function() { - var _t = this; - _t.spawnBubble(); + this.spawnBubble(); Script.update.connect(BubbleWand.update); } } - function collectGarbage() { - // print('COLLECTING GARBAGE!!!') - Entities.deleteEntity(BubbleWand.currentBubble); - while (BubbleWand.bubbles.length > 0) { - Entities.deleteEntity(BubbleWand.bubbles.pop()); - } - } - BubbleWand.init(); - - }) \ No newline at end of file