diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 43c18da72d..862c4e72e6 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -44,8 +44,8 @@ var PICK_WITH_HAND_RAY = true; var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position -var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified -var DISTANCE_HOLDING_UNITY_DISTANCE = 6; // The distance at which the distance holding action timeframe is unmodified +var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified +var DISTANCE_HOLDING_UNITY_DISTANCE = 6; // The distance at which the distance holding action timeframe is unmodified var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did var MOVE_WITH_HEAD = true; // experimental head-control of distantly held objects var FAR_TO_NEAR_GRAB_PADDING_FACTOR = 1.2; @@ -126,6 +126,7 @@ var GRABBABLE_PROPERTIES = [ var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js +var GRAB_CONSTRAINTS_USER_DATA_KEY = "grabConstraintsKey" var DEFAULT_GRABBABLE_DATA = { disableReleaseVelocity: false @@ -345,7 +346,7 @@ function MyController(hand) { this.grabSphereOff(); if (WANT_DEBUG || WANT_DEBUG_STATE) { print("STATE (" + this.hand + "): " + stateToName(this.state) + " --> " + - stateToName(newState) + ", hand: " + this.hand); + stateToName(newState) + ", hand: " + this.hand); } this.state = newState; }; @@ -424,11 +425,15 @@ function MyController(hand) { } this.grabSphereOn = function() { - var color = {red: 0, green: 255, blue: 0}; + var color = { + red: 0, + green: 255, + blue: 0 + }; if (this.grabSphere === null) { var sphereProperties = { position: this.getHandPosition(), - size: GRAB_RADIUS*2, + size: GRAB_RADIUS * 2, color: color, alpha: 0.1, solid: true, @@ -438,7 +443,7 @@ function MyController(hand) { } else { Overlays.editOverlay(this.grabSphere, { position: this.getHandPosition(), - size: GRAB_RADIUS*2, + size: GRAB_RADIUS * 2, color: color, alpha: 0.1, solid: true, @@ -491,12 +496,10 @@ function MyController(hand) { } var searchSphereLocation = Vec3.sum(distantPickRay.origin, - Vec3.multiply(distantPickRay.direction, this.searchSphereDistance)); - this.searchSphereOn(searchSphereLocation, SEARCH_SPHERE_SIZE * this.searchSphereDistance, - (this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR); + Vec3.multiply(distantPickRay.direction, this.searchSphereDistance)); + this.searchSphereOn(searchSphereLocation, SEARCH_SPHERE_SIZE * this.searchSphereDistance, (this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR); if ((USE_OVERLAY_LINES_FOR_SEARCHING === true) && PICK_WITH_HAND_RAY) { - this.overlayLineOn(handPosition, searchSphereLocation, - (this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR); + this.overlayLineOn(handPosition, searchSphereLocation, (this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR); } } @@ -823,8 +826,8 @@ function MyController(hand) { var distantPickRay = { origin: PICK_WITH_HAND_RAY ? handPosition : Camera.position, direction: PICK_WITH_HAND_RAY ? Quat.getUp(this.getHandRotation()) : Vec3.mix(Quat.getUp(this.getHandRotation()), - Quat.getFront(Camera.orientation), - HAND_HEAD_MIX_RATIO), + Quat.getFront(Camera.orientation), + HAND_HEAD_MIX_RATIO), length: PICK_MAX_DISTANCE }; @@ -999,6 +1002,8 @@ function MyController(hand) { intersectionPointToCenterDistance * FAR_TO_NEAR_GRAB_PADDING_FACTOR); } + + this.setState(this.state == STATE_SEARCHING ? STATE_DISTANCE_HOLDING : STATE_EQUIP); this.searchSphereOff(); return; @@ -1145,9 +1150,9 @@ function MyController(hand) { // double delta controller rotation var handChange = Quat.multiply(Quat.slerp(this.previousControllerRotation, - controllerRotation, - DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), - Quat.inverse(this.previousControllerRotation)); + controllerRotation, + DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), + Quat.inverse(this.previousControllerRotation)); // update the currentObject position and rotation. this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, handMoved); @@ -1275,7 +1280,9 @@ function MyController(hand) { }; this.hasPresetOffsets = function() { - var wearableData = getEntityCustomData('wearable', this.grabbedEntity, {joints: {}}); + var wearableData = getEntityCustomData('wearable', this.grabbedEntity, { + joints: {} + }); if ("joints" in wearableData) { var allowedJoints = wearableData.joints; var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; @@ -1287,7 +1294,9 @@ function MyController(hand) { } this.getPresetPosition = function() { - var wearableData = getEntityCustomData('wearable', this.grabbedEntity, {joints: {}}); + var wearableData = getEntityCustomData('wearable', this.grabbedEntity, { + joints: {} + }); var allowedJoints = wearableData.joints; var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; if (handJointName in allowedJoints) { @@ -1296,7 +1305,9 @@ function MyController(hand) { } this.getPresetRotation = function() { - var wearableData = getEntityCustomData('wearable', this.grabbedEntity, {joints: {}}); + var wearableData = getEntityCustomData('wearable', this.grabbedEntity, { + joints: {} + }); var allowedJoints = wearableData.joints; var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; if (handJointName in allowedJoints) { @@ -1304,7 +1315,23 @@ function MyController(hand) { } } + this.getGrabConstraints = function() { + var defaultConstraints = { + positionLocked: false, + rotationLocked: false, + positionMod: false, + rotationMod: { + pitch: false, + yaw: false, + roll: false + } + } + var constraints = getEntityCustomData(GRAB_CONSTRAINTS_USER_DATA_KEY, this.grabbedEntity, defaultConstraints); + return constraints; + } + this.nearGrabbing = function() { + print('NEAR GRAB') var now = Date.now(); if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { @@ -1370,6 +1397,7 @@ function MyController(hand) { reparentProps["localRotation"] = this.offsetRotation; } Entities.editEntity(this.grabbedEntity, reparentProps); + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'equip', grabbedEntity: this.grabbedEntity @@ -1392,6 +1420,7 @@ function MyController(hand) { }; this.continueNearGrabbing = function() { + print('CONTINUE NEAR GRAB') if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("releaseGrab"); @@ -1427,7 +1456,7 @@ function MyController(hand) { Vec3.length(props.localPosition) > NEAR_PICK_MAX_DISTANCE * 2.0) { // for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip. print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." + - props.parentID + " " + vec3toStr(props.position)); + props.parentID + " " + vec3toStr(props.position)); this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed(this.state == STATE_NEAR_GRABBING ? "releaseGrab" : "releaseEquip"); return; @@ -1457,7 +1486,19 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("continueNearGrab"); } + + var constraints = this.getGrabConstraints(); + if (constraints.positionLocked === true) { + print('IT HAS ITS POSITION LOCKED!!') + } + if (constraints.rotationMod !== false) { + print('IT HAS A ROTATION MOD!!!') + } + + + // so it never seems to hit this currently if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { + // if less than a 5 seconds left, refresh the actions ttl var success = Entities.updateAction(this.grabbedEntity, this.actionID, { hand: this.hand === RIGHT_HAND ? "right" : "left", @@ -1493,6 +1534,9 @@ function MyController(hand) { } this.callEntityMethodOnGrabbed("startNearTrigger"); this.setState(STATE_CONTINUE_NEAR_TRIGGER); + print('START NEAR TRIGGER') + + }; this.farTrigger = function() { @@ -1511,6 +1555,71 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("stopNearTrigger"); return; } + + + var constraints = this.getGrabConstraints(); + if (constraints.rotationMod !== false) { + //implement the rotation modifier + var grabbedProps = Entities.getEntityProperties(this.grabbedEntity); + + var handPosition = this.getHandPosition(); + + var modTypes = []; + + if (constraints.rotationMod.pitch !== false) { + modTypes.push('pitch') + } + if (constraints.rotationMod.yaw !== false) { + modTypes.push('yaw') + } + if (constraints.rotationMod.roll !== false) { + modTypes.push('roll') + } + + + finalRotation = { + x: 0, + y: 0, + z: 0 + } + modTypes.forEach(function(modType) { + + var value = handPosition[constraints.rotationMod[modType].startingAxis]; + var min1 = constraints.rotationMod[modType].startingPoint; + + var finalAngle = scale(value, min1, constraints.rotationMod[modType].distanceToMax, constraints.rotationMod[modType].min, constraints.rotationMod[modType].max) + // print('VARS: ') + // print('CONSTRAINTS:: ' + JSON.stringify(constraints)) + // print('value: ' + value) + // print('min1:' + min1) + // print('max1:' + constraints.rotationMod[modType].distanceToMax) + // print('min2: ' + constraints.rotationMod[modType].min) + // print('max2: ' + constraints.rotationMod[modType].max) + // print('FINAL ANGLE::' + finalAngle) + + + if (finalAngle < constraints.rotationMod[modType].min) { + finalAngle = constraints.rotationMod[modType].min; + } + + if (finalAngle > constraints.rotationMod[modType].max) { + finalAngle = constraints.rotationMod[modType].max; + } + + if (modType === 'pitch') { + finalRotation.x = finalAngle + } + if (modType === 'yaw') { + finalRotation.y = finalAngle + } + if (modType === 'roll') { + finalRotation.z = finalAngle + } + }); + + Entities.callEntityMethod(this.grabbedEntity, constraints.callback, [JSON.stringify(finalRotation)]); + + } this.callEntityMethodOnGrabbed("continueNearTrigger"); }; @@ -1624,6 +1733,7 @@ function MyController(hand) { // sometimes we want things to stay right where they are when we let go. var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {}); var releaseVelocityData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + print('RELEASE DATA::' + JSON.stringify(releaseVelocityData)) if (releaseVelocityData.disableReleaseVelocity === true || // this next line allowed both: // (1) far-grab, pull to self, near grab, then throw @@ -1638,6 +1748,7 @@ function MyController(hand) { this.actionID = null; this.setState(STATE_OFF); + print('HAS VELOCITY AT RELEASE?? ' + noVelocity) Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'release', grabbedEntity: this.grabbedEntity, @@ -1705,6 +1816,7 @@ function MyController(hand) { // when using string values "collidesWith": COLLIDES_WITH_WHILE_GRABBED }; + print('ACTIVATING ENTITY') Entities.editEntity(entityID, whileHeldProperties); } else if (data["refCount"] > 1) { if (data["heartBeat"] === undefined || @@ -1722,9 +1834,12 @@ function MyController(hand) { // people are holding something and one of them will be able (if the other releases at the right time) to // bootstrap themselves with the held object. This happens because the meaning of "otherAvatar" in // the collision mask hinges on who the physics simulation owner is. - Entities.editEntity(entityID, {"collidesWith": COLLIDES_WITH_WHILE_MULTI_GRABBED}); + Entities.editEntity(entityID, { + "collidesWith": COLLIDES_WITH_WHILE_MULTI_GRABBED + }); } } + print('ACTIVATED ENTITY!!!') setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); return data; }; @@ -1736,7 +1851,9 @@ function MyController(hand) { var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, handJointIndex); children.forEach(function(childID) { print("disconnecting stray child of hand: (" + _this.hand + ") " + childID); - Entities.editEntity(childID, {parentID: NULL_UUID}); + Entities.editEntity(childID, { + parentID: NULL_UUID + }); }); } @@ -1764,11 +1881,23 @@ function MyController(hand) { data["dynamic"] && data["parentID"] == NULL_UUID && !data["collisionless"]) { - deactiveProps["velocity"] = {x: 0.0, y: 0.1, z: 0.0}; + deactiveProps["velocity"] = { + x: 0.0, + y: 0.1, + z: 0.0 + }; } if (noVelocity) { - deactiveProps["velocity"] = {x: 0.0, y: 0.0, z: 0.0}; - deactiveProps["angularVelocity"] = {x: 0.0, y: 0.0, z: 0.0}; + deactiveProps["velocity"] = { + x: 0.0, + y: 0.0, + z: 0.0 + }; + deactiveProps["angularVelocity"] = { + x: 0.0, + y: 0.0, + z: 0.0 + }; } Entities.editEntity(entityID, deactiveProps); @@ -1778,13 +1907,31 @@ function MyController(hand) { var deactiveProps = { parentID: this.previousParentID, parentJointIndex: this.previousParentJointIndex, - velocity: {x: 0.0, y: 0.0, z: 0.0}, - angularVelocity: {x: 0.0, y: 0.0, z: 0.0} + velocity: { + x: 0.0, + y: 0.0, + z: 0.0 + }, + angularVelocity: { + x: 0.0, + y: 0.0, + z: 0.0 + } }; Entities.editEntity(entityID, deactiveProps); } else if (noVelocity) { - Entities.editEntity(entityID, {velocity: {x: 0.0, y: 0.0, z: 0.0}, - angularVelocity: {x: 0.0, y: 0.0, z: 0.0}}); + Entities.editEntity(entityID, { + velocity: { + x: 0.0, + y: 0.0, + z: 0.0 + }, + angularVelocity: { + x: 0.0, + y: 0.0, + z: 0.0 + } + }); } } else { data = null; @@ -1916,3 +2063,8 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); Script.update.connect(update); + + +function scale(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} \ No newline at end of file diff --git a/unpublishedScripts/DomainContent/Home/musicBox/createMusicBox.js b/unpublishedScripts/DomainContent/Home/musicBox/createMusicBox.js new file mode 100644 index 0000000000..43abe429bb --- /dev/null +++ b/unpublishedScripts/DomainContent/Home/musicBox/createMusicBox.js @@ -0,0 +1,144 @@ +var SHOULD_CLEANUP = true; + +var WHITE = { + red: 255, + green: 255, + blue: 255 +}; + +var RED = { + red: 255, + green: 0, + blue: 0 +}; + +var GREEN = { + red: 0, + green: 255, + blue: 0 +}; + +var BLUE = { + red: 0, + green: 0, + blue: 255 +}; + +var center = Vec3.sum(Vec3.sum(MyAvatar.position, { + x: 0, + y: 0.5, + z: 0 +}), Vec3.multiply(1, Quat.getFront(Camera.getOrientation()))); + +var BASE_DIMENSIONS = { + x: 0.5, + y: 0.5, + z: 0.5 +}; + +var BASE_POSITION = center; + +var LID_DIMENSIONS = { + x: BASE_DIMENSIONS.x, + y: BASE_DIMENSIONS.y / 8, + z: BASE_DIMENSIONS.z +}; + +var LID_OFFSET = { + x: 0, + y: BASE_DIMENSIONS.y / 2 + (LID_DIMENSIONS.y / 2), + z: 0.25 +}; + +var LID_REGISTRATION_POINT = { + x: 0.5, + y: 0.5, + z: 1 +} + +var LID_SCRIPT_URL = Script.resolvePath('lid.js?'+Math.random()); + +var base, lid; + + +function createBase() { + var baseProperties = { + name: 'hifi-home-music-box-base', + type: 'Box', + color: WHITE, + position: BASE_POSITION, + dimensions: BASE_DIMENSIONS + } + + base = Entities.addEntity(baseProperties); +}; + + +function createLid() { + + var lidPosition = Vec3.sum(BASE_POSITION, LID_OFFSET); + print('LID DIMENSIONS:' + JSON.stringify(lidPosition)) + var lidProperties = { + name: 'hifi-home-music-box-lid', + type: 'Box', + color: BLUE, + dimensions: LID_DIMENSIONS, + position: lidPosition, + registrationPoint: LID_REGISTRATION_POINT, + dynamic: false, + script:LID_SCRIPT_URL, + collidesWith: 'myAvatar,otherAvatar', + userData: JSON.stringify({ + grabConstraintsKey: { + callback:'rotateLid', + positionLocked: true, + rotationLocked: false, + positionMod: false, + rotationMod: { + pitch: { + min: 0, + max: 75, + startingAxis:'y', + startingPoint:lidPosition.y, + distanceToMax:lidPosition.y+0.35, + }, + yaw: false, + roll: false + } + }, + grabbableKey: { + wantsTrigger: true, + disableReleaseVelocity: true, + disableMoveWithHead: true, + disableFarGrab:true, + disableEquip:true + } + }) + } + + lid = Entities.addEntity(lidProperties); + +}; + +var theta = 0; +var min = 0; +var max = 60; +var direction = "up"; + +function animateLid() { + theata += 1 +} + + + +if (SHOULD_CLEANUP === true) { + Script.scriptEnding.connect(cleanup); +} + +function cleanup() { + Entities.deleteEntity(base); + Entities.deleteEntity(lid); +} + +createBase(); +createLid(); \ No newline at end of file diff --git a/unpublishedScripts/DomainContent/Home/musicBox/lid.js b/unpublishedScripts/DomainContent/Home/musicBox/lid.js new file mode 100644 index 0000000000..41c59a24be --- /dev/null +++ b/unpublishedScripts/DomainContent/Home/musicBox/lid.js @@ -0,0 +1,132 @@ +(function() { + + var _this; + + function Lid() { + _this = this; + return this; + } + + var MUSIC_URL = 'https://hifi-content.s3.amazonaws.com/DomainContent/Home/Sounds/aquarium_small.L.wav'; + var SHUT_SOUND_URL = 'http://hifi-content.s3.amazonaws.com/DomainContent/Home/Sounds/book_fall.L.wav'; + var OPEN_SOUND_URL = 'http://public.highfidelity.io/sounds/Switches%20and%20sliders/lamp_switch_2.wav' + + Lid.prototype = { + preload: function(entityID) { + print('PRELOAD LID') + _this.entityID = entityID; + _this.music = SoundCache.getSound(MUSIC_URL); + _this.shutSound = SoundCache.getSound(SHUT_SOUND_URL); + _this.openSound = SoundCache.getSound(OPEN_SOUND_URL); + _this.musicIsPlaying = false; + _this.shut = true; + _this.shutSoundInjector = { + isPlaying: false + }; + _this.musicInjector = null; + _this.openSoundInjector = { + isPlaying: false + } + + + }, + continueNearTrigger: function() { + var properties = Entities.getEntityProperties(this.entityID); + + }, + playMusic: function() { + if (this.musicIsPlaying !== true) { + + var properties = Entities.getEntityProperties(this.entityID); + + var audioOptions = { + position: properties.position, + volume: 0.75, + loop: true + } + this.musicInjector = Audio.playSound(this.music, audioOptions); + this.musicIsPlaying = true; + } + }, + + stopMusic: function() { + this.musicInjector.stop(); + this.musicIsPlaying = false; + }, + + playOpenSound: function() { + if (this.openSoundInjector.isPlaying !== true) { + + var properties = Entities.getEntityProperties(this.entityID); + + var audioOptions = { + position: properties.position, + volume: 1, + loop: false + } + this.openSoundInjector = Audio.playSound(this.openSound, audioOptions); + } + }, + playShutSound: function() { + if (this.shutSoundInjector.isPlaying !== true) { + + var properties = Entities.getEntityProperties(this.entityID); + + var audioOptions = { + position: properties.position, + volume: 1, + loop: false + } + this.shutSoundInjector = Audio.playSound(this.shutSound, audioOptions); + } + }, + rotateLid: function(myID, paramsArray) { + + var finalRotation; + paramsArray.forEach(function(param) { + var p; + // print('param is:' + param) + try { + p = JSON.parse(param); + finalRotation = p; + + } catch (err) { + // print('not a json param') + return; + p = param; + } + + }); + + if (finalRotation.x > 20 && this.musicIsPlaying === false) { + this.playMusic(); + print('play music!!') + } + if (finalRotation.x <= 20 && this.musicIsPlaying === true) { + print('stop music!!') + this.stopMusic(); + } + if (finalRotation.x > 0 && this.shut === true) { + print('play open sound!!') + this.shut = false; + this.playOpenSound(); + } else if (finalRotation.x <= 0 && this.shut === false) { + print('play shut sound!!') + this.shut = true; + this.playShutSound(); + } + Entities.editEntity(this.entityID, { + rotation: Quat.fromPitchYawRollDegrees(finalRotation.x, finalRotation.y, finalRotation.z) + }) + + }, + + unload: function() { + this.musicInjector.stop(); + + }, + } + + + return new Lid(); +}) \ No newline at end of file