From 14323a06b35c8814c1d48df6e1cb5ee365edb69d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 29 Aug 2016 10:28:27 -0700 Subject: [PATCH 001/109] Add interface to disable hand controller grab functionality --- .../system/controllers/handControllerGrab.js | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 954093854e..2ebf7a828d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -184,6 +184,10 @@ var STATE_FAR_TRIGGER = 5; var STATE_HOLD = 6; var STATE_ENTITY_TOUCHING = 7; +var holdEnabled = true; +var nearGrabEnabled = true; +var farGrabEnabled = true; + // "collidesWith" is specified by comma-separated list of group names // the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar var COLLIDES_WITH_WHILE_GRABBED = "dynamic,otherAvatar"; @@ -1440,7 +1444,7 @@ function MyController(hand) { var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateHotSpotEntities); if (potentialEquipHotspot) { - if (this.triggerSmoothedGrab()) { + if (this.triggerSmoothedGrab() && holdEnabled) { this.grabbedHotspot = potentialEquipHotspot; this.grabbedEntity = potentialEquipHotspot.entityID; this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); @@ -1483,7 +1487,7 @@ function MyController(hand) { // potentialNearTriggerEntity = entity; } } else { - if (this.triggerSmoothedGrab()) { + if (this.triggerSmoothedGrab() && nearGrabEnabled) { var props = entityPropertiesCache.getProps(entity); var grabProps = entityPropertiesCache.getGrabProps(entity); var refCount = grabProps.refCount ? grabProps.refCount : 0; @@ -1571,7 +1575,7 @@ function MyController(hand) { // potentialFarTriggerEntity = entity; } } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { - if (this.triggerSmoothedGrab() && !isEditing()) { + if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled) { this.grabbedEntity = entity; this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); return; @@ -1589,7 +1593,9 @@ function MyController(hand) { equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); } - this.searchIndicatorOn(rayPickInfo.searchRay); + if (farGrabEnabled) { + this.searchIndicatorOn(rayPickInfo.searchRay); + } Reticle.setVisible(false); }; @@ -2219,7 +2225,9 @@ function MyController(hand) { if (intersection.intersects) { this.intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); } - this.searchIndicatorOn(pickRay); + if (farGrabEnabled) { + this.searchIndicatorOn(pickRay); + } } } @@ -2327,7 +2335,9 @@ function MyController(hand) { } this.intersectionDistance = intersectInfo.distance; - this.searchIndicatorOn(intersectInfo.searchRay); + if (farGrabEnabled) { + this.searchIndicatorOn(intersectInfo.searchRay); + } Reticle.setVisible(false); } else { this.setState(STATE_OFF, "grabbed entity was destroyed"); @@ -2681,6 +2691,7 @@ function update(deltaTime) { entityPropertiesCache.update(); } +Messages.subscribe('Hifi-Grab-Disable'); Messages.subscribe('Hifi-Hand-Disabler'); Messages.subscribe('Hifi-Hand-Grab'); Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); From 8ba03abce6ecbffd86aa93959c65be002d1a1ee7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 31 Aug 2016 15:51:47 -0700 Subject: [PATCH 002/109] Add tutorial and vive controller script --- tutorial/entityData.js | 596 ++++++++++++++++++++++++++++++++++++++ tutorial/success.wav | Bin 0 -> 39758 bytes tutorial/success48.wav | Bin 0 -> 39758 bytes tutorial/tutorial.js | 545 +++++++++++++++++++++++++++++++++++ tutorial/viveHandsv2.js | 612 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1753 insertions(+) create mode 100644 tutorial/entityData.js create mode 100644 tutorial/success.wav create mode 100644 tutorial/success48.wav create mode 100644 tutorial/tutorial.js create mode 100644 tutorial/viveHandsv2.js diff --git a/tutorial/entityData.js b/tutorial/entityData.js new file mode 100644 index 0000000000..e0adf4f32d --- /dev/null +++ b/tutorial/entityData.js @@ -0,0 +1,596 @@ +Step1EntityData = [ + { + "clientOnly": 0, + "color": { + "blue": 255, + "green": 0, + "red": 255 + }, + "created": "2016-08-29T22:57:55Z", + "dimensions": { + "x": 0.018359377980232239, + "y": 0.018359377980232239, + "z": 0.018359377980232239 + }, + "id": "{3bb83d9c-11db-4bc1-a61b-36921370cb40}", + "name": "tutorial/box_spawn", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "collisionless": 1, + "position": { + "x": 0, + "y": 0.8, + "z": 0.7790381908416748 + }, + "queryAACube": { + "scale": 0.031799376010894775, + "x": -0.015899688005447388, + "y": 0.79706859588623047, + "z": 0.7631385326385498 + }, + "rotation": { + "w": 1, + "x": 0, + "y": 0, + "z": 0 + }, + "shape": "Cube", + "type": "Box", + "userData": "{\"tag\":\"step2\"}", + "visible": 0 + }, + { + "color": { + "blue": 181, + "green": 181, + "red": 181 + }, + "dimensions": { + "x": 0.37322089076042175, + "y": 0.8015166997909546, + "z": 0.37322089076042175 + }, + "name": "tutorial/pillar2", + //"shapeType": "simple-hull", + "position": { + "x": 0.019208565354347229, + "y": -0.1, + "z": 0.75276124477386475 + }, + "rotation": { + "w": 1, + "x": 0, + "y": 0, + "z": 0 + }, + "shape": "Cube", + "type": "Box", + "userData": "{\"tag\":\"step2\"}" + }, + { + "clientOnly": 0, + "compoundShapeURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj", + "created": "2016-08-29T22:57:55Z", + "dimensions": { + "x": 0.57461458444595337, + "y": 0.35781359672546387, + "z": 0.57461458444595337 + }, + "gravity": { + "x": 0, + "y": -5, + "z": 0 + }, + "id": "{2a8a9cb8-4501-4089-8fb8-6b1b5100db10}", + "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx", + "name": "tutorial/basket", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 0.022034257650375366, + "y": 0.47968916893005371, + "z": 0 + }, + "queryAACube": { + "scale": 0.88791579008102417, + "x": -0.42192363739013672, + "y": 0.23573127388954163, + "z": -0.44395789504051208 + }, + "rotation": { + "w": 1, + "x": -1.52587890625e-05, + "y": -1.52587890625e-05, + "z": -1.52587890625e-05 + }, + "shapeType": "compound", + "type": "Model", + "userData": "{\"hifiHomeKey\":{\"reset\":true},\"tag\":\"step2\"}" + }, + { + "clientOnly": 0, + "collisionless": 1, + "color": { + "blue": 255, + "green": 0, + "red": 255 + }, + "created": "2016-08-29T22:57:55Z", + "dimensions": { + "x": 0.43770244717597961, + "y": 0.33723857998847961, + "z": 0.43770244717597961 + }, + "id": "{436aec80-15e8-4fc3-bd74-f173b731a922}", + "ignoreForCollisions": 1, + "name": "tutorial/basket_collider", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 0.02785143256187439, + "y": 0.50166182518005371, + "z": 0.0017895996570587158 + }, + "queryAACube": { + "scale": 0.70490902662277222, + "x": -0.32460308074951172, + "y": 0.3492073118686676, + "z": -0.35066491365432739 + }, + "rotation": { + "w": 1, + "x": 0, + "y": 0, + "z": 0 + }, + "type": "Sphere", + "userData": "{\"tag\":\"step2\"}", + "visible": 0 + }, + { + "clientOnly": 0, + "color": { + "blue": 181, + "green": 181, + "red": 181 + }, + "created": "2016-08-29T22:57:55Z", + "dimensions": { + "x": 0.37322089076042175, + "y": 0.8015000104904175, + "z": 0.37322089076042175 + }, + "id": "{221be6c2-e0d6-4a7c-b9d4-a77e6b7d1c9a}", + "name": "tutorial/pillar1", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 0.019208565354347229, + "y": -0.1, + "z": 0.025902509689331055 + }, + "queryAACube": { + "scale": 1.1320732831954956, + "x": -0.54682809114456177, + "y": -0.5660366415977478, + "z": -0.54013413190841675 + }, + "rotation": { + "w": 1, + "x": 0, + "y": 0, + "z": 0 + }, + "shape": "Cube", + "type": "Box", + "userData": "{\"tag\":\"step2\"}" + } + ]; + +//Step1EntityData = [ +// { +// "clientOnly": 0, +// "color": { +// "blue": 255, +// "green": 0, +// "red": 255 +// }, +// "created": "2016-08-23T16:29:15Z", +// "dimensions": { +// "x": 0.018359377980232239, +// "y": 0.018359377980232239, +// "z": 0.018359377980232239 +// }, +// "id": "{387765d2-366d-4775-8e6e-ea45119cf69d}", +// visible: false, +// "name": "tutorial/box_spawn", +// "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", +// "position": { +// "x": 0.016568422317504883, +// "y": 0.6591796875, +// "z": 1.3308790922164917 +// }, +// "queryAACube": { +// "scale": 0.031799376010894775, +// "x": 0.00066873431205749512, +// "y": 0.643280029296875, +// "z": 1.3149794340133667 +// }, +// "shape": "Cube", +// "type": "Box" +// }, +// { +// "clientOnly": 0, +// "compoundShapeURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj", +// "created": "2016-08-22T21:20:11Z", +// "dimensions": { +// "x": 0.57461458444595337, +// "y": 0.35781359672546387, +// "z": 0.57461458444595337 +// }, +// "gravity": { +// "x": 0, +// "y": -5, +// "z": 0 +// }, +// "id": "{ddcb3906-3d80-4111-9171-3a73a2f4f1bb}", +// "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx", +// "name": "tutorial/basket", +// "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", +// "position": { +// "x": 0, +// "y": 0.6480712890625, +// "z": 0 +// }, +// "queryAACube": { +// "scale": 0.88791579008102417, +// "x": -0.44395789504051208, +// "y": 0.20411339402198792, +// "z": -0.44395789504051208 +// }, +// "rotation": { +// "w": 1, +// "x": -1.52587890625e-05, +// "y": -1.52587890625e-05, +// "z": -1.52587890625e-05 +// }, +// "shapeType": "compound", +// "type": "Model", +// "userData": "{\"hifiHomeKey\":{\"reset\":true}}" +// }, +// { +// "clientOnly": 0, +// "created": "2016-08-22T21:22:22Z", +// "dimensions": { +// "x": 2.4929797649383545, +// "y": 0.94968640804290771, +// "z": 1.0870213508605957 +// }, +// "id": "{e7030a2d-f573-4c4b-a0aa-ea80e9e25399}", +// "name": "tutorial/table", +// "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/table2_re-oriented.fbx", +// "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", +// "position": { +// "x": 0.018214225769042969, +// "y": 0, +// "z": 0.60948663949966431 +// }, +// "queryAACube": { +// "scale": 2.8807060718536377, +// "x": -1.4221388101577759, +// "y": -1.4403530359268188, +// "z": -0.83086639642715454 +// }, +// "rotation": { +// "w": 0.70705735683441162, +// "x": -1.52587890625e-05, +// "y": -0.70717936754226685, +// "z": -1.52587890625e-05 +// }, +// "shapeType": "static-mesh", +// "type": "Model" +// }, +// { +// visible: false, +// "clientOnly": 0, +// "collisionless": 1, +// "color": { +// "blue": 255, +// "green": 0, +// "red": 255 +// }, +// "created": "2016-08-23T18:09:44Z", +// "dimensions": { +// "x": 0.43770244717597961, +// "y": 0.33723857998847961, +// "z": 0.43770244717597961 +// }, +// "id": "{54e1d825-b552-48e4-b9c8-1c83c79a673e}", +// "ignoreForCollisions": 1, +// "name": "tutorial/basket_collider", +// "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", +// "position": { +// "x": 0.0058171749114990234, +// "y": 0.6700439453125, +// "z": 0.0017895996570587158 +// }, +// "queryAACube": { +// "scale": 0.70490902662277222, +// "x": -0.34663733839988708, +// "y": 0.31758943200111389, +// "z": -0.35066491365432739 +// }, +// "type": "Sphere" +// } +//]; + +Step1BlockData = { + "clientOnly": 0, + "color": { + "blue": 0, + "green": 0, + "red": 255 + }, + "created": "2016-08-22T22:54:07Z", + "dimensions": { + "x": 0.20000000298023224, + "y": 0.20000000298023224, + "z": 0.20000000298023224 + }, + name: "tutorial/block", + "collisionsWillMove": 1, + velocity: { + x: 0, + y: -0.2, + z: 0 + }, + "dynamic": 1, + "gravity": { + "x": 0, + "y": -10, + "z": 0 + }, + "id": "{5c7223f8-3bc5-4cb4-913c-0e93f5994ca2}", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "queryAACube": { + "scale": 0.34641015529632568, + "x": -0.17320507764816284, + "y": -0.17320507764816284, + "z": -0.17320507764816284 + }, + "rotation": { + "w": 1, + "x": -0.0001373291015625, + "y": -7.62939453125e-05, + "z": -0.0003204345703125 + }, + "shape": "Cube", + "type": "Box", + "userData": "{}", +}; + +StepGunData = [ + { + "clientOnly": 0, + "created": "2016-08-23T22:18:46Z", + "dimensions": { + "x": 2.4929797649383545, + "y": 0.94968640804290771, + "z": 1.0870213508605957 + }, + "id": "{de28363f-d1f8-4001-8e6b-1b5876699f49}", + "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/table2_re-oriented.fbx", + "name": "tutorial/table", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 0.17360222339630127, + "y": 0, + "z": 0 + }, + "queryAACube": { + "scale": 2.8807060718536377, + "x": -1.2667508125305176, + "y": -1.4403530359268188, + "z": -1.4403530359268188 + }, + "rotation": { + "w": 0.70705735683441162, + "x": -1.52587890625e-05, + "y": -0.70717936754226685, + "z": -1.52587890625e-05 + }, + "shapeType": "static-mesh", + "type": "Model", + "userData": "{\"tag\":\"step4\"}" + }, + { + "clientOnly": 0, + "compoundShapeURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj", + "created": "2016-08-23T22:18:46Z", + "dimensions": { + "x": 0.57461458444595337, + "y": 0.35781359672546387, + "z": 0.57461458444595337 + }, + "gravity": { + "x": 0, + "y": -5, + "z": 0 + }, + "id": "{51e7cf16-e624-44a8-b835-47c35c6ad5f0}", + "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx", + "name": "tutorial/basket", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 3.8134055137634277, + "y": 0.6480712890625, + "z": 0.015498995780944824 + }, + "queryAACube": { + "scale": 0.88791579008102417, + "x": 3.3694477081298828, + "y": 0.20411339402198792, + "z": -0.42845889925956726 + }, + "rotation": { + "w": 1, + "x": -1.52587890625e-05, + "y": -1.52587890625e-05, + "z": -1.52587890625e-05 + }, + "shapeType": "compound", + "type": "Model", + "userData": "{\"hifiHomeKey\":{\"reset\":true},\"tag\":\"step4\"}" + }, + { + "clientOnly": 0, + "collisionless": 1, + "color": { + "blue": 0, + "green": 0, + "red": 255 + }, + "created": "2016-08-23T22:20:57Z", + "dimensions": { + "x": 0.0649842768907547, + "y": 0.0649842768907547, + "z": 0.0649842768907547 + }, + "id": "{264943d2-600f-4d22-ad30-ccd57f7c4424}", + "ignoreForCollisions": 1, + "name": "tutorial/gun_spawn", + visible: false, + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 0, + "y": 0.62629544734954834, + "z": 0.028602004051208496 + }, + "queryAACube": { + "scale": 0.11255607008934021, + "x": -0.056278035044670105, + "y": 0.57001739740371704, + "z": -0.027676030993461609 + }, + "shape": "Cube", + "type": "Box" + }, + { + "clientOnly": 0, + "collisionless": 1, + "color": { + "blue": 255, + "green": 0, + "red": 255 + }, + "created": "2016-08-23T22:18:46Z", + "dimensions": { + "x": 0.43770244717597961, + "y": 0.33723857998847961, + "z": 0.43770244717597961 + }, + "id": "{a8944645-3234-484f-aed1-1a63d76aa51c}", + "ignoreForCollisions": 1, + "name": "tutorial/basket_collider", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 3.8192229270935059, + "y": 0.6700439453125, + "z": 0.017288565635681152 + }, + "queryAACube": { + "scale": 0.70490902662277222, + "x": 3.4667685031890869, + "y": 0.31758943200111389, + "z": -0.33516594767570496 + }, + "rotation": { + "w": 1, + "x": 0, + "y": 0, + "z": 0 + }, + "type": "Sphere", + "userData": "{\"tag\":\"step4\"}", + "visible": 0 + } +]; + +GunData = { + "clientOnly": 0, + "collisionsWillMove": 1, + "compoundShapeURL": "http://hifi-production.s3.amazonaws.com/tutorials/pingPongGun/Pingpong-Gun-New.obj", + "created": "2016-08-23T22:12:13Z", + "dimensions": { + "x": 0.125, + "y": 0.38749998807907104, + "z": 0.99309998750686646 + }, + "dynamic": 1, + "gravity": { + "x": 0, + "y": -5, + "z": 0 + }, + + "id": "{8d3fa3f2-8b59-4f47-8bb4-c03574239c9f}", + "modelURL": "http://hifi-production.s3.amazonaws.com/tutorials/pingPongGun/Pingpong-Gun-New.fbx", + "name": "tutorial/gun", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "queryAACube": { + "scale": 3.219977855682373, + "x": -2.5046753883361816, + "y": -1.8901374340057373, + "z": -0.68512386083602905 + }, + velocity: { + x: 0, + y: -1, + z: 0 + }, + "rotation": { + "w": 0.69534718990325928, + "x": -0.13302478194236755, + "y": -0.12684555351734161, + "z": 0.69477111101150513 + }, + "script": "http://hifi-production.s3.amazonaws.com/tutorials/entity_scripts/pingPongGun.js", + "shapeType": "compound", + "type": "Model", + "userData": "{\"grabbableKey\":{\"invertSolidWhileHeld\":true},\"wearable\":{\"joints\":{\"RightHand\":[{\"x\":0.1177130937576294,\"y\":0.12922893464565277,\"z\":0.08307232707738876},{\"x\":0.4934672713279724,\"y\":0.3605862259864807,\"z\":0.6394805908203125,\"w\":-0.4664038419723511}],\"LeftHand\":[{\"x\":0.09151676297187805,\"y\":0.13639454543590546,\"z\":0.09354984760284424},{\"x\":-0.19628101587295532,\"y\":0.6418180465698242,\"z\":0.2830369472503662,\"w\":0.6851521730422974}]}}}" +}; + +HandsAboveHeadData = [ +{ + name: "tutorial/sign", + "backgroundColor": { + "blue": 187, + "green": 242, + "red": 198 + }, + "clientOnly": 0, + "created": "2016-08-23T22:42:48Z", + "dimensions": { + "x": 0.58140444755554199, + "y": 0.38676983118057251, + "z": 0.0099999997764825821 + }, + "id": "{c0ceabcf-501e-41fe-99e9-aca47a44122f}", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "queryAACube": { + "scale": 0.69837099313735962, + "x": -0.34918549656867981, + "y": -0.34918549656867981, + "z": -0.34918549656867981 + }, + "rotation": { + "w": 0.70710676908493042, + "x": 0, + "y": -0.70710670948028564, + "z": 0 + }, + "text": "Put your hands above your head.", + "textColor": { + "blue": 0, + "green": 0, + "red": 0 + }, + "type": "Text" +} +]; diff --git a/tutorial/success.wav b/tutorial/success.wav new file mode 100644 index 0000000000000000000000000000000000000000..597e2b91b66c071fb260675d27f08c235af0a01b GIT binary patch literal 39758 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 Q0WX*d$UJ5) 0 ? entities[0] : null; +} + +// Return all entities with properties `properties` within radius `searchRadius` +findEntities = function(properties, searchRadius, filterFn) { + if (!filterFn) { + filterFn = function(properties, key, value) { + return value == properties[key]; + } + } + searchRadius = searchRadius ? searchRadius : 100000; + var entities = Entities.findEntities({ x: 0, y: 0, z: 0 }, searchRadius); + var matchedEntities = []; + var keys = Object.keys(properties); + for (var i = 0; i < entities.length; ++i) { + var match = true; + var candidateProperties = Entities.getEntityProperties(entities[i], keys); + for (var key in properties) { + if (!filterFn(properties, key, candidateProperties[key])) { + // This isn't a match, move to next entity + match = false; + break; + } + } + if (match) { + matchedEntities.push(entities[i]); + } + } + + return matchedEntities; +} +// On start tutorial... + +// Load assets +var BOX_SPAWN_NAME = "tutorial/box_spawn"; +var BASKET_COLLIDER_NAME = "tutorial/basket_collider"; +var GUN_SPAWN_NAME = "tutorial/gun_spawn"; +var GUN_AMMO_NAME = "Tutorial Ping Pong Ball" + +function spawn(entityData, transform, modifyFn) { + print("Creating: ", entityData); + if (!transform) { + transform = { + position: { x: 0, y: 0, z: 0 }, + rotation: { x: 0, y: 0, z: 0, w: 1 } + } + } + var ids = []; + for (var i = 0; i < entityData.length; ++i) { + var data = entityData[i]; + print("Creating: ", data.name); + data.position = Vec3.sum(transform.position, data.position); + data.rotation = Quat.multiply(data.rotation, transform.rotation); + if (modifyFn) { + data = modifyFn(data); + } + var id = Entities.addEntity(data); + ids.push(id); + } + return ids; +} + +function parseJSON(jsonString) { + var data; + try { + data = JSON.parse(jsonString); + } catch(e) { + data = {}; + } + return data; +} + +function spawnWithTag(entityData, transform, tag) { + function modifyFn(data) { + var userData = parseJSON(data.userData); + userData.tag = tag; + data.userData = JSON.stringify(userData); + return data; + } + return spawn(entityData, transform, modifyFn); +} + +function findEntitiesWithTag(tag) { + return findEntities({ userData: "" }, 10000, function(properties, key, value) { + data = parseJSON(value); + return data.tag == tag; + }); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: DISABLE CONTROLLERS // +// // +/////////////////////////////////////////////////////////////////////////////// +var stepDisableControllers = function(name) { + this.tag = name; +} +stepDisableControllers.prototype = { + start: function(onFinish) { + Menu.setIsOptionChecked("Overlays", false); + Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'both'); + Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ + nearGrabEnabled: true, + holdEnabled: false, + farGrabEnabled: false, + })); + onFinish(); + }, + cleanup: function() { + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: Raise hands above head // +// // +/////////////////////////////////////////////////////////////////////////////// +var stepRaiseAboveHead = function(name) { + this.tag = name; +} +stepRaiseAboveHead.prototype = { + start: function(onFinish) { + var tag = this.tag; + + var defaultTransform = { + position: { + x: 0.2459, + y: 0.9011, + z: 0.7266 + }, + rotation: { + x: 0, + y: 0, + z: 0, + w: 1 + } + }; + + // Spawn content set + spawnWithTag(HandsAboveHeadData, defaultTransform, tag); + + var checkIntervalID = null; + function checkForHandsAboveHead() { + print("Checking..."); + if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { + Script.clearInterval(checkIntervalID); + this.soundInjector = Audio.playSound(successSound, { + position: defaultTransform.position, + volume: 0.7, + loop: false + }); + onFinish(); + } + } + checkIntervalID = Script.setInterval(checkForHandsAboveHead, 500); + }, + cleanup: function() { + var entityIDs = findEntitiesWithTag(this.tag); + print("entities: ", entityIDs.length); + for (var i = 0; i < entityIDs.length; ++i) { + print("Deleting: ", entityIDs[i]); + Entities.deleteEntity(entityIDs[i]); + } + } +}; + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: Near Grab // +// // +/////////////////////////////////////////////////////////////////////////////// +var stepNearGrab = function(name) { + this.tag = name; +} +stepNearGrab.prototype = { + start: function(onFinish) { + var tag = this.tag; + + // Spawn content set + spawnWithTag(Step1EntityData, null, tag); + + var basketColliderID = findEntity({ name: BASKET_COLLIDER_NAME }, 10000); + var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; + + function createBlock() { + var boxSpawnID = findEntity({ name: BOX_SPAWN_NAME }, 10000); + if (!boxSpawnID) { + print("Error creating block, cannot find spawn"); + return null; + } + + Step1BlockData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; + return spawnWithTag([Step1BlockData], null, tag)[0]; + } + + // Enabled grab + // Create table ? + // Create blocks and basket + var boxID = createBlock(); + print("Created", boxID); + + function onHit() { + onFinish(); + } + + // When block collides with basket start step 2 + var checkCollidesTimer = null; + function checkCollides() { + print("CHECKING..."); + if (Vec3.distance(basketPosition, Entities.getEntityProperties(boxID, 'position').position) < 0.1) { + Script.clearInterval(checkCollidesTimer); + this.soundInjector = Audio.playSound(successSound, { + position: basketPosition, + volume: 0.7, + loop: false + }); + Script.setTimeout(onHit, 1000); + } + } + checkCollidesTimer = Script.setInterval(checkCollides, 500); + + // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block + }, + cleanup: function() { + var entityIDs = findEntitiesWithTag(this.tag); + print("entities: ", entityIDs.length); + for (var i = 0; i < entityIDs.length; ++i) { + print("Deleting: ", entityIDs[i]); + Entities.deleteEntity(entityIDs[i]); + } + } +}; + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: Far Grab // +// // +/////////////////////////////////////////////////////////////////////////////// +var stepFarGrab = function(name) { + this.tag = name; +} +stepFarGrab.prototype = { + start: function(onFinish) { + Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ + farGrabEnabled: true, + })); + var tag = this.tag; + var transform = { + position: { x: 3, y: 0, z: 0 }, + rotation: { x: 0, y: 0, z: 0, w: 1 } + } + + // Spawn content set + spawnWithTag(Step1EntityData, transform, tag); + + var basketColliderID = findEntity({ name: BASKET_COLLIDER_NAME }, 10000); + var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; + + function createBlock() { + var boxSpawnID = findEntity({ name: BOX_SPAWN_NAME }, 10000); + if (!boxSpawnID) { + print("Error creating block, cannot find spawn"); + return null; + } + + Step1BlockData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; + return spawnWithTag([Step1BlockData], null, tag)[0]; + } + + // Enabled grab + // Create table ? + // Create blocks and basket + var boxID = createBlock(); + print("Created", boxID); + + function onHit() { + onFinish(); + } + + // When block collides with basket start step 2 + var checkCollidesTimer = null; + function checkCollides() { + print("CHECKING..."); + if (Vec3.distance(basketPosition, Entities.getEntityProperties(boxID, 'position').position) < 0.1) { + Script.clearInterval(checkCollidesTimer); + this.soundInjector = Audio.playSound(successSound, { + position: basketPosition, + volume: 0.7, + loop: false + }); + Script.setTimeout(onHit, 1000); + } + } + checkCollidesTimer = Script.setInterval(checkCollides, 500); + + // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block + }, + cleanup: function() { + var entityIDs = findEntitiesWithTag(this.tag); + print("entities: ", entityIDs.length); + for (var i = 0; i < entityIDs.length; ++i) { + print("Deleting: ", entityIDs[i]); + Entities.deleteEntity(entityIDs[i]); + } + } +}; + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: Equip // +// // +/////////////////////////////////////////////////////////////////////////////// +var stepEquip = function(name) { + this.tag = name; +} +stepEquip.prototype = { + start: function(onFinish) { + Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ + holdEnabled: true, + })); + + var tag = this.tag; + + var defaultTransform = { + position: { + x: 0.0, + y: 0.0, + z: 0.75 + }, + rotation: { + x: 0, + y: 0, + z: 0, + w: 1 + } + }; + + // Spawn content set + spawnWithTag(StepGunData, defaultTransform, tag); + + var basketColliderID = findEntity({ name: BASKET_COLLIDER_NAME }, 10000); + var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; + + function createGun() { + var boxSpawnID = findEntity({ name: GUN_SPAWN_NAME }, 10000); + if (!boxSpawnID) { + print("Error creating block, cannot find spawn"); + return null; + } + + GunData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; + Vec3.print("spawn", GunData.position); + print("Adding: ", JSON.stringify(GunData)); + return spawnWithTag([GunData], null, tag)[0]; + } + + // Enabled grab + // Create table ? + // Create blocks and basket + var gunID = createGun(); + print("Created", gunID); + + function onHit() { + onFinish(); + } + + // When block collides with basket start step 2 + var checkCollidesTimer = null; + function checkCollides() { + print("CHECKING..."); + var ammoIDs = findEntities({ name: GUN_AMMO_NAME }, 15); + for (var i = 0; i < ammoIDs.length; ++i) { + if (Vec3.distance(basketPosition, Entities.getEntityProperties(ammoIDs[i], 'position').position) < 0.2) { + Script.clearInterval(checkCollidesTimer); + this.soundInjector = Audio.playSound(successSound, { + position: basketPosition, + volume: 0.7, + loop: false + }); + Script.setTimeout(onHit, 1000); + } + } + } + checkCollidesTimer = Script.setInterval(checkCollides, 500); + + // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block + }, + cleanup: function() { + var entityIDs = findEntitiesWithTag(this.tag); + print("entities: ", entityIDs.length); + for (var i = 0; i < entityIDs.length; ++i) { + print("Deleting: ", entityIDs[i]); + Entities.deleteEntity(entityIDs[i]); + } + } +}; + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: Teleport // +// // +/////////////////////////////////////////////////////////////////////////////// +var stepTeleport = function(name) { + this.tag = name; +} +stepTeleport.prototype = { + start: function(onFinish) { + Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); + Menu.setIsOptionChecked("Overlays", false); + }, + cleanup: function() { + var entityIDs = findEntitiesWithTag(this.tag); + print("entities: ", entityIDs.length); + for (var i = 0; i < entityIDs.length; ++i) { + print("Deleting: ", entityIDs[i]); + Entities.deleteEntity(entityIDs[i]); + } + } +}; + + + + + +var STEPS; + +var currentStepNum = -1; +var currentStep = null; +function startTutorial() { + currentStepNum = -1; + currentStep = null; + STEPS = [ + new stepDisableControllers("step0"), + //new stepRaiseAboveHead("step1"), + new stepNearGrab("step2"), + new stepFarGrab("step3"), + new stepEquip("step4"), + new stepTeleport("teleport"), + ] + startNextStep(); +} + +function startNextStep() { + if (currentStep) { + //currentStep.cleanup(); + } + + ++currentStepNum; + + if (currentStepNum >= STEPS.length) { + // Done + print("DONE WITH TUTORIAL"); + } else { + print("Starting step", currentStepNum); + currentStep = STEPS[currentStepNum]; + currentStep.start(startNextStep); + startNextStep(); + } +} + +function skipTutorial() { +} + +function stopTutorial() { + if (currentStep) { + currentStep.cleanup(); + } +} + +location = "/tutorial"; +startTutorial(); + +Script.scriptEnding.connect(stopTutorial); + diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js new file mode 100644 index 0000000000..715dffe089 --- /dev/null +++ b/tutorial/viveHandsv2.js @@ -0,0 +1,612 @@ +var PARENT_ID = MyAvatar.sessionUUID; +var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); +var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); +var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("LeftHand"); +var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("RightHand"); + +var zeroPosition = { x: 0, y: 0, z: 0 }; +var zeroRotation = { x: 0, y: 0, z: 0, w: 1 }; + +var CONTROLLER_LENGTH_OFFSET = 0.0762; + +var naturalPosition = { + x: 0, + y: -0.034076502197422087, + z: 0.06380049744620919 +}; +var naturalPositionL = { + x: 0, + y: 0.034076502197422087, + z: 0.06380049744620919 +}; +var naturalPositionR = { + x: 0.0, + y: 0.034076502197422087, + z: 0.06380049744620919 +}; + +// THe CONTROLLER_LEFTHAND +var leftBasePosition = { + x: CONTROLLER_LENGTH_OFFSET / 2, + y: CONTROLLER_LENGTH_OFFSET * 2, + z: CONTROLLER_LENGTH_OFFSET / 2 +}; +var rightBasePosition = { + x: -CONTROLLER_LENGTH_OFFSET / 2, + y: CONTROLLER_LENGTH_OFFSET * 2, + z: CONTROLLER_LENGTH_OFFSET / 2 +}; + +var leftBasePositionVive = Vec3.sum(leftBasePosition, { x: 0.005, y: 0.03, z: 0 }); +var rightBasePositionVive = Vec3.sum(rightBasePosition, { x: -0.005, y: 0.03, z: 0 }); + +Vec3.print("left offset: ", leftBasePosition); + +var leftBaseRotation = Quat.multiply( + Quat.fromPitchYawRollDegrees(0, 0, 45), + Quat.multiply( + Quat.fromPitchYawRollDegrees(90, 0, 0), + Quat.fromPitchYawRollDegrees(0, 0, 90) + ) +); + +var rightBaseRotation = Quat.multiply( + Quat.fromPitchYawRollDegrees(0, 0, -45), + Quat.multiply( + Quat.fromPitchYawRollDegrees(90, 0, 0), + Quat.fromPitchYawRollDegrees(0, 0, -90) + ) +); + + +var touchLeftBaseRotation = Quat.multiply( + Quat.fromPitchYawRollDegrees(0, 0, 0), + Quat.multiply( + Quat.fromPitchYawRollDegrees(0, 0, -45), + Quat.multiply( + Quat.fromPitchYawRollDegrees(180, 0, 0), + Quat.fromPitchYawRollDegrees(0, -90, 0) + ) + ) +); + +var touchRightBaseRotation = Quat.multiply( + Quat.fromPitchYawRollDegrees(0, 0, 45), + Quat.multiply( + Quat.fromPitchYawRollDegrees(180, 0, 0), + Quat.fromPitchYawRollDegrees(0, 90, 0) + ) +); + +var TOUCH_CONTROLLER_CONFIGURATION = { + name: "Touch", + controllers: [ + { + modelURL: "C:/Users/Ryan/Assets/controller/touch_l_full.fbx", + jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"), + rotation: touchLeftBaseRotation, + //position: Vec3.sum(leftBasePosition, { x: 0.032, y: 0.0, z: -0.02 }), + position: Vec3.sum(leftBasePosition, { x: 0.0, y: -0.016, z: -0.02 }), + //dimensions: naturalDimensions, + }, + { + modelURL: "C:/Users/Ryan/Assets/controller/touch_r_full.fbx", + jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), + rotation: touchRightBaseRotation, + //position: rightBasePosition, + position: Vec3.sum(rightBasePosition, { x: 0.0, y: -0.016, z: -0.02 }), + //dimensions: naturalDimensions, + } + ] +} + +var TOUCH_2_CONTROLLER_CONFIGURATION = { + name: "Touch", + controllers: [ + { + modelURL: "C:/Users/Ryan/Assets/controller/oculus_touch_l.fbx", + naturalPosition: { + x: 0.016486000269651413, + y: -0.035518500953912735, + z: -0.018527504056692123 + }, + jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"), + rotation: touchLeftBaseRotation, + position: leftBasePosition, + + annotationTextRotation: Quat.fromPitchYawRollDegrees(20, -90, 0), + annotations: { + + buttonX: { + position: { + x: -0.00931, + y: 0.00212, + z: -0.01259, + }, + direction: "left", + color: { red: 100, green: 100, blue: 100 }, + }, + buttonY: { + position: { + x: -0.01617, + y: 0.00216, + z: 0.00177, + }, + direction: "left", + color: { red: 100, green: 255, blue: 100 }, + }, + bumper: { + position: { + x: 0.00678, + y: -0.02740, + z: -0.02537, + }, + direction: "left", + color: { red: 100, green: 100, blue: 255 }, + }, + trigger: { + position: { + x: -0.01275, + y: -0.01992, + z: 0.02314, + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + } + }, + }, + { + modelURL: "C:/Users/Ryan/Assets/controller/oculus_touch_r.fbx", + naturalPosition: { + x: -0.016486000269651413, + y: -0.035518500953912735, + z: -0.018527504056692123 + }, + jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), + rotation: touchRightBaseRotation, + position: rightBasePosition, + + annotationTextRotation: Quat.fromPitchYawRollDegrees(20, 90, 0), + annotations: { + + buttonA: { + position: { + x: 0.00931, + y: 0.00212, + z: -0.01259, + }, + direction: "right", + color: { red: 100, green: 100, blue: 100 }, + }, + buttonB: { + position: { + x: 0.01617, + y: 0.00216, + z: 0.00177, + }, + direction: "right", + color: { red: 100, green: 255, blue: 100 }, + }, + bumper: { + position: { + x: 0.00678, + y: -0.02740, + z: -0.02537, + }, + direction: "right", + color: { red: 100, green: 100, blue: 255 }, + }, + trigger: { + position: { + x: 0.01275, + y: -0.01992, + z: 0.02314, + }, + direction: "right", + color: { red: 255, green: 100, blue: 100 }, + } + }, + } + ] +} + + +var viveNaturalDimensions = { + x: 0.1174320001155138, + y: 0.08361100335605443, + z: 0.21942697931081057 +}; +var viveNaturalPosition = { + x: 0, + y: -0.034076502197422087, + z: 0.06380049744620919 +}; + +var VIVE_CONTROLLER_CONFIGURATION = { + name: "Vive", + controllers: [ + { + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vr_controller_vive_1_5.obj", + modelURL: "C:\\Users\\Ryan\\Assets\\controller\\vive2.fbx", + jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"), + naturalPosition: viveNaturalPosition, + rotation: leftBaseRotation, + position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, 45), leftBasePosition), + + dimensions: viveNaturalDimensions, + + annotationTextRotation: Quat.fromPitchYawRollDegrees(20, -90, 0), + annotations: { +// red: { +// debug: true, +// position: { +// x: 0.1, +// y: 0.0, +// z: 0.0 +// }, +// direction: "right", +// color: { red: 255, green: 0, blue: 0 }, +// }, +// green: { +// debug: true, +// position: { +// x: 0.0, +// y: 0.1, +// z: 0.0 +// }, +// direction: "right", +// color: { red: 0, green: 255, blue: 0 }, +// }, +// blue: { +// debug: true, +// position: { +// x: 0.0, +// y: 0.0, +// z: 0.1 +// }, +// direction: "right", +// color: { red: 0, green: 0, blue: 255 }, +// }, +// white: { +// debug: true, +// position: { +// x: 0.0, +// y: 0.0, +// z: 0.0 +// }, +// direction: "right", +// color: { red: 255, green: 255, blue: 255 }, +// }, + + center: { + position: zeroPosition, + direction: "center", + color: { red: 100, green: 255, blue: 255 }, + }, + trigger: { + position: { + x: 0, + y: -0.023978, + z: 0.04546 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + menu: { + position: { + x: 0, + y: 0.00770, + z: 0.01979 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + grip: { + position: { + x: 0.01980, + y: -0.01561, + z: 0.08721 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + pad: { + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + steam: { + position: { + x: 0, + y: 0.00303, + z: 0.08838 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + }, + }, + { + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vr_controller_vive_1_5.obj", + modelURL: "C:\\Users\\Ryan\\Assets\\controller\\vive2.fbx", + jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), + //rotation: rightBaseRotation, + //position: rightBasePosition, + //position: Vec3.sum(Vec3.multiplyQbyV(rightBaseRotation, naturalPositionR), rightBasePositionVive), + + //rotation: zeroRotation, + //position: zeroPosition, + rotation: rightBaseRotation, + position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, -45), rightBasePosition), + + dimensions: viveNaturalDimensions, + + naturalPosition: { + x: 0, + y: -0.034076502197422087, + z: 0.06380049744620919 + }, + //rotation: touchRightBaseRotation, + //position: rightBasePosition, + + annotationTextRotation: Quat.fromPitchYawRollDegrees(20, -90, 0), + annotations: { + + trigger: { + position: { + x: 0, + y: -0.023978, + z: 0.04546 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + menu: { + position: { + x: 0, + y: 0.00770, + z: 0.01979 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + grip: { + position: { + x: 0.01980, + y: -0.01561, + z: 0.08721 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + pad: { + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + steam: { + position: { + x: 0, + y: 0.00303, + z: 0.08838 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + } + } + ] +} + +var DEBUG = true; + +function setupController(config) { + var controllerDisplay = { + overlays: [], + annotations: { + }, + mappingName: "" + }; + for (var i = 0; i < config.controllers.length; ++i) { + var controller = config.controllers[i]; + var position = controller.position; + if (controller.naturalPosition) { + position = Vec3.sum(Vec3.multiplyQbyV( + controller.rotation, controller.naturalPosition), position); + } + var overlayID = Overlays.addOverlay("model", { + url: controller.modelURL, + dimensions: controller.dimensions, + localRotation: controller.rotation, + localPosition: position, + parentID: PARENT_ID, + parentJointIndex: controller.jointIndex, + ignoreRayIntersection: true, + }); + + controllerDisplay.overlays.push(overlayID); + + if (controller.annotations) { + for (var key in controller.annotations) { + var annotation = controller.annotations[key]; + var annotationPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, annotation.position)); + if (DEBUG) { + overlayID = Overlays.addOverlay("sphere", { + localPosition: annotationPosition, + //localPosition: Vec3.sum(controller.position, annotation.position), + //localPosition: Vec3.sum(position, annotation.position), + color: annotation.color || { red: 255, green: 100, blue: 100 }, + dimensions: { + x: 0.01, + y: 0.01, + z: 0.01 + }, + parentID: PARENT_ID, + parentJointIndex: controller.jointIndex, + }); + controllerDisplay.overlays.push(overlayID); + + controllerDisplay.annotations[key] = { + overlay: overlayID, + }; + } + + var sign = annotation.direction == "right" ? 1 : -1; + var textOffset = annotation.direction == "right" ? 0.04 : -0.01; + var textOverlayID = Overlays.addOverlay("text3d", { + text: key, + localPosition: Vec3.sum(annotationPosition, Vec3.multiplyQbyV(controller.rotation, { x: textOffset, y: 0, z: 0.0 })), + localRotation: controller.annotationTextRotation, + lineHeight: 0.01, + leftMargin: 0, + rightMargin: 0, + topMargin: 0, + bottomMargin: 0, + backgroundAlpha: 0, + dimensions: { x: 0.003, y: 0.003, z: 0.003 }, + //localPosition: Vec3.sum(controller.position, annotation.position), + //localPosition: Vec3.sum(position, annotation.position), + color: annotation.textColor || { red: 255, green: 255, blue: 255 }, + parentID: PARENT_ID, + parentJointIndex: controller.jointIndex, + }); + controllerDisplay.overlays.push(textOverlayID); + + var offset = { x: 0, y: 0, z: annotation.direction == "right" ? -0.1 : 0.1 }; + var lineOverlayID = Overlays.addOverlay("line3d", { + visible: false, + localPosition: annotationPosition, + localStart: { x: 0, y: 0, z: 0 }, + localEnd: offset, + //localPosition: Vec3.sum(controller.position, annotation.position), + //localPosition: Vec3.sum(position, annotation.position), + color: annotation.color || { red: 255, green: 100, blue: 100 }, + parentID: PARENT_ID, + parentJointIndex: controller.jointIndex, + }); + controllerDisplay.overlays.push(lineOverlayID); + } + } + } + return controllerDisplay; +} + +ControllerDisplay = function() { +}; + +function deleteControllerDisplay(controllerDisplay) { + for (var i = 0; i < controllerDisplay.overlays.length; ++i) { + Overlays.deleteOverlay(controllerDisplay.overlays[i]); + } + Controller.disableMapping(controllerDisplay.mappingName); +} + +// var triggerAnnotationOverlayID = Overlays.addOverlay("text3d", { +// text: "Trigger", +// lineHeight: 0.025, +// backgroundAlpha: 0.0, +// dimensions: { +// x: 0.2, +// y: 0.2, +// }, +// localPosition: Vec3.sum(leftBasePosition, { x: -0.09, y: -0.025, z: 0.03 }), +// localRotation: Quat.multiply(Quat.fromPitchYawRollDegrees(180, 0, 90), leftBaseRotation), +// parentID: MyAvatar.sessionUUID, +// parentJointIndex: MyAvatar.getJointIndex("LeftHand") +// }); + +// var leftOverlayID = Overlays.addOverlay("model", { +// url: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vr_controller_vive_1_5.obj", +// dimensions: naturalDimensions, +// localRotation: leftBaseRotation, +// localPosition: leftBasePosition, +// parentID: PARENT_ID, +// parentJointIndex: LEFT_JOINT_INDEX +// }); +// +// var leftTriggerOverlayID = Overlays.addOverlay("model", { +// url: "C:/Users/Ryan/Assets/controller/touch_l_trigger.fbx", +// visible: false, +// localRotation: leftBaseRotation, +// localPosition: Vec3.sum(leftBasePosition, { x: -0.05, y: -0.025, z: 0.02 }), +// parentID: PARENT_ID, +// parentJointIndex: LEFT_JOINT_INDEX +// }); + +// var rightOverlayID = Overlays.addOverlay("model", { +// url: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vr_controller_vive_1_5.obj", +// dimensions: naturalDimensions, +// localRotation: rightBaseRotation, +// localPosition: rightBasePosition, +// parentID: PARENT_ID, +// parentJointIndex: RIGHT_JOINT_INDEX +// }); +// +// var rightTriggerOverlayID = Overlays.addOverlay("model", { +// url: "C:/Users/Ryan/Assets/controller/touch_r_trigger.fbx", +// visible: false, +// localRotation: rightBaseRotation, +// localPosition: Vec3.sum(rightBasePosition, { x: 0.05, y: -0.025, z: 0.02 }), +// parentID: PARENT_ID, +// parentJointIndex: RIGHT_JOINT_INDEX +// }); +// +var overlays = [ + // leftOverlayID, + // leftTriggerOverlayID, + // triggerAnnotationOverlayID, + + // rightOverlayID, + // rightTriggerOverlayID, +]; +// +// Script.setInterval(function() { +// // var pose = MyAvatar.getLeftHandControllerPoseInWorldFrame(); +// // Overlays.editOverlay(leftOverlayID, { +// // position: pose.translation, +// // rotation: pose.rotation +// // }); +// // pose = MyAvatar.getRightHandControllerPoseInWorldFrame(); +// // Overlays.editOverlay(rightOverlayID, { +// // position: pose.translation, +// // rotation: pose.rotation +// // }); +// }, 10); + + +var MAPPING_NAME = "com.highfidelity.handControllerGrab.disable"; +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from([Controller.Standard.LT]).to(function(value) { + // print(value); + // Overlays.editOverlay(leftTriggerOverlayID, { + // localRotation: Quat.multiply(Quat.fromPitchYawRollDegrees(0, 0, value * -45), leftBaseRotation) + // }); +}); +mapping.from([Controller.Standard.RT]).to(function(value) { + // print(value); + // Overlays.editOverlay(rightTriggerOverlayID, { + // localRotation: Quat.multiply(Quat.fromPitchYawRollDegrees(0, 0, value * 45), rightBaseRotation) + // }); +}); +Controller.enableMapping(MAPPING_NAME); + +//var c = setupController(TOUCH_CONTROLLER_CONFIGURATION); +//var c = setupController(TOUCH_2_CONTROLLER_CONFIGURATION); +var c = setupController(VIVE_CONTROLLER_CONFIGURATION); +//MyAvatar.shouldRenderLocally = false; +Script.scriptEnding.connect(function() { + deleteControllerDisplay(c); + MyAvatar.shouldRenderLocally = true; + for (var i = 0; i < overlays.length; ++i) { + Overlays.deleteOverlay(overlays[i]); + } + Controller.disableMapping(MAPPING_NAME); +}); From ea71ef91b637db04ab0e9a59955b8f06b437b632 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 1 Sep 2016 09:08:18 -0700 Subject: [PATCH 003/109] Modify tutorial to adjust visiblity of tutorial entities --- tutorial/tutorial.js | 222 +++++++++++++++++++++++++++++++------------ 1 file changed, 159 insertions(+), 63 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 861ceb5545..29713d9ad5 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -1,4 +1,33 @@ + +if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 + // internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function() {}, + fBound = function() { + return fToBind.apply(this instanceof fNOP + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + if (this.prototype) { + // Function.prototype doesn't have a prototype property + fNOP.prototype = this.prototype; + } + fBound.prototype = new fNOP(); + + return fBound; + }; +} Script.include("entityData.js"); + // // var FAR_GRAB_INPUTS = [ // Controller.Standard.RT @@ -89,8 +118,11 @@ findEntities = function(properties, searchRadius, filterFn) { // On start tutorial... // Load assets -var BOX_SPAWN_NAME = "tutorial/box_spawn"; -var BASKET_COLLIDER_NAME = "tutorial/basket_collider"; +var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn"; +var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn"; +var NEAR_BASKET_COLLIDER_NAME = "tutorial/nearGrab/basket_collider"; +var FAR_BASKET_COLLIDER_NAME = "tutorial/farGrab/basket_collider"; +var GUN_BASKET_COLLIDER_NAME = "tutorial/equip/basket_collider"; var GUN_SPAWN_NAME = "tutorial/gun_spawn"; var GUN_AMMO_NAME = "Tutorial Ping Pong Ball" @@ -113,6 +145,7 @@ function spawn(entityData, transform, modifyFn) { } var id = Entities.addEntity(data); ids.push(id); + print("data:", JSON.stringify(data)); } return ids; } @@ -132,11 +165,34 @@ function spawnWithTag(entityData, transform, tag) { var userData = parseJSON(data.userData); userData.tag = tag; data.userData = JSON.stringify(userData); + print("In modify", tag, userData, data.userData); return data; } return spawn(entityData, transform, modifyFn); } +function deleteEntitiesWithTag(tag) { + print("searching for...:", tag); + var entityIDs = findEntitiesWithTag(tag); + for (var i = 0; i < entityIDs.length; ++i) { + print("Deleteing:", entityIDs[i]); + Entities.deleteEntity(entityIDs[i]); + } +} +function editEntitiesWithTag(tag, propertiesOrFn) { + print("Editing:", tag); + var entityIDs = findEntitiesWithTag(tag); + print("Editing...", entityIDs); + for (var i = 0; i < entityIDs.length; ++i) { + print("Editing...", entityIDs[i]); + if (isFunction(propertiesOrFn)) { + Entities.editEntity(entityIDs[i], propertiesOrFn(entityIDs[i])); + } else { + Entities.editEntity(entityIDs[i], propertiesOrFn); + } + } +} + function findEntitiesWithTag(tag) { return findEntities({ userData: "" }, 10000, function(properties, key, value) { data = parseJSON(value); @@ -144,6 +200,12 @@ function findEntitiesWithTag(tag) { }); } +// From http://stackoverflow.com/questions/5999998/how-can-i-check-if-a-javascript-variable-is-function-type +function isFunction(functionToCheck) { + var getType = {}; + return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; +} + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // @@ -176,6 +238,7 @@ stepDisableControllers.prototype = { /////////////////////////////////////////////////////////////////////////////// var stepRaiseAboveHead = function(name) { this.tag = name; + this.tempTag = name + "-temporary"; } stepRaiseAboveHead.prototype = { start: function(onFinish) { @@ -196,13 +259,17 @@ stepRaiseAboveHead.prototype = { }; // Spawn content set - spawnWithTag(HandsAboveHeadData, defaultTransform, tag); + //spawnWithTag(HandsAboveHeadData, defaultTransform, tag); + print("raise hands...", this.tag); + editEntitiesWithTag(this.tag, { visible: true }); - var checkIntervalID = null; + + this.checkIntervalID = null; function checkForHandsAboveHead() { print("Checking..."); if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { - Script.clearInterval(checkIntervalID); + Script.clearInterval(this.checkIntervalID); + this.checkIntervalID = null; this.soundInjector = Audio.playSound(successSound, { position: defaultTransform.position, volume: 0.7, @@ -211,15 +278,14 @@ stepRaiseAboveHead.prototype = { onFinish(); } } - checkIntervalID = Script.setInterval(checkForHandsAboveHead, 500); + this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); }, cleanup: function() { - var entityIDs = findEntitiesWithTag(this.tag); - print("entities: ", entityIDs.length); - for (var i = 0; i < entityIDs.length; ++i) { - print("Deleting: ", entityIDs[i]); - Entities.deleteEntity(entityIDs[i]); + if (this.checkIntervalID != null) { + Script.clearInterval(this.checkIntervalID); } + editEntitiesWithTag(this.tag, { visible: false, collisionless: 1 }); + deleteEntitiesWithTag(this.tempTag); } }; @@ -233,32 +299,34 @@ stepRaiseAboveHead.prototype = { /////////////////////////////////////////////////////////////////////////////// var stepNearGrab = function(name) { this.tag = name; + this.tempTag = name + "-temporary"; } stepNearGrab.prototype = { start: function(onFinish) { var tag = this.tag; // Spawn content set - spawnWithTag(Step1EntityData, null, tag); + //spawnWithTag(Step1EntityData, null, tag); + editEntitiesWithTag(this.tag, { visible: true }); - var basketColliderID = findEntity({ name: BASKET_COLLIDER_NAME }, 10000); + var basketColliderID = findEntity({ name: NEAR_BASKET_COLLIDER_NAME }, 10000); var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; function createBlock() { - var boxSpawnID = findEntity({ name: BOX_SPAWN_NAME }, 10000); + var boxSpawnID = findEntity({ name: NEAR_BOX_SPAWN_NAME }, 10000); if (!boxSpawnID) { print("Error creating block, cannot find spawn"); return null; } Step1BlockData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; - return spawnWithTag([Step1BlockData], null, tag)[0]; + return spawnWithTag([Step1BlockData], null, this.tempTag)[0]; } // Enabled grab // Create table ? // Create blocks and basket - var boxID = createBlock(); + var boxID = createBlock.bind(this)(); print("Created", boxID); function onHit() { @@ -268,7 +336,7 @@ stepNearGrab.prototype = { // When block collides with basket start step 2 var checkCollidesTimer = null; function checkCollides() { - print("CHECKING..."); + print(this.tag, "CHECKING..."); if (Vec3.distance(basketPosition, Entities.getEntityProperties(boxID, 'position').position) < 0.1) { Script.clearInterval(checkCollidesTimer); this.soundInjector = Audio.playSound(successSound, { @@ -276,20 +344,16 @@ stepNearGrab.prototype = { volume: 0.7, loop: false }); - Script.setTimeout(onHit, 1000); + Script.setTimeout(onHit.bind(this), 1000); } } - checkCollidesTimer = Script.setInterval(checkCollides, 500); + checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, cleanup: function() { - var entityIDs = findEntitiesWithTag(this.tag); - print("entities: ", entityIDs.length); - for (var i = 0; i < entityIDs.length; ++i) { - print("Deleting: ", entityIDs[i]); - Entities.deleteEntity(entityIDs[i]); - } + editEntitiesWithTag(this.tag, { visible: false}); + deleteEntitiesWithTag(this.tempTag); } }; @@ -303,6 +367,7 @@ stepNearGrab.prototype = { /////////////////////////////////////////////////////////////////////////////// var stepFarGrab = function(name) { this.tag = name; + this.tempTag = name + "-temporary"; } stepFarGrab.prototype = { start: function(onFinish) { @@ -316,26 +381,27 @@ stepFarGrab.prototype = { } // Spawn content set - spawnWithTag(Step1EntityData, transform, tag); + //spawnWithTag(Step1EntityData, transform, tag); + editEntitiesWithTag(this.tag, { visible: true}); - var basketColliderID = findEntity({ name: BASKET_COLLIDER_NAME }, 10000); + var basketColliderID = findEntity({ name: FAR_BASKET_COLLIDER_NAME }, 10000); var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; function createBlock() { - var boxSpawnID = findEntity({ name: BOX_SPAWN_NAME }, 10000); + var boxSpawnID = findEntity({ name: FAR_BOX_SPAWN_NAME }, 10000); if (!boxSpawnID) { print("Error creating block, cannot find spawn"); return null; } Step1BlockData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; - return spawnWithTag([Step1BlockData], null, tag)[0]; + return spawnWithTag([Step1BlockData], null, this.tempTag)[0]; } // Enabled grab // Create table ? // Create blocks and basket - var boxID = createBlock(); + var boxID = createBlock.bind(this)(); print("Created", boxID); function onHit() { @@ -353,20 +419,16 @@ stepFarGrab.prototype = { volume: 0.7, loop: false }); - Script.setTimeout(onHit, 1000); + Script.setTimeout(onHit.bind(this), 1000); } } - checkCollidesTimer = Script.setInterval(checkCollides, 500); + checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, cleanup: function() { - var entityIDs = findEntitiesWithTag(this.tag); - print("entities: ", entityIDs.length); - for (var i = 0; i < entityIDs.length; ++i) { - print("Deleting: ", entityIDs[i]); - Entities.deleteEntity(entityIDs[i]); - } + editEntitiesWithTag(this.tag, { visible: false}); + deleteEntitiesWithTag(this.tempTag); } }; @@ -380,6 +442,7 @@ stepFarGrab.prototype = { /////////////////////////////////////////////////////////////////////////////// var stepEquip = function(name) { this.tag = name; + this.tempTag = name + "-temporary"; } stepEquip.prototype = { start: function(onFinish) { @@ -404,9 +467,10 @@ stepEquip.prototype = { }; // Spawn content set - spawnWithTag(StepGunData, defaultTransform, tag); + //spawnWithTag(StepGunData, defaultTransform, tag); + editEntitiesWithTag(this.tag, { visible: true}); - var basketColliderID = findEntity({ name: BASKET_COLLIDER_NAME }, 10000); + var basketColliderID = findEntity({ name: GUN_BASKET_COLLIDER_NAME }, 10000); var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; function createGun() { @@ -419,13 +483,13 @@ stepEquip.prototype = { GunData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; Vec3.print("spawn", GunData.position); print("Adding: ", JSON.stringify(GunData)); - return spawnWithTag([GunData], null, tag)[0]; + return spawnWithTag([GunData], null, this.tempTag)[0]; } // Enabled grab // Create table ? // Create blocks and basket - var gunID = createGun(); + var gunID = createGun.bind(this)(); print("Created", gunID); function onHit() { @@ -445,21 +509,17 @@ stepEquip.prototype = { volume: 0.7, loop: false }); - Script.setTimeout(onHit, 1000); + Script.setTimeout(onHit.bind(this), 1000); } } } - checkCollidesTimer = Script.setInterval(checkCollides, 500); + checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, cleanup: function() { - var entityIDs = findEntitiesWithTag(this.tag); - print("entities: ", entityIDs.length); - for (var i = 0; i < entityIDs.length; ++i) { - print("Deleting: ", entityIDs[i]); - Entities.deleteEntity(entityIDs[i]); - } + editEntitiesWithTag(this.tag, { visible: false}); + deleteEntitiesWithTag(this.tempTag); } }; @@ -473,22 +533,45 @@ stepEquip.prototype = { /////////////////////////////////////////////////////////////////////////////// var stepTeleport = function(name) { this.tag = name; + this.tempTag = name + "-temporary"; } stepTeleport.prototype = { start: function(onFinish) { Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); Menu.setIsOptionChecked("Overlays", false); + + editEntitiesWithTag(this.tag, { visible: true }); }, cleanup: function() { - var entityIDs = findEntitiesWithTag(this.tag); - print("entities: ", entityIDs.length); - for (var i = 0; i < entityIDs.length; ++i) { - print("Deleting: ", entityIDs[i]); - Entities.deleteEntity(entityIDs[i]); - } + editEntitiesWithTag(this.tag, { visible: false }); + deleteEntitiesWithTag(this.tempTag); } }; +function showEntitiesWithTag(tag) { + editEntitiesWithTag(tag, function(entityID) { + var userData = Entities.getEntityProperties(entityID, "userData").userData; + var data = parseJSON(userData); + var newProperties = { + visible: data.visible == false ? false : true, + collisionless: data.collisionless == false ? false : true, + }; + Entities.editEntity(entityID, newProperties); + }); +} +function hideEntitiesWithTag(tag) { + editEntitiesWithTag(tag, function(entityID) { + var userData = Entities.getEntityProperties(entityID, "userData").userData; + var data = parseJSON(userData); + var newProperties = { + visible: false, + collisionless: 1, + ignoreForCollisions: 1, + }; + Entities.editEntity(entityID, newProperties); + }); +} + @@ -501,11 +584,11 @@ function startTutorial() { currentStepNum = -1; currentStep = null; STEPS = [ - new stepDisableControllers("step0"), - //new stepRaiseAboveHead("step1"), - new stepNearGrab("step2"), - new stepFarGrab("step3"), - new stepEquip("step4"), + //new stepDisableControllers("step0"), + new stepRaiseAboveHead("raiseHands"), + new stepNearGrab("nearGrab"), + new stepFarGrab("farGrab"), + new stepEquip("equip"), new stepTeleport("teleport"), ] startNextStep(); @@ -513,7 +596,7 @@ function startTutorial() { function startNextStep() { if (currentStep) { - //currentStep.cleanup(); + currentStep.cleanup(); } ++currentStepNum; @@ -525,7 +608,6 @@ function startNextStep() { print("Starting step", currentStepNum); currentStep = STEPS[currentStepNum]; currentStep.start(startNextStep); - startNextStep(); } } @@ -536,6 +618,8 @@ function stopTutorial() { if (currentStep) { currentStep.cleanup(); } + currentStepNum = -1; + currentStep = null; } location = "/tutorial"; @@ -543,3 +627,15 @@ startTutorial(); Script.scriptEnding.connect(stopTutorial); + + +Controller.keyReleaseEvent.connect(function (event) { + if (event.text == ",") { + startNextStep(); + } else if (event.text == ".") { + stopTutorial(); + } else if (event.text == "r") { + stopTutorial(); + startTutorial(); + } +}); From a1280d0958ee4e707bc9cd2e6ed7a8816091e46e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 1 Sep 2016 10:54:01 -0700 Subject: [PATCH 004/109] Update vive controller model --- tutorial/viveHandsv2.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 715dffe089..a483af7c26 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -1,8 +1,8 @@ var PARENT_ID = MyAvatar.sessionUUID; var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); -var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("LeftHand"); -var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("RightHand"); +//var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("LeftHand"); +//var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("RightHand"); var zeroPosition = { x: 0, y: 0, z: 0 }; var zeroRotation = { x: 0, y: 0, z: 0, w: 1 }; @@ -25,7 +25,6 @@ var naturalPositionR = { z: 0.06380049744620919 }; -// THe CONTROLLER_LEFTHAND var leftBasePosition = { x: CONTROLLER_LENGTH_OFFSET / 2, y: CONTROLLER_LENGTH_OFFSET * 2, @@ -221,13 +220,13 @@ var viveNaturalPosition = { y: -0.034076502197422087, z: 0.06380049744620919 }; +var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive2.fbx"; var VIVE_CONTROLLER_CONFIGURATION = { name: "Vive", controllers: [ { - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vr_controller_vive_1_5.obj", - modelURL: "C:\\Users\\Ryan\\Assets\\controller\\vive2.fbx", + modelURL: viveModelURL, jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"), naturalPosition: viveNaturalPosition, rotation: leftBaseRotation, @@ -331,15 +330,9 @@ var VIVE_CONTROLLER_CONFIGURATION = { }, }, { - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vr_controller_vive_1_5.obj", - modelURL: "C:\\Users\\Ryan\\Assets\\controller\\vive2.fbx", + modelURL: viveModelURL, jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), - //rotation: rightBaseRotation, - //position: rightBasePosition, - //position: Vec3.sum(Vec3.multiplyQbyV(rightBaseRotation, naturalPositionR), rightBasePositionVive), - //rotation: zeroRotation, - //position: zeroPosition, rotation: rightBaseRotation, position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, -45), rightBasePosition), @@ -350,8 +343,6 @@ var VIVE_CONTROLLER_CONFIGURATION = { y: -0.034076502197422087, z: 0.06380049744620919 }, - //rotation: touchRightBaseRotation, - //position: rightBasePosition, annotationTextRotation: Quat.fromPitchYawRollDegrees(20, -90, 0), annotations: { From 54498a8f3fdef3b4b628ad8ee82be87e3cdebf72 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 1 Sep 2016 14:56:11 -0700 Subject: [PATCH 005/109] Add the ability to ignore invisible entities in findRayIntersection --- interface/src/ui/overlays/Line3DOverlay.cpp | 12 ++ plugins/openvr/src/OpenVrHelpers.cpp | 2 + tutorial/tutorial.js | 168 +++++++++++++++++--- tutorial/viveHandsv2.js | 2 +- 4 files changed, 160 insertions(+), 24 deletions(-) diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index c3a6c5920e..1616d4c2e2 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -124,6 +124,12 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { } properties.remove("start"); // so that Base3DOverlay doesn't respond to it + auto localStart = properties["localStart"]; + if (localStart.isValid()) { + _start = vec3FromVariant(localStart); + } + properties.remove("localStart"); // so that Base3DOverlay doesn't respond to it + auto end = properties["end"]; // if "end" property was not there, check to see if they included aliases: endPoint if (!end.isValid()) { @@ -133,6 +139,12 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { setEnd(vec3FromVariant(end)); } + auto localEnd = properties["localEnd"]; + if (localEnd.isValid()) { + _end = vec3FromVariant(localEnd); + } + properties.remove("localEnd"); // so that Base3DOverlay doesn't respond to it + auto glow = properties["glow"]; if (glow.isValid()) { setGlow(glow.toFloat()); diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index f5e36492bd..4429eb274f 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -296,6 +296,8 @@ controller::Pose openVrControllerPoseToHandPose(bool isLeftHand, const mat4& mat auto translationOffset = (isLeftHand ? leftTranslationOffset : rightTranslationOffset); auto rotationOffset = (isLeftHand ? leftRotationOffset : rightRotationOffset); + //qDebug() << "TRANSLATION OFFSET: " << isLeftHand << ", " << translationOffset.x << ", " << translationOffset.y << ", " << translationOffset.z; + glm::vec3 position = extractTranslation(mat); glm::quat rotation = glm::normalize(glm::quat_cast(mat)); diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 29713d9ad5..b6b5fdd79d 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -125,6 +125,7 @@ var FAR_BASKET_COLLIDER_NAME = "tutorial/farGrab/basket_collider"; var GUN_BASKET_COLLIDER_NAME = "tutorial/equip/basket_collider"; var GUN_SPAWN_NAME = "tutorial/gun_spawn"; var GUN_AMMO_NAME = "Tutorial Ping Pong Ball" +var TELEPORT_PAD_NAME = "tutorial/teleport/pad" function spawn(entityData, transform, modifyFn) { print("Creating: ", entityData); @@ -145,7 +146,7 @@ function spawn(entityData, transform, modifyFn) { } var id = Entities.addEntity(data); ids.push(id); - print("data:", JSON.stringify(data)); + print(id, "data:", JSON.stringify(data)); } return ids; } @@ -307,7 +308,7 @@ stepNearGrab.prototype = { // Spawn content set //spawnWithTag(Step1EntityData, null, tag); - editEntitiesWithTag(this.tag, { visible: true }); + showEntitiesWithTag(this.tag, { visible: true }); var basketColliderID = findEntity({ name: NEAR_BASKET_COLLIDER_NAME }, 10000); var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; @@ -334,11 +335,11 @@ stepNearGrab.prototype = { } // When block collides with basket start step 2 - var checkCollidesTimer = null; function checkCollides() { print(this.tag, "CHECKING..."); if (Vec3.distance(basketPosition, Entities.getEntityProperties(boxID, 'position').position) < 0.1) { - Script.clearInterval(checkCollidesTimer); + Script.clearInterval(this.checkCollidesTimer); + this.checkCollidesTimer = null; this.soundInjector = Audio.playSound(successSound, { position: basketPosition, volume: 0.7, @@ -347,12 +348,15 @@ stepNearGrab.prototype = { Script.setTimeout(onHit.bind(this), 1000); } } - checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); + this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, cleanup: function() { - editEntitiesWithTag(this.tag, { visible: false}); + if (this.checkCollidesTimer) { + Script.clearInterval(this.checkCollidesTimer); + } + hideEntitiesWithTag(this.tag, { visible: false}); deleteEntitiesWithTag(this.tempTag); } }; @@ -382,7 +386,7 @@ stepFarGrab.prototype = { // Spawn content set //spawnWithTag(Step1EntityData, transform, tag); - editEntitiesWithTag(this.tag, { visible: true}); + showEntitiesWithTag(this.tag); var basketColliderID = findEntity({ name: FAR_BASKET_COLLIDER_NAME }, 10000); var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; @@ -427,7 +431,7 @@ stepFarGrab.prototype = { // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, cleanup: function() { - editEntitiesWithTag(this.tag, { visible: false}); + hideEntitiesWithTag(this.tag, { visible: false}); deleteEntitiesWithTag(this.tempTag); } }; @@ -442,6 +446,8 @@ stepFarGrab.prototype = { /////////////////////////////////////////////////////////////////////////////// var stepEquip = function(name) { this.tag = name; + this.tagPart1 = name + "-part1"; + this.tagPart2 = name + "-part2"; this.tempTag = name + "-temporary"; } stepEquip.prototype = { @@ -468,7 +474,8 @@ stepEquip.prototype = { // Spawn content set //spawnWithTag(StepGunData, defaultTransform, tag); - editEntitiesWithTag(this.tag, { visible: true}); + showEntitiesWithTag(this.tag); + showEntitiesWithTag(this.tagPart1); var basketColliderID = findEntity({ name: GUN_BASKET_COLLIDER_NAME }, 10000); var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; @@ -481,6 +488,7 @@ stepEquip.prototype = { } GunData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; + GunData.rotation = Entities.getEntityProperties(boxSpawnID, 'rotation').rotation; Vec3.print("spawn", GunData.position); print("Adding: ", JSON.stringify(GunData)); return spawnWithTag([GunData], null, this.tempTag)[0]; @@ -489,21 +497,26 @@ stepEquip.prototype = { // Enabled grab // Create table ? // Create blocks and basket - var gunID = createGun.bind(this)(); - print("Created", gunID); + this.gunID = createGun.bind(this)(); + print("Created", this.gunID); + this.onFinish = onFinish; function onHit() { - onFinish(); + hideEntitiesWithTag(this.tagPart1); + showEntitiesWithTag(this.tagPart2); + print("HIT, wiating for unequip..."); + Messages.subscribe('Hifi-Object-Manipulation'); + Messages.messageReceived.connect(this.onMessage.bind(this)); } // When block collides with basket start step 2 - var checkCollidesTimer = null; function checkCollides() { - print("CHECKING..."); + print("CHECKING FOR PING PONG..."); var ammoIDs = findEntities({ name: GUN_AMMO_NAME }, 15); for (var i = 0; i < ammoIDs.length; ++i) { if (Vec3.distance(basketPosition, Entities.getEntityProperties(ammoIDs[i], 'position').position) < 0.2) { - Script.clearInterval(checkCollidesTimer); + Script.clearInterval(this.checkCollidesTimer); + this.checkCollidesTimer = null; this.soundInjector = Audio.playSound(successSound, { position: basketPosition, volume: 0.7, @@ -513,18 +526,63 @@ stepEquip.prototype = { } } } - checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); + this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, + onMessage: function(channel, message, sender) { + print("Got message", channel, message, sender, MyAvatar.sessionUUID); + //if (sender === MyAvatar.sessionUUID) { + var data = parseJSON(message); + print("Here", data.action, data.grabbedEntity, this.gunID); + if (data.action == 'release' && data.grabbedEntity == this.gunID) { + print("FINISHED"); + this.onFinish(); + } + //} + }, cleanup: function() { - editEntitiesWithTag(this.tag, { visible: false}); + try { + Messages.messageReceived.disconnect(this.onMessage); + } catch(e) { + print("error disconnecting"); + } + if (this.checkCollidesTimer) { + Script.clearInterval(this.checkCollidesTimer); + } + hideEntitiesWithTag(this.tagPart1); + hideEntitiesWithTag(this.tagPart2); + hideEntitiesWithTag(this.tag); deleteEntitiesWithTag(this.tempTag); } }; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: Turn Around // +// // +/////////////////////////////////////////////////////////////////////////////// +var stepTurnAround = function(name) { + this.tag = name; + this.tempTag = name + "-temporary"; +} +stepTurnAround.prototype = { + start: function(onFinish) { + showEntitiesWithTag(this.tag); + }, + cleanup: function() { + hideEntitiesWithTag(this.tag); + deleteEntitiesWithTag(this.tempTag); + } +}; + + + + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // @@ -538,23 +596,79 @@ var stepTeleport = function(name) { stepTeleport.prototype = { start: function(onFinish) { Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); - Menu.setIsOptionChecked("Overlays", false); + Menu.setIsOptionChecked("Overlays", true); - editEntitiesWithTag(this.tag, { visible: true }); + // Wait until touching teleport pad... + var padID = findEntity({ name: TELEPORT_PAD_NAME }, 100); + print(padID); + var padProps = Entities.getEntityProperties(padID, ["position", "dimensions"]); + print(Object.keys(padProps)); + var xMin = padProps.position.x - padProps.dimensions.x / 2; + var xMax = padProps.position.x + padProps.dimensions.x / 2; + var zMin = padProps.position.z - padProps.dimensions.z / 2; + var zMax = padProps.position.z + padProps.dimensions.z / 2; + function checkCollides() { + print("Checking if on pad..."); + var pos = MyAvatar.position; + print('x', pos.x, xMin, xMax); + print('z', pos.z, zMin, zMax); + if (pos.x > xMin && pos.x < xMax && pos.z > zMin && pos.z < zMax) { + print("On pad!!"); + Script.clearInterval(this.checkCollidesTimer); + this.checkCollidesTimer = null; + onFinish(); + } + } + this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); + + showEntitiesWithTag(this.tag); }, cleanup: function() { - editEntitiesWithTag(this.tag, { visible: false }); + if (this.checkCollidesTimer) { + Script.clearInterval(this.checkCollidesTimer); + } + hideEntitiesWithTag(this.tag); deleteEntitiesWithTag(this.tempTag); } }; + + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: Finish // +// // +/////////////////////////////////////////////////////////////////////////////// +var stepFinish = function(name) { + this.tag = name; + this.tempTag = name + "-temporary"; +} +stepFinish.prototype = { + start: function(onFinish) { + showEntitiesWithTag(this.tag); + }, + cleanup: function() { + hideEntitiesWithTag(this.tag); + deleteEntitiesWithTag(this.tempTag); + } +}; + + + + + + function showEntitiesWithTag(tag) { editEntitiesWithTag(tag, function(entityID) { var userData = Entities.getEntityProperties(entityID, "userData").userData; var data = parseJSON(userData); var newProperties = { visible: data.visible == false ? false : true, - collisionless: data.collisionless == false ? false : true, + collisionless: data.visible == false ? true : false , + //collisionless: data.collisionless == true ? true : false, }; Entities.editEntity(entityID, newProperties); }); @@ -584,12 +698,14 @@ function startTutorial() { currentStepNum = -1; currentStep = null; STEPS = [ - //new stepDisableControllers("step0"), + new stepDisableControllers("step0"), new stepRaiseAboveHead("raiseHands"), new stepNearGrab("nearGrab"), new stepFarGrab("farGrab"), new stepEquip("equip"), + new stepTurnAround("turnAround"), new stepTeleport("teleport"), + new stepFinish("finish"), ] startNextStep(); } @@ -604,10 +720,14 @@ function startNextStep() { if (currentStepNum >= STEPS.length) { // Done print("DONE WITH TUTORIAL"); + currentStepNum = -1; + currentStep = null; + return false; } else { print("Starting step", currentStepNum); currentStep = STEPS[currentStepNum]; currentStep.start(startNextStep); + return true; } } @@ -631,7 +751,9 @@ Script.scriptEnding.connect(stopTutorial); Controller.keyReleaseEvent.connect(function (event) { if (event.text == ",") { - startNextStep(); + if (!startNextStep()) { + startTutorial(); + } } else if (event.text == ".") { stopTutorial(); } else if (event.text == "r") { diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index a483af7c26..986a47e398 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -1,4 +1,4 @@ -var PARENT_ID = MyAvatar.sessionUUID; +var PARENT_ID = "{00000000-0000-0000-0000-000000000001}"; var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); //var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("LeftHand"); From de757698eaab4c7433b40fccdc71fa6cf8dc9aa9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 1 Sep 2016 15:33:12 -0700 Subject: [PATCH 006/109] Disable ray picking against invisible objects in far grab and teleport --- tutorial/viveHandsv2.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 986a47e398..a483af7c26 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -1,4 +1,4 @@ -var PARENT_ID = "{00000000-0000-0000-0000-000000000001}"; +var PARENT_ID = MyAvatar.sessionUUID; var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); //var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("LeftHand"); From 5d9cc4782dd61897bc5ee8ec19135a8c6bb26791 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 1 Sep 2016 16:47:01 -0700 Subject: [PATCH 007/109] Fix annotations --- tutorial/tutorial.js | 20 ++++++ tutorial/viveHandsv2.js | 142 ++++++++++++++++------------------------ 2 files changed, 75 insertions(+), 87 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index b6b5fdd79d..211725a591 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -290,6 +290,12 @@ stepRaiseAboveHead.prototype = { } }; +function setControllerVisible(name, visible) { + Messages.sendLocalMessage('Controller-Display', JSON.stringify({ + name: name, + visible: visible, + })); +} /////////////////////////////////////////////////////////////////////////////// @@ -304,6 +310,7 @@ var stepNearGrab = function(name) { } stepNearGrab.prototype = { start: function(onFinish) { + setControllerVisible("trigger", true); var tag = this.tag; // Spawn content set @@ -353,6 +360,7 @@ stepNearGrab.prototype = { // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, cleanup: function() { + setControllerVisible("trigger", false); if (this.checkCollidesTimer) { Script.clearInterval(this.checkCollidesTimer); } @@ -375,6 +383,7 @@ var stepFarGrab = function(name) { } stepFarGrab.prototype = { start: function(onFinish) { + setControllerVisible("trigger", true); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ farGrabEnabled: true, })); @@ -431,6 +440,7 @@ stepFarGrab.prototype = { // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, cleanup: function() { + setControllerVisible("trigger", false); hideEntitiesWithTag(this.tag, { visible: false}); deleteEntitiesWithTag(this.tempTag); } @@ -452,6 +462,7 @@ var stepEquip = function(name) { } stepEquip.prototype = { start: function(onFinish) { + setControllerVisible("trigger", true); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ holdEnabled: true, })); @@ -523,6 +534,7 @@ stepEquip.prototype = { loop: false }); Script.setTimeout(onHit.bind(this), 1000); + return; } } } @@ -542,6 +554,7 @@ stepEquip.prototype = { //} }, cleanup: function() { + setControllerVisible("trigger", false); try { Messages.messageReceived.disconnect(this.onMessage); } catch(e) { @@ -595,6 +608,7 @@ var stepTeleport = function(name) { } stepTeleport.prototype = { start: function(onFinish) { + setControllerVisible("teleport", true); Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); Menu.setIsOptionChecked("Overlays", true); @@ -624,6 +638,7 @@ stepTeleport.prototype = { showEntitiesWithTag(this.tag); }, cleanup: function() { + setControllerVisible("teleport", false); if (this.checkCollidesTimer) { Script.clearInterval(this.checkCollidesTimer); } @@ -761,3 +776,8 @@ Controller.keyReleaseEvent.connect(function (event) { startTutorial(); } }); + +// Messages.sendLocalMessage('Controller-Display', JSON.stringify({ +// name: "menu", +// visible: false, +// })); diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index a483af7c26..df3392f831 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -234,7 +234,7 @@ var VIVE_CONTROLLER_CONFIGURATION = { dimensions: viveNaturalDimensions, - annotationTextRotation: Quat.fromPitchYawRollDegrees(20, -90, 0), + annotationTextRotation: Quat.fromPitchYawRollDegrees(45, -90, 0), annotations: { // red: { // debug: true, @@ -277,11 +277,12 @@ var VIVE_CONTROLLER_CONFIGURATION = { // color: { red: 255, green: 255, blue: 255 }, // }, - center: { - position: zeroPosition, - direction: "center", - color: { red: 100, green: 255, blue: 255 }, - }, + // center: { + // position: zeroPosition, + // direction: "center", + // color: { red: 100, green: 255, blue: 255 }, + // }, + trigger: { position: { x: 0, @@ -309,7 +310,8 @@ var VIVE_CONTROLLER_CONFIGURATION = { direction: "left", color: { red: 255, green: 100, blue: 100 }, }, - pad: { + teleport: { + textOffset: { x: -0.015, y: 0.004, z: -0.005 }, position: { x: 0, y: 0.00378, @@ -344,9 +346,8 @@ var VIVE_CONTROLLER_CONFIGURATION = { z: 0.06380049744620919 }, - annotationTextRotation: Quat.fromPitchYawRollDegrees(20, -90, 0), + annotationTextRotation: Quat.fromPitchYawRollDegrees(180 + 45, 90, 180), annotations: { - trigger: { position: { x: 0, @@ -374,7 +375,8 @@ var VIVE_CONTROLLER_CONFIGURATION = { direction: "left", color: { red: 255, green: 100, blue: 100 }, }, - pad: { + teleport: { + textOffset: { x: -0.015, y: 0.004, z: -0.005 }, position: { x: 0, y: 0.00378, @@ -397,7 +399,8 @@ var VIVE_CONTROLLER_CONFIGURATION = { ] } -var DEBUG = true; +var DEBUG = false; +var VISIBLE_BY_DEFAULT = false; function setupController(config) { var controllerDisplay = { @@ -445,16 +448,20 @@ function setupController(config) { }); controllerDisplay.overlays.push(overlayID); - controllerDisplay.annotations[key] = { - overlay: overlayID, - }; } + var ANNOTATION_TEXT_OFFSET = 0.1; var sign = annotation.direction == "right" ? 1 : -1; - var textOffset = annotation.direction == "right" ? 0.04 : -0.01; + var textOffset = annotation.direction == "right" ? 0.08 : 0.02; + if (annotation.textOffset) { + var pos = Vec3.sum(annotationPosition, Vec3.multiplyQbyV(controller.rotation, annotation.textOffset)); + } else { + var pos = Vec3.sum(annotationPosition, Vec3.multiplyQbyV(controller.rotation, { x: textOffset, y: 0, z: -0.005 })); + } var textOverlayID = Overlays.addOverlay("text3d", { + visible: VISIBLE_BY_DEFAULT, text: key, - localPosition: Vec3.sum(annotationPosition, Vec3.multiplyQbyV(controller.rotation, { x: textOffset, y: 0, z: 0.0 })), + localPosition: pos, localRotation: controller.annotationTextRotation, lineHeight: 0.01, leftMargin: 0, @@ -469,9 +476,16 @@ function setupController(config) { parentID: PARENT_ID, parentJointIndex: controller.jointIndex, }); - controllerDisplay.overlays.push(textOverlayID); - var offset = { x: 0, y: 0, z: annotation.direction == "right" ? -0.1 : 0.1 }; + controllerDisplay.overlays.push(textOverlayID); + if (key in controllerDisplay.annotations) { + controllerDisplay.annotations[key].push(textOverlayID); + } else { + controllerDisplay.annotations[key] = [textOverlayID]; + } + + var ANNOTATION_OFFSET = 0.5; + var offset = { x: 0, y: 0, z: annotation.direction == "right" ? -1 * ANNOTATION_OFFSET : ANNOTATION_OFFSET }; var lineOverlayID = Overlays.addOverlay("line3d", { visible: false, localPosition: annotationPosition, @@ -500,79 +514,33 @@ function deleteControllerDisplay(controllerDisplay) { Controller.disableMapping(controllerDisplay.mappingName); } -// var triggerAnnotationOverlayID = Overlays.addOverlay("text3d", { -// text: "Trigger", -// lineHeight: 0.025, -// backgroundAlpha: 0.0, -// dimensions: { -// x: 0.2, -// y: 0.2, -// }, -// localPosition: Vec3.sum(leftBasePosition, { x: -0.09, y: -0.025, z: 0.03 }), -// localRotation: Quat.multiply(Quat.fromPitchYawRollDegrees(180, 0, 90), leftBaseRotation), -// parentID: MyAvatar.sessionUUID, -// parentJointIndex: MyAvatar.getJointIndex("LeftHand") -// }); - -// var leftOverlayID = Overlays.addOverlay("model", { -// url: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vr_controller_vive_1_5.obj", -// dimensions: naturalDimensions, -// localRotation: leftBaseRotation, -// localPosition: leftBasePosition, -// parentID: PARENT_ID, -// parentJointIndex: LEFT_JOINT_INDEX -// }); -// -// var leftTriggerOverlayID = Overlays.addOverlay("model", { -// url: "C:/Users/Ryan/Assets/controller/touch_l_trigger.fbx", -// visible: false, -// localRotation: leftBaseRotation, -// localPosition: Vec3.sum(leftBasePosition, { x: -0.05, y: -0.025, z: 0.02 }), -// parentID: PARENT_ID, -// parentJointIndex: LEFT_JOINT_INDEX -// }); - -// var rightOverlayID = Overlays.addOverlay("model", { -// url: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vr_controller_vive_1_5.obj", -// dimensions: naturalDimensions, -// localRotation: rightBaseRotation, -// localPosition: rightBasePosition, -// parentID: PARENT_ID, -// parentJointIndex: RIGHT_JOINT_INDEX -// }); -// -// var rightTriggerOverlayID = Overlays.addOverlay("model", { -// url: "C:/Users/Ryan/Assets/controller/touch_r_trigger.fbx", -// visible: false, -// localRotation: rightBaseRotation, -// localPosition: Vec3.sum(rightBasePosition, { x: 0.05, y: -0.025, z: 0.02 }), -// parentID: PARENT_ID, -// parentJointIndex: RIGHT_JOINT_INDEX -// }); -// var overlays = [ - // leftOverlayID, - // leftTriggerOverlayID, - // triggerAnnotationOverlayID, - - // rightOverlayID, - // rightTriggerOverlayID, ]; -// -// Script.setInterval(function() { -// // var pose = MyAvatar.getLeftHandControllerPoseInWorldFrame(); -// // Overlays.editOverlay(leftOverlayID, { -// // position: pose.translation, -// // rotation: pose.rotation -// // }); -// // pose = MyAvatar.getRightHandControllerPoseInWorldFrame(); -// // Overlays.editOverlay(rightOverlayID, { -// // position: pose.translation, -// // rotation: pose.rotation -// // }); -// }, 10); +Messages.subscribe('Controller-Display'); +var handleMessages = function(channel, message, sender) { + print("MESSASGE>>>>", channel, message, sender); + if (sender === MyAvatar.sessionUUID) { + if (channel === 'Controller-Display') { + print('here'); + var data = JSON.parse(message); + var name = data.name; + var visible = data.visible; + //c.setDisplayAnnotation(name, visible); + if (name in c.annotations) { + print("hiding"); + for (var i = 0; i < c.annotations[name].length; ++i) { + print("hiding", i); + Overlays.editOverlay(c.annotations[name][i], { visible: visible }); + } + } + } + } +} + +Messages.messageReceived.connect(handleMessages); + var MAPPING_NAME = "com.highfidelity.handControllerGrab.disable"; var mapping = Controller.newMapping(MAPPING_NAME); mapping.from([Controller.Standard.LT]).to(function(value) { From e8a1f50aeedbf655cccd75292fd8cfb54de037ff Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Sep 2016 08:47:09 -0700 Subject: [PATCH 008/109] Ready for user testing v1 --- tutorial/tutorial.js | 60 +++++++++++++++++++++++++++++++++++++++-- tutorial/viveHandsv2.js | 6 ++--- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 211725a591..bdad34e9bc 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -207,6 +207,27 @@ function isFunction(functionToCheck) { return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; } + var defaultTransform = { + position: { + x: 0.2459, + y: 0.9011, + z: 0.7266 + }, + rotation: { + x: 0, + y: 0, + z: 0, + w: 1 + } + }; +function playSuccessSound() { + this.soundInjector = Audio.playSound(successSound, { + position: defaultTransform.position, + volume: 0.7, + loop: false + }); +} + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // @@ -488,6 +509,8 @@ stepEquip.prototype = { showEntitiesWithTag(this.tag); showEntitiesWithTag(this.tagPart1); + this.hasFinished = false; + var basketColliderID = findEntity({ name: GUN_BASKET_COLLIDER_NAME }, 10000); var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; @@ -543,13 +566,23 @@ stepEquip.prototype = { // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, onMessage: function(channel, message, sender) { + if (this.hasFinished) { + return; + } print("Got message", channel, message, sender, MyAvatar.sessionUUID); //if (sender === MyAvatar.sessionUUID) { var data = parseJSON(message); print("Here", data.action, data.grabbedEntity, this.gunID); if (data.action == 'release' && data.grabbedEntity == this.gunID) { + try { + Messages.messageReceived.disconnect(this.onMessage); + } catch(e) { + } + playSuccessSound(); print("FINISHED"); - this.onFinish(); + Script.setTimeout(this.onFinish.bind(this), 1500); + this.hasFinished = true; + //this.onFinish(); } //} }, @@ -586,8 +619,31 @@ var stepTurnAround = function(name) { stepTurnAround.prototype = { start: function(onFinish) { showEntitiesWithTag(this.tag); + var hasTurnedAround = false; + this.interval = Script.setInterval(function() { + var dir = Quat.getFront(MyAvatar.orientation); + var angle = Math.atan2(dir.z, dir.x); + var angleDegrees = ((angle / Math.PI) * 180); + print("CHECK"); + if (!hasTurnedAround) { + if (Math.abs(angleDegrees) > 100) { + hasTurnedAround = true; + print("half way there..."); + } + } else { + if (Math.abs(angleDegrees) < 30) { + Script.clearInterval(this.interval); + this.interval = null; + print("DONE"); + onFinish(); + } + } + }.bind(this), 100); }, cleanup: function() { + if (this.interval) { + Script.clearInterval(this.interval); + } hideEntitiesWithTag(this.tag); deleteEntitiesWithTag(this.tempTag); } @@ -610,7 +666,6 @@ stepTeleport.prototype = { start: function(onFinish) { setControllerVisible("teleport", true); Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); - Menu.setIsOptionChecked("Overlays", true); // Wait until touching teleport pad... var padID = findEntity({ name: TELEPORT_PAD_NAME }, 100); @@ -663,6 +718,7 @@ var stepFinish = function(name) { } stepFinish.prototype = { start: function(onFinish) { + Menu.setIsOptionChecked("Overlays", true); showEntitiesWithTag(this.tag); }, cleanup: function() { diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index df3392f831..35fb282175 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -285,7 +285,7 @@ var VIVE_CONTROLLER_CONFIGURATION = { trigger: { position: { - x: 0, + x: 0.0055, y: -0.023978, z: 0.04546 }, @@ -350,7 +350,7 @@ var VIVE_CONTROLLER_CONFIGURATION = { annotations: { trigger: { position: { - x: 0, + x: -0.075, y: -0.023978, z: 0.04546 }, @@ -400,7 +400,7 @@ var VIVE_CONTROLLER_CONFIGURATION = { } var DEBUG = false; -var VISIBLE_BY_DEFAULT = false; +var VISIBLE_BY_DEFAULT = true; function setupController(config) { var controllerDisplay = { From f4f2ec5da841bc73ef94f6c0430e0caf321fa35f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Sep 2016 09:01:50 -0700 Subject: [PATCH 009/109] Make adjustments to tutorial --- tutorial/tutorial.js | 36 +++++++++++++++++++++--------------- tutorial/viveHandsv2.js | 2 +- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index bdad34e9bc..ea92aa59fc 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -239,6 +239,7 @@ var stepDisableControllers = function(name) { } stepDisableControllers.prototype = { start: function(onFinish) { + editEntitiesWithTag('door', { visible: true }); Menu.setIsOptionChecked("Overlays", false); Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'both'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ @@ -341,22 +342,26 @@ stepNearGrab.prototype = { var basketColliderID = findEntity({ name: NEAR_BASKET_COLLIDER_NAME }, 10000); var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; + var boxSpawnID = findEntity({ name: NEAR_BOX_SPAWN_NAME }, 10000); + if (!boxSpawnID) { + print("Error creating block, cannot find spawn"); + return null; + } + var boxSpawnPosition = Entities.getEntityProperties(boxSpawnID, 'position').position; function createBlock() { - var boxSpawnID = findEntity({ name: NEAR_BOX_SPAWN_NAME }, 10000); - if (!boxSpawnID) { - print("Error creating block, cannot find spawn"); - return null; - } - - Step1BlockData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; + Step1BlockData.position = boxSpawnPosition; return spawnWithTag([Step1BlockData], null, this.tempTag)[0]; } // Enabled grab // Create table ? // Create blocks and basket - var boxID = createBlock.bind(this)(); - print("Created", boxID); + this.boxID = createBlock.bind(this)(); + print("Created", this.boxID); + + //function posChecker() { + //Vec3.distance( + //} function onHit() { onFinish(); @@ -365,7 +370,7 @@ stepNearGrab.prototype = { // When block collides with basket start step 2 function checkCollides() { print(this.tag, "CHECKING..."); - if (Vec3.distance(basketPosition, Entities.getEntityProperties(boxID, 'position').position) < 0.1) { + if (Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position) < 0.1) { Script.clearInterval(this.checkCollidesTimer); this.checkCollidesTimer = null; this.soundInjector = Audio.playSound(successSound, { @@ -435,8 +440,8 @@ stepFarGrab.prototype = { // Enabled grab // Create table ? // Create blocks and basket - var boxID = createBlock.bind(this)(); - print("Created", boxID); + this.boxID = createBlock.bind(this)(); + print("Created", this.boxID); function onHit() { onFinish(); @@ -446,7 +451,7 @@ stepFarGrab.prototype = { var checkCollidesTimer = null; function checkCollides() { print("CHECKING..."); - if (Vec3.distance(basketPosition, Entities.getEntityProperties(boxID, 'position').position) < 0.1) { + if (Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position) < 0.1) { Script.clearInterval(checkCollidesTimer); this.soundInjector = Audio.playSound(successSound, { position: basketPosition, @@ -548,7 +553,7 @@ stepEquip.prototype = { print("CHECKING FOR PING PONG..."); var ammoIDs = findEntities({ name: GUN_AMMO_NAME }, 15); for (var i = 0; i < ammoIDs.length; ++i) { - if (Vec3.distance(basketPosition, Entities.getEntityProperties(ammoIDs[i], 'position').position) < 0.2) { + if (Vec3.distance(basketPosition, Entities.getEntityProperties(ammoIDs[i], 'position').position) < 0.25) { Script.clearInterval(this.checkCollidesTimer); this.checkCollidesTimer = null; this.soundInjector = Audio.playSound(successSound, { @@ -561,7 +566,7 @@ stepEquip.prototype = { } } } - this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); + this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 100); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, @@ -718,6 +723,7 @@ var stepFinish = function(name) { } stepFinish.prototype = { start: function(onFinish) { + editEntitiesWithTag('door', { visible: false }); Menu.setIsOptionChecked("Overlays", true); showEntitiesWithTag(this.tag); }, diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 35fb282175..59725c8178 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -400,7 +400,7 @@ var VIVE_CONTROLLER_CONFIGURATION = { } var DEBUG = false; -var VISIBLE_BY_DEFAULT = true; +var VISIBLE_BY_DEFAULT = false; function setupController(config) { var controllerDisplay = { From f937b57070a0b180d31f708c668b22546a59f5ae Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Sep 2016 09:08:04 -0700 Subject: [PATCH 010/109] Add left/right annotations --- tutorial/tutorial.js | 6 ++++++ tutorial/viveHandsv2.js | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index ea92aa59fc..ca5d365602 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -623,6 +623,9 @@ var stepTurnAround = function(name) { } stepTurnAround.prototype = { start: function(onFinish) { + setControllerVisible("left", true); + setControllerVisible("right", true); + showEntitiesWithTag(this.tag); var hasTurnedAround = false; this.interval = Script.setInterval(function() { @@ -646,6 +649,9 @@ stepTurnAround.prototype = { }.bind(this), 100); }, cleanup: function() { + setControllerVisible("left", false); + setControllerVisible("right", false); + if (this.interval) { Script.clearInterval(this.interval); } diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 59725c8178..50475e563e 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -283,6 +283,28 @@ var VIVE_CONTROLLER_CONFIGURATION = { // color: { red: 100, green: 255, blue: 255 }, // }, + left: { + textOffset: { x: -0.035, y: 0.004, z: -0.005 }, + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + right: { + textOffset: { x: 0.023, y: 0.004, z: -0.005 }, + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + + trigger: { position: { x: 0.0055, @@ -348,6 +370,28 @@ var VIVE_CONTROLLER_CONFIGURATION = { annotationTextRotation: Quat.fromPitchYawRollDegrees(180 + 45, 90, 180), annotations: { + + left: { + textOffset: { x: -0.035, y: 0.004, z: -0.005 }, + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + right: { + textOffset: { x: 0.023, y: 0.004, z: -0.005 }, + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + trigger: { position: { x: -0.075, From 0d5017b5ddb611c1c19a3c0780b099c590421a5d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Sep 2016 09:20:54 -0700 Subject: [PATCH 011/109] Add restartStep to tutorial --- tutorial/tutorial.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index ca5d365602..4d1e6164cc 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -73,6 +73,7 @@ Script.include("entityData.js"); // }); // // Controller.enableMapping(MAPPING_NAME); +//{ "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"}, var BASKET_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx"; var BASKET_COLLIDER_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj"; @@ -813,6 +814,12 @@ function startNextStep() { return true; } } +function restartStep() { + if (currentStep) { + currentStep.cleanup(); + currentStep.start(startNextStep); + } +} function skipTutorial() { } @@ -828,17 +835,31 @@ function stopTutorial() { location = "/tutorial"; startTutorial(); +var DISABLE_SPIN_MAPPING = "com.highfidelity.spin.disable"; +var mapping = Controller.newMapping(DISABLE_SPIN_MAPPING); +function noop(value) { } +mapping.from([Controller.Standard.RY]).to(noop); +//mapping.from([Controller.Standard.RY]).when("Controller.Application.Grounded").to(noop); +//mapping.from([Controller.Standard.RY]).when(Controller.Application.Grounded).to(noop); + +Controller.enableMapping(DISABLE_SPIN_MAPPING); + +Script.scriptEnding.connect(function() { + Controller.disableMapping(DISABLE_SPIN_MAPPING); +}); + Script.scriptEnding.connect(stopTutorial); Controller.keyReleaseEvent.connect(function (event) { + print(event.text); if (event.text == ",") { if (!startNextStep()) { startTutorial(); } - } else if (event.text == ".") { - stopTutorial(); + } else if (event.text == "F12") { + restartStep(); } else if (event.text == "r") { stopTutorial(); startTutorial(); From a89064f96f0df2de15de7e129bc513016702e583 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Sep 2016 09:34:38 -0700 Subject: [PATCH 012/109] Update startTutorial to move avatar to start location --- tutorial/tutorial.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 4d1e6164cc..2ffa47523b 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -791,6 +791,7 @@ function startTutorial() { new stepTeleport("teleport"), new stepFinish("finish"), ] + location = "/tutorial"; startNextStep(); } @@ -832,7 +833,6 @@ function stopTutorial() { currentStep = null; } -location = "/tutorial"; startTutorial(); var DISABLE_SPIN_MAPPING = "com.highfidelity.spin.disable"; From eb195e2fb81bdce25362660ef68160f0f27ddade Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Sep 2016 09:43:24 -0700 Subject: [PATCH 013/109] Add success sound --- tutorial/tutorial.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 2ffa47523b..53b9b7c3f7 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -644,6 +644,7 @@ stepTurnAround.prototype = { Script.clearInterval(this.interval); this.interval = null; print("DONE"); + playSuccessSound(); onFinish(); } } @@ -697,6 +698,7 @@ stepTeleport.prototype = { print("On pad!!"); Script.clearInterval(this.checkCollidesTimer); this.checkCollidesTimer = null; + playSuccessSound(); onFinish(); } } From a4ffa7b23f710703425b5ab480e02b76455180b0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Sep 2016 12:43:56 -0700 Subject: [PATCH 014/109] Add disabling of right click menu with vive --- .../controllers/handControllerPointer.js | 2 +- tutorial/tutorial.js | 34 ++++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index ce98ed6d8e..f96e117d26 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -363,7 +363,7 @@ function makeToggleAction(hand) { // return a function(0|1) that makes the speci }; } -var clickMapping = Controller.newMapping(Script.resolvePath('') + '-click'); +var clickMapping = Controller.newMapping('handControllerPointer-click'); Script.scriptEnding.connect(clickMapping.disable); // Gather the trigger data for smoothing. diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 53b9b7c3f7..52f57a83f6 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -837,18 +837,34 @@ function stopTutorial() { startTutorial(); -var DISABLE_SPIN_MAPPING = "com.highfidelity.spin.disable"; -var mapping = Controller.newMapping(DISABLE_SPIN_MAPPING); -function noop(value) { } -mapping.from([Controller.Standard.RY]).to(noop); +var TUTORIAL_DISABLE_MAPPING = "com.highfidelity.tutorial.disable"; +var mapping = Controller.newMapping(TUTORIAL_DISABLE_MAPPING); + +function noop(value) { + print("NOOP"); +} + +mapping.from([ + Controller.Vive.LSCenter, + Controller.Vive.LeftApplicationMenu, + Controller.Standard.LeftSecondaryThumb, + Controller.Standard.LeftPrimraryThumb + ]).to(noop); +mapping.from([]).to(noop); +mapping.from([]).to(noop); + +Controller.enableMapping(TUTORIAL_DISABLE_MAPPING); + +Script.scriptEnding.connect(function() { + Controller.disableMapping(TUTORIAL_DISABLE_MAPPING); +}); +Controller.disableMapping('handControllerPointer-click'); + +//mapping.from([Controller.Standard.RY]).to(noop); + //{ "from": "Vive.LeftApplicationMenu", "to": "Standard.LeftSecondaryThumb" }, //mapping.from([Controller.Standard.RY]).when("Controller.Application.Grounded").to(noop); //mapping.from([Controller.Standard.RY]).when(Controller.Application.Grounded).to(noop); -Controller.enableMapping(DISABLE_SPIN_MAPPING); - -Script.scriptEnding.connect(function() { - Controller.disableMapping(DISABLE_SPIN_MAPPING); -}); Script.scriptEnding.connect(stopTutorial); From d3885b607d0f12a2613a35bad9681848d4855984 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Sep 2016 13:50:09 -0700 Subject: [PATCH 015/109] Add disabling of right-click menu in tutorial --- tutorial/tutorial.js | 68 +------------------------------------------- 1 file changed, 1 insertion(+), 67 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 52f57a83f6..15227745c1 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -1,4 +1,3 @@ - if (!Function.prototype.bind) { Function.prototype.bind = function(oThis) { if (typeof this !== 'function') { @@ -28,53 +27,6 @@ if (!Function.prototype.bind) { } Script.include("entityData.js"); -// -// var FAR_GRAB_INPUTS = [ -// Controller.Standard.RT -// Controller.Standard.RTClick -// Controller.Standard.LT -// Controller.Standard.LTClick -// ]; -// -// var TELEPORT_INPUTS = [ -// Controller.Standard.LeftPrimaryThumb -// Controller.Standard.RightPrimaryThumb -// ]; -// -// function noop(value) { } -// var FAR_GRAB_MAPPING_NAME = "com.highfidelity.farGrab.disable"; -// var farGrabMapping = Controller.newMapping(FAR_GRAB_MAPPING_NAME); -// for (var i = 0; i < FAR_GRAB_INPUTS.length; ++i) { -// mapping.from([FAR_GRAB_INPUTS[i]]).to(noop); -// } -// -// var TELEPORT_MAPPING_NAME = "com.highfidelity.teleport.disable"; -// var teleportMapping = Controller.newMapping(TELEPORT_MAPPING_NAME); -// for (var i = 0; i < FAR_GRAB_INPUTS.length; ++i) { -// mapping.from([TELEPORT_INPUTS[i]]).to(noop); -// } -// -// mapping.from([Controller.Standard.RT]).to(noop); -// mapping.from([Controller.Standard.RTClick]).to(noop); -// -// mapping.from([Controller.Standard.LT]).to(noop); -// mapping.from([Controller.Standard.LTClick]).to(noop); -// -// mapping.from([Controller.Standard.RB]).to(noop); -// mapping.from([Controller.Standard.LB]).to(noop); -// mapping.from([Controller.Standard.LeftGrip]).to(noop); -// mapping.from([Controller.Standard.RightGrip]).to(noop); -// -// mapping.from([Controller.Standard.LeftPrimaryThumb]).to(noop); -// mapping.from([Controller.Standard.RightPrimaryThumb]).to(noop); -// -// Script.scriptEnding.connect(function() { -// Controller.disableMapping(MAPPING_NAME); -// }); -// -// Controller.enableMapping(MAPPING_NAME); -//{ "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"}, - var BASKET_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx"; var BASKET_COLLIDER_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj"; var successSound = SoundCache.getSound(Script.resolvePath("success48.wav")); @@ -837,26 +789,8 @@ function stopTutorial() { startTutorial(); -var TUTORIAL_DISABLE_MAPPING = "com.highfidelity.tutorial.disable"; -var mapping = Controller.newMapping(TUTORIAL_DISABLE_MAPPING); - -function noop(value) { - print("NOOP"); -} - -mapping.from([ - Controller.Vive.LSCenter, - Controller.Vive.LeftApplicationMenu, - Controller.Standard.LeftSecondaryThumb, - Controller.Standard.LeftPrimraryThumb - ]).to(noop); -mapping.from([]).to(noop); -mapping.from([]).to(noop); - -Controller.enableMapping(TUTORIAL_DISABLE_MAPPING); - Script.scriptEnding.connect(function() { - Controller.disableMapping(TUTORIAL_DISABLE_MAPPING); + Controller.enableMapping('handControllerPointer-click'); }); Controller.disableMapping('handControllerPointer-click'); From 827506e929161942e0a9e1b6d7e3a0525e974881 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Sep 2016 13:50:41 -0700 Subject: [PATCH 016/109] Disable 180 spin in tutorial --- .../controllers/toggleAdvancedMovementForHandControllers.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index f5ab42cf53..21eeb68b96 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -54,6 +54,7 @@ function registerBasicMapping() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); basicMapping = Controller.newMapping(mappingName); basicMapping.from(Controller.Standard.LY).to(function(value) { + return; var stick = Controller.getValue(Controller.Standard.LS); if (value === 1 && Controller.Hardware.OculusTouch !== undefined) { rotate180(); @@ -70,6 +71,7 @@ function registerBasicMapping() { }); basicMapping.from(Controller.Standard.LX).to(Controller.Standard.RX); basicMapping.from(Controller.Standard.RY).to(function(value) { + return; var stick = Controller.getValue(Controller.Standard.RS); if (value === 1 && Controller.Hardware.OculusTouch !== undefined) { rotate180(); From 52120de44aec3061d12f74820ae5fad9aa6220d3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Sep 2016 14:25:03 -0700 Subject: [PATCH 017/109] Disable displaying of overlays at end of tutorial --- tutorial/tutorial.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 15227745c1..4b30fa645d 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -685,10 +685,10 @@ var stepFinish = function(name) { stepFinish.prototype = { start: function(onFinish) { editEntitiesWithTag('door', { visible: false }); - Menu.setIsOptionChecked("Overlays", true); showEntitiesWithTag(this.tag); }, cleanup: function() { + //Menu.setIsOptionChecked("Overlays", true); hideEntitiesWithTag(this.tag); deleteEntitiesWithTag(this.tempTag); } From 6f457e10eb19a1985b8273a5c2b4c44b6b320d2a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 Sep 2016 11:21:10 -0700 Subject: [PATCH 018/109] Add trigger part to vive controller display --- tutorial/tutorial.js | 4 ---- tutorial/viveHandsv2.js | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 4b30fa645d..ed091a6526 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -724,10 +724,6 @@ function hideEntitiesWithTag(tag) { }); } - - - - var STEPS; var currentStepNum = -1; diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 50475e563e..e53de1bfc9 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -368,6 +368,20 @@ var VIVE_CONTROLLER_CONFIGURATION = { z: 0.06380049744620919 }, + parts: { + { + type: "linear", + modelURL: "", + input: Controller.Hardware.Vive.RT, + minValue: 0.0, + maxValue: 1.0, + textOffset: { x: -0.035, y: 0.004, z: -0.005 }, + minPosition: { x: -0.035, y: 0.004, z: -0.005 }, + maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, + } + + }, + annotationTextRotation: Quat.fromPitchYawRollDegrees(180 + 45, 90, 180), annotations: { From 2bbc4204b9bfda7d413f801e8be49dc94a9e6a72 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 Sep 2016 12:06:55 -0700 Subject: [PATCH 019/109] Add support for parts to controller display" --- tutorial/viveHandsv2.js | 175 +++++++++++++++++++++++++++++++++------- 1 file changed, 145 insertions(+), 30 deletions(-) diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index e53de1bfc9..24f17dc138 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -78,28 +78,6 @@ var touchRightBaseRotation = Quat.multiply( ); var TOUCH_CONTROLLER_CONFIGURATION = { - name: "Touch", - controllers: [ - { - modelURL: "C:/Users/Ryan/Assets/controller/touch_l_full.fbx", - jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"), - rotation: touchLeftBaseRotation, - //position: Vec3.sum(leftBasePosition, { x: 0.032, y: 0.0, z: -0.02 }), - position: Vec3.sum(leftBasePosition, { x: 0.0, y: -0.016, z: -0.02 }), - //dimensions: naturalDimensions, - }, - { - modelURL: "C:/Users/Ryan/Assets/controller/touch_r_full.fbx", - jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), - rotation: touchRightBaseRotation, - //position: rightBasePosition, - position: Vec3.sum(rightBasePosition, { x: 0.0, y: -0.016, z: -0.02 }), - //dimensions: naturalDimensions, - } - ] -} - -var TOUCH_2_CONTROLLER_CONFIGURATION = { name: "Touch", controllers: [ { @@ -221,6 +199,7 @@ var viveNaturalPosition = { z: 0.06380049744620919 }; var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive2.fbx"; +var viveModelURL = "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_body.fbx"; var VIVE_CONTROLLER_CONFIGURATION = { name: "Vive", @@ -369,17 +348,59 @@ var VIVE_CONTROLLER_CONFIGURATION = { }, parts: { - { - type: "linear", - modelURL: "", - input: Controller.Hardware.Vive.RT, + //{ + // type: "linear", + // modelURL: "", + // input: "Controller.Hardware.Vive.RT", + // minValue: 0.0, + // maxValue: 1.0, + // textOffset: { x: -0.035, y: 0.004, z: -0.005 }, + // minPosition: { x: -0.035, y: 0.004, z: -0.005 }, + // maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, + //}, + + // The touchpad type draws a dot indicating the current touch/thumb position + // and swaps in textures based on the thumb position. + touchpad: { + type: "touchpad", + modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", + visibleInput: "Vive.RSTouch", + xInput: "Vive.RX", + yInput: "Vive.RY", + naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, minValue: 0.0, maxValue: 1.0, - textOffset: { x: -0.035, y: 0.004, z: -0.005 }, minPosition: { x: -0.035, y: 0.004, z: -0.005 }, maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - } + textureName: "Tex.touchpad-blank", + areas: [ + { + textureURL: "c:%5CUsers%5CRyan%5CAssets%5Ccontroller%5Cvive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", + minX: 0, + maxX: 50, + minY: 0, + maxY: 50 + }, + { + textureURL: "...", + minX: 0, + maxX: 50, + minY: 0, + maxY: 50 + } + ] + }, + trigger: { + type: "rotational", + modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trigger.fbx", + input: Controller.Standard.RT, + naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, + minValue: 0.0, + maxValue: 1.0, + axis: { x: -1, y: 0, z: 0 }, + maxAngle: 90, + } }, annotationTextRotation: Quat.fromPitchYawRollDegrees(180 + 45, 90, 180), @@ -463,10 +484,24 @@ var VISIBLE_BY_DEFAULT = false; function setupController(config) { var controllerDisplay = { overlays: [], + partOverlays: { + }, annotations: { }, - mappingName: "" + mappingName: "mapping-display", + + hidePart: function(partName) { + Overlays.editOverlay(this.partOverlays[partName], { + visible: false + }); + }, + showPart: function(partName) { + Overlays.editOverlay(this.partOverlays[partName], { + visible: true + }); + }, }; + var mapping = Controller.newMapping(controllerDisplay.mappingName); for (var i = 0; i < config.controllers.length; ++i) { var controller = config.controllers[i]; var position = controller.position; @@ -558,7 +593,88 @@ function setupController(config) { controllerDisplay.overlays.push(lineOverlayID); } } + + function clamp(value, min, max) { + if (value < min) { + return min; + } else if (value > max) { + return max + } + return value; + } + + if (controller.parts) { + for (var partName in controller.parts) { + var part = controller.parts[partName]; + var partPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition)); + var innerRotation = controller.rotation + + Vec3.print("controller", controller.position); + Vec3.print("part", partPosition); + + var overlayID = Overlays.addOverlay("model", { + url: part.modelURL, + localPosition: partPosition, + localRotation: innerRotation, + parentID: PARENT_ID, + parentJointIndex: controller.jointIndex, + ignoreRayIntersection: true, + }); + + if (part.type == "rotational") { + var range = part.maxValue - part.minValue; + mapping.from([part.input]).peek().to(function(value) { + print(value); + + value = clamp(value, part.minValue, part.maxValue); + + var pct = (value - part.minValue) / part.maxValue; + var angle = pct * part.maxAngle; + var rotation = Quat.angleAxis(angle, part.axis); + print(value, pct, angle); + + Overlays.editOverlay(overlayID, { + localRotation: Quat.multiply(innerRotation, rotation) + }); + }); + } else if (part.type == "touchpad") { + function resolveHardware(path) { + var parts = path.split("."); + function resolveInner(base, path, i) { + print(path[i]); + if (i >= path.length) { + return base; + } + return resolveInner(base[path[i]], path, ++i); + } + return resolveInner(Controller.Hardware, parts, 0); + } + + var visibleInput = resolveHardware(part.visibleInput); + var xinput = resolveHardware(part.xInput); + var yinput = resolveHardware(part.yInput); + + print("visible:", visibleInput); + + mapping.from([visibleInput]).peek().to(function(value) { + print("visible", value); + }); + mapping.from([xinput]).peek().to(function(value) { + print("X", value); + }); + mapping.from([yinput]).peek().invert().to(function(value) { + print("Y", value); + }); + } else { + print("TYPE NOT SUPPORTED: ", part.type); + } + + controllerDisplay.overlays.push(overlayID); + controllerDisplay.partOverlays[partName] = overlayID; + } + } } + Controller.enableMapping(controllerDisplay.mappingName); return controllerDisplay; } @@ -616,7 +732,6 @@ mapping.from([Controller.Standard.RT]).to(function(value) { Controller.enableMapping(MAPPING_NAME); //var c = setupController(TOUCH_CONTROLLER_CONFIGURATION); -//var c = setupController(TOUCH_2_CONTROLLER_CONFIGURATION); var c = setupController(VIVE_CONTROLLER_CONFIGURATION); //MyAvatar.shouldRenderLocally = false; Script.scriptEnding.connect(function() { From 7d57c0631366dda59227cf4c586188a78d61369d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 Sep 2016 14:45:16 -0700 Subject: [PATCH 020/109] Add welcome to tutorial --- tutorial/tutorial.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index ed091a6526..b4b731b052 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -206,6 +206,25 @@ stepDisableControllers.prototype = { } }; +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: Welcome // +// // +/////////////////////////////////////////////////////////////////////////////// +var stepWelcome = function(name) { + this.tag = name; +} +stepWelcome.prototype = { + start: function(onFinish) { + Script.setTimeout(onFinish, 8000); + showEntitiesWithTag(this.tag); + }, + cleanup: function() { + hideEntitiesWithTag(this.tag); + } +}; + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // @@ -733,6 +752,7 @@ function startTutorial() { currentStep = null; STEPS = [ new stepDisableControllers("step0"), + new stepWelcome("welcome"), new stepRaiseAboveHead("raiseHands"), new stepNearGrab("nearGrab"), new stepFarGrab("farGrab"), From 324365decaee0c08760047dbe8519107b3126922 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 Sep 2016 16:13:18 -0700 Subject: [PATCH 021/109] Add controller part hiding/showing depending on step --- tutorial/tutorial.js | 52 +++++++-- tutorial/viveHandsv2.js | 228 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 260 insertions(+), 20 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index b4b731b052..e6cbae11ce 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -29,7 +29,8 @@ Script.include("entityData.js"); var BASKET_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx"; var BASKET_COLLIDER_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj"; -var successSound = SoundCache.getSound(Script.resolvePath("success48.wav")); +//var successSound = SoundCache.getSound(Script.resolvePath("success48.wav")); +var successSound = SoundCache.getSound("http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Sounds/good_one.L.wav"); function beginsWithFilter(value, key) { return value.indexOf(properties[key]) == 0; @@ -200,6 +201,11 @@ stepDisableControllers.prototype = { holdEnabled: false, farGrabEnabled: false, })); + setControllerPartsVisible({ + touchpad: true, + touchpad_teleport: false, + touchpad_arrows: false + }); onFinish(); }, cleanup: function() { @@ -217,10 +223,11 @@ var stepWelcome = function(name) { } stepWelcome.prototype = { start: function(onFinish) { - Script.setTimeout(onFinish, 8000); + this.timerID = Script.setTimeout(onFinish, 8000); showEntitiesWithTag(this.tag); }, cleanup: function() { + Script.clearTimeout(this.timerID); hideEntitiesWithTag(this.tag); } }; @@ -291,6 +298,10 @@ function setControllerVisible(name, visible) { })); } +function setControllerPartsVisible(parts) { + Messages.sendLocalMessage('Controller-Display-Parts', JSON.stringify(parts)); +} + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -341,8 +352,9 @@ stepNearGrab.prototype = { // When block collides with basket start step 2 function checkCollides() { - print(this.tag, "CHECKING..."); - if (Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position) < 0.1) { + var dist = Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position); + print(this.tag, "CHECKING...", dist); + if (dist < 0.1) { Script.clearInterval(this.checkCollidesTimer); this.checkCollidesTimer = null; this.soundInjector = Audio.playSound(successSound, { @@ -423,7 +435,7 @@ stepFarGrab.prototype = { var checkCollidesTimer = null; function checkCollides() { print("CHECKING..."); - if (Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position) < 0.1) { + if (Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position) < 0.2) { Script.clearInterval(checkCollidesTimer); this.soundInjector = Audio.playSound(successSound, { position: basketPosition, @@ -598,6 +610,12 @@ stepTurnAround.prototype = { setControllerVisible("left", true); setControllerVisible("right", true); + setControllerPartsVisible({ + touchpad: false, + touchpad_teleport: false, + touchpad_arrows: true + }); + showEntitiesWithTag(this.tag); var hasTurnedAround = false; this.interval = Script.setInterval(function() { @@ -625,6 +643,12 @@ stepTurnAround.prototype = { setControllerVisible("left", false); setControllerVisible("right", false); + setControllerPartsVisible({ + touchpad: true, + touchpad_teleport: false, + touchpad_arrows: false + }); + if (this.interval) { Script.clearInterval(this.interval); } @@ -648,7 +672,14 @@ var stepTeleport = function(name) { } stepTeleport.prototype = { start: function(onFinish) { - setControllerVisible("teleport", true); + //setControllerVisible("teleport", true); + + setControllerPartsVisible({ + touchpad: false, + touchpad_teleport: true, + touchpad_arrows: false + }); + Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); // Wait until touching teleport pad... @@ -678,7 +709,14 @@ stepTeleport.prototype = { showEntitiesWithTag(this.tag); }, cleanup: function() { - setControllerVisible("teleport", false); + //setControllerVisible("teleport", false); + + setControllerPartsVisible({ + touchpad: true, + touchpad_teleport: false, + touchpad_arrows: false + }); + if (this.checkCollidesTimer) { Script.clearInterval(this.checkCollidesTimer); } diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 24f17dc138..8271fdcffe 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -213,6 +213,123 @@ var VIVE_CONTROLLER_CONFIGURATION = { dimensions: viveNaturalDimensions, + parts: { + //{ + // type: "linear", + // modelURL: "", + // input: "Controller.Hardware.Vive.RT", + // minValue: 0.0, + // maxValue: 1.0, + // textOffset: { x: -0.035, y: 0.004, z: -0.005 }, + // minPosition: { x: -0.035, y: 0.004, z: -0.005 }, + // maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, + //}, + + // The touchpad type draws a dot indicating the current touch/thumb position + // and swaps in textures based on the thumb position. + touchpad: { + type: "touchpad", + //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", + visibleInput: "Vive.RSTouch", + xInput: "Vive.RX", + yInput: "Vive.RY", + naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, + minValue: 0.0, + maxValue: 1.0, + minPosition: { x: -0.035, y: 0.004, z: -0.005 }, + maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, + textureName: "Tex.touchpad-blank", + }, + + touchpad_teleport: { + type: "touchpad", + //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", + visibleInput: "Vive.RSTouch", + xInput: "Vive.RX", + yInput: "Vive.RY", + naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, + minValue: 0.0, + maxValue: 1.0, + minPosition: { x: -0.035, y: 0.004, z: -0.005 }, + maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, + textureName: "Tex.touchpad-blank", + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport.jpg", + }, + + touchpad_arrows: { + type: "touchpad", + //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", + visibleInput: "Vive.RSTouch", + xInput: "Vive.RX", + yInput: "Vive.RY", + naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, + minValue: 0.0, + maxValue: 1.0, + minPosition: { x: -0.035, y: 0.004, z: -0.005 }, + maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, + textureName: "Tex.touchpad-blank", + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", + areas: [ + { + textureURL: "c:%5CUsers%5CRyan%5CAssets%5Ccontroller%5Cvive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", + minX: 0, + maxX: 50, + minY: 0, + maxY: 50 + }, + { + textureURL: "...", + minX: 0, + maxX: 50, + minY: 0, + maxY: 50 + } + ] + }, + + trigger: { + type: "rotational", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trigger.fbx", + input: Controller.Standard.RT, + naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, + minValue: 0.0, + maxValue: 1.0, + axis: { x: -1, y: 0, z: 0 }, + maxAngle: 90, + }, + + l_grip: { + type: "ignore", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_l_grip.fbx", + naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + }, + + r_grip: { + type: "ignore", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_r_grip.fbx", + naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + }, + + sys_button: { + type: "ignore", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_sys_button.fbx", + naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}, + }, + + button: { + type: "ignore", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + }, + button2: { + type: "ignore", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + }, + }, annotationTextRotation: Quat.fromPitchYawRollDegrees(45, -90, 0), annotations: { // red: { @@ -363,7 +480,8 @@ var VIVE_CONTROLLER_CONFIGURATION = { // and swaps in textures based on the thumb position. touchpad: { type: "touchpad", - modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", + //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", visibleInput: "Vive.RSTouch", xInput: "Vive.RX", yInput: "Vive.RY", @@ -373,6 +491,38 @@ var VIVE_CONTROLLER_CONFIGURATION = { minPosition: { x: -0.035, y: 0.004, z: -0.005 }, maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, textureName: "Tex.touchpad-blank", + }, + + touchpad_teleport: { + type: "touchpad", + //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", + visibleInput: "Vive.RSTouch", + xInput: "Vive.RX", + yInput: "Vive.RY", + naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, + minValue: 0.0, + maxValue: 1.0, + minPosition: { x: -0.035, y: 0.004, z: -0.005 }, + maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, + textureName: "Tex.touchpad-blank", + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport.jpg", + }, + + touchpad_arrows: { + type: "touchpad", + //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", + visibleInput: "Vive.RSTouch", + xInput: "Vive.RX", + yInput: "Vive.RY", + naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, + minValue: 0.0, + maxValue: 1.0, + minPosition: { x: -0.035, y: 0.004, z: -0.005 }, + maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, + textureName: "Tex.touchpad-blank", + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", areas: [ { textureURL: "c:%5CUsers%5CRyan%5CAssets%5Ccontroller%5Cvive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", @@ -393,14 +543,43 @@ var VIVE_CONTROLLER_CONFIGURATION = { trigger: { type: "rotational", - modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trigger.fbx", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trigger.fbx", input: Controller.Standard.RT, naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, minValue: 0.0, maxValue: 1.0, axis: { x: -1, y: 0, z: 0 }, maxAngle: 90, - } + }, + + l_grip: { + type: "ignore", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_l_grip.fbx", + naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + }, + + r_grip: { + type: "ignore", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_r_grip.fbx", + naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + }, + + sys_button: { + type: "ignore", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_sys_button.fbx", + naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}, + }, + + button: { + type: "ignore", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + }, + button2: { + type: "ignore", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + }, }, annotationTextRotation: Quat.fromPitchYawRollDegrees(180 + 45, 90, 180), @@ -490,15 +669,16 @@ function setupController(config) { }, mappingName: "mapping-display", - hidePart: function(partName) { - Overlays.editOverlay(this.partOverlays[partName], { - visible: false - }); - }, - showPart: function(partName) { - Overlays.editOverlay(this.partOverlays[partName], { - visible: true - }); + setPartVisible: function(partName, visible) { + print("Setting part visible", partName, visible); + if (partName in this.partOverlays) { + print("FOUND"); + for (var i = 0; i < this.partOverlays[partName].length; ++i) { + Overlays.editOverlay(this.partOverlays[partName][i], { + visible: visible + }); + } + } }, }; var mapping = Controller.newMapping(controllerDisplay.mappingName); @@ -619,6 +799,7 @@ function setupController(config) { parentID: PARENT_ID, parentJointIndex: controller.jointIndex, ignoreRayIntersection: true, + //visible: false }); if (part.type == "rotational") { @@ -665,12 +846,22 @@ function setupController(config) { mapping.from([yinput]).peek().invert().to(function(value) { print("Y", value); }); + if (part.defaultTextureURL) { + var textures = {}; + textures[part.textureName] = part.defaultTextureURL; + Overlays.editOverlay(overlayID, { + textures: textures + }); + } } else { print("TYPE NOT SUPPORTED: ", part.type); } controllerDisplay.overlays.push(overlayID); - controllerDisplay.partOverlays[partName] = overlayID; + if (!(partName in controllerDisplay.partOverlays)) { + controllerDisplay.partOverlays[partName] = []; + } + controllerDisplay.partOverlays[partName].push(overlayID); } } } @@ -709,10 +900,18 @@ var handleMessages = function(channel, message, sender) { Overlays.editOverlay(c.annotations[name][i], { visible: visible }); } } + } else if (channel === 'Controller-Display-Parts') { + print('here part'); + var data = JSON.parse(message); + for (var name in data) { + var visible = data[name]; + c.setPartVisible(name, visible); + } } } } + Messages.messageReceived.connect(handleMessages); var MAPPING_NAME = "com.highfidelity.handControllerGrab.disable"; @@ -733,6 +932,9 @@ Controller.enableMapping(MAPPING_NAME); //var c = setupController(TOUCH_CONTROLLER_CONFIGURATION); var c = setupController(VIVE_CONTROLLER_CONFIGURATION); +//c.setPartVisible("touchpad", false); +//c.setPartVisible("touchpad_teleport", false); + //MyAvatar.shouldRenderLocally = false; Script.scriptEnding.connect(function() { deleteControllerDisplay(c); From 352f0f7ea711d53a3979ced5e3ea1ba566c443ad Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 Sep 2016 16:22:16 -0700 Subject: [PATCH 022/109] Update controller model URL --- tutorial/tutorial.js | 2 ++ tutorial/viveHandsv2.js | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index e6cbae11ce..ee33b4428a 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -866,6 +866,8 @@ Controller.keyReleaseEvent.connect(function (event) { } } else if (event.text == "F12") { restartStep(); + } else if (event.text == "F10") { + MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; } else if (event.text == "r") { stopTutorial(); startTutorial(); diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 8271fdcffe..0cd761e034 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -199,7 +199,7 @@ var viveNaturalPosition = { z: 0.06380049744620919 }; var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive2.fbx"; -var viveModelURL = "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_body.fbx"; +var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_body.fbx", var VIVE_CONTROLLER_CONFIGURATION = { name: "Vive", @@ -935,7 +935,6 @@ var c = setupController(VIVE_CONTROLLER_CONFIGURATION); //c.setPartVisible("touchpad", false); //c.setPartVisible("touchpad_teleport", false); -//MyAvatar.shouldRenderLocally = false; Script.scriptEnding.connect(function() { deleteControllerDisplay(c); MyAvatar.shouldRenderLocally = true; From 3149bb834dd7d3901de4d1a2a8f7a3a65ea95123 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 Sep 2016 16:25:00 -0700 Subject: [PATCH 023/109] Fix typo in vive controller display --- tutorial/viveHandsv2.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 0cd761e034..9677f6d821 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -199,7 +199,7 @@ var viveNaturalPosition = { z: 0.06380049744620919 }; var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive2.fbx"; -var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_body.fbx", +var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_body.fbx"; var VIVE_CONTROLLER_CONFIGURATION = { name: "Vive", From f8fdbe549958a7c700ccc00c73a6b3483711dbd3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 8 Sep 2016 16:41:04 -0700 Subject: [PATCH 024/109] Fix sound position --- tutorial/tutorial.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index ee33b4428a..4ea38d51a2 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -176,7 +176,7 @@ function isFunction(functionToCheck) { }; function playSuccessSound() { this.soundInjector = Audio.playSound(successSound, { - position: defaultTransform.position, + position: MyAvatar.position, volume: 0.7, loop: false }); From 170502e8a14d1bb22beb0e3b826cf51bfd691959 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 9 Sep 2016 10:28:59 -0700 Subject: [PATCH 025/109] Update controller display part implementation to only have a single layer active --- tutorial/tutorial.js | 36 +++---- tutorial/viveHandsv2.js | 208 +++++++++++++++++++--------------------- 2 files changed, 110 insertions(+), 134 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 4ea38d51a2..5262e789bf 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -201,11 +201,7 @@ stepDisableControllers.prototype = { holdEnabled: false, farGrabEnabled: false, })); - setControllerPartsVisible({ - touchpad: true, - touchpad_teleport: false, - touchpad_arrows: false - }); + setControllerPartLayer('touchpad', 'blank'); onFinish(); }, cleanup: function() { @@ -302,6 +298,12 @@ function setControllerPartsVisible(parts) { Messages.sendLocalMessage('Controller-Display-Parts', JSON.stringify(parts)); } +function setControllerPartLayer(part, layer) { + data = {}; + data[part] = layer; + Messages.sendLocalMessage('Controller-Set-Part-Layer', JSON.stringify(data)); +} + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -610,11 +612,7 @@ stepTurnAround.prototype = { setControllerVisible("left", true); setControllerVisible("right", true); - setControllerPartsVisible({ - touchpad: false, - touchpad_teleport: false, - touchpad_arrows: true - }); + setControllerPartLayer('touchpad', 'arrows'); showEntitiesWithTag(this.tag); var hasTurnedAround = false; @@ -643,11 +641,7 @@ stepTurnAround.prototype = { setControllerVisible("left", false); setControllerVisible("right", false); - setControllerPartsVisible({ - touchpad: true, - touchpad_teleport: false, - touchpad_arrows: false - }); + setControllerPartLayer('touchpad', 'blank'); if (this.interval) { Script.clearInterval(this.interval); @@ -674,11 +668,7 @@ stepTeleport.prototype = { start: function(onFinish) { //setControllerVisible("teleport", true); - setControllerPartsVisible({ - touchpad: false, - touchpad_teleport: true, - touchpad_arrows: false - }); + setControllerPartLayer('touchpad', 'teleport'); Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); @@ -711,11 +701,7 @@ stepTeleport.prototype = { cleanup: function() { //setControllerVisible("teleport", false); - setControllerPartsVisible({ - touchpad: true, - touchpad_teleport: false, - touchpad_arrows: false - }); + setControllerPartLayer('touchpad', 'blank'); if (this.checkCollidesTimer) { Script.clearInterval(this.checkCollidesTimer); diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 9677f6d821..7bbd471622 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -232,73 +232,39 @@ var VIVE_CONTROLLER_CONFIGURATION = { //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", visibleInput: "Vive.RSTouch", - xInput: "Vive.RX", - yInput: "Vive.RY", + xInput: "Vive.LX", + yInput: "Vive.LY", naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, minValue: 0.0, maxValue: 1.0, minPosition: { x: -0.035, y: 0.004, z: -0.005 }, maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, textureName: "Tex.touchpad-blank", - }, - touchpad_teleport: { - type: "touchpad", - //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", - visibleInput: "Vive.RSTouch", - xInput: "Vive.RX", - yInput: "Vive.RY", - naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, - minValue: 0.0, - maxValue: 1.0, - minPosition: { x: -0.035, y: 0.004, z: -0.005 }, - maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - textureName: "Tex.touchpad-blank", - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport.jpg", - }, - - touchpad_arrows: { - type: "touchpad", - //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", - visibleInput: "Vive.RSTouch", - xInput: "Vive.RX", - yInput: "Vive.RY", - naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, - minValue: 0.0, - maxValue: 1.0, - minPosition: { x: -0.035, y: 0.004, z: -0.005 }, - maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - textureName: "Tex.touchpad-blank", - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", - areas: [ - { - textureURL: "c:%5CUsers%5CRyan%5CAssets%5Ccontroller%5Cvive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", - minX: 0, - maxX: 50, - minY: 0, - maxY: 50 + defaultTextureLayer: "blank", + textureLayers: { + blank: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", }, - { - textureURL: "...", - minX: 0, - maxX: 50, - minY: 0, - maxY: 50 + teleport: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport.jpg", + }, + arrows: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", } - ] + } }, trigger: { type: "rotational", modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trigger.fbx", - input: Controller.Standard.RT, + input: Controller.Standard.LT, naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, + origin: { x: 0, y: -0.015, z: -0.00 }, minValue: 0.0, maxValue: 1.0, axis: { x: -1, y: 0, z: 0 }, - maxAngle: 90, + maxAngle: 25, }, l_grip: { @@ -404,9 +370,10 @@ var VIVE_CONTROLLER_CONFIGURATION = { trigger: { position: { x: 0.0055, - y: -0.023978, + y: -0.032978, z: 0.04546 }, + lineHeight: 0.013, direction: "left", color: { red: 255, green: 100, blue: 100 }, }, @@ -491,54 +458,19 @@ var VIVE_CONTROLLER_CONFIGURATION = { minPosition: { x: -0.035, y: 0.004, z: -0.005 }, maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, textureName: "Tex.touchpad-blank", - }, - touchpad_teleport: { - type: "touchpad", - //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", - visibleInput: "Vive.RSTouch", - xInput: "Vive.RX", - yInput: "Vive.RY", - naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, - minValue: 0.0, - maxValue: 1.0, - minPosition: { x: -0.035, y: 0.004, z: -0.005 }, - maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - textureName: "Tex.touchpad-blank", - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport.jpg", - }, - - touchpad_arrows: { - type: "touchpad", - //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", - visibleInput: "Vive.RSTouch", - xInput: "Vive.RX", - yInput: "Vive.RY", - naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, - minValue: 0.0, - maxValue: 1.0, - minPosition: { x: -0.035, y: 0.004, z: -0.005 }, - maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - textureName: "Tex.touchpad-blank", - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", - areas: [ - { - textureURL: "c:%5CUsers%5CRyan%5CAssets%5Ccontroller%5Cvive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", - minX: 0, - maxX: 50, - minY: 0, - maxY: 50 + defaultTextureLayer: "blank", + textureLayers: { + blank: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", }, - { - textureURL: "...", - minX: 0, - maxX: 50, - minY: 0, - maxY: 50 + teleport: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport.jpg", + }, + arrows: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", } - ] + } }, trigger: { @@ -546,10 +478,11 @@ var VIVE_CONTROLLER_CONFIGURATION = { modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trigger.fbx", input: Controller.Standard.RT, naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, + origin: { x: 0, y: -0.015, z: -0.00 }, minValue: 0.0, maxValue: 1.0, axis: { x: -1, y: 0, z: 0 }, - maxAngle: 90, + maxAngle: 25, }, l_grip: { @@ -609,9 +542,10 @@ var VIVE_CONTROLLER_CONFIGURATION = { trigger: { position: { x: -0.075, - y: -0.023978, + y: -0.032978, z: 0.04546 }, + lineHeight: 0.013, direction: "left", color: { red: 255, green: 100, blue: 100 }, }, @@ -665,6 +599,8 @@ function setupController(config) { overlays: [], partOverlays: { }, + parts: { + }, annotations: { }, mappingName: "mapping-display", @@ -680,6 +616,29 @@ function setupController(config) { } } }, + + setLayerForPart: function(partName, layerName) { + print("Setting layer...", partName, layerName); + if (partName in this.parts) { + var part = this.parts[partName]; + print("FOnd", JSON.stringify(part)); + if (layerName in part.textureLayers) { + print("got it", layerName); + var layer = part.textureLayers[layerName]; + var textures = {}; + if (layer.defaultTextureURL) { + print("default texture"); + textures[part.textureName] = layer.defaultTextureURL; + } + for (var i = 0; i < this.partOverlays[partName].length; ++i) { + print("updating", JSON.stringify(textures)); + Overlays.editOverlay(this.partOverlays[partName][i], { + textures: textures + }); + } + } + } + } }; var mapping = Controller.newMapping(controllerDisplay.mappingName); for (var i = 0; i < config.controllers.length; ++i) { @@ -736,7 +695,7 @@ function setupController(config) { text: key, localPosition: pos, localRotation: controller.annotationTextRotation, - lineHeight: 0.01, + lineHeight: annotation.lineHeight ? annotation.lineHeight : 0.01, leftMargin: 0, rightMargin: 0, topMargin: 0, @@ -792,6 +751,8 @@ function setupController(config) { Vec3.print("controller", controller.position); Vec3.print("part", partPosition); + controllerDisplay.parts[partName] = controller.parts[partName]; + var overlayID = Overlays.addOverlay("model", { url: part.modelURL, localPosition: partPosition, @@ -804,20 +765,36 @@ function setupController(config) { if (part.type == "rotational") { var range = part.maxValue - part.minValue; - mapping.from([part.input]).peek().to(function(value) { - print(value); + mapping.from([part.input]).peek().to(function(controller, overlayID, part) { + return function(value) { + //print(value); + //print(JSON.stringify(part)); - value = clamp(value, part.minValue, part.maxValue); + value = clamp(value, part.minValue, part.maxValue); - var pct = (value - part.minValue) / part.maxValue; - var angle = pct * part.maxAngle; - var rotation = Quat.angleAxis(angle, part.axis); - print(value, pct, angle); + var pct = (value - part.minValue) / part.maxValue; + var angle = pct * part.maxAngle; + var rotation = Quat.angleAxis(angle, part.axis); + print(value, pct, angle); - Overlays.editOverlay(overlayID, { - localRotation: Quat.multiply(innerRotation, rotation) - }); - }); + var offset = { x: 0, y: 0, z: 0 }; + if (part.origin) { + //print(rotation.x, rotation.y, rotation.z, rotation.w); + var offset = Vec3.multiplyQbyV(rotation, part.origin); + offset = Vec3.subtract(offset, part.origin); + Vec3.print('offset', offset); + //partPosition = Vec3.sum(partPosition, offset); + } + + var partPosition = Vec3.sum(controller.position, + Vec3.multiplyQbyV(controller.rotation, Vec3.sum(offset, part.naturalPosition))); + + Overlays.editOverlay(overlayID, { + localPosition: partPosition, + localRotation: Quat.multiply(controller.rotation, rotation) + }); + } + }(controller, overlayID, part)); } else if (part.type == "touchpad") { function resolveHardware(path) { var parts = path.split("."); @@ -907,6 +884,12 @@ var handleMessages = function(channel, message, sender) { var visible = data[name]; c.setPartVisible(name, visible); } + } else if (channel === 'Controller-Set-Part-Layer') { + var data = JSON.parse(message); + for (var name in data) { + var layer = data[name]; + c.setLayerForPart(name, layer); + } } } } @@ -934,6 +917,13 @@ Controller.enableMapping(MAPPING_NAME); var c = setupController(VIVE_CONTROLLER_CONFIGURATION); //c.setPartVisible("touchpad", false); //c.setPartVisible("touchpad_teleport", false); +//layers = ["blank", "teleport", 'arrows']; +//num = 0; +//Script.setInterval(function() { +// print('num', num); +// num = (num + 1) % layers.length; +// c.setLayerForPart("touchpad", layers[num]); +//}, 2000); Script.scriptEnding.connect(function() { deleteControllerDisplay(c); From 7bbf9bebecff06d2909626b48edf8d2d68e3aaa6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 9 Sep 2016 11:38:14 -0700 Subject: [PATCH 026/109] Tweak tutorial and controllers --- tutorial/tutorial.js | 4 ++-- tutorial/viveHandsv2.js | 28 +++++++++++++--------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 5262e789bf..2d06d06a82 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -356,7 +356,7 @@ stepNearGrab.prototype = { function checkCollides() { var dist = Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position); print(this.tag, "CHECKING...", dist); - if (dist < 0.1) { + if (dist < 0.15) { Script.clearInterval(this.checkCollidesTimer); this.checkCollidesTimer = null; this.soundInjector = Audio.playSound(successSound, { @@ -622,7 +622,7 @@ stepTurnAround.prototype = { var angleDegrees = ((angle / Math.PI) * 180); print("CHECK"); if (!hasTurnedAround) { - if (Math.abs(angleDegrees) > 100) { + if (Math.abs(angleDegrees) > 140) { hasTurnedAround = true; print("half way there..."); } diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 7bbd471622..0d99cede77 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -468,7 +468,7 @@ var VIVE_CONTROLLER_CONFIGURATION = { defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport.jpg", }, arrows: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows-active.jpg", } } }, @@ -621,17 +621,17 @@ function setupController(config) { print("Setting layer...", partName, layerName); if (partName in this.parts) { var part = this.parts[partName]; - print("FOnd", JSON.stringify(part)); + //print("FOnd", JSON.stringify(part)); if (layerName in part.textureLayers) { - print("got it", layerName); + //print("got it", layerName); var layer = part.textureLayers[layerName]; var textures = {}; if (layer.defaultTextureURL) { - print("default texture"); + //print("default texture"); textures[part.textureName] = layer.defaultTextureURL; } for (var i = 0; i < this.partOverlays[partName].length; ++i) { - print("updating", JSON.stringify(textures)); + //print("updating", JSON.stringify(textures)); Overlays.editOverlay(this.partOverlays[partName][i], { textures: textures }); @@ -748,8 +748,8 @@ function setupController(config) { var partPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition)); var innerRotation = controller.rotation - Vec3.print("controller", controller.position); - Vec3.print("part", partPosition); + //Vec3.print("controller", controller.position); + //Vec3.print("part", partPosition); controllerDisplay.parts[partName] = controller.parts[partName]; @@ -775,14 +775,14 @@ function setupController(config) { var pct = (value - part.minValue) / part.maxValue; var angle = pct * part.maxAngle; var rotation = Quat.angleAxis(angle, part.axis); - print(value, pct, angle); + //print(value, pct, angle); var offset = { x: 0, y: 0, z: 0 }; if (part.origin) { //print(rotation.x, rotation.y, rotation.z, rotation.w); var offset = Vec3.multiplyQbyV(rotation, part.origin); offset = Vec3.subtract(offset, part.origin); - Vec3.print('offset', offset); + //Vec3.print('offset', offset); //partPosition = Vec3.sum(partPosition, offset); } @@ -799,7 +799,7 @@ function setupController(config) { function resolveHardware(path) { var parts = path.split("."); function resolveInner(base, path, i) { - print(path[i]); + //print(path[i]); if (i >= path.length) { return base; } @@ -812,16 +812,14 @@ function setupController(config) { var xinput = resolveHardware(part.xInput); var yinput = resolveHardware(part.yInput); - print("visible:", visibleInput); - mapping.from([visibleInput]).peek().to(function(value) { - print("visible", value); + //print("visible", value); }); mapping.from([xinput]).peek().to(function(value) { - print("X", value); + //print("X", value); }); mapping.from([yinput]).peek().invert().to(function(value) { - print("Y", value); + //print("Y", value); }); if (part.defaultTextureURL) { var textures = {}; From dee389e204284426154f496c85af5a35d95e37da Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 9 Sep 2016 12:02:23 -0700 Subject: [PATCH 027/109] Fix tutorial sounds --- tutorial/tutorial.js | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 2d06d06a82..873fb1879d 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -175,7 +175,7 @@ function isFunction(functionToCheck) { } }; function playSuccessSound() { - this.soundInjector = Audio.playSound(successSound, { + Audio.playSound(successSound, { position: MyAvatar.position, volume: 0.7, loop: false @@ -268,11 +268,7 @@ stepRaiseAboveHead.prototype = { if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; - this.soundInjector = Audio.playSound(successSound, { - position: defaultTransform.position, - volume: 0.7, - loop: false - }); + playSuccessSound(); onFinish(); } } @@ -359,11 +355,7 @@ stepNearGrab.prototype = { if (dist < 0.15) { Script.clearInterval(this.checkCollidesTimer); this.checkCollidesTimer = null; - this.soundInjector = Audio.playSound(successSound, { - position: basketPosition, - volume: 0.7, - loop: false - }); + playSuccessSound(); Script.setTimeout(onHit.bind(this), 1000); } } @@ -439,11 +431,7 @@ stepFarGrab.prototype = { print("CHECKING..."); if (Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position) < 0.2) { Script.clearInterval(checkCollidesTimer); - this.soundInjector = Audio.playSound(successSound, { - position: basketPosition, - volume: 0.7, - loop: false - }); + playSuccessSound(); Script.setTimeout(onHit.bind(this), 1000); } } @@ -542,11 +530,7 @@ stepEquip.prototype = { if (Vec3.distance(basketPosition, Entities.getEntityProperties(ammoIDs[i], 'position').position) < 0.25) { Script.clearInterval(this.checkCollidesTimer); this.checkCollidesTimer = null; - this.soundInjector = Audio.playSound(successSound, { - position: basketPosition, - volume: 0.7, - loop: false - }); + playSuccessSound(); Script.setTimeout(onHit.bind(this), 1000); return; } From b3205a651341dee56b5911fd25276e794a317c9e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 9 Sep 2016 12:47:04 -0700 Subject: [PATCH 028/109] Fix teleport overlay on controller --- tutorial/viveHandsv2.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 0d99cede77..dfcf682cd2 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -247,7 +247,7 @@ var VIVE_CONTROLLER_CONFIGURATION = { defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", }, teleport: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport.jpg", + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg", }, arrows: { defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", From 3886bcaa7305e41fe8e26eb9eca6ce9f40a826db Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 9 Sep 2016 12:56:41 -0700 Subject: [PATCH 029/109] Fix teleport overlay --- tutorial/viveHandsv2.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index dfcf682cd2..8f13775783 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -465,7 +465,7 @@ var VIVE_CONTROLLER_CONFIGURATION = { defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", }, teleport: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport.jpg", + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg", }, arrows: { defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows-active.jpg", From 3fb3e5cf4ce995558812112ae44d9dbc8f862feb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 12 Sep 2016 15:07:30 -0700 Subject: [PATCH 030/109] Update controller display to cleanup properly when taking HMD off --- tutorial/controllerDisplay.js | 270 +++++++ tutorial/touchControllerConfiguration.js | 142 ++++ tutorial/viveControllerConfiguration.js | 361 +++++++++ tutorial/viveHandsv2.js | 945 ++--------------------- 4 files changed, 839 insertions(+), 879 deletions(-) create mode 100644 tutorial/controllerDisplay.js create mode 100644 tutorial/touchControllerConfiguration.js create mode 100644 tutorial/viveControllerConfiguration.js diff --git a/tutorial/controllerDisplay.js b/tutorial/controllerDisplay.js new file mode 100644 index 0000000000..ecce58e605 --- /dev/null +++ b/tutorial/controllerDisplay.js @@ -0,0 +1,270 @@ +var DEBUG = false; +var VISIBLE_BY_DEFAULT = false; +var PARENT_ID = MyAvatar.sessionUUID; + +createControllerDisplay = function(config) { + var controllerDisplay = { + overlays: [], + partOverlays: { + }, + parts: { + }, + annotations: { + }, + mappingName: "mapping-display", + + setPartVisible: function(partName, visible) { + print("Setting part visible", partName, visible); + if (partName in this.partOverlays) { + print("FOUND"); + for (var i = 0; i < this.partOverlays[partName].length; ++i) { + Overlays.editOverlay(this.partOverlays[partName][i], { + visible: visible + }); + } + } + }, + + setLayerForPart: function(partName, layerName) { + print("Setting layer...", partName, layerName); + if (partName in this.parts) { + var part = this.parts[partName]; + //print("FOnd", JSON.stringify(part)); + if (layerName in part.textureLayers) { + //print("got it", layerName); + var layer = part.textureLayers[layerName]; + var textures = {}; + if (layer.defaultTextureURL) { + //print("default texture"); + textures[part.textureName] = layer.defaultTextureURL; + } + for (var i = 0; i < this.partOverlays[partName].length; ++i) { + //print("updating", JSON.stringify(textures)); + Overlays.editOverlay(this.partOverlays[partName][i], { + textures: textures + }); + } + } + } + } + }; + var mapping = Controller.newMapping(controllerDisplay.mappingName); + for (var i = 0; i < config.controllers.length; ++i) { + var controller = config.controllers[i]; + var position = controller.position; + //position = { x: 0, y: 5, z: 5 }; + Vec3.print("position", position); + print("position", position.x, position.y, position.z); + if (controller.naturalPosition) { + position = Vec3.sum(Vec3.multiplyQbyV( + controller.rotation, controller.naturalPosition), position); + } + Vec3.print("Got controller position", position); + var overlayID = Overlays.addOverlay("model", { + url: controller.modelURL, + dimensions: controller.dimensions, + localRotation: controller.rotation, + localPosition: position, + parentID: PARENT_ID, + parentJointIndex: controller.jointIndex, + ignoreRayIntersection: true, + }); + + controllerDisplay.overlays.push(overlayID); + overlayID = null; + + if (controller.annotations) { + for (var key in controller.annotations) { + var annotation = controller.annotations[key]; + var annotationPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, annotation.position)); + if (DEBUG) { + overlayID = Overlays.addOverlay("sphere", { + localPosition: annotationPosition, + //localPosition: Vec3.sum(controller.position, annotation.position), + //localPosition: Vec3.sum(position, annotation.position), + color: annotation.color || { red: 255, green: 100, blue: 100 }, + dimensions: { + x: 0.01, + y: 0.01, + z: 0.01 + }, + parentID: PARENT_ID, + parentJointIndex: controller.jointIndex, + }); + controllerDisplay.overlays.push(overlayID); + + } + + var ANNOTATION_TEXT_OFFSET = 0.1; + var sign = annotation.direction == "right" ? 1 : -1; + var textOffset = annotation.direction == "right" ? 0.08 : 0.02; + if (annotation.textOffset) { + var pos = Vec3.sum(annotationPosition, Vec3.multiplyQbyV(controller.rotation, annotation.textOffset)); + } else { + var pos = Vec3.sum(annotationPosition, Vec3.multiplyQbyV(controller.rotation, { x: textOffset, y: 0, z: -0.005 })); + } + var textOverlayID = Overlays.addOverlay("text3d", { + visible: VISIBLE_BY_DEFAULT, + text: key, + localPosition: pos, + localRotation: controller.annotationTextRotation, + lineHeight: annotation.lineHeight ? annotation.lineHeight : 0.01, + leftMargin: 0, + rightMargin: 0, + topMargin: 0, + bottomMargin: 0, + backgroundAlpha: 0, + dimensions: { x: 0.003, y: 0.003, z: 0.003 }, + //localPosition: Vec3.sum(controller.position, annotation.position), + //localPosition: Vec3.sum(position, annotation.position), + color: annotation.textColor || { red: 255, green: 255, blue: 255 }, + parentID: PARENT_ID, + parentJointIndex: controller.jointIndex, + }); + + controllerDisplay.overlays.push(textOverlayID); + if (key in controllerDisplay.annotations) { + controllerDisplay.annotations[key].push(textOverlayID); + } else { + controllerDisplay.annotations[key] = [textOverlayID]; + } + + var ANNOTATION_OFFSET = 0.5; + var offset = { x: 0, y: 0, z: annotation.direction == "right" ? -1 * ANNOTATION_OFFSET : ANNOTATION_OFFSET }; + var lineOverlayID = Overlays.addOverlay("line3d", { + visible: false, + localPosition: annotationPosition, + localStart: { x: 0, y: 0, z: 0 }, + localEnd: offset, + //localPosition: Vec3.sum(controller.position, annotation.position), + //localPosition: Vec3.sum(position, annotation.position), + color: annotation.color || { red: 255, green: 100, blue: 100 }, + parentID: PARENT_ID, + parentJointIndex: controller.jointIndex, + }); + controllerDisplay.overlays.push(lineOverlayID); + } + } + + function clamp(value, min, max) { + if (value < min) { + return min; + } else if (value > max) { + return max + } + return value; + } + + if (controller.parts) { + for (var partName in controller.parts) { + var part = controller.parts[partName]; + var partPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition)); + var innerRotation = controller.rotation + + //Vec3.print("controller", controller.position); + //Vec3.print("part", partPosition); + + controllerDisplay.parts[partName] = controller.parts[partName]; + + var overlayID = Overlays.addOverlay("model", { + url: part.modelURL, + localPosition: partPosition, + localRotation: innerRotation, + parentID: PARENT_ID, + parentJointIndex: controller.jointIndex, + ignoreRayIntersection: true, + //visible: false + }); + + if (part.type == "rotational") { + var range = part.maxValue - part.minValue; + mapping.from([part.input]).peek().to(function(controller, overlayID, part) { + return function(value) { + //print(value); + //print(JSON.stringify(part)); + + value = clamp(value, part.minValue, part.maxValue); + + var pct = (value - part.minValue) / part.maxValue; + var angle = pct * part.maxAngle; + var rotation = Quat.angleAxis(angle, part.axis); + //print(value, pct, angle); + + var offset = { x: 0, y: 0, z: 0 }; + if (part.origin) { + //print(rotation.x, rotation.y, rotation.z, rotation.w); + var offset = Vec3.multiplyQbyV(rotation, part.origin); + offset = Vec3.subtract(offset, part.origin); + //Vec3.print('offset', offset); + //partPosition = Vec3.sum(partPosition, offset); + } + + var partPosition = Vec3.sum(controller.position, + Vec3.multiplyQbyV(controller.rotation, Vec3.sum(offset, part.naturalPosition))); + + Overlays.editOverlay(overlayID, { + localPosition: partPosition, + localRotation: Quat.multiply(controller.rotation, rotation) + }); + } + }(controller, overlayID, part)); + } else if (part.type == "touchpad") { + function resolveHardware(path) { + var parts = path.split("."); + function resolveInner(base, path, i) { + //print(path[i]); + if (i >= path.length) { + return base; + } + return resolveInner(base[path[i]], path, ++i); + } + return resolveInner(Controller.Hardware, parts, 0); + } + + var visibleInput = resolveHardware(part.visibleInput); + var xinput = resolveHardware(part.xInput); + var yinput = resolveHardware(part.yInput); + + mapping.from([visibleInput]).peek().to(function(value) { + //print("visible", value); + }); + mapping.from([xinput]).peek().to(function(value) { + //print("X", value); + }); + mapping.from([yinput]).peek().invert().to(function(value) { + //print("Y", value); + }); + if (part.defaultTextureURL) { + var textures = {}; + textures[part.textureName] = part.defaultTextureURL; + Overlays.editOverlay(overlayID, { + textures: textures + }); + } + } else if (part.type == "static") { + } else { + print("TYPE NOT SUPPORTED: ", part.type); + } + + controllerDisplay.overlays.push(overlayID); + if (!(partName in controllerDisplay.partOverlays)) { + controllerDisplay.partOverlays[partName] = []; + } + controllerDisplay.partOverlays[partName].push(overlayID); + } + } + } + Controller.enableMapping(controllerDisplay.mappingName); + return controllerDisplay; +} + +ControllerDisplay = function() { +}; + +deleteControllerDisplay = function(controllerDisplay) { + for (var i = 0; i < controllerDisplay.overlays.length; ++i) { + Overlays.deleteOverlay(controllerDisplay.overlays[i]); + } + Controller.disableMapping(controllerDisplay.mappingName); +} + diff --git a/tutorial/touchControllerConfiguration.js b/tutorial/touchControllerConfiguration.js new file mode 100644 index 0000000000..644215ce60 --- /dev/null +++ b/tutorial/touchControllerConfiguration.js @@ -0,0 +1,142 @@ +var CONTROLLER_LENGTH_OFFSET = 0.0762; +var leftBasePosition = { + x: CONTROLLER_LENGTH_OFFSET / 2, + y: CONTROLLER_LENGTH_OFFSET * 2, + z: CONTROLLER_LENGTH_OFFSET / 2 +}; +var rightBasePosition = { + x: -CONTROLLER_LENGTH_OFFSET / 2, + y: CONTROLLER_LENGTH_OFFSET * 2, + z: CONTROLLER_LENGTH_OFFSET / 2 +}; + + +var touchLeftBaseRotation = Quat.multiply( + Quat.fromPitchYawRollDegrees(0, 0, 0), + Quat.multiply( + Quat.fromPitchYawRollDegrees(0, 0, -45), + Quat.multiply( + Quat.fromPitchYawRollDegrees(180, 0, 0), + Quat.fromPitchYawRollDegrees(0, -90, 0) + ) + ) +); + +var touchRightBaseRotation = Quat.multiply( + Quat.fromPitchYawRollDegrees(0, 0, 45), + Quat.multiply( + Quat.fromPitchYawRollDegrees(180, 0, 0), + Quat.fromPitchYawRollDegrees(0, 90, 0) + ) +); + +var TOUCH_CONTROLLER_CONFIGURATION = { + name: "Touch", + controllers: [ + { + modelURL: "C:/Users/Ryan/Assets/controller/oculus_touch_l.fbx", + naturalPosition: { + x: 0.016486000269651413, + y: -0.035518500953912735, + z: -0.018527504056692123 + }, + jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"), + rotation: touchLeftBaseRotation, + position: leftBasePosition, + + annotationTextRotation: Quat.fromPitchYawRollDegrees(20, -90, 0), + annotations: { + + buttonX: { + position: { + x: -0.00931, + y: 0.00212, + z: -0.01259, + }, + direction: "left", + color: { red: 100, green: 100, blue: 100 }, + }, + buttonY: { + position: { + x: -0.01617, + y: 0.00216, + z: 0.00177, + }, + direction: "left", + color: { red: 100, green: 255, blue: 100 }, + }, + bumper: { + position: { + x: 0.00678, + y: -0.02740, + z: -0.02537, + }, + direction: "left", + color: { red: 100, green: 100, blue: 255 }, + }, + trigger: { + position: { + x: -0.01275, + y: -0.01992, + z: 0.02314, + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + } + }, + }, + { + modelURL: "C:/Users/Ryan/Assets/controller/oculus_touch_r.fbx", + naturalPosition: { + x: -0.016486000269651413, + y: -0.035518500953912735, + z: -0.018527504056692123 + }, + jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), + rotation: touchRightBaseRotation, + position: rightBasePosition, + + annotationTextRotation: Quat.fromPitchYawRollDegrees(20, 90, 0), + annotations: { + + buttonA: { + position: { + x: 0.00931, + y: 0.00212, + z: -0.01259, + }, + direction: "right", + color: { red: 100, green: 100, blue: 100 }, + }, + buttonB: { + position: { + x: 0.01617, + y: 0.00216, + z: 0.00177, + }, + direction: "right", + color: { red: 100, green: 255, blue: 100 }, + }, + bumper: { + position: { + x: 0.00678, + y: -0.02740, + z: -0.02537, + }, + direction: "right", + color: { red: 100, green: 100, blue: 255 }, + }, + trigger: { + position: { + x: 0.01275, + y: -0.01992, + z: 0.02314, + }, + direction: "right", + color: { red: 255, green: 100, blue: 100 }, + } + }, + } + ] +} + diff --git a/tutorial/viveControllerConfiguration.js b/tutorial/viveControllerConfiguration.js new file mode 100644 index 0000000000..d2f9403d8a --- /dev/null +++ b/tutorial/viveControllerConfiguration.js @@ -0,0 +1,361 @@ +var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive2.fbx"; + +var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); +var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); + +var leftBaseRotation = Quat.multiply( + Quat.fromPitchYawRollDegrees(0, 0, 45), + Quat.multiply( + Quat.fromPitchYawRollDegrees(90, 0, 0), + Quat.fromPitchYawRollDegrees(0, 0, 90) + ) +); + +var rightBaseRotation = Quat.multiply( + Quat.fromPitchYawRollDegrees(0, 0, -45), + Quat.multiply( + Quat.fromPitchYawRollDegrees(90, 0, 0), + Quat.fromPitchYawRollDegrees(0, 0, -90) + ) +); +var CONTROLLER_LENGTH_OFFSET = 0.0762; +var leftBasePosition = { + x: CONTROLLER_LENGTH_OFFSET / 2, + y: CONTROLLER_LENGTH_OFFSET * 2, + z: CONTROLLER_LENGTH_OFFSET / 2 +}; +var rightBasePosition = { + x: -CONTROLLER_LENGTH_OFFSET / 2, + y: CONTROLLER_LENGTH_OFFSET * 2, + z: CONTROLLER_LENGTH_OFFSET / 2 +}; + +var viveNaturalDimensions = { + x: 0.1174320001155138, + y: 0.08361100335605443, + z: 0.21942697931081057 +}; + +var viveNaturalPosition = { + x: 0, + y: -0.034076502197422087, + z: 0.06380049744620919 +}; + +var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_body.fbx"; + +VIVE_CONTROLLER_CONFIGURATION = { + name: "Vive", + controllers: [ + { + modelURL: viveModelURL, + jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"), + naturalPosition: viveNaturalPosition, + rotation: leftBaseRotation, + position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, 45), leftBasePosition), + + dimensions: viveNaturalDimensions, + + parts: { + // The touchpad type draws a dot indicating the current touch/thumb position + // and swaps in textures based on the thumb position. + touchpad: { + type: "touchpad", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", + visibleInput: "Vive.RSTouch", + xInput: "Vive.LX", + yInput: "Vive.LY", + naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, + minValue: 0.0, + maxValue: 1.0, + minPosition: { x: -0.035, y: 0.004, z: -0.005 }, + maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, + textureName: "Tex.touchpad-blank", + + defaultTextureLayer: "blank", + textureLayers: { + blank: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", + }, + teleport: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg", + }, + arrows: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", + } + } + }, + + trigger: { + type: "rotational", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trigger.fbx", + input: Controller.Standard.LT, + naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, + origin: { x: 0, y: -0.015, z: -0.00 }, + minValue: 0.0, + maxValue: 1.0, + axis: { x: -1, y: 0, z: 0 }, + maxAngle: 20, + }, + + l_grip: { + type: "static", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_l_grip.fbx", + naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + }, + + r_grip: { + type: "static", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_r_grip.fbx", + naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + }, + + sys_button: { + type: "static", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_sys_button.fbx", + naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}, + }, + + button: { + type: "static", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + }, + button2: { + type: "static", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + }, + }, + annotationTextRotation: Quat.fromPitchYawRollDegrees(45, -90, 0), + annotations: { + + left: { + textOffset: { x: -0.035, y: 0.004, z: -0.005 }, + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + right: { + textOffset: { x: 0.023, y: 0.004, z: -0.005 }, + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + + + trigger: { + position: { + x: 0.0055, + y: -0.032978, + z: 0.04546 + }, + lineHeight: 0.013, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + menu: { + position: { + x: 0, + y: 0.00770, + z: 0.01979 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + grip: { + position: { + x: 0.01980, + y: -0.01561, + z: 0.08721 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + steam: { + position: { + x: 0, + y: 0.00303, + z: 0.08838 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + }, + }, + + + + + { + modelURL: viveModelURL, + jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), + + rotation: rightBaseRotation, + position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, -45), rightBasePosition), + + dimensions: viveNaturalDimensions, + + naturalPosition: { + x: 0, + y: -0.034076502197422087, + z: 0.06380049744620919 + }, + + parts: { + + // The touchpad type draws a dot indicating the current touch/thumb position + // and swaps in textures based on the thumb position. + touchpad: { + type: "touchpad", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", + visibleInput: "Vive.RSTouch", + xInput: "Vive.RX", + yInput: "Vive.RY", + naturalPosition: { x: 0, y: 0.000979491975158453, z: 0.04872849956154823 }, + minValue: 0.0, + maxValue: 1.0, + minPosition: { x: -0.035, y: 0.004, z: -0.005 }, + maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, + textureName: "Tex.touchpad-blank", + + defaultTextureLayer: "blank", + textureLayers: { + blank: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", + }, + teleport: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg", + }, + arrows: { + defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows-active.jpg", + } + } + }, + + trigger: { + type: "rotational", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trigger.fbx", + input: Controller.Standard.RT, + naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, + origin: { x: 0, y: -0.015, z: -0.00 }, + minValue: 0.0, + maxValue: 1.0, + axis: { x: -1, y: 0, z: 0 }, + maxAngle: 25, + }, + + l_grip: { + type: "static", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_l_grip.fbx", + naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + }, + + r_grip: { + type: "static", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_r_grip.fbx", + naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, + }, + + sys_button: { + type: "static", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_sys_button.fbx", + naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}, + }, + + button: { + type: "static", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + }, + button2: { + type: "static", + modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} + }, + }, + + annotationTextRotation: Quat.fromPitchYawRollDegrees(180 + 45, 90, 180), + annotations: { + + left: { + textOffset: { x: -0.035, y: 0.004, z: -0.005 }, + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + right: { + textOffset: { x: 0.023, y: 0.004, z: -0.005 }, + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + + trigger: { + position: { + x: -0.075, + y: -0.032978, + z: 0.04546 + }, + lineHeight: 0.013, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + menu: { + position: { + x: 0, + y: 0.00770, + z: 0.01979 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + grip: { + position: { + x: 0.01980, + y: -0.01561, + z: 0.08721 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + teleport: { + textOffset: { x: -0.015, y: 0.004, z: -0.005 }, + position: { + x: 0, + y: 0.00378, + z: 0.04920 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + steam: { + position: { + x: 0, + y: 0.00303, + z: 0.08838 + }, + direction: "left", + color: { red: 255, green: 100, blue: 100 }, + }, + } + } + ] +} + diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 8f13775783..17cf0185fb 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -1,918 +1,107 @@ -var PARENT_ID = MyAvatar.sessionUUID; -var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); -var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); -//var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("LeftHand"); -//var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("RightHand"); +Script.include("controllerDisplay.js"); +Script.include("viveControllerConfiguration.js"); + +function debug() { + var args = Array.prototype.slice.call(arguments); + args.unshift("CONTROLLER DEBUG:"); + print.apply(this, args); +} var zeroPosition = { x: 0, y: 0, z: 0 }; var zeroRotation = { x: 0, y: 0, z: 0, w: 1 }; -var CONTROLLER_LENGTH_OFFSET = 0.0762; +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Management of controller display // +/////////////////////////////////////////////////////////////////////////////// -var naturalPosition = { - x: 0, - y: -0.034076502197422087, - z: 0.06380049744620919 -}; -var naturalPositionL = { - x: 0, - y: 0.034076502197422087, - z: 0.06380049744620919 -}; -var naturalPositionR = { - x: 0.0, - y: 0.034076502197422087, - z: 0.06380049744620919 -}; +var controllerDisplay = null; +var activeController = null; +var controllerCheckerIntervalID = null; -var leftBasePosition = { - x: CONTROLLER_LENGTH_OFFSET / 2, - y: CONTROLLER_LENGTH_OFFSET * 2, - z: CONTROLLER_LENGTH_OFFSET / 2 -}; -var rightBasePosition = { - x: -CONTROLLER_LENGTH_OFFSET / 2, - y: CONTROLLER_LENGTH_OFFSET * 2, - z: CONTROLLER_LENGTH_OFFSET / 2 -}; - -var leftBasePositionVive = Vec3.sum(leftBasePosition, { x: 0.005, y: 0.03, z: 0 }); -var rightBasePositionVive = Vec3.sum(rightBasePosition, { x: -0.005, y: 0.03, z: 0 }); - -Vec3.print("left offset: ", leftBasePosition); - -var leftBaseRotation = Quat.multiply( - Quat.fromPitchYawRollDegrees(0, 0, 45), - Quat.multiply( - Quat.fromPitchYawRollDegrees(90, 0, 0), - Quat.fromPitchYawRollDegrees(0, 0, 90) - ) -); - -var rightBaseRotation = Quat.multiply( - Quat.fromPitchYawRollDegrees(0, 0, -45), - Quat.multiply( - Quat.fromPitchYawRollDegrees(90, 0, 0), - Quat.fromPitchYawRollDegrees(0, 0, -90) - ) -); - - -var touchLeftBaseRotation = Quat.multiply( - Quat.fromPitchYawRollDegrees(0, 0, 0), - Quat.multiply( - Quat.fromPitchYawRollDegrees(0, 0, -45), - Quat.multiply( - Quat.fromPitchYawRollDegrees(180, 0, 0), - Quat.fromPitchYawRollDegrees(0, -90, 0) - ) - ) -); - -var touchRightBaseRotation = Quat.multiply( - Quat.fromPitchYawRollDegrees(0, 0, 45), - Quat.multiply( - Quat.fromPitchYawRollDegrees(180, 0, 0), - Quat.fromPitchYawRollDegrees(0, 90, 0) - ) -); - -var TOUCH_CONTROLLER_CONFIGURATION = { - name: "Touch", - controllers: [ - { - modelURL: "C:/Users/Ryan/Assets/controller/oculus_touch_l.fbx", - naturalPosition: { - x: 0.016486000269651413, - y: -0.035518500953912735, - z: -0.018527504056692123 - }, - jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"), - rotation: touchLeftBaseRotation, - position: leftBasePosition, - - annotationTextRotation: Quat.fromPitchYawRollDegrees(20, -90, 0), - annotations: { - - buttonX: { - position: { - x: -0.00931, - y: 0.00212, - z: -0.01259, - }, - direction: "left", - color: { red: 100, green: 100, blue: 100 }, - }, - buttonY: { - position: { - x: -0.01617, - y: 0.00216, - z: 0.00177, - }, - direction: "left", - color: { red: 100, green: 255, blue: 100 }, - }, - bumper: { - position: { - x: 0.00678, - y: -0.02740, - z: -0.02537, - }, - direction: "left", - color: { red: 100, green: 100, blue: 255 }, - }, - trigger: { - position: { - x: -0.01275, - y: -0.01992, - z: 0.02314, - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - } - }, - }, - { - modelURL: "C:/Users/Ryan/Assets/controller/oculus_touch_r.fbx", - naturalPosition: { - x: -0.016486000269651413, - y: -0.035518500953912735, - z: -0.018527504056692123 - }, - jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), - rotation: touchRightBaseRotation, - position: rightBasePosition, - - annotationTextRotation: Quat.fromPitchYawRollDegrees(20, 90, 0), - annotations: { - - buttonA: { - position: { - x: 0.00931, - y: 0.00212, - z: -0.01259, - }, - direction: "right", - color: { red: 100, green: 100, blue: 100 }, - }, - buttonB: { - position: { - x: 0.01617, - y: 0.00216, - z: 0.00177, - }, - direction: "right", - color: { red: 100, green: 255, blue: 100 }, - }, - bumper: { - position: { - x: 0.00678, - y: -0.02740, - z: -0.02537, - }, - direction: "right", - color: { red: 100, green: 100, blue: 255 }, - }, - trigger: { - position: { - x: 0.01275, - y: -0.01992, - z: 0.02314, - }, - direction: "right", - color: { red: 255, green: 100, blue: 100 }, - } - }, - } - ] -} - - -var viveNaturalDimensions = { - x: 0.1174320001155138, - y: 0.08361100335605443, - z: 0.21942697931081057 -}; -var viveNaturalPosition = { - x: 0, - y: -0.034076502197422087, - z: 0.06380049744620919 -}; -var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive2.fbx"; -var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_body.fbx"; - -var VIVE_CONTROLLER_CONFIGURATION = { - name: "Vive", - controllers: [ - { - modelURL: viveModelURL, - jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"), - naturalPosition: viveNaturalPosition, - rotation: leftBaseRotation, - position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, 45), leftBasePosition), - - dimensions: viveNaturalDimensions, - - parts: { - //{ - // type: "linear", - // modelURL: "", - // input: "Controller.Hardware.Vive.RT", - // minValue: 0.0, - // maxValue: 1.0, - // textOffset: { x: -0.035, y: 0.004, z: -0.005 }, - // minPosition: { x: -0.035, y: 0.004, z: -0.005 }, - // maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - //}, - - // The touchpad type draws a dot indicating the current touch/thumb position - // and swaps in textures based on the thumb position. - touchpad: { - type: "touchpad", - //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", - visibleInput: "Vive.RSTouch", - xInput: "Vive.LX", - yInput: "Vive.LY", - naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, - minValue: 0.0, - maxValue: 1.0, - minPosition: { x: -0.035, y: 0.004, z: -0.005 }, - maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - textureName: "Tex.touchpad-blank", - - defaultTextureLayer: "blank", - textureLayers: { - blank: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", - }, - teleport: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg", - }, - arrows: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", - } - } - }, - - trigger: { - type: "rotational", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trigger.fbx", - input: Controller.Standard.LT, - naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, - origin: { x: 0, y: -0.015, z: -0.00 }, - minValue: 0.0, - maxValue: 1.0, - axis: { x: -1, y: 0, z: 0 }, - maxAngle: 25, - }, - - l_grip: { - type: "ignore", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_l_grip.fbx", - naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, - }, - - r_grip: { - type: "ignore", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_r_grip.fbx", - naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, - }, - - sys_button: { - type: "ignore", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_sys_button.fbx", - naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}, - }, - - button: { - type: "ignore", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", - naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} - }, - button2: { - type: "ignore", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", - naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} - }, - }, - annotationTextRotation: Quat.fromPitchYawRollDegrees(45, -90, 0), - annotations: { -// red: { -// debug: true, -// position: { -// x: 0.1, -// y: 0.0, -// z: 0.0 -// }, -// direction: "right", -// color: { red: 255, green: 0, blue: 0 }, -// }, -// green: { -// debug: true, -// position: { -// x: 0.0, -// y: 0.1, -// z: 0.0 -// }, -// direction: "right", -// color: { red: 0, green: 255, blue: 0 }, -// }, -// blue: { -// debug: true, -// position: { -// x: 0.0, -// y: 0.0, -// z: 0.1 -// }, -// direction: "right", -// color: { red: 0, green: 0, blue: 255 }, -// }, -// white: { -// debug: true, -// position: { -// x: 0.0, -// y: 0.0, -// z: 0.0 -// }, -// direction: "right", -// color: { red: 255, green: 255, blue: 255 }, -// }, - - // center: { - // position: zeroPosition, - // direction: "center", - // color: { red: 100, green: 255, blue: 255 }, - // }, - - left: { - textOffset: { x: -0.035, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - right: { - textOffset: { x: 0.023, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - - - trigger: { - position: { - x: 0.0055, - y: -0.032978, - z: 0.04546 - }, - lineHeight: 0.013, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - menu: { - position: { - x: 0, - y: 0.00770, - z: 0.01979 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - grip: { - position: { - x: 0.01980, - y: -0.01561, - z: 0.08721 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - teleport: { - textOffset: { x: -0.015, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - steam: { - position: { - x: 0, - y: 0.00303, - z: 0.08838 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - }, - }, - { - modelURL: viveModelURL, - jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), - - rotation: rightBaseRotation, - position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, -45), rightBasePosition), - - dimensions: viveNaturalDimensions, - - naturalPosition: { - x: 0, - y: -0.034076502197422087, - z: 0.06380049744620919 - }, - - parts: { - //{ - // type: "linear", - // modelURL: "", - // input: "Controller.Hardware.Vive.RT", - // minValue: 0.0, - // maxValue: 1.0, - // textOffset: { x: -0.035, y: 0.004, z: -0.005 }, - // minPosition: { x: -0.035, y: 0.004, z: -0.005 }, - // maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - //}, - - // The touchpad type draws a dot indicating the current touch/thumb position - // and swaps in textures based on the thumb position. - touchpad: { - type: "touchpad", - //modelURL: "file:///C:\\Users\\Ryan\\Assets\\controller\\vive_trackpad.fbx", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", - visibleInput: "Vive.RSTouch", - xInput: "Vive.RX", - yInput: "Vive.RY", - naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823}, - minValue: 0.0, - maxValue: 1.0, - minPosition: { x: -0.035, y: 0.004, z: -0.005 }, - maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - textureName: "Tex.touchpad-blank", - - defaultTextureLayer: "blank", - textureLayers: { - blank: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", - }, - teleport: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg", - }, - arrows: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows-active.jpg", - } - } - }, - - trigger: { - type: "rotational", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trigger.fbx", - input: Controller.Standard.RT, - naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, - origin: { x: 0, y: -0.015, z: -0.00 }, - minValue: 0.0, - maxValue: 1.0, - axis: { x: -1, y: 0, z: 0 }, - maxAngle: 25, - }, - - l_grip: { - type: "ignore", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_l_grip.fbx", - naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, - }, - - r_grip: { - type: "ignore", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_r_grip.fbx", - naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, - }, - - sys_button: { - type: "ignore", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_sys_button.fbx", - naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}, - }, - - button: { - type: "ignore", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", - naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} - }, - button2: { - type: "ignore", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", - naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} - }, - }, - - annotationTextRotation: Quat.fromPitchYawRollDegrees(180 + 45, 90, 180), - annotations: { - - left: { - textOffset: { x: -0.035, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - right: { - textOffset: { x: 0.023, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - - trigger: { - position: { - x: -0.075, - y: -0.032978, - z: 0.04546 - }, - lineHeight: 0.013, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - menu: { - position: { - x: 0, - y: 0.00770, - z: 0.01979 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - grip: { - position: { - x: 0.01980, - y: -0.01561, - z: 0.08721 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - teleport: { - textOffset: { x: -0.015, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - steam: { - position: { - x: 0, - y: 0.00303, - z: 0.08838 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, +function updateControllers() { + if (HMD.active) { + if ("Vive" in Controller.Hardware) { + if (!activeController) { + debug("Found vive!"); + activeController = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION); + } + // We've found the controllers, we no longer need to look for active controllers + if (controllerCheckerIntervalID) { + Script.clearInterval(controllerCheckerIntervalID); + controllerCheckerIntervalID = null; + } + } else { + debug("HMD active, but no controllers found"); + if (activeController) { + deleteControllerDisplay(activeController); + activeController = null; + } + if (controllerCheckerIntervalID == null) { + controllerCheckerIntervalID = Script.setInterval(updateControllers, 1000); } } - ] -} - -var DEBUG = false; -var VISIBLE_BY_DEFAULT = false; - -function setupController(config) { - var controllerDisplay = { - overlays: [], - partOverlays: { - }, - parts: { - }, - annotations: { - }, - mappingName: "mapping-display", - - setPartVisible: function(partName, visible) { - print("Setting part visible", partName, visible); - if (partName in this.partOverlays) { - print("FOUND"); - for (var i = 0; i < this.partOverlays[partName].length; ++i) { - Overlays.editOverlay(this.partOverlays[partName][i], { - visible: visible - }); - } - } - }, - - setLayerForPart: function(partName, layerName) { - print("Setting layer...", partName, layerName); - if (partName in this.parts) { - var part = this.parts[partName]; - //print("FOnd", JSON.stringify(part)); - if (layerName in part.textureLayers) { - //print("got it", layerName); - var layer = part.textureLayers[layerName]; - var textures = {}; - if (layer.defaultTextureURL) { - //print("default texture"); - textures[part.textureName] = layer.defaultTextureURL; - } - for (var i = 0; i < this.partOverlays[partName].length; ++i) { - //print("updating", JSON.stringify(textures)); - Overlays.editOverlay(this.partOverlays[partName][i], { - textures: textures - }); - } - } - } + } else { + debug("HMD inactive"); + // We aren't in HMD mode, we no longer need to look for active controllers + if (controllerCheckerIntervalID) { + debug("Clearing controller checker interval"); + Script.clearInterval(controllerCheckerIntervalID); + controllerCheckerIntervalID = null; } - }; - var mapping = Controller.newMapping(controllerDisplay.mappingName); - for (var i = 0; i < config.controllers.length; ++i) { - var controller = config.controllers[i]; - var position = controller.position; - if (controller.naturalPosition) { - position = Vec3.sum(Vec3.multiplyQbyV( - controller.rotation, controller.naturalPosition), position); - } - var overlayID = Overlays.addOverlay("model", { - url: controller.modelURL, - dimensions: controller.dimensions, - localRotation: controller.rotation, - localPosition: position, - parentID: PARENT_ID, - parentJointIndex: controller.jointIndex, - ignoreRayIntersection: true, - }); - - controllerDisplay.overlays.push(overlayID); - - if (controller.annotations) { - for (var key in controller.annotations) { - var annotation = controller.annotations[key]; - var annotationPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, annotation.position)); - if (DEBUG) { - overlayID = Overlays.addOverlay("sphere", { - localPosition: annotationPosition, - //localPosition: Vec3.sum(controller.position, annotation.position), - //localPosition: Vec3.sum(position, annotation.position), - color: annotation.color || { red: 255, green: 100, blue: 100 }, - dimensions: { - x: 0.01, - y: 0.01, - z: 0.01 - }, - parentID: PARENT_ID, - parentJointIndex: controller.jointIndex, - }); - controllerDisplay.overlays.push(overlayID); - - } - - var ANNOTATION_TEXT_OFFSET = 0.1; - var sign = annotation.direction == "right" ? 1 : -1; - var textOffset = annotation.direction == "right" ? 0.08 : 0.02; - if (annotation.textOffset) { - var pos = Vec3.sum(annotationPosition, Vec3.multiplyQbyV(controller.rotation, annotation.textOffset)); - } else { - var pos = Vec3.sum(annotationPosition, Vec3.multiplyQbyV(controller.rotation, { x: textOffset, y: 0, z: -0.005 })); - } - var textOverlayID = Overlays.addOverlay("text3d", { - visible: VISIBLE_BY_DEFAULT, - text: key, - localPosition: pos, - localRotation: controller.annotationTextRotation, - lineHeight: annotation.lineHeight ? annotation.lineHeight : 0.01, - leftMargin: 0, - rightMargin: 0, - topMargin: 0, - bottomMargin: 0, - backgroundAlpha: 0, - dimensions: { x: 0.003, y: 0.003, z: 0.003 }, - //localPosition: Vec3.sum(controller.position, annotation.position), - //localPosition: Vec3.sum(position, annotation.position), - color: annotation.textColor || { red: 255, green: 255, blue: 255 }, - parentID: PARENT_ID, - parentJointIndex: controller.jointIndex, - }); - - controllerDisplay.overlays.push(textOverlayID); - if (key in controllerDisplay.annotations) { - controllerDisplay.annotations[key].push(textOverlayID); - } else { - controllerDisplay.annotations[key] = [textOverlayID]; - } - - var ANNOTATION_OFFSET = 0.5; - var offset = { x: 0, y: 0, z: annotation.direction == "right" ? -1 * ANNOTATION_OFFSET : ANNOTATION_OFFSET }; - var lineOverlayID = Overlays.addOverlay("line3d", { - visible: false, - localPosition: annotationPosition, - localStart: { x: 0, y: 0, z: 0 }, - localEnd: offset, - //localPosition: Vec3.sum(controller.position, annotation.position), - //localPosition: Vec3.sum(position, annotation.position), - color: annotation.color || { red: 255, green: 100, blue: 100 }, - parentID: PARENT_ID, - parentJointIndex: controller.jointIndex, - }); - controllerDisplay.overlays.push(lineOverlayID); - } - } - - function clamp(value, min, max) { - if (value < min) { - return min; - } else if (value > max) { - return max - } - return value; - } - - if (controller.parts) { - for (var partName in controller.parts) { - var part = controller.parts[partName]; - var partPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition)); - var innerRotation = controller.rotation - - //Vec3.print("controller", controller.position); - //Vec3.print("part", partPosition); - - controllerDisplay.parts[partName] = controller.parts[partName]; - - var overlayID = Overlays.addOverlay("model", { - url: part.modelURL, - localPosition: partPosition, - localRotation: innerRotation, - parentID: PARENT_ID, - parentJointIndex: controller.jointIndex, - ignoreRayIntersection: true, - //visible: false - }); - - if (part.type == "rotational") { - var range = part.maxValue - part.minValue; - mapping.from([part.input]).peek().to(function(controller, overlayID, part) { - return function(value) { - //print(value); - //print(JSON.stringify(part)); - - value = clamp(value, part.minValue, part.maxValue); - - var pct = (value - part.minValue) / part.maxValue; - var angle = pct * part.maxAngle; - var rotation = Quat.angleAxis(angle, part.axis); - //print(value, pct, angle); - - var offset = { x: 0, y: 0, z: 0 }; - if (part.origin) { - //print(rotation.x, rotation.y, rotation.z, rotation.w); - var offset = Vec3.multiplyQbyV(rotation, part.origin); - offset = Vec3.subtract(offset, part.origin); - //Vec3.print('offset', offset); - //partPosition = Vec3.sum(partPosition, offset); - } - - var partPosition = Vec3.sum(controller.position, - Vec3.multiplyQbyV(controller.rotation, Vec3.sum(offset, part.naturalPosition))); - - Overlays.editOverlay(overlayID, { - localPosition: partPosition, - localRotation: Quat.multiply(controller.rotation, rotation) - }); - } - }(controller, overlayID, part)); - } else if (part.type == "touchpad") { - function resolveHardware(path) { - var parts = path.split("."); - function resolveInner(base, path, i) { - //print(path[i]); - if (i >= path.length) { - return base; - } - return resolveInner(base[path[i]], path, ++i); - } - return resolveInner(Controller.Hardware, parts, 0); - } - - var visibleInput = resolveHardware(part.visibleInput); - var xinput = resolveHardware(part.xInput); - var yinput = resolveHardware(part.yInput); - - mapping.from([visibleInput]).peek().to(function(value) { - //print("visible", value); - }); - mapping.from([xinput]).peek().to(function(value) { - //print("X", value); - }); - mapping.from([yinput]).peek().invert().to(function(value) { - //print("Y", value); - }); - if (part.defaultTextureURL) { - var textures = {}; - textures[part.textureName] = part.defaultTextureURL; - Overlays.editOverlay(overlayID, { - textures: textures - }); - } - } else { - print("TYPE NOT SUPPORTED: ", part.type); - } - - controllerDisplay.overlays.push(overlayID); - if (!(partName in controllerDisplay.partOverlays)) { - controllerDisplay.partOverlays[partName] = []; - } - controllerDisplay.partOverlays[partName].push(overlayID); - } + if (activeController) { + debug("Deleting controller"); + deleteControllerDisplay(activeController); + activeController = null; } } - Controller.enableMapping(controllerDisplay.mappingName); - return controllerDisplay; } -ControllerDisplay = function() { -}; - -function deleteControllerDisplay(controllerDisplay) { - for (var i = 0; i < controllerDisplay.overlays.length; ++i) { - Overlays.deleteOverlay(controllerDisplay.overlays[i]); - } - Controller.disableMapping(controllerDisplay.mappingName); -} - -var overlays = [ -]; +HMD.displayModeChanged.connect(updateControllers); +updateControllers(); Messages.subscribe('Controller-Display'); var handleMessages = function(channel, message, sender) { - print("MESSASGE>>>>", channel, message, sender); + if (!activeController) { + return; + } + if (sender === MyAvatar.sessionUUID) { if (channel === 'Controller-Display') { - print('here'); + debug('here'); var data = JSON.parse(message); var name = data.name; var visible = data.visible; //c.setDisplayAnnotation(name, visible); - if (name in c.annotations) { - print("hiding"); - for (var i = 0; i < c.annotations[name].length; ++i) { - print("hiding", i); - Overlays.editOverlay(c.annotations[name][i], { visible: visible }); + if (name in activeController.annotations) { + debug("hiding"); + for (var i = 0; i < activeController.annotations[name].length; ++i) { + debug("hiding", i); + Overlays.editOverlay(activeController.annotations[name][i], { visible: visible }); } } } else if (channel === 'Controller-Display-Parts') { - print('here part'); + debug('here part'); var data = JSON.parse(message); for (var name in data) { var visible = data[name]; - c.setPartVisible(name, visible); + activeController.setPartVisible(name, visible); } } else if (channel === 'Controller-Set-Part-Layer') { var data = JSON.parse(message); for (var name in data) { var layer = data[name]; - c.setLayerForPart(name, layer); + activeController.setLayerForPart(name, layer); } } } } - Messages.messageReceived.connect(handleMessages); -var MAPPING_NAME = "com.highfidelity.handControllerGrab.disable"; -var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from([Controller.Standard.LT]).to(function(value) { - // print(value); - // Overlays.editOverlay(leftTriggerOverlayID, { - // localRotation: Quat.multiply(Quat.fromPitchYawRollDegrees(0, 0, value * -45), leftBaseRotation) - // }); -}); -mapping.from([Controller.Standard.RT]).to(function(value) { - // print(value); - // Overlays.editOverlay(rightTriggerOverlayID, { - // localRotation: Quat.multiply(Quat.fromPitchYawRollDegrees(0, 0, value * 45), rightBaseRotation) - // }); -}); -Controller.enableMapping(MAPPING_NAME); - //var c = setupController(TOUCH_CONTROLLER_CONFIGURATION); -var c = setupController(VIVE_CONTROLLER_CONFIGURATION); +//var c = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION); //c.setPartVisible("touchpad", false); //c.setPartVisible("touchpad_teleport", false); //layers = ["blank", "teleport", 'arrows']; @@ -922,12 +111,10 @@ var c = setupController(VIVE_CONTROLLER_CONFIGURATION); // num = (num + 1) % layers.length; // c.setLayerForPart("touchpad", layers[num]); //}, 2000); - +// Script.scriptEnding.connect(function() { - deleteControllerDisplay(c); - MyAvatar.shouldRenderLocally = true; - for (var i = 0; i < overlays.length; ++i) { - Overlays.deleteOverlay(overlays[i]); + if (activeController) { + deleteControllerDisplay(activeController); } - Controller.disableMapping(MAPPING_NAME); + //MyAvatar.shouldRenderLocally = true; }); From 92670ed0019d93a797838599baed74b9e427dcb4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 13 Sep 2016 16:28:01 -0700 Subject: [PATCH 031/109] Update tutorial to work with fire --- tutorial/entityData.js | 2 +- tutorial/tutorial.js | 109 +++++++++++++++++++++++++++-------------- 2 files changed, 73 insertions(+), 38 deletions(-) diff --git a/tutorial/entityData.js b/tutorial/entityData.js index e0adf4f32d..55f4c65abe 100644 --- a/tutorial/entityData.js +++ b/tutorial/entityData.js @@ -362,7 +362,7 @@ Step1BlockData = { }, "shape": "Cube", "type": "Box", - "userData": "{}", + "userData": JSON.stringify({ hifiHomeKey: { reset: true } }), }; StepGunData = [ diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 873fb1879d..fdc954b9e0 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -264,11 +264,12 @@ stepRaiseAboveHead.prototype = { this.checkIntervalID = null; function checkForHandsAboveHead() { - print("Checking..."); + print("Checking for hands above head..."); if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; playSuccessSound(); + location = "/tutorial"; onFinish(); } } @@ -313,12 +314,16 @@ var stepNearGrab = function(name) { } stepNearGrab.prototype = { start: function(onFinish) { + this.finished = false; + this.onFinish = onFinish; + setControllerVisible("trigger", true); var tag = this.tag; // Spawn content set //spawnWithTag(Step1EntityData, null, tag); showEntitiesWithTag(this.tag, { visible: true }); + showEntitiesWithTag('bothGrab', { visible: true }); var basketColliderID = findEntity({ name: NEAR_BASKET_COLLIDER_NAME }, 10000); var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; @@ -344,30 +349,28 @@ stepNearGrab.prototype = { //Vec3.distance( //} - function onHit() { - onFinish(); - } - - // When block collides with basket start step 2 - function checkCollides() { - var dist = Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position); - print(this.tag, "CHECKING...", dist); - if (dist < 0.15) { - Script.clearInterval(this.checkCollidesTimer); - this.checkCollidesTimer = null; - playSuccessSound(); - Script.setTimeout(onHit.bind(this), 1000); - } - } - this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); + Messages.subscribe("Entity-Exploded"); + Messages.messageReceived.connect(this.onMessage.bind(this)); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, - cleanup: function() { - setControllerVisible("trigger", false); - if (this.checkCollidesTimer) { - Script.clearInterval(this.checkCollidesTimer); + onMessage: function(channel, message, seneder) { + if (this.finished) { + return; } + if (channel == "Entity-Exploded") { + print("TUTORIAL: Got entity-exploded message"); + var data = parseJSON(message); + if (data.entityID == this.boxID) { + this.finished = true; + this.onFinish(); + } + } + }, + cleanup: function() { + print("cleaning up near grab"); + this.finished = true; + setControllerVisible("trigger", false); hideEntitiesWithTag(this.tag, { visible: false}); deleteEntitiesWithTag(this.tempTag); } @@ -387,6 +390,9 @@ var stepFarGrab = function(name) { } stepFarGrab.prototype = { start: function(onFinish) { + this.finished = false; + this.onFinish = onFinish; + setControllerVisible("trigger", true); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ farGrabEnabled: true, @@ -421,27 +427,42 @@ stepFarGrab.prototype = { this.boxID = createBlock.bind(this)(); print("Created", this.boxID); - function onHit() { - onFinish(); - } + Messages.subscribe("Entity-Exploded"); + Messages.messageReceived.connect(this.onMessage.bind(this)); // When block collides with basket start step 2 - var checkCollidesTimer = null; - function checkCollides() { - print("CHECKING..."); - if (Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position) < 0.2) { - Script.clearInterval(checkCollidesTimer); - playSuccessSound(); - Script.setTimeout(onHit.bind(this), 1000); - } - } - checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); + //var checkCollidesTimer = null; + // function checkCollides() { + // print("CHECKING..."); + // if (Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position) < 0.2) { + // Script.clearInterval(checkCollidesTimer); + // playSuccessSound(); + // Script.setTimeout(onHit.bind(this), 1000); + // } + // } + // checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, + onMessage: function(channel, message, seneder) { + if (this.finished) { + return; + } + if (channel == "Entity-Exploded") { + print("TUTORIAL: Got entity-exploded message"); + var data = parseJSON(message); + if (data.entityID == this.boxID) { + this.finished = true; + this.onFinish(); + } + } + }, cleanup: function() { + //Messages.messageReceived.disconnect(this.onMessage.bind(this)); + this.finished = true; setControllerVisible("trigger", false); hideEntitiesWithTag(this.tag, { visible: false}); + hideEntitiesWithTag('bothGrab', { visible: false}); deleteEntitiesWithTag(this.tempTag); } }; @@ -524,7 +545,7 @@ stepEquip.prototype = { // When block collides with basket start step 2 function checkCollides() { - print("CHECKING FOR PING PONG..."); + //print("CHECKING FOR PING PONG..."); var ammoIDs = findEntities({ name: GUN_AMMO_NAME }, 15); for (var i = 0; i < ammoIDs.length; ++i) { if (Vec3.distance(basketPosition, Entities.getEntityProperties(ammoIDs[i], 'position').position) < 0.25) { @@ -590,6 +611,16 @@ stepEquip.prototype = { var stepTurnAround = function(name) { this.tag = name; this.tempTag = name + "-temporary"; + + + //var name = "mapping-name"; + //var mapping = Controller.newMapping(name); + //mapping.from([Controller.Actions.StepYaw]).to(function() { + // print("STEPYAW"); + //}); + //Script.scriptEnding.connect(function() { + // Controller.disableMapping(name); + //}); } stepTurnAround.prototype = { start: function(onFinish) { @@ -730,9 +761,13 @@ function showEntitiesWithTag(tag) { editEntitiesWithTag(tag, function(entityID) { var userData = Entities.getEntityProperties(entityID, "userData").userData; var data = parseJSON(userData); + var collisionless = data.visible === false ? true : false; + if (data.collidable !== undefined) { + collisionless = data.collidable === true ? false : true; + } var newProperties = { visible: data.visible == false ? false : true, - collisionless: data.visible == false ? true : false , + collisionless: collisionless, //collisionless: data.collisionless == true ? true : false, }; Entities.editEntity(entityID, newProperties); @@ -769,7 +804,7 @@ function startTutorial() { new stepTeleport("teleport"), new stepFinish("finish"), ] - location = "/tutorial"; + location = "/tutorial_begin"; startNextStep(); } From d8f7850976926207a1fa48e857c05c6d3032472a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 Sep 2016 09:52:19 -0700 Subject: [PATCH 032/109] Update tutorial to use new fireworks --- tutorial/entityData.js | 594 +++++++++++++++++++---------------------- tutorial/tutorial.js | 15 +- 2 files changed, 284 insertions(+), 325 deletions(-) diff --git a/tutorial/entityData.js b/tutorial/entityData.js index 55f4c65abe..cd2579f018 100644 --- a/tutorial/entityData.js +++ b/tutorial/entityData.js @@ -1,324 +1,280 @@ Step1EntityData = [ - { - "clientOnly": 0, - "color": { - "blue": 255, - "green": 0, - "red": 255 - }, - "created": "2016-08-29T22:57:55Z", - "dimensions": { - "x": 0.018359377980232239, - "y": 0.018359377980232239, - "z": 0.018359377980232239 - }, - "id": "{3bb83d9c-11db-4bc1-a61b-36921370cb40}", - "name": "tutorial/box_spawn", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "collisionless": 1, - "position": { - "x": 0, - "y": 0.8, - "z": 0.7790381908416748 - }, - "queryAACube": { - "scale": 0.031799376010894775, - "x": -0.015899688005447388, - "y": 0.79706859588623047, - "z": 0.7631385326385498 - }, - "rotation": { - "w": 1, - "x": 0, - "y": 0, - "z": 0 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"tag\":\"step2\"}", - "visible": 0 - }, - { - "color": { - "blue": 181, - "green": 181, - "red": 181 - }, - "dimensions": { - "x": 0.37322089076042175, - "y": 0.8015166997909546, - "z": 0.37322089076042175 - }, - "name": "tutorial/pillar2", - //"shapeType": "simple-hull", - "position": { - "x": 0.019208565354347229, - "y": -0.1, - "z": 0.75276124477386475 - }, - "rotation": { - "w": 1, - "x": 0, - "y": 0, - "z": 0 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"tag\":\"step2\"}" - }, - { - "clientOnly": 0, - "compoundShapeURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj", - "created": "2016-08-29T22:57:55Z", - "dimensions": { - "x": 0.57461458444595337, - "y": 0.35781359672546387, - "z": 0.57461458444595337 - }, - "gravity": { - "x": 0, - "y": -5, - "z": 0 - }, - "id": "{2a8a9cb8-4501-4089-8fb8-6b1b5100db10}", - "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx", - "name": "tutorial/basket", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.022034257650375366, - "y": 0.47968916893005371, - "z": 0 - }, - "queryAACube": { - "scale": 0.88791579008102417, - "x": -0.42192363739013672, - "y": 0.23573127388954163, - "z": -0.44395789504051208 - }, - "rotation": { - "w": 1, - "x": -1.52587890625e-05, - "y": -1.52587890625e-05, - "z": -1.52587890625e-05 - }, - "shapeType": "compound", - "type": "Model", - "userData": "{\"hifiHomeKey\":{\"reset\":true},\"tag\":\"step2\"}" - }, - { - "clientOnly": 0, - "collisionless": 1, - "color": { - "blue": 255, - "green": 0, - "red": 255 - }, - "created": "2016-08-29T22:57:55Z", - "dimensions": { - "x": 0.43770244717597961, - "y": 0.33723857998847961, - "z": 0.43770244717597961 - }, - "id": "{436aec80-15e8-4fc3-bd74-f173b731a922}", - "ignoreForCollisions": 1, - "name": "tutorial/basket_collider", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.02785143256187439, - "y": 0.50166182518005371, - "z": 0.0017895996570587158 - }, - "queryAACube": { - "scale": 0.70490902662277222, - "x": -0.32460308074951172, - "y": 0.3492073118686676, - "z": -0.35066491365432739 - }, - "rotation": { - "w": 1, - "x": 0, - "y": 0, - "z": 0 - }, - "type": "Sphere", - "userData": "{\"tag\":\"step2\"}", - "visible": 0 - }, - { - "clientOnly": 0, - "color": { - "blue": 181, - "green": 181, - "red": 181 - }, - "created": "2016-08-29T22:57:55Z", - "dimensions": { - "x": 0.37322089076042175, - "y": 0.8015000104904175, - "z": 0.37322089076042175 - }, - "id": "{221be6c2-e0d6-4a7c-b9d4-a77e6b7d1c9a}", - "name": "tutorial/pillar1", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.019208565354347229, - "y": -0.1, - "z": 0.025902509689331055 - }, - "queryAACube": { - "scale": 1.1320732831954956, - "x": -0.54682809114456177, - "y": -0.5660366415977478, - "z": -0.54013413190841675 - }, - "rotation": { - "w": 1, - "x": 0, - "y": 0, - "z": 0 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"tag\":\"step2\"}" - } - ]; +{ + "clientOnly": 0, + "color": { + "blue": 255, + "green": 0, + "red": 255 + }, + "created": "2016-08-29T22:57:55Z", + "dimensions": { + "x": 0.018359377980232239, + "y": 0.018359377980232239, + "z": 0.018359377980232239 + }, + "id": "{3bb83d9c-11db-4bc1-a61b-36921370cb40}", + "name": "tutorial/box_spawn", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "collisionless": 1, + "position": { + "x": 0, + "y": 0.8, + "z": 0.7790381908416748 + }, + "queryAACube": { + "scale": 0.031799376010894775, + "x": -0.015899688005447388, + "y": 0.79706859588623047, + "z": 0.7631385326385498 + }, + "rotation": { + "w": 1, + "x": 0, + "y": 0, + "z": 0 + }, + "shape": "Cube", + "type": "Box", + "userData": "{\"tag\":\"step2\"}", + "visible": 0 +}, +{ + "color": { + "blue": 181, + "green": 181, + "red": 181 + }, + "dimensions": { + "x": 0.37322089076042175, + "y": 0.8015166997909546, + "z": 0.37322089076042175 + }, + "name": "tutorial/pillar2", + //"shapeType": "simple-hull", + "position": { + "x": 0.019208565354347229, + "y": -0.1, + "z": 0.75276124477386475 + }, + "rotation": { + "w": 1, + "x": 0, + "y": 0, + "z": 0 + }, + "shape": "Cube", + "type": "Box", + "userData": "{\"tag\":\"step2\"}" +}, +{ + "clientOnly": 0, + "compoundShapeURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj", + "created": "2016-08-29T22:57:55Z", + "dimensions": { + "x": 0.57461458444595337, + "y": 0.35781359672546387, + "z": 0.57461458444595337 + }, + "gravity": { + "x": 0, + "y": -5, + "z": 0 + }, + "id": "{2a8a9cb8-4501-4089-8fb8-6b1b5100db10}", + "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx", + "name": "tutorial/basket", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 0.022034257650375366, + "y": 0.47968916893005371, + "z": 0 + }, + "queryAACube": { + "scale": 0.88791579008102417, + "x": -0.42192363739013672, + "y": 0.23573127388954163, + "z": -0.44395789504051208 + }, + "rotation": { + "w": 1, + "x": -1.52587890625e-05, + "y": -1.52587890625e-05, + "z": -1.52587890625e-05 + }, + "shapeType": "compound", + "type": "Model", + "userData": "{\"hifiHomeKey\":{\"reset\":true},\"tag\":\"step2\"}" +}, +{ + "clientOnly": 0, + "collisionless": 1, + "color": { + "blue": 255, + "green": 0, + "red": 255 + }, + "created": "2016-08-29T22:57:55Z", + "dimensions": { + "x": 0.43770244717597961, + "y": 0.33723857998847961, + "z": 0.43770244717597961 + }, + "id": "{436aec80-15e8-4fc3-bd74-f173b731a922}", + "ignoreForCollisions": 1, + "name": "tutorial/basket_collider", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 0.02785143256187439, + "y": 0.50166182518005371, + "z": 0.0017895996570587158 + }, + "queryAACube": { + "scale": 0.70490902662277222, + "x": -0.32460308074951172, + "y": 0.3492073118686676, + "z": -0.35066491365432739 + }, + "rotation": { + "w": 1, + "x": 0, + "y": 0, + "z": 0 + }, + "type": "Sphere", + "userData": "{\"tag\":\"step2\"}", + "visible": 0 +}, +{ + "clientOnly": 0, + "color": { + "blue": 181, + "green": 181, + "red": 181 + }, + "created": "2016-08-29T22:57:55Z", + "dimensions": { + "x": 0.37322089076042175, + "y": 0.8015000104904175, + "z": 0.37322089076042175 + }, + "id": "{221be6c2-e0d6-4a7c-b9d4-a77e6b7d1c9a}", + "name": "tutorial/pillar1", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 0.019208565354347229, + "y": -0.1, + "z": 0.025902509689331055 + }, + "queryAACube": { + "scale": 1.1320732831954956, + "x": -0.54682809114456177, + "y": -0.5660366415977478, + "z": -0.54013413190841675 + }, + "rotation": { + "w": 1, + "x": 0, + "y": 0, + "z": 0 + }, + "shape": "Cube", + "type": "Box", + "userData": "{\"tag\":\"step2\"}" +} +]; -//Step1EntityData = [ -// { -// "clientOnly": 0, -// "color": { -// "blue": 255, -// "green": 0, -// "red": 255 -// }, -// "created": "2016-08-23T16:29:15Z", -// "dimensions": { -// "x": 0.018359377980232239, -// "y": 0.018359377980232239, -// "z": 0.018359377980232239 -// }, -// "id": "{387765d2-366d-4775-8e6e-ea45119cf69d}", -// visible: false, -// "name": "tutorial/box_spawn", -// "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", -// "position": { -// "x": 0.016568422317504883, -// "y": 0.6591796875, -// "z": 1.3308790922164917 -// }, -// "queryAACube": { -// "scale": 0.031799376010894775, -// "x": 0.00066873431205749512, -// "y": 0.643280029296875, -// "z": 1.3149794340133667 -// }, -// "shape": "Cube", -// "type": "Box" -// }, -// { -// "clientOnly": 0, -// "compoundShapeURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj", -// "created": "2016-08-22T21:20:11Z", -// "dimensions": { -// "x": 0.57461458444595337, -// "y": 0.35781359672546387, -// "z": 0.57461458444595337 -// }, -// "gravity": { -// "x": 0, -// "y": -5, -// "z": 0 -// }, -// "id": "{ddcb3906-3d80-4111-9171-3a73a2f4f1bb}", -// "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx", -// "name": "tutorial/basket", -// "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", -// "position": { -// "x": 0, -// "y": 0.6480712890625, -// "z": 0 -// }, -// "queryAACube": { -// "scale": 0.88791579008102417, -// "x": -0.44395789504051208, -// "y": 0.20411339402198792, -// "z": -0.44395789504051208 -// }, -// "rotation": { -// "w": 1, -// "x": -1.52587890625e-05, -// "y": -1.52587890625e-05, -// "z": -1.52587890625e-05 -// }, -// "shapeType": "compound", -// "type": "Model", -// "userData": "{\"hifiHomeKey\":{\"reset\":true}}" -// }, -// { -// "clientOnly": 0, -// "created": "2016-08-22T21:22:22Z", -// "dimensions": { -// "x": 2.4929797649383545, -// "y": 0.94968640804290771, -// "z": 1.0870213508605957 -// }, -// "id": "{e7030a2d-f573-4c4b-a0aa-ea80e9e25399}", -// "name": "tutorial/table", -// "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/table2_re-oriented.fbx", -// "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", -// "position": { -// "x": 0.018214225769042969, -// "y": 0, -// "z": 0.60948663949966431 -// }, -// "queryAACube": { -// "scale": 2.8807060718536377, -// "x": -1.4221388101577759, -// "y": -1.4403530359268188, -// "z": -0.83086639642715454 -// }, -// "rotation": { -// "w": 0.70705735683441162, -// "x": -1.52587890625e-05, -// "y": -0.70717936754226685, -// "z": -1.52587890625e-05 -// }, -// "shapeType": "static-mesh", -// "type": "Model" -// }, -// { -// visible: false, -// "clientOnly": 0, -// "collisionless": 1, -// "color": { -// "blue": 255, -// "green": 0, -// "red": 255 -// }, -// "created": "2016-08-23T18:09:44Z", -// "dimensions": { -// "x": 0.43770244717597961, -// "y": 0.33723857998847961, -// "z": 0.43770244717597961 -// }, -// "id": "{54e1d825-b552-48e4-b9c8-1c83c79a673e}", -// "ignoreForCollisions": 1, -// "name": "tutorial/basket_collider", -// "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", -// "position": { -// "x": 0.0058171749114990234, -// "y": 0.6700439453125, -// "z": 0.0017895996570587158 -// }, -// "queryAACube": { -// "scale": 0.70490902662277222, -// "x": -0.34663733839988708, -// "y": 0.31758943200111389, -// "z": -0.35066491365432739 -// }, -// "type": "Sphere" -// } -//]; +birdFirework1 = { + "clientOnly": 0, + "collisionsWillMove": 1, + "created": "2016-09-13T23:05:08Z", + "dimensions": { + "x": 0.10120716691017151, + "y": 0.12002291530370712, + "z": 0.18833979964256287 + }, + "collisionsWillMove": 1, + velocity: { + x: 0, + y: -0.2, + z: 0 + }, + "dynamic": 1, + "gravity": { + "x": 0, + "y": -10, + "z": 0 + }, + "id": "{1c4061bc-b2e7-4435-bc47-3fcc39ae6624}", + "modelURL": "http://hifi-content.s3.amazonaws.com/jimi/tutorialroom/birdStatue15.fbx", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 0.11612319946289062, + "y": 0, + "z": 0.21749019622802734 + }, + "queryAACube": { + "scale": 0.24519434571266174, + "x": -0.0064739733934402466, + "y": -0.12259717285633087, + "z": 0.094893023371696472 + }, + "rotation": { + "w": -0.083054840564727783, + "x": 0.93615627288818359, + "y": 0.34154272079467773, + "z": -0.0073701143264770508 + }, + "shapeType": "simple-hull", + "type": "Model", + "userData": "{\n \"hifiHomeKey\": {\n \"reset\": true\n }\n}" +} ; + +birdFirework2 = { + "clientOnly": 0, + "collisionsWillMove": 1, + "created": "2016-09-12T22:56:48Z", + "dimensions": { + "x": 0.098819166421890259, + "y": 0.11143554747104645, + "z": 0.18833979964256287 + }, + "collisionsWillMove": 1, + velocity: { + x: 0, + y: -0.2, + z: 0 + }, + "dynamic": 1, + "gravity": { + "x": 0, + "y": -10, + "z": 0 + }, + "id": "{ba067084-8d0f-4eeb-a8a1-c6814527c1bb}", + "modelURL": "http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Models/statuebird4.fbx", + "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", + "position": { + "x": 0, + "y": 0.014694660902023315, + "z": 0 + }, + "queryAACube": { + "scale": 0.24011452496051788, + "x": -0.12005726248025894, + "y": -0.10536260157823563, + "z": -0.12005726248025894 + }, + "rotation": { + "w": 0.55410087108612061, + "x": 0.36000609397888184, + "y": -0.33641564846038818, + "z": -0.67092394828796387 + }, + "shapeType": "simple-compound", + "type": "Model", + "userData": "{\n \"hifiHomeKey\": {\n \"reset\": true\n }\n}" +}; + Step1BlockData = { "clientOnly": 0, diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index fdc954b9e0..f2450ef2d1 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -335,8 +335,9 @@ stepNearGrab.prototype = { } var boxSpawnPosition = Entities.getEntityProperties(boxSpawnID, 'position').position; function createBlock() { - Step1BlockData.position = boxSpawnPosition; - return spawnWithTag([Step1BlockData], null, this.tempTag)[0]; + //Step1BlockData.position = boxSpawnPosition; + birdFirework1.position = boxSpawnPosition; + return spawnWithTag([birdFirework1], null, this.tempTag)[0]; } // Enabled grab @@ -361,10 +362,10 @@ stepNearGrab.prototype = { if (channel == "Entity-Exploded") { print("TUTORIAL: Got entity-exploded message"); var data = parseJSON(message); - if (data.entityID == this.boxID) { + //if (data.entityID == this.boxID) { this.finished = true; this.onFinish(); - } + //} } }, cleanup: function() { @@ -393,6 +394,8 @@ stepFarGrab.prototype = { this.finished = false; this.onFinish = onFinish; + showEntitiesWithTag('bothGrab', { visible: true }); + setControllerVisible("trigger", true); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ farGrabEnabled: true, @@ -417,8 +420,8 @@ stepFarGrab.prototype = { return null; } - Step1BlockData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; - return spawnWithTag([Step1BlockData], null, this.tempTag)[0]; + birdFirework1.position = Entities.getEntityProperties(boxSpawnID, 'position').position; + return spawnWithTag([birdFirework1], null, this.tempTag)[0]; } // Enabled grab From 4adc83da84d92d1dd792959a80a1d64a79eed726 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 Sep 2016 10:15:09 -0700 Subject: [PATCH 033/109] Temporarily disable begin area for tutorial --- tutorial/tutorial.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index f2450ef2d1..5153070f44 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -25,8 +25,11 @@ if (!Function.prototype.bind) { return fBound; }; } + Script.include("entityData.js"); +Script.include("viveHandsv2.js"); + var BASKET_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx"; var BASKET_COLLIDER_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj"; //var successSound = SoundCache.getSound(Script.resolvePath("success48.wav")); @@ -269,7 +272,7 @@ stepRaiseAboveHead.prototype = { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; playSuccessSound(); - location = "/tutorial"; + //location = "/tutorial"; onFinish(); } } @@ -807,7 +810,8 @@ function startTutorial() { new stepTeleport("teleport"), new stepFinish("finish"), ] - location = "/tutorial_begin"; + //location = "/tutorial_begin"; + location = "/tutorial"; startNextStep(); } From 8d2f558ac393ecfc22ee64802f8d63476b2649c2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 Sep 2016 10:17:49 -0700 Subject: [PATCH 034/109] Add proper cleanup on start of tutorial --- tutorial/tutorial.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 5153070f44..ea2684fc89 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -226,7 +226,10 @@ stepWelcome.prototype = { showEntitiesWithTag(this.tag); }, cleanup: function() { - Script.clearTimeout(this.timerID); + if (this.timerID) { + Script.clearTimeout(this.timerID); + this.timerID = null; + } hideEntitiesWithTag(this.tag); } }; @@ -809,7 +812,10 @@ function startTutorial() { new stepTurnAround("turnAround"), new stepTeleport("teleport"), new stepFinish("finish"), - ] + ]; + for (var i = 0; i < STEPS.length; ++i) { + STEPS[i].cleanup(); + } //location = "/tutorial_begin"; location = "/tutorial"; startNextStep(); From 80b53a30d7e5fe8f3810f0312c428ed35f0dfc12 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 Sep 2016 11:13:08 -0700 Subject: [PATCH 035/109] Fix success sound for tutorial --- tutorial/tutorial.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index ea2684fc89..7724cb2edf 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -367,6 +367,7 @@ stepNearGrab.prototype = { } if (channel == "Entity-Exploded") { print("TUTORIAL: Got entity-exploded message"); + playSuccessSound(); var data = parseJSON(message); //if (data.entityID == this.boxID) { this.finished = true; @@ -459,6 +460,7 @@ stepFarGrab.prototype = { } if (channel == "Entity-Exploded") { print("TUTORIAL: Got entity-exploded message"); + playSuccessSound(); var data = parseJSON(message); if (data.entityID == this.boxID) { this.finished = true; @@ -818,6 +820,7 @@ function startTutorial() { } //location = "/tutorial_begin"; location = "/tutorial"; + MyAvatar.shouldRenderLocally = false; startNextStep(); } From 09652e24ef9188013d630ab4cabdf48a9deae975 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 Sep 2016 16:30:15 -0700 Subject: [PATCH 036/109] Add support for spinner to tutorial --- tutorial/tutorial.js | 86 ++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 7724cb2edf..ed73c92c5f 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -491,6 +491,9 @@ var stepEquip = function(name) { this.tagPart1 = name + "-part1"; this.tagPart2 = name + "-part2"; this.tempTag = name + "-temporary"; + this.PART1 = 0; + this.PART2 = 1; + this.COMPLETE = 2; } stepEquip.prototype = { start: function(onFinish) { @@ -520,7 +523,7 @@ stepEquip.prototype = { showEntitiesWithTag(this.tag); showEntitiesWithTag(this.tagPart1); - this.hasFinished = false; + this.currentPart = this.PART1; var basketColliderID = findEntity({ name: GUN_BASKET_COLLIDER_NAME }, 10000); var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; @@ -545,56 +548,63 @@ stepEquip.prototype = { this.gunID = createGun.bind(this)(); print("Created", this.gunID); this.onFinish = onFinish; + Messages.subscribe('Tutorial-Spinner'); + Messages.messageReceived.connect(this.onMessage.bind(this)); - function onHit() { - hideEntitiesWithTag(this.tagPart1); - showEntitiesWithTag(this.tagPart2); - print("HIT, wiating for unequip..."); - Messages.subscribe('Hifi-Object-Manipulation'); - Messages.messageReceived.connect(this.onMessage.bind(this)); - } +// function onHit() { +// } +// +// // When block collides with basket start step 2 +// function checkCollides() { +// //print("CHECKING FOR PING PONG..."); +// var ammoIDs = findEntities({ name: GUN_AMMO_NAME }, 15); +// for (var i = 0; i < ammoIDs.length; ++i) { +// if (Vec3.distance(basketPosition, Entities.getEntityProperties(ammoIDs[i], 'position').position) < 0.25) { +// Script.clearInterval(this.checkCollidesTimer); +// this.checkCollidesTimer = null; +// playSuccessSound(); +// Script.setTimeout(onHit.bind(this), 1000); +// return; +// } +// } +// } +// this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 100); - // When block collides with basket start step 2 - function checkCollides() { - //print("CHECKING FOR PING PONG..."); - var ammoIDs = findEntities({ name: GUN_AMMO_NAME }, 15); - for (var i = 0; i < ammoIDs.length; ++i) { - if (Vec3.distance(basketPosition, Entities.getEntityProperties(ammoIDs[i], 'position').position) < 0.25) { - Script.clearInterval(this.checkCollidesTimer); - this.checkCollidesTimer = null; - playSuccessSound(); - Script.setTimeout(onHit.bind(this), 1000); - return; - } - } - } - this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 100); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, onMessage: function(channel, message, sender) { - if (this.hasFinished) { + if (this.currentPart == this.COMPLETE) { return; } print("Got message", channel, message, sender, MyAvatar.sessionUUID); - //if (sender === MyAvatar.sessionUUID) { - var data = parseJSON(message); - print("Here", data.action, data.grabbedEntity, this.gunID); - if (data.action == 'release' && data.grabbedEntity == this.gunID) { - try { - Messages.messageReceived.disconnect(this.onMessage); - } catch(e) { - } - playSuccessSound(); - print("FINISHED"); - Script.setTimeout(this.onFinish.bind(this), 1500); - this.hasFinished = true; - //this.onFinish(); + if (channel == "Tutorial-Spinner") { + if (this.currentPart == this.PART1 && message == "wasLit") { + hideEntitiesWithTag(this.tagPart1); + showEntitiesWithTag(this.tagPart2); + Messages.subscribe('Hifi-Object-Manipulation'); } - //} + } else if (channel == "Hifi-Object-Manipulation") { + if (this.currentPart == this.PART2) { + var data = parseJSON(message); + print("Here", data.action, data.grabbedEntity, this.gunID); + if (data.action == 'release' && data.grabbedEntity == this.gunID) { + try { + Messages.messageReceived.disconnect(this.onMessage); + } catch(e) { + } + playSuccessSound(); + print("FINISHED"); + Script.setTimeout(this.onFinish.bind(this), 1500); + this.currentPart = this.COMPLETE; + //this.onFinish(); + } + } + } }, cleanup: function() { setControllerVisible("trigger", false); + this.currentPart = this.COMPLETE; try { Messages.messageReceived.disconnect(this.onMessage); } catch(e) { From 8663742dd09e075b86ac7238533eb2def3678bf5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Sep 2016 12:58:31 -0700 Subject: [PATCH 037/109] Add initial orientation step to tutorial --- tutorial/tutorial.js | 108 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index ed73c92c5f..56a7a76e30 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -234,6 +234,109 @@ stepWelcome.prototype = { } }; +function StayInFrontOverlay(type, properties, distance, positionOffset) { + this.currentOrientation = MyAvatar.orientation; + this.currentPosition = MyAvatar.position; + this.distance = distance; + this.positionOffset = positionOffset; + + var forward = Vec3.multiply(this.distance, Quat.getFront(this.currentOrientation)); + + properties.rotation = this.currentOrientation; + properties.position = Vec3.sum(Vec3.sum(this.currentPosition, forward), this.positionOffset); + this.overlayID = Overlays.addOverlay(type, properties); + + + this.distance = distance; + + this.boundUpdate = this.update.bind(this); + Script.update.connect(this.boundUpdate); +} +StayInFrontOverlay.prototype = { + update: function(dt) { + print("Updating..."); + var targetOrientation = MyAvatar.orientation; + var targetPosition = MyAvatar.position; + this.currentOrientation = Quat.slerp(this.currentOrientation, targetOrientation, 0.05); + this.currentPosition = Vec3.mix(this.currentPosition, targetPosition, 0.05); + + var forward = Vec3.multiply(this.distance, Quat.getFront(this.currentOrientation)); + Overlays.editOverlay(this.overlayID, { + position: Vec3.sum(Vec3.sum(this.currentPosition, forward), this.positionOffset), + rotation: this.currentOrientation, + }); + }, + destroy: function() { + Overlays.deleteOverlay(this.overlayID); + Script.update.disconnect(this.boundUpdate); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: Orient and raise hands above head // +// // +/////////////////////////////////////////////////////////////////////////////// +var stepOrient = function(name) { + this.tag = name; + this.tempTag = name + "-temporary"; +} +stepOrient.prototype = { + start: function(onFinish) { + var tag = this.tag; + + var defaultTransform = { + position: { + x: 0.2459, + y: 0.9011, + z: 0.7266 + }, + rotation: { + x: 0, + y: 0, + z: 0, + w: 1 + } + }; + + this.overlay = new StayInFrontOverlay("model", { + url: "http://hifi-content.s3.amazonaws.com/alan/dev/Prompt-Cards/raiseHands.fbx?11", + ignoreRayIntersection: true, + }, 2, { x: 0, y: 0.3, z: 0 }); + + // Spawn content set + //spawnWithTag(HandsAboveHeadData, defaultTransform, tag); + print("raise hands...", this.tag); + editEntitiesWithTag(this.tag, { visible: true }); + + + this.checkIntervalID = null; + function checkForHandsAboveHead() { + print("Checking for hands above head..."); + if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { + Script.clearInterval(this.checkIntervalID); + this.checkIntervalID = null; + playSuccessSound(); + location = "/tutorial"; + onFinish(); + } + } + this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); + }, + cleanup: function() { + if (this.overlay) { + this.overlay.destroy(); + this.overlay = null; + } + if (this.checkIntervalID != null) { + Script.clearInterval(this.checkIntervalID); + } + editEntitiesWithTag(this.tag, { visible: false, collisionless: 1 }); + deleteEntitiesWithTag(this.tempTag); + } +}; + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // @@ -816,6 +919,7 @@ function startTutorial() { currentStep = null; STEPS = [ new stepDisableControllers("step0"), + new stepOrient("orient"), new stepWelcome("welcome"), new stepRaiseAboveHead("raiseHands"), new stepNearGrab("nearGrab"), @@ -828,8 +932,8 @@ function startTutorial() { for (var i = 0; i < STEPS.length; ++i) { STEPS[i].cleanup(); } - //location = "/tutorial_begin"; - location = "/tutorial"; + location = "/tutorial_begin"; + //location = "/tutorial"; MyAvatar.shouldRenderLocally = false; startNextStep(); } From 7f91491f04e728513410dddfa7563c95e79ece73 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Sep 2016 13:11:50 -0700 Subject: [PATCH 038/109] Add entity scripts for tutorial --- tutorial/firePit/fire.js | 169 ++++++++++++++++++++++++++++++++++++ tutorial/firePit/flicker.js | 51 +++++++++++ tutorial/fuse.js | 74 ++++++++++++++++ tutorial/spinner.js | 66 ++++++++++++++ 4 files changed, 360 insertions(+) create mode 100644 tutorial/firePit/fire.js create mode 100644 tutorial/firePit/flicker.js create mode 100644 tutorial/fuse.js create mode 100644 tutorial/spinner.js diff --git a/tutorial/firePit/fire.js b/tutorial/firePit/fire.js new file mode 100644 index 0000000000..0747bc9f14 --- /dev/null +++ b/tutorial/firePit/fire.js @@ -0,0 +1,169 @@ +// this script turns an entity into an exploder -- anything that collides with it will be vaporized! +// +// + +(function() { + + var _this = this; + + function Fire() { + _this = this; + } + + var RED = { + red: 255, + green: 0, + blue: 0 + }; + + var ORANGE = { + red: 255, + green: 165, + blue: 0 + }; + + var YELLOW = { + red: 255, + green: 255, + blue: 0 + }; + + var GREEN = { + red: 0, + green: 255, + blue: 0 + }; + + var BLUE = { + red: 0, + green: 0, + blue: 255 + }; + + var INDIGO = { + red: 128, + green: 0, + blue: 128 + }; + + var VIOLET = { + red: 75, + green: 0, + blue: 130 + }; + + var colors = [RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET]; + + Fire.prototype = { + preload: function(entityID) { + this.entityID = entityID; + this.EXPLOSION_SOUND = SoundCache.getSound("atp:/firepit/fire_burst.wav"); + print("IN FIRE SCRIPT"); + }, + collisionWithEntity: function(myID, otherID, collisionInfo) { + print("FIRE SCRIPT: COLLIDED"); + var otherProps = Entities.getEntityProperties(otherID); + var data = null; + print("FIRE SCRIPT: 2 COLLIDED"); + try { + print("parsing.."); + data = JSON.parse(otherProps.userData) + print("done parsing.."); + } catch (err) { + print('ERROR GETTING USERDATA!'); + } + print("HERE"); + if (data === null || "") { + return; + } else { + if (data.hasOwnProperty('hifiHomeKey')) { + if (data.hifiHomeKey.reset === true) { + print('FLAMMABLE THING, EXPLODE IT!'); + _this.playSoundAtCurrentPosition(); + _this.explodeWithColor(); + Entities.deleteEntity(otherID) + Messages.sendMessage('Entity-Exploded', JSON.stringify({ + entityID: otherID, + })); + } + } + } + }, + explodeWithColor: function() { + print('EXPLODE!') + var myProps = Entities.getEntityProperties(this.entityID); + var color = colors[Math.floor(Math.random() * colors.length)]; + var explosionParticleProperties = { + "color": color, + "isEmitting": 1, + "maxParticles": 1000, + "lifespan": 0.25, + "emitRate": 1, + "emitSpeed": 0.1, + "speedSpread": 1, + "emitOrientation": Quat.getUp(myProps.rotation), + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "polarStart": 0, + "polarFinish": 0, + "azimuthStart": 0, + "azimuthFinish": 0, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": 0, + "y": 0, + "z": 0 + }, + "particleRadius": 0.829, + "radiusSpread": 0, + "radiusStart": 0.361, + "radiusFinish": 0.294, + "colorSpread": { + "red": 0, + "green": 0, + "blue": 0 + }, + "colorStart": { + "red": 255, + "green": 255, + "blue": 255 + }, + "colorFinish": { + "red": 255, + "green": 255, + "blue": 255 + }, + "alpha": 1, + "alphaSpread": 0, + "alphaStart": -0.2, + "alphaFinish": 0.5, + "emitterShouldTrail": 0, + "textures": "atp:/firepit/explode.png", + "type": "ParticleEffect", + lifetime: 1, + position: myProps.position + }; + + var explosion = Entities.addEntity(explosionParticleProperties); + print('explosion is: ' + explosion) + }, + playSoundAtCurrentPosition: function() { + + var audioProperties = { + volume: 0.5, + position: Entities.getEntityProperties(this.entityID).position + }; + + Audio.playSound(this.EXPLOSION_SOUND, audioProperties); + }, + } + + return new Fire(); +}); diff --git a/tutorial/firePit/flicker.js b/tutorial/firePit/flicker.js new file mode 100644 index 0000000000..43148dabba --- /dev/null +++ b/tutorial/firePit/flicker.js @@ -0,0 +1,51 @@ +(function() { + + var MINIMUM_LIGHT_INTENSITY = 50.0; + var MAXIMUM_LIGHT_INTENSITY = 200.0; + var LIGHT_FALLOFF_RADIUS = 0.1; + var LIGHT_INTENSITY_RANDOMNESS = 0.1; + + function randFloat(low, high) { + return low + Math.random() * (high - low); + } + + var _this; + + function FlickeringFlame() { + _this = this; + } + + var totalTime = 0; + var spacer = 2; + FlickeringFlame.prototype = { + preload: function(entityID) { + this.entityID = entityID; + Script.update.connect(this.update); + }, + update: function(deltaTime) { + + totalTime += deltaTime; + if (totalTime > spacer) { + var howManyAvatars = AvatarList.getAvatarIdentifiers().length; + var intensity = (MINIMUM_LIGHT_INTENSITY + (MAXIMUM_LIGHT_INTENSITY + (Math.sin(totalTime) * MAXIMUM_LIGHT_INTENSITY))); + intensity += randFloat(-LIGHT_INTENSITY_RANDOMNESS, LIGHT_INTENSITY_RANDOMNESS); + + Entities.editEntity(_this.entityID, { + intensity: intensity + }); + + spacer = Math.random(0, 100) * (2 / howManyAvatars); + totalTime = 0; + } else { + //just keep counting + } + }, + unload: function() { + Script.update.disconnect(this.update) + } + } + + return new FlickeringFlame + + +}); \ No newline at end of file diff --git a/tutorial/fuse.js b/tutorial/fuse.js new file mode 100644 index 0000000000..d09dc11bd1 --- /dev/null +++ b/tutorial/fuse.js @@ -0,0 +1,74 @@ +(function() { + var fuseSound = SoundCache.getSound("https://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Sounds/fuse.wav"); + function getChildProperties(entityID, propertyNames) { + var childEntityIDs = Entities.getChildrenIDs(entityID); + var results = {} + for (var i = 0; i < childEntityIDs.length; ++i) { + var childEntityID = childEntityIDs[i]; + var properties = Entities.getEntityProperties(childEntityID, propertyNames); + results[childEntityID] = properties; + } + return results; + } + var Fuse = function() { + }; + Fuse.prototype = { + onLit: function() { + print("LIT", this.entityID); + Entities.editEntity(this.entityID, { + animation: { + currentFrame: 0, + //"lastFrame": 130, + running: 1, + url: "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/fuse/fuse.fbx", + loop: 0 + }, + }); + var injector = Audio.playSound(fuseSound, { + position: Entities.getEntityProperties(this.entityID, 'position').position, + volume: 0.7, + loop: true + }); + + var childrenProps = getChildProperties(this.entityID, ['type']); + for (var childEntityID in childrenProps) { + var props = childrenProps[childEntityID]; + if (props.type == "ParticleEffect") { + Entities.editEntity(childEntityID, { + emitRate: 140, + }); + } else if (props.type == "Light") { + Entities.editEntity(childEntityID, { + visible: true, + }); + } + } + + var self = this; + Script.setTimeout(function() { + print("BLOW UP"); + Entities.callEntityMethod("{dd13fcd5-616f-4749-ab28-2e1e8bc512e9}", "onLit"); + injector.stop(); + + var childrenProps = getChildProperties(self.entityID, ['type']); + for (var childEntityID in childrenProps) { + var props = childrenProps[childEntityID]; + if (props.type == "ParticleEffect") { + Entities.editEntity(childEntityID, { + emitRate: 0, + }); + } else if (props.type == "Light") { + Entities.editEntity(childEntityID, { + visible: false, + }); + } + } + + }, 4900); + }, + preload: function(entityID) { + this.entityID = entityID; + }, + }; + return new Fuse(); +}); diff --git a/tutorial/spinner.js b/tutorial/spinner.js new file mode 100644 index 0000000000..348e250bec --- /dev/null +++ b/tutorial/spinner.js @@ -0,0 +1,66 @@ +(function() { + var spinnerSound = SoundCache.getSound("http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Sounds/Pinwheel.L.wav"); + var Spinner = function() { + }; + function getChildProperties(entityID, propertyNames) { + var childEntityIDs = Entities.getChildrenIDs(entityID); + var results = {} + for (var i = 0; i < childEntityIDs.length; ++i) { + var childEntityID = childEntityIDs[i]; + var properties = Entities.getEntityProperties(childEntityID, propertyNames); + results[childEntityID] = properties; + } + return results; + } + Spinner.prototype = { + onLit: function() { + print("LIT SPINNER", this.entityID); + Entities.editEntity(this.entityID, { + "angularDamping": 0.1, + "angularVelocity": { + "x": 20.471975326538086, + "y": 0, + "z": 0 + }, + }); + var injector = Audio.playSound(spinnerSound, { + position: Entities.getEntityProperties(this.entityID, 'position').position, + volume: 0.7, + loop: true + }); + + print("HERE2"); + var childrenProps = getChildProperties(this.entityID, ['type']); + for (var childEntityID in childrenProps) { + var props = childrenProps[childEntityID]; + if (props.type == "ParticleEffect") { + Entities.editEntity(childEntityID, { + emitRate: 140, + }); + } + } + Messages.sendLocalMessage("Tutorial-Spinner", "wasLit"); + + var self = this; + Script.setTimeout(function() { + print("BLOW UP"); + injector.stop(); + + print("HERE"); + var childrenProps = getChildProperties(self.entityID, ['type']); + for (var childEntityID in childrenProps) { + var props = childrenProps[childEntityID]; + if (props.type == "ParticleEffect") { + Entities.editEntity(childEntityID, { + emitRate: 0, + }); + } + } + }, 4900); + }, + preload: function(entityID) { + this.entityID = entityID; + }, + }; + return new Spinner(); +}); From 55586ccedbef7591bc060b64983a57c67d096d68 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Sep 2016 17:32:25 -0700 Subject: [PATCH 039/109] Update controllers to include tips --- tutorial/controllerDisplay.js | 22 +++++---- tutorial/viveControllerConfiguration.js | 66 ++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/tutorial/controllerDisplay.js b/tutorial/controllerDisplay.js index ecce58e605..85dd50ce26 100644 --- a/tutorial/controllerDisplay.js +++ b/tutorial/controllerDisplay.js @@ -1,6 +1,7 @@ var DEBUG = false; var VISIBLE_BY_DEFAULT = false; -var PARENT_ID = MyAvatar.sessionUUID; +//var PARENT_ID = MyAvatar.sessionUUID; +var PARENT_ID = "{00000000-0000-0000-0000-000000000001}"; createControllerDisplay = function(config) { var controllerDisplay = { @@ -166,7 +167,7 @@ createControllerDisplay = function(config) { controllerDisplay.parts[partName] = controller.parts[partName]; - var overlayID = Overlays.addOverlay("model", { + var properties = { url: part.modelURL, localPosition: partPosition, localRotation: innerRotation, @@ -174,7 +175,15 @@ createControllerDisplay = function(config) { parentJointIndex: controller.jointIndex, ignoreRayIntersection: true, //visible: false - }); + }; + + if (part.defaultTextureLayer) { + var textures = {}; + textures[part.textureName] = part.textureLayers[part.defaultTextureLayer].defaultTextureURL; + properties['textures'] = textures; + } + + var overlayID = Overlays.addOverlay("model", properties); if (part.type == "rotational") { var range = part.maxValue - part.minValue; @@ -234,13 +243,6 @@ createControllerDisplay = function(config) { mapping.from([yinput]).peek().invert().to(function(value) { //print("Y", value); }); - if (part.defaultTextureURL) { - var textures = {}; - textures[part.textureName] = part.defaultTextureURL; - Overlays.editOverlay(overlayID, { - textures: textures - }); - } } else if (part.type == "static") { } else { print("TYPE NOT SUPPORTED: ", part.type); diff --git a/tutorial/viveControllerConfiguration.js b/tutorial/viveControllerConfiguration.js index d2f9403d8a..be0bfe5428 100644 --- a/tutorial/viveControllerConfiguration.js +++ b/tutorial/viveControllerConfiguration.js @@ -43,6 +43,7 @@ var viveNaturalPosition = { }; var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_body.fbx"; +var viveTipsModelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Models/vive_tips.fbx" VIVE_CONTROLLER_CONFIGURATION = { name: "Vive", @@ -57,6 +58,33 @@ VIVE_CONTROLLER_CONFIGURATION = { dimensions: viveNaturalDimensions, parts: { + tips: { + type: "static", + modelURL: viveTipsModelURL, + naturalPosition: {"x":-0.004377640783786774,"y":-0.034371938556432724,"z":0.06769277155399323}, + + textureName: "Tex.Blank", + + defaultTextureLayer: "trigger", + textureLayers: { + blank: { + defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Blank.png", + }, + trigger: { + defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Trigger.png", + }, + arrows: { + defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Rotate.png", + }, + grip: { + defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Grip.png", + }, + teleport: { + defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Teleport.png", + }, + } + }, + // The touchpad type draws a dot indicating the current touch/thumb position // and swaps in textures based on the thumb position. touchpad: { @@ -70,10 +98,10 @@ VIVE_CONTROLLER_CONFIGURATION = { maxValue: 1.0, minPosition: { x: -0.035, y: 0.004, z: -0.005 }, maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - textureName: "Tex.touchpad-blank", + disable_textureName: "Tex.touchpad-blank", - defaultTextureLayer: "blank", - textureLayers: { + disable_defaultTextureLayer: "blank", + disable_textureLayers: { blank: { defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", }, @@ -211,6 +239,32 @@ VIVE_CONTROLLER_CONFIGURATION = { }, parts: { + tips: { + type: "static", + modelURL: viveTipsModelURL, + naturalPosition: {"x":-0.004377640783786774,"y":-0.034371938556432724,"z":0.06769277155399323}, + + textureName: "Tex.Blank", + + defaultTextureLayer: "trigger", + textureLayers: { + blank: { + defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Blank.png", + }, + trigger: { + defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Trigger.png", + }, + arrows: { + defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Rotate.png", + }, + grip: { + defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Grip.png", + }, + teleport: { + defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Teleport.png", + }, + } + }, // The touchpad type draws a dot indicating the current touch/thumb position // and swaps in textures based on the thumb position. @@ -225,10 +279,10 @@ VIVE_CONTROLLER_CONFIGURATION = { maxValue: 1.0, minPosition: { x: -0.035, y: 0.004, z: -0.005 }, maxPosition: { x: -0.035, y: 0.004, z: -0.005 }, - textureName: "Tex.touchpad-blank", + disable_textureName: "Tex.touchpad-blank", - defaultTextureLayer: "blank", - textureLayers: { + disable_defaultTextureLayer: "blank", + disable_textureLayers: { blank: { defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", }, From a6fadd2fa8fbe304a24d381df6b0ddc9a1387e91 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Sep 2016 17:32:50 -0700 Subject: [PATCH 040/109] Update fuse and add collider --- tutorial/fuse.js | 49 +++++++++++++++++++++++++++++--- tutorial/fuseCollider.js | 60 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 tutorial/fuseCollider.js diff --git a/tutorial/fuse.js b/tutorial/fuse.js index d09dc11bd1..48efeb28e6 100644 --- a/tutorial/fuse.js +++ b/tutorial/fuse.js @@ -1,4 +1,38 @@ (function() { + var findEntity = function(properties, searchRadius, filterFn) { + var entities = findEntities(properties, searchRadius, filterFn); + return entities.length > 0 ? entities[0] : null; + } + + // Return all entities with properties `properties` within radius `searchRadius` + var findEntities = function(properties, searchRadius, filterFn) { + if (!filterFn) { + filterFn = function(properties, key, value) { + return value == properties[key]; + } + } + searchRadius = searchRadius ? searchRadius : 100000; + var entities = Entities.findEntities({ x: 0, y: 0, z: 0 }, searchRadius); + var matchedEntities = []; + var keys = Object.keys(properties); + for (var i = 0; i < entities.length; ++i) { + var match = true; + var candidateProperties = Entities.getEntityProperties(entities[i], keys); + for (var key in properties) { + if (!filterFn(properties, key, candidateProperties[key])) { + // This isn't a match, move to next entity + match = false; + break; + } + } + if (match) { + matchedEntities.push(entities[i]); + } + } + + return matchedEntities; + } + var fuseSound = SoundCache.getSound("https://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Sounds/fuse.wav"); function getChildProperties(entityID, propertyNames) { var childEntityIDs = Entities.getChildrenIDs(entityID); @@ -13,12 +47,18 @@ var Fuse = function() { }; Fuse.prototype = { - onLit: function() { + light: function() { print("LIT", this.entityID); + var anim = Entities.getEntityProperties(this.entityID, ['animation']).animation; + print("anim: ", anim.currentFrame, Object.keys(anim)); + + if (anim.currentFrame < 140) { + return; + } Entities.editEntity(this.entityID, { animation: { - currentFrame: 0, - //"lastFrame": 130, + currentFrame: 1, + lastFrame: 150, running: 1, url: "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/fuse/fuse.fbx", loop: 0 @@ -47,7 +87,8 @@ var self = this; Script.setTimeout(function() { print("BLOW UP"); - Entities.callEntityMethod("{dd13fcd5-616f-4749-ab28-2e1e8bc512e9}", "onLit"); + var spinnerID = findEntity({ name: "tutorial/equip/spinner" }, 20); + Entities.callEntityMethod(spinnerID, "onLit"); injector.stop(); var childrenProps = getChildProperties(self.entityID, ['type']); diff --git a/tutorial/fuseCollider.js b/tutorial/fuseCollider.js new file mode 100644 index 0000000000..dd8195d9b0 --- /dev/null +++ b/tutorial/fuseCollider.js @@ -0,0 +1,60 @@ +(function() { + var findEntity = function(properties, searchRadius, filterFn) { + var entities = findEntities(properties, searchRadius, filterFn); + return entities.length > 0 ? entities[0] : null; + } + + // Return all entities with properties `properties` within radius `searchRadius` + var findEntities = function(properties, searchRadius, filterFn) { + if (!filterFn) { + filterFn = function(properties, key, value) { + return value == properties[key]; + } + } + searchRadius = searchRadius ? searchRadius : 100000; + var entities = Entities.findEntities({ x: 0, y: 0, z: 0 }, searchRadius); + var matchedEntities = []; + var keys = Object.keys(properties); + for (var i = 0; i < entities.length; ++i) { + var match = true; + var candidateProperties = Entities.getEntityProperties(entities[i], keys); + for (var key in properties) { + if (!filterFn(properties, key, candidateProperties[key])) { + // This isn't a match, move to next entity + match = false; + break; + } + } + if (match) { + matchedEntities.push(entities[i]); + } + } + + return matchedEntities; + } + + function getChildProperties(entityID, propertyNames) { + var childEntityIDs = Entities.getChildrenIDs(entityID); + var results = {} + for (var i = 0; i < childEntityIDs.length; ++i) { + var childEntityID = childEntityIDs[i]; + var properties = Entities.getEntityProperties(childEntityID, propertyNames); + results[childEntityID] = properties; + } + return results; + } + + var Fuse = function() { + }; + Fuse.prototype = { + onLit: function() { + print("LIT", this.entityID); + var fuseID = findEntity({ name: "tutorial/equip/fuse" }, 20); + Entities.callEntityMethod(fuseID, "light"); + }, + preload: function(entityID) { + this.entityID = entityID; + }, + }; + return new Fuse(); +}); From 1975343496bb934300698abf5ca9b2e5342b09d6 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 15 Sep 2016 17:33:00 -0700 Subject: [PATCH 041/109] Add butane lighter --- tutorial/lighter/butaneLighter.js | 188 ++++++++++++++++++++ tutorial/lighter/createButaneLighter.js | 219 ++++++++++++++++++++++++ 2 files changed, 407 insertions(+) create mode 100644 tutorial/lighter/butaneLighter.js create mode 100644 tutorial/lighter/createButaneLighter.js diff --git a/tutorial/lighter/butaneLighter.js b/tutorial/lighter/butaneLighter.js new file mode 100644 index 0000000000..2592d8ec4a --- /dev/null +++ b/tutorial/lighter/butaneLighter.js @@ -0,0 +1,188 @@ +// +// Created by Thijs Wenker on September 14, 2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var _this; + + function getResourceURL(file) { + return 'http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/' + file; + }; + + const LIGHTER_ON_SOUND_URL = getResourceURL('Sounds/lighter_on.wav'); + const BUTANE_SOUND_URL = getResourceURL('Sounds/butane.wav'); + + // TODO: fix this in the client, changing the sound volume while a sound is playing doesn't seem to work right now + const DYNAMIC_SOUND_VOLUME = false; + const BUTANE_MIN_SOUND_VOLUME = 0.05; + + const FLAME_LENGTH = 0.05; + + const BUTANE_SOUND_SETTINGS = { + volume: 0.4, + loop: true, + playbackGap: 1000, + playbackGapRange: 1000 + }; + + const LIGHTER_ON_SOUND_SETTINGS = { + volume: 0.5, + loop: false + }; + + function RemoteLogSender(channel, identifier) { + this.channel = channel; + this.identifier = identifier; + } + + RemoteLogSender.prototype = { + channel: null, + identifier: null, + debug: function(message) { + Messages.sendLocalMessage(this.channel, JSON.stringify({ + message: '[DEBUG ' + this.identifier + '] ' + message + })); + } + }; + + var remoteLogSender = null; + + function debugPrint(message) { + if (remoteLogSender !== null) { + remoteLogSender.debug(message); + } + } + + ButaneLighter = function() { + _this = this; + _this.triggerValue = 0.0; // be sure to set this value in the constructor, otherwise it will be a shared value + _this.isFiring = false; + } + + ButaneLighter.prototype = { + entityID: null, + lighterOnSound: null, + butaneSound: null, + lighterOnSoundInjector: null, + butaneSoundInjector: null, + butaneSoundInjectorOptions: null, + lighterParticleEntity: null, + buttonBeingPressed: null, + triggerValue: null, + isFiring: null, + getLighterParticleEntity: function() { + var result = null; + Entities.getChildrenIDs(_this.entityID).forEach(function(entity) { + var name = Entities.getEntityProperties(entity, ['name']).name; + if (name === 'lighter_particle') { + result = entity; + } + }); + return result; + }, + preload: function(entityID) { + _this.entityID = entityID; + _this.lighterOnSound = SoundCache.getSound(LIGHTER_ON_SOUND_URL); + _this.butaneSound = SoundCache.getSound(BUTANE_SOUND_URL); + var properties = Entities.getEntityProperties(_this.entityID, ['userData']); + try { + var userData = JSON.parse(properties.userData); + if (userData['debug'] !== undefined && userData['debug']['sessionUUID'] === MyAvatar.sessionUUID && + userData['debug']['channel'] !== undefined) + { + remoteLogSender = new RemoteLogSender(userData['debug']['channel'], _this.entityID); + remoteLogSender.debug('Debugger initialized'); + } + } catch (e) { + //failed to detect if we have a debugger + } + debugPrint(_this.getLighterParticleEntity()); + }, + startEquip: function(entityID, args) { + _this.lighterParticleEntity = _this.getLighterParticleEntity(); + }, + continueEquip: function(entityID, args) { + _this.triggerValue = Controller.getValue(args[0] === 'left' ? Controller.Standard.LT : Controller.Standard.RT); + if (_this.triggerValue > 0.2) { + if (!_this.isFiring) { + _this.startFiring(); + } + _this.tryToIgnite(); + _this.updateButaneSound() + return; + } + _this.stopFiring(); + }, + startFiring: function() { + if (_this.isFiring) { + return; + } + _this.isFiring = true; + if (_this.lighterOnSound.downloaded) { + // We don't want to override the default volume setting, so lets clone the default SETTINGS object + var lighterOnOptions = JSON.parse(JSON.stringify(LIGHTER_ON_SOUND_SETTINGS)); + lighterOnOptions['position'] = Entities.getEntityProperties(_this.entityID, ['position']).position; + _this.lighterOnSoundInjector = Audio.playSound(_this.lighterOnSound, lighterOnOptions); + } + if (_this.butaneSound.downloaded) { + _this.butaneSoundInjectorOptions = JSON.parse(JSON.stringify(BUTANE_SOUND_SETTINGS)); + _this.butaneSoundInjectorOptions['position'] = Entities.getEntityProperties(_this.lighterParticleEntity, ['position']).position; + if (DYNAMIC_SOUND_VOLUME) { + _this.butaneSoundInjectorOptions['volume'] = BUTANE_MIN_SOUND_VOLUME; + } + _this.butaneSoundInjector = Audio.playSound(_this.butaneSound, _this.butaneSoundInjectorOptions); + } + Entities.editEntity(_this.lighterParticleEntity, {isEmitting: _this.isFiring}); + + }, + stopFiring: function() { + if (!_this.isFiring) { + return; + } + _this.isFiring = false; + Entities.editEntity(_this.lighterParticleEntity, {isEmitting: _this.isFiring}); + _this.stopButaneSound(); + }, + tryToIgnite: function() { + var flameProperties = Entities.getEntityProperties(_this.lighterParticleEntity, ['position', 'rotation']); + var pickRay = { + origin: flameProperties.position, + direction: Quat.getFront(flameProperties.rotation) + } + var intersection = Entities.findRayIntersection(pickRay, true); + if (intersection.intersects) { + debugPrint(JSON.stringify(intersection)); + } + }, + releaseEquip: function(entityID, args) { + _this.stopFiring(); + // reset trigger value; + _this.triggerValue = 0.0; + }, + updateButaneSound: function() { + if (_this.butaneSoundInjector !== null && _this.butaneSoundInjector.isPlaying()) { + _this.butaneSoundInjectorOptions = _this.butaneSoundInjector.options; + _this.butaneSoundInjectorOptions['position'] = Entities.getEntityProperties(_this.entityID, ['position']).position; + if (DYNAMIC_SOUND_VOLUME) { + _this.butaneSoundInjectorOptions['volume'] = ((BUTANE_SOUND_SETTINGS.volume - BUTANE_MIN_SOUND_VOLUME) * + _this.triggerValue) + BUTANE_MIN_SOUND_VOLUME; + } + _this.butaneSoundInjector.options = _this.butaneSoundInjectorOptions; + } + }, + stopButaneSound: function() { + if (_this.butaneSoundInjector !== null && _this.butaneSoundInjector.isPlaying()) { + _this.butaneSoundInjector.stop(); + } + _this.butaneSoundInjector = null; + }, + unload: function() { + _this.stopButaneSound(); + }, + }; + return new ButaneLighter(); +}) diff --git a/tutorial/lighter/createButaneLighter.js b/tutorial/lighter/createButaneLighter.js new file mode 100644 index 0000000000..ec305ecb05 --- /dev/null +++ b/tutorial/lighter/createButaneLighter.js @@ -0,0 +1,219 @@ +// +// Created by Thijs Wenker on September 14, 2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +const TEST_MODE = false; +const SCRIPT_URL = 'https://dl.dropboxusercontent.com/u/14997455/hifi/butaneLighter/butaneLighter.js?v=' + Date.now(); +//const SCRIPT_URL = Script.resolvePath("butaneLighter.js"); + +function getResourceURL(file) { + return 'http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/' + file; +}; + +//Creates an entity and returns a mixed object of the creation properties and the assigned entityID +var createEntity = function(entityProperties, parent) { + if (parent.rotation !== undefined) { + if (entityProperties.rotation !== undefined) { + entityProperties.rotation = Quat.multiply(parent.rotation, entityProperties.rotation); + } else { + entityProperties.rotation = parent.rotation; + } + } + if (parent.position !== undefined) { + var localPosition = (parent.rotation !== undefined) ? Vec3.multiplyQbyV(parent.rotation, entityProperties.position) : entityProperties.position; + entityProperties.position = Vec3.sum(localPosition, parent.position) + } + if (parent.id !== undefined) { + entityProperties.parentID = parent.id; + } + entityProperties.id = Entities.addEntity(entityProperties); + return entityProperties; +}; + +createButaneLighter = function(transform) { + var entityProperties = { + collisionsWillMove: true, + dimensions: { + x: 0.025599999353289604, + y: 0.057399999350309372, + z: 0.37419998645782471 + }, + dynamic: true, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + modelURL: getResourceURL('Models/lighterIceCreamSandwich.fbx'), + name: 'BrutaneLighter', + shapeType: 'simple-compound', + type: 'Model', + userData: JSON.stringify({ + tag: "equip-temporary", + grabbableKey: { + invertSolidWhileHeld: true + }, + wearable: { + joints: { + RightHand: [{ + x: 0.029085848480463028, + y: 0.09807153046131134, + z: 0.03062543272972107 + }, { + x: 0.5929139256477356, + y: 0.3207578659057617, + z: 0.7151655554771423, + w: -0.18468326330184937 + }], + LeftHand: [{ + x: -0.029085848480463028, + y: 0.09807153046131134, + z: 0.03062543272972107 + }, { + x: -0.5929139256477356, + y: 0.3207578659057617, + z: 0.7151655554771423, + w: -0.18468326330184937 + }] + } + } + }), + script: SCRIPT_URL + }; + return createEntity(entityProperties, transform); +} + +function createFireParticle(butaneLighter) { + var entityProperties = { + userData: JSON.stringify({ tag: "equip-temporary" }), + accelerationSpread: { + x: 0.1, + y: 0, + z: 0.1 + }, + alpha: 0.039999999105930328, + alphaFinish: 0.039999999105930328, + alphaStart: 0.039999999105930328, + azimuthFinish: 0.039999999105930328, + azimuthStart: 0, + dimensions: { + x: 0.49194091558456421, + y: 0.49194091558456421, + z: 0.49194091558456421 + }, + emitAcceleration: { + x: 0, + y: 0, + z: 0 + }, + emitOrientation: { + w: 1, + x: -1.52587890625e-05, + y: -1.52587890625e-05, + z: -1.52587890625e-05 + }, + emitRate: 770, + emitSpeed: 0.014000000432133675, + isEmitting: false, + lifespan: 0.37000000476837158, + maxParticles: 820, + name: 'lighter_particle', + particleRadius: 0.0027000000700354576, + position: { + x: -0.00044769048690795898, + y: 0.016354814171791077, + z: 0.19217036664485931 + }, + radiusFinish: 0.0027000000700354576, + radiusSpread: 3, + radiusStart: 0.0027000000700354576, + rotation: { + w: 1, + x: -0.0001678466796875, + y: -1.52587890625e-05, + z: -1.52587890625e-05 + }, + speedSpread: 0.56999999284744263, + textures: 'atp:/textures/fire3.png', + type: 'ParticleEffect', + + + "color": { + "red": 255, + "green": 255, + "blue": 255 + }, + "isEmitting": 0, + "maxParticles": 820, + "lifespan": 0.28, + "emitRate": 1100, + "emitSpeed": 0.007, + "speedSpread": 0.5699999928474426, + "emitOrientation": { + "x": -0.0000152587890625, + "y": -0.0000152587890625, + "z": -0.0000152587890625, + "w": 1 + }, + "emitDimensions": { + "x": 0, + "y": 0, + "z": 0 + }, + "polarStart": 0, + "polarFinish": 0, + "azimuthStart": 0, + "azimuthFinish": 0.03999999910593033, + "emitAcceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "accelerationSpread": { + "x": 0, + "y": 0, + "z": 0 + }, + "particleRadius": 0.0037, + "radiusSpread": 3, + "radiusStart": 0.008, + "radiusFinish": 0.0004, + "colorSpread": { + "red": 0, + "green": 0, + "blue": 0 + }, + "colorStart": { + "red": 255, + "green": 255, + "blue": 255 + }, + "colorFinish": { + "red": 255, + "green": 255, + "blue": 255 + }, + "alpha": 0.03999999910593033, + "alphaSpread": 0, + "alphaStart": 0.141, + "alphaFinish": 0.02, + "emitterShouldTrail": 0, + "textures": "atp:/textures/fire3.png" + }; + return createEntity(entityProperties, butaneLighter); +} + +doCreateButaneLighter = function(transform) { + var butaneLighter = createButaneLighter(transform); + createFireParticle(butaneLighter); + return butaneLighter; +} From d9cec7f4c717485311528b8296c24b5cfccf1d06 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Sep 2016 17:33:45 -0700 Subject: [PATCH 042/109] Update tutorial to work with lighter and tips --- tutorial/tutorial.js | 71 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 56a7a76e30..ac6359c7fb 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -29,6 +29,7 @@ if (!Function.prototype.bind) { Script.include("entityData.js"); Script.include("viveHandsv2.js"); +Script.include("lighter/createButaneLighter.js"); var BASKET_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx"; var BASKET_COLLIDER_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj"; @@ -74,6 +75,21 @@ findEntities = function(properties, searchRadius, filterFn) { } // On start tutorial... + +function triggerHapticPulse() { + function scheduleHaptics(delay, strength, duration) { + Script.setTimeout(function() { + Controller.triggerHapticPulse(strength, duration, 0); + Controller.triggerHapticPulse(strength, duration, 1); + }, delay); + } + scheduleHaptics(0, 0.8, 100); + scheduleHaptics(300, 0.5, 100); + scheduleHaptics(600, 0.3, 100); + scheduleHaptics(900, 0.2, 100); + scheduleHaptics(1200, 0.1, 100); +} + // Load assets var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn"; var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn"; @@ -205,6 +221,7 @@ stepDisableControllers.prototype = { farGrabEnabled: false, })); setControllerPartLayer('touchpad', 'blank'); + setControllerPartLayer('tips', 'grip'); onFinish(); }, cleanup: function() { @@ -254,7 +271,6 @@ function StayInFrontOverlay(type, properties, distance, positionOffset) { } StayInFrontOverlay.prototype = { update: function(dt) { - print("Updating..."); var targetOrientation = MyAvatar.orientation; var targetPosition = MyAvatar.position; this.currentOrientation = Quat.slerp(this.currentOrientation, targetOrientation, 0.05); @@ -394,6 +410,7 @@ stepRaiseAboveHead.prototype = { }; function setControllerVisible(name, visible) { + return; Messages.sendLocalMessage('Controller-Display', JSON.stringify({ name: name, visible: visible, @@ -427,6 +444,7 @@ stepNearGrab.prototype = { this.onFinish = onFinish; setControllerVisible("trigger", true); + setControllerPartLayer('tips', 'trigger'); var tag = this.tag; // Spawn content set @@ -482,6 +500,7 @@ stepNearGrab.prototype = { print("cleaning up near grab"); this.finished = true; setControllerVisible("trigger", false); + setControllerPartLayer('tips', 'blank'); hideEntitiesWithTag(this.tag, { visible: false}); deleteEntitiesWithTag(this.tempTag); } @@ -507,6 +526,7 @@ stepFarGrab.prototype = { showEntitiesWithTag('bothGrab', { visible: true }); setControllerVisible("trigger", true); + setControllerPartLayer('tips', 'trigger'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ farGrabEnabled: true, })); @@ -575,6 +595,7 @@ stepFarGrab.prototype = { //Messages.messageReceived.disconnect(this.onMessage.bind(this)); this.finished = true; setControllerVisible("trigger", false); + setControllerPartLayer('tips', 'blank'); hideEntitiesWithTag(this.tag, { visible: false}); hideEntitiesWithTag('bothGrab', { visible: false}); deleteEntitiesWithTag(this.tempTag); @@ -601,6 +622,7 @@ var stepEquip = function(name) { stepEquip.prototype = { start: function(onFinish) { setControllerVisible("trigger", true); + setControllerPartLayer('tips', 'trigger'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ holdEnabled: true, })); @@ -638,17 +660,24 @@ stepEquip.prototype = { return null; } + var transform = {}; + GunData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; GunData.rotation = Entities.getEntityProperties(boxSpawnID, 'rotation').rotation; + transform.position = Entities.getEntityProperties(boxSpawnID, 'position').position; + transform.rotation = Entities.getEntityProperties(boxSpawnID, 'rotation').rotation; + this.spawnTransform = transform; Vec3.print("spawn", GunData.position); print("Adding: ", JSON.stringify(GunData)); - return spawnWithTag([GunData], null, this.tempTag)[0]; + return doCreateButaneLighter(transform).id;//spawnWithTag([GunData], null, this.tempTag)[0]; } + // Enabled grab // Create table ? // Create blocks and basket this.gunID = createGun.bind(this)(); + this.startWatchingGun(); print("Created", this.gunID); this.onFinish = onFinish; Messages.subscribe('Tutorial-Spinner'); @@ -676,6 +705,23 @@ stepEquip.prototype = { // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, + startWatchingGun: function() { + if (!this.watcherIntervalID) { + this.watcherIntervalID = Script.setInterval(function() { + var props = Entities.getEntityProperties(this.gunID, ['position']); + if (props.position.y < -0.4 + || Vec3.distance(this.spawnTransform.position, props.position) > 4) { + Entities.editEntity(this.gunID, this.spawnTransform); + } + }.bind(this), 1000); + } + }, + stopWatchingGun: function() { + if (this.watcherIntervalID) { + Script.clearInterval(this.watcherIntervalID); + this.watcherIntervalID = null; + } + }, onMessage: function(channel, message, sender) { if (this.currentPart == this.COMPLETE) { return; @@ -685,6 +731,7 @@ stepEquip.prototype = { if (this.currentPart == this.PART1 && message == "wasLit") { hideEntitiesWithTag(this.tagPart1); showEntitiesWithTag(this.tagPart2); + setControllerPartLayer('tips', 'grip'); Messages.subscribe('Hifi-Object-Manipulation'); } } else if (channel == "Hifi-Object-Manipulation") { @@ -692,6 +739,7 @@ stepEquip.prototype = { var data = parseJSON(message); print("Here", data.action, data.grabbedEntity, this.gunID); if (data.action == 'release' && data.grabbedEntity == this.gunID) { + this.stopWatchingGun(); try { Messages.messageReceived.disconnect(this.onMessage); } catch(e) { @@ -707,6 +755,8 @@ stepEquip.prototype = { }, cleanup: function() { setControllerVisible("trigger", false); + setControllerPartLayer('tips', 'blank'); + this.stopWatchingGun(); this.currentPart = this.COMPLETE; try { Messages.messageReceived.disconnect(this.onMessage); @@ -752,6 +802,7 @@ stepTurnAround.prototype = { setControllerVisible("right", true); setControllerPartLayer('touchpad', 'arrows'); + setControllerPartLayer('tips', 'arrows'); showEntitiesWithTag(this.tag); var hasTurnedAround = false; @@ -781,6 +832,7 @@ stepTurnAround.prototype = { setControllerVisible("right", false); setControllerPartLayer('touchpad', 'blank'); + setControllerPartLayer('tips', 'blank'); if (this.interval) { Script.clearInterval(this.interval); @@ -808,6 +860,7 @@ stepTeleport.prototype = { //setControllerVisible("teleport", true); setControllerPartLayer('touchpad', 'teleport'); + setControllerPartLayer('tips', 'teleport'); Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); @@ -841,6 +894,7 @@ stepTeleport.prototype = { //setControllerVisible("teleport", false); setControllerPartLayer('touchpad', 'blank'); + setControllerPartLayer('tips', 'blank'); if (this.checkCollidesTimer) { Script.clearInterval(this.checkCollidesTimer); @@ -889,9 +943,14 @@ function showEntitiesWithTag(tag) { if (data.collidable !== undefined) { collisionless = data.collidable === true ? false : true; } + if (data.soundKey) { + print("Setting sound key to true"); + data.soundKey.playing = true; + } var newProperties = { visible: data.visible == false ? false : true, collisionless: collisionless, + userData: JSON.stringify(data), //collisionless: data.collisionless == true ? true : false, }; Entities.editEntity(entityID, newProperties); @@ -901,10 +960,14 @@ function hideEntitiesWithTag(tag) { editEntitiesWithTag(tag, function(entityID) { var userData = Entities.getEntityProperties(entityID, "userData").userData; var data = parseJSON(userData); + if (data.soundKey) { + data.soundKey.playing = false; + } var newProperties = { visible: false, collisionless: 1, ignoreForCollisions: 1, + userData: JSON.stringify(data), }; Entities.editEntity(entityID, newProperties); }); @@ -932,7 +995,7 @@ function startTutorial() { for (var i = 0; i < STEPS.length; ++i) { STEPS[i].cleanup(); } - location = "/tutorial_begin"; + //location = "/tutorial_begin"; //location = "/tutorial"; MyAvatar.shouldRenderLocally = false; startNextStep(); @@ -999,7 +1062,7 @@ Controller.keyReleaseEvent.connect(function (event) { if (!startNextStep()) { startTutorial(); } - } else if (event.text == "F12") { + } else if (event.text == "F11") { restartStep(); } else if (event.text == "F10") { MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; From c84ccaaac03fa7a7944d6884373c079ad260a587 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Sep 2016 17:49:03 -0700 Subject: [PATCH 043/109] Update overlay for tutorial --- tutorial/tutorial.js | 9 +++++---- tutorial/viveControllerConfiguration.js | 2 +- unpublishedScripts/DomainContent/Home/firePit/fire.js | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index ac6359c7fb..242608aac6 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -221,7 +221,7 @@ stepDisableControllers.prototype = { farGrabEnabled: false, })); setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('tips', 'grip'); + setControllerPartLayer('tips', 'blank'); onFinish(); }, cleanup: function() { @@ -317,7 +317,7 @@ stepOrient.prototype = { }; this.overlay = new StayInFrontOverlay("model", { - url: "http://hifi-content.s3.amazonaws.com/alan/dev/Prompt-Cards/raiseHands.fbx?11", + url: "http://hifi-content.s3.amazonaws.com/alan/dev/Prompt-Cards/welcome.fbx?11", ignoreRayIntersection: true, }, 2, { x: 0, y: 0.3, z: 0 }); @@ -729,6 +729,7 @@ stepEquip.prototype = { print("Got message", channel, message, sender, MyAvatar.sessionUUID); if (channel == "Tutorial-Spinner") { if (this.currentPart == this.PART1 && message == "wasLit") { + this.currentPart = this.PART2; hideEntitiesWithTag(this.tagPart1); showEntitiesWithTag(this.tagPart2); setControllerPartLayer('tips', 'grip'); @@ -983,7 +984,7 @@ function startTutorial() { STEPS = [ new stepDisableControllers("step0"), new stepOrient("orient"), - new stepWelcome("welcome"), + //new stepWelcome("welcome"), new stepRaiseAboveHead("raiseHands"), new stepNearGrab("nearGrab"), new stepFarGrab("farGrab"), @@ -995,7 +996,7 @@ function startTutorial() { for (var i = 0; i < STEPS.length; ++i) { STEPS[i].cleanup(); } - //location = "/tutorial_begin"; + location = "/tutorial_begin"; //location = "/tutorial"; MyAvatar.shouldRenderLocally = false; startNextStep(); diff --git a/tutorial/viveControllerConfiguration.js b/tutorial/viveControllerConfiguration.js index be0bfe5428..3604f4a412 100644 --- a/tutorial/viveControllerConfiguration.js +++ b/tutorial/viveControllerConfiguration.js @@ -65,7 +65,7 @@ VIVE_CONTROLLER_CONFIGURATION = { textureName: "Tex.Blank", - defaultTextureLayer: "trigger", + defaultTextureLayer: "blank", textureLayers: { blank: { defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Blank.png", diff --git a/unpublishedScripts/DomainContent/Home/firePit/fire.js b/unpublishedScripts/DomainContent/Home/firePit/fire.js index b0ed9a61b6..2f73438ba9 100644 --- a/unpublishedScripts/DomainContent/Home/firePit/fire.js +++ b/unpublishedScripts/DomainContent/Home/firePit/fire.js @@ -78,6 +78,9 @@ _this.explodeWithColor(); _this.smokePuff(); Entities.deleteEntity(otherID) + Messages.sendMessage('Entity-Exploded', JSON.stringify({ + entityID: otherID, + })); } } } From c95b69452485f54f4afbca9103c536d4558ddeb6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 16 Sep 2016 09:00:13 -0700 Subject: [PATCH 044/109] Tweak tutorial to add delays between steps --- tutorial/tutorial.js | 39 +++++++++++-------- .../DomainContent/Home/firePit/fire.js | 2 +- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 242608aac6..049bb9e8a1 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -319,7 +319,7 @@ stepOrient.prototype = { this.overlay = new StayInFrontOverlay("model", { url: "http://hifi-content.s3.amazonaws.com/alan/dev/Prompt-Cards/welcome.fbx?11", ignoreRayIntersection: true, - }, 2, { x: 0, y: 0.3, z: 0 }); + }, 1.5, { x: 0, y: 0.3, z: 0 }); // Spawn content set //spawnWithTag(HandsAboveHeadData, defaultTransform, tag); @@ -387,18 +387,20 @@ stepRaiseAboveHead.prototype = { editEntitiesWithTag(this.tag, { visible: true }); - this.checkIntervalID = null; - function checkForHandsAboveHead() { - print("Checking for hands above head..."); - if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { - Script.clearInterval(this.checkIntervalID); - this.checkIntervalID = null; - playSuccessSound(); - //location = "/tutorial"; - onFinish(); + Script.setTimeout(function() { + this.checkIntervalID = null; + function checkForHandsAboveHead() { + print("Checking for hands above head..."); + if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { + Script.clearInterval(this.checkIntervalID); + this.checkIntervalID = null; + playSuccessSound(); + //location = "/tutorial"; + onFinish(); + } } - } - this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); + this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); + }.bind(this), 2000); }, cleanup: function() { if (this.checkIntervalID != null) { @@ -729,17 +731,20 @@ stepEquip.prototype = { print("Got message", channel, message, sender, MyAvatar.sessionUUID); if (channel == "Tutorial-Spinner") { if (this.currentPart == this.PART1 && message == "wasLit") { - this.currentPart = this.PART2; - hideEntitiesWithTag(this.tagPart1); - showEntitiesWithTag(this.tagPart2); - setControllerPartLayer('tips', 'grip'); - Messages.subscribe('Hifi-Object-Manipulation'); + Script.setTimeout(function() { + this.currentPart = this.PART2; + hideEntitiesWithTag(this.tagPart1); + showEntitiesWithTag(this.tagPart2); + setControllerPartLayer('tips', 'grip'); + Messages.subscribe('Hifi-Object-Manipulation'); + }.bind(this), 2000); } } else if (channel == "Hifi-Object-Manipulation") { if (this.currentPart == this.PART2) { var data = parseJSON(message); print("Here", data.action, data.grabbedEntity, this.gunID); if (data.action == 'release' && data.grabbedEntity == this.gunID) { + print("got release"); this.stopWatchingGun(); try { Messages.messageReceived.disconnect(this.onMessage); diff --git a/unpublishedScripts/DomainContent/Home/firePit/fire.js b/unpublishedScripts/DomainContent/Home/firePit/fire.js index 2f73438ba9..c68124d73c 100644 --- a/unpublishedScripts/DomainContent/Home/firePit/fire.js +++ b/unpublishedScripts/DomainContent/Home/firePit/fire.js @@ -165,4 +165,4 @@ } return new Fire(); -}); \ No newline at end of file +}); From 52f31addb2244f88d2dd9cb0ed7ec58633b59651 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 16 Sep 2016 11:06:07 -0700 Subject: [PATCH 045/109] Disable welcome overlay that follows you in tutorial --- tutorial/tutorial.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 049bb9e8a1..709a36ad21 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -319,6 +319,7 @@ stepOrient.prototype = { this.overlay = new StayInFrontOverlay("model", { url: "http://hifi-content.s3.amazonaws.com/alan/dev/Prompt-Cards/welcome.fbx?11", ignoreRayIntersection: true, + visible: false }, 1.5, { x: 0, y: 0.3, z: 0 }); // Spawn content set From c898b70dd7b4443fff8793e5704b1de26b28335f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 16 Sep 2016 11:11:58 -0700 Subject: [PATCH 046/109] Add more bird fireworks to tutorial --- tutorial/tutorial.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 709a36ad21..9512220f62 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -474,6 +474,8 @@ stepNearGrab.prototype = { // Create table ? // Create blocks and basket this.boxID = createBlock.bind(this)(); + this.boxID = createBlock.bind(this)(); + this.boxID = createBlock.bind(this)(); print("Created", this.boxID); //function posChecker() { @@ -561,6 +563,8 @@ stepFarGrab.prototype = { // Create table ? // Create blocks and basket this.boxID = createBlock.bind(this)(); + this.boxID = createBlock.bind(this)(); + this.boxID = createBlock.bind(this)(); print("Created", this.boxID); Messages.subscribe("Entity-Exploded"); From c411722d52f405ec5f1bc6654ec52f5c3cc34759 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 16 Sep 2016 13:34:03 -0700 Subject: [PATCH 047/109] Cleanup tutorial --- tutorial/controllerDisplay.js | 6 +- tutorial/spinner.js | 4 +- tutorial/tutorial.js | 167 +++++++++++----------------------- 3 files changed, 57 insertions(+), 120 deletions(-) diff --git a/tutorial/controllerDisplay.js b/tutorial/controllerDisplay.js index 85dd50ce26..5626c6897a 100644 --- a/tutorial/controllerDisplay.js +++ b/tutorial/controllerDisplay.js @@ -30,17 +30,13 @@ createControllerDisplay = function(config) { print("Setting layer...", partName, layerName); if (partName in this.parts) { var part = this.parts[partName]; - //print("FOnd", JSON.stringify(part)); - if (layerName in part.textureLayers) { - //print("got it", layerName); + if (part.textureLayers && layerName in part.textureLayers) { var layer = part.textureLayers[layerName]; var textures = {}; if (layer.defaultTextureURL) { - //print("default texture"); textures[part.textureName] = layer.defaultTextureURL; } for (var i = 0; i < this.partOverlays[partName].length; ++i) { - //print("updating", JSON.stringify(textures)); Overlays.editOverlay(this.partOverlays[partName][i], { textures: textures }); diff --git a/tutorial/spinner.js b/tutorial/spinner.js index 348e250bec..bf0f1274ed 100644 --- a/tutorial/spinner.js +++ b/tutorial/spinner.js @@ -25,8 +25,8 @@ }); var injector = Audio.playSound(spinnerSound, { position: Entities.getEntityProperties(this.entityID, 'position').position, - volume: 0.7, - loop: true + volume: 1.0, + loop: false }); print("HERE2"); diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 9512220f62..0671267b67 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -300,6 +300,8 @@ var stepOrient = function(name) { } stepOrient.prototype = { start: function(onFinish) { + this.active = true; + var tag = this.tag; var defaultTransform = { @@ -334,20 +336,26 @@ stepOrient.prototype = { if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; - playSuccessSound(); location = "/tutorial"; + Script.setTimeout(playSuccessSound, 150); + this.active = false; onFinish(); } } this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); }, cleanup: function() { + if (this.active) { + location = "/tutorial"; + this.active = false; + } if (this.overlay) { this.overlay.destroy(); this.overlay = null; } - if (this.checkIntervalID != null) { + if (this.checkIntervalID) { Script.clearInterval(this.checkIntervalID); + this.checkIntervalID = null; } editEntitiesWithTag(this.tag, { visible: false, collisionless: 1 }); deleteEntitiesWithTag(this.tempTag); @@ -383,12 +391,12 @@ stepRaiseAboveHead.prototype = { }; // Spawn content set - //spawnWithTag(HandsAboveHeadData, defaultTransform, tag); print("raise hands...", this.tag); editEntitiesWithTag(this.tag, { visible: true }); - Script.setTimeout(function() { + // Wait 2 seconds before starting to check for hands + this.waitTimeoutID = Script.setTimeout(function() { this.checkIntervalID = null; function checkForHandsAboveHead() { print("Checking for hands above head..."); @@ -396,7 +404,6 @@ stepRaiseAboveHead.prototype = { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; playSuccessSound(); - //location = "/tutorial"; onFinish(); } } @@ -404,8 +411,13 @@ stepRaiseAboveHead.prototype = { }.bind(this), 2000); }, cleanup: function() { - if (this.checkIntervalID != null) { + if (this.checkIntervalID) { Script.clearInterval(this.checkIntervalID); + this.checkIntervalID = null + } + if (this.waitTimeoutID) { + Script.clearTimeout(this.waitTimeoutID); + this.waitTimeoutID = null; } editEntitiesWithTag(this.tag, { visible: false, collisionless: 1 }); deleteEntitiesWithTag(this.tempTag); @@ -440,6 +452,10 @@ function setControllerPartLayer(part, layer) { var stepNearGrab = function(name) { this.tag = name; this.tempTag = name + "-temporary"; + this.birdIDs = []; + + Messages.subscribe("Entity-Exploded"); + Messages.messageReceived.connect(this.onMessage.bind(this)); } stepNearGrab.prototype = { start: function(onFinish) { @@ -455,9 +471,6 @@ stepNearGrab.prototype = { showEntitiesWithTag(this.tag, { visible: true }); showEntitiesWithTag('bothGrab', { visible: true }); - var basketColliderID = findEntity({ name: NEAR_BASKET_COLLIDER_NAME }, 10000); - var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; - var boxSpawnID = findEntity({ name: NEAR_BOX_SPAWN_NAME }, 10000); if (!boxSpawnID) { print("Error creating block, cannot find spawn"); @@ -473,17 +486,10 @@ stepNearGrab.prototype = { // Enabled grab // Create table ? // Create blocks and basket - this.boxID = createBlock.bind(this)(); - this.boxID = createBlock.bind(this)(); - this.boxID = createBlock.bind(this)(); - print("Created", this.boxID); - - //function posChecker() { - //Vec3.distance( - //} - - Messages.subscribe("Entity-Exploded"); - Messages.messageReceived.connect(this.onMessage.bind(this)); + this.birdIDs = []; + this.birdIDs.push(createBlock.bind(this)()); + this.birdIDs.push(createBlock.bind(this)()); + this.birdIDs.push(createBlock.bind(this)()); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, @@ -493,12 +499,13 @@ stepNearGrab.prototype = { } if (channel == "Entity-Exploded") { print("TUTORIAL: Got entity-exploded message"); - playSuccessSound(); + var data = parseJSON(message); - //if (data.entityID == this.boxID) { + if (this.birdIDs.indexOf(data.entityID) >= 0) { + playSuccessSound(); this.finished = true; this.onFinish(); - //} + } } }, cleanup: function() { @@ -522,6 +529,11 @@ stepNearGrab.prototype = { var stepFarGrab = function(name) { this.tag = name; this.tempTag = name + "-temporary"; + this.finished = true; + this.birdIDs = []; + + Messages.subscribe("Entity-Exploded"); + Messages.messageReceived.connect(this.onMessage.bind(this)); } stepFarGrab.prototype = { start: function(onFinish) { @@ -545,9 +557,6 @@ stepFarGrab.prototype = { //spawnWithTag(Step1EntityData, transform, tag); showEntitiesWithTag(this.tag); - var basketColliderID = findEntity({ name: FAR_BASKET_COLLIDER_NAME }, 10000); - var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; - function createBlock() { var boxSpawnID = findEntity({ name: FAR_BOX_SPAWN_NAME }, 10000); if (!boxSpawnID) { @@ -559,30 +568,10 @@ stepFarGrab.prototype = { return spawnWithTag([birdFirework1], null, this.tempTag)[0]; } - // Enabled grab - // Create table ? - // Create blocks and basket - this.boxID = createBlock.bind(this)(); - this.boxID = createBlock.bind(this)(); - this.boxID = createBlock.bind(this)(); - print("Created", this.boxID); - - Messages.subscribe("Entity-Exploded"); - Messages.messageReceived.connect(this.onMessage.bind(this)); - - // When block collides with basket start step 2 - //var checkCollidesTimer = null; - // function checkCollides() { - // print("CHECKING..."); - // if (Vec3.distance(basketPosition, Entities.getEntityProperties(this.boxID, 'position').position) < 0.2) { - // Script.clearInterval(checkCollidesTimer); - // playSuccessSound(); - // Script.setTimeout(onHit.bind(this), 1000); - // } - // } - // checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); - - // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block + this.birdIDs = []; + this.birdIDs.push(createBlock.bind(this)()); + this.birdIDs.push(createBlock.bind(this)()); + this.birdIDs.push(createBlock.bind(this)()); }, onMessage: function(channel, message, seneder) { if (this.finished) { @@ -590,16 +579,15 @@ stepFarGrab.prototype = { } if (channel == "Entity-Exploded") { print("TUTORIAL: Got entity-exploded message"); - playSuccessSound(); var data = parseJSON(message); - if (data.entityID == this.boxID) { + if (this.birdIDs.indexOf(data.entityID) >= 0) { + playSuccessSound(); this.finished = true; this.onFinish(); } } }, cleanup: function() { - //Messages.messageReceived.disconnect(this.onMessage.bind(this)); this.finished = true; setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); @@ -625,6 +613,9 @@ var stepEquip = function(name) { this.PART1 = 0; this.PART2 = 1; this.COMPLETE = 2; + + Messages.subscribe('Tutorial-Spinner'); + Messages.messageReceived.connect(this.onMessage.bind(this)); } stepEquip.prototype = { start: function(onFinish) { @@ -636,30 +627,12 @@ stepEquip.prototype = { var tag = this.tag; - var defaultTransform = { - position: { - x: 0.0, - y: 0.0, - z: 0.75 - }, - rotation: { - x: 0, - y: 0, - z: 0, - w: 1 - } - }; - // Spawn content set - //spawnWithTag(StepGunData, defaultTransform, tag); showEntitiesWithTag(this.tag); showEntitiesWithTag(this.tagPart1); this.currentPart = this.PART1; - var basketColliderID = findEntity({ name: GUN_BASKET_COLLIDER_NAME }, 10000); - var basketPosition = Entities.getEntityProperties(basketColliderID, 'position').position; - function createGun() { var boxSpawnID = findEntity({ name: GUN_SPAWN_NAME }, 10000); if (!boxSpawnID) { @@ -669,14 +642,10 @@ stepEquip.prototype = { var transform = {}; - GunData.position = Entities.getEntityProperties(boxSpawnID, 'position').position; - GunData.rotation = Entities.getEntityProperties(boxSpawnID, 'rotation').rotation; transform.position = Entities.getEntityProperties(boxSpawnID, 'position').position; transform.rotation = Entities.getEntityProperties(boxSpawnID, 'rotation').rotation; this.spawnTransform = transform; - Vec3.print("spawn", GunData.position); - print("Adding: ", JSON.stringify(GunData)); - return doCreateButaneLighter(transform).id;//spawnWithTag([GunData], null, this.tempTag)[0]; + return doCreateButaneLighter(transform).id; } @@ -687,30 +656,6 @@ stepEquip.prototype = { this.startWatchingGun(); print("Created", this.gunID); this.onFinish = onFinish; - Messages.subscribe('Tutorial-Spinner'); - Messages.messageReceived.connect(this.onMessage.bind(this)); - -// function onHit() { -// } -// -// // When block collides with basket start step 2 -// function checkCollides() { -// //print("CHECKING FOR PING PONG..."); -// var ammoIDs = findEntities({ name: GUN_AMMO_NAME }, 15); -// for (var i = 0; i < ammoIDs.length; ++i) { -// if (Vec3.distance(basketPosition, Entities.getEntityProperties(ammoIDs[i], 'position').position) < 0.25) { -// Script.clearInterval(this.checkCollidesTimer); -// this.checkCollidesTimer = null; -// playSuccessSound(); -// Script.setTimeout(onHit.bind(this), 1000); -// return; -// } -// } -// } -// this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 100); - - - // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, startWatchingGun: function() { if (!this.watcherIntervalID) { @@ -733,7 +678,9 @@ stepEquip.prototype = { if (this.currentPart == this.COMPLETE) { return; } + print("Got message", channel, message, sender, MyAvatar.sessionUUID); + if (channel == "Tutorial-Spinner") { if (this.currentPart == this.PART1 && message == "wasLit") { Script.setTimeout(function() { @@ -742,38 +689,32 @@ stepEquip.prototype = { showEntitiesWithTag(this.tagPart2); setControllerPartLayer('tips', 'grip'); Messages.subscribe('Hifi-Object-Manipulation'); - }.bind(this), 2000); + }.bind(this), 9000); } } else if (channel == "Hifi-Object-Manipulation") { if (this.currentPart == this.PART2) { var data = parseJSON(message); - print("Here", data.action, data.grabbedEntity, this.gunID); if (data.action == 'release' && data.grabbedEntity == this.gunID) { print("got release"); this.stopWatchingGun(); - try { - Messages.messageReceived.disconnect(this.onMessage); - } catch(e) { - } playSuccessSound(); - print("FINISHED"); Script.setTimeout(this.onFinish.bind(this), 1500); this.currentPart = this.COMPLETE; - //this.onFinish(); } } } }, cleanup: function() { + if (this.watcherIntervalID) { + Script.clearInterval(this.watcherIntervalID); + this.watcherIntervalID = null; + } + setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); this.stopWatchingGun(); this.currentPart = this.COMPLETE; - try { - Messages.messageReceived.disconnect(this.onMessage); - } catch(e) { - print("error disconnecting"); - } + if (this.checkCollidesTimer) { Script.clearInterval(this.checkCollidesTimer); } From 69e3e429f173e7853b4542a11faf2de297b835f7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 16 Sep 2016 14:32:30 -0700 Subject: [PATCH 048/109] Update particle rate and fix default vive controller labels --- tutorial/spinner.js | 2 +- tutorial/viveControllerConfiguration.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorial/spinner.js b/tutorial/spinner.js index bf0f1274ed..4d34f31890 100644 --- a/tutorial/spinner.js +++ b/tutorial/spinner.js @@ -35,7 +35,7 @@ var props = childrenProps[childEntityID]; if (props.type == "ParticleEffect") { Entities.editEntity(childEntityID, { - emitRate: 140, + emitRate: 35, }); } } diff --git a/tutorial/viveControllerConfiguration.js b/tutorial/viveControllerConfiguration.js index 3604f4a412..a33a701641 100644 --- a/tutorial/viveControllerConfiguration.js +++ b/tutorial/viveControllerConfiguration.js @@ -246,7 +246,7 @@ VIVE_CONTROLLER_CONFIGURATION = { textureName: "Tex.Blank", - defaultTextureLayer: "trigger", + defaultTextureLayer: "blank", textureLayers: { blank: { defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Blank.png", From 23e2447c602842870f81d341013e940cc8171140 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 16 Sep 2016 16:02:13 -0700 Subject: [PATCH 049/109] Add position watching to near and far grab --- tutorial/entityData.js | 2 +- tutorial/tutorial.js | 51 ++++++++++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/tutorial/entityData.js b/tutorial/entityData.js index cd2579f018..1d36293586 100644 --- a/tutorial/entityData.js +++ b/tutorial/entityData.js @@ -241,7 +241,7 @@ birdFirework2 = { "collisionsWillMove": 1, velocity: { x: 0, - y: -0.2, + y: -0.01, z: 0 }, "dynamic": 1, diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 0671267b67..9e8982bef4 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -490,6 +490,7 @@ stepNearGrab.prototype = { this.birdIDs.push(createBlock.bind(this)()); this.birdIDs.push(createBlock.bind(this)()); this.birdIDs.push(createBlock.bind(this)()); + this.positionWatcher = new PositionWatcher(this.birdIDs, boxSpawnPosition, -0.4, 4); // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, @@ -515,6 +516,10 @@ stepNearGrab.prototype = { setControllerPartLayer('tips', 'blank'); hideEntitiesWithTag(this.tag, { visible: false}); deleteEntitiesWithTag(this.tempTag); + if (this.positionWatcher) { + this.positionWatcher.destroy(); + this.positionWatcher = null; + } } }; @@ -548,23 +553,18 @@ stepFarGrab.prototype = { farGrabEnabled: true, })); var tag = this.tag; - var transform = { - position: { x: 3, y: 0, z: 0 }, - rotation: { x: 0, y: 0, z: 0, w: 1 } - } // Spawn content set - //spawnWithTag(Step1EntityData, transform, tag); showEntitiesWithTag(this.tag); + var boxSpawnID = findEntity({ name: FAR_BOX_SPAWN_NAME }, 10000); + if (!boxSpawnID) { + print("Error creating block, cannot find spawn"); + return null; + } + var boxSpawnPosition = Entities.getEntityProperties(boxSpawnID, 'position').position; function createBlock() { - var boxSpawnID = findEntity({ name: FAR_BOX_SPAWN_NAME }, 10000); - if (!boxSpawnID) { - print("Error creating block, cannot find spawn"); - return null; - } - - birdFirework1.position = Entities.getEntityProperties(boxSpawnID, 'position').position; + birdFirework1.position = boxSpawnPosition; return spawnWithTag([birdFirework1], null, this.tempTag)[0]; } @@ -572,6 +572,7 @@ stepFarGrab.prototype = { this.birdIDs.push(createBlock.bind(this)()); this.birdIDs.push(createBlock.bind(this)()); this.birdIDs.push(createBlock.bind(this)()); + this.positionWatcher = new PositionWatcher(this.birdIDs, boxSpawnPosition, -0.4, 4); }, onMessage: function(channel, message, seneder) { if (this.finished) { @@ -594,6 +595,32 @@ stepFarGrab.prototype = { hideEntitiesWithTag(this.tag, { visible: false}); hideEntitiesWithTag('bothGrab', { visible: false}); deleteEntitiesWithTag(this.tempTag); + if (this.positionWatcher) { + this.positionWatcher.destroy(); + this.positionWatcher = null; + } + } +}; + +function PositionWatcher(entityIDs, originalPosition, minY, maxDistance) { + this.watcherIntervalID = Script.setInterval(function() { + for (var i = 0; i < entityIDs.length; ++i) { + var entityID = entityIDs[i]; + var props = Entities.getEntityProperties(entityID, ['position']); + if (props.position.y < minY || Vec3.distance(originalPosition, props.position) > maxDistance) { + Entities.editEntity(entityID, { + position: originalPosition, + velocity: { x: 0, y: -0.01, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0 } + }); + } + } + }, 1000); +} + +PositionWatcher.prototype = { + destroy: function() { + Script.clearInterval(this.watcherIntervalID); } }; From 2bf13b9cf6aca2c038556f71cf46e7035411d76e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 16 Sep 2016 16:10:43 -0700 Subject: [PATCH 050/109] Fix fuse getting lit 2+ times in tutorial --- tutorial/tutorial.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 9e8982bef4..537931080d 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -710,8 +710,8 @@ stepEquip.prototype = { if (channel == "Tutorial-Spinner") { if (this.currentPart == this.PART1 && message == "wasLit") { + this.currentPart = this.PART2; Script.setTimeout(function() { - this.currentPart = this.PART2; hideEntitiesWithTag(this.tagPart1); showEntitiesWithTag(this.tagPart2); setControllerPartLayer('tips', 'grip'); @@ -724,9 +724,9 @@ stepEquip.prototype = { if (data.action == 'release' && data.grabbedEntity == this.gunID) { print("got release"); this.stopWatchingGun(); + this.currentPart = this.COMPLETE; playSuccessSound(); Script.setTimeout(this.onFinish.bind(this), 1500); - this.currentPart = this.COMPLETE; } } } From a05f68170474bef32a9d1dd5b8ed3cce808ed213 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Sep 2016 13:02:33 -0700 Subject: [PATCH 051/109] Add ownership token tot tutorial --- tutorial/ownershipToken.js | 188 +++++++++++++++++++++++++++++++++++ tutorial/tutorial.js | 197 ++++++++++++++++++++----------------- 2 files changed, 292 insertions(+), 93 deletions(-) create mode 100644 tutorial/ownershipToken.js diff --git a/tutorial/ownershipToken.js b/tutorial/ownershipToken.js new file mode 100644 index 0000000000..ae212baa84 --- /dev/null +++ b/tutorial/ownershipToken.js @@ -0,0 +1,188 @@ +if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 + // internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function() {}, + fBound = function() { + return fToBind.apply(this instanceof fNOP + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + if (this.prototype) { + // Function.prototype doesn't have a prototype property + fNOP.prototype = this.prototype; + } + fBound.prototype = new fNOP(); + + return fBound; + }; +} + +function getOption(options, key, defaultValue) { + if (options.hasOwnProperty(key)) { + return options[key]; + } + return defaultValue; +} + +var TOKEN_NAME_PREFIX = "ownership_token-"; + +function getOwnershipTokenID(parentEntityID) { + var childEntityIDs = Entities.getChildrenIDs(parentEntityID); + var ownerID = null; + var ownerName = ''; + for (var i = 0; i < childEntityIDs.length; ++i) { + var childID = childEntityIDs[i]; + var properties = Entities.getEntityProperties(childID, ['name', 'userData', 'lifetime', 'age']); + var childName = properties.name; + //debug("Owner lifetime: ", properties.lifetime, properties.age); + if (properties.age > 0.5 && childName.indexOf(TOKEN_NAME_PREFIX) == 0) { + if (ownerID === null || childName < ownerName) { + ownerID = childID; + ownerName = childName; + } + } + } + return ownerID; +} + +function createOwnershipToken(name, parentEntityID) { + return Entities.addEntity({ + type: "Box", + name: TOKEN_NAME_PREFIX + name, + visible: false, + parentID: parentEntityID, + locationPosition: { x: 0, y: 0, z: 0 }, + dimensions: { x: 100, y: 100, z: 100 }, + collisionless: true, + lifetime: 5 + }); +} + +var DEBUG = true; +function debug() { + if (DEBUG) { + var args = Array.prototype.slice.call(arguments); + print.apply(this, args); + } +} + +var TOKEN_STATE_DESTROYED = -1; +var TOKEN_STATE_UNOWNED = 0; +var TOKEN_STATE_REQUESTING_OWNERSHIP = 1; +var TOKEN_STATE_OWNED = 2; + +OwnershipToken = function(name, parentEntityID, options) { + this.name = MyAvatar.sessionUUID + "-" + Math.floor(Math.random() * 10000000); + this.name = Math.floor(Math.random() * 10000000); + this.parentEntityID = parentEntityID; + + // How often to check whether the token is available if we don't currently own it + this.checkEverySeconds = getOption(options, 'checkEverySeconds', 1000); + this.updateTokenLifetimeEvery = getOption(options, 'updateTokenLifetimeEvery', 2000); + + //this.onRequestingOwnership = getOption(options, 'onRequestingOwnership', function() { }); + this.onGainedOwnership = getOption(options, 'onGainedOwnership', function() { }); + this.onLostOwnership = getOption(options, 'onLostOwnership', function() { }); + + this.ownershipTokenID = null; + this.setState(TOKEN_STATE_UNOWNED); +}; + +OwnershipToken.prototype = { + destroy: function() { + debug(this.name, "Destroying token"); + this.setState(TOKEN_STATE_DESTROYED); + }, + + setState: function(newState) { + if (this.state == newState) { + debug(this.name, "Warning: Trying to set state to the current state"); + return; + } + + if (this.updateLifetimeID) { + Script.clearInterval(this.updateLifetimeID); + this.updateLifetimeID = null; + } + + if (this.checkOwnershipAvailableID) { + Script.clearInterval(this.checkOwnershipAvailableID); + this.checkOwnershipAvailableID = null; + } + + if (this.state == TOKEN_STATE_OWNED) { + this.onLostOwnership(this); + } + + if (newState == TOKEN_STATE_UNOWNED) { + this.checkOwnershipAvailableID = Script.setInterval( + this.tryRequestingOwnership.bind(this), this.checkEverySeconds); + + } else if (newState == TOKEN_STATE_REQUESTING_OWNERSHIP) { + + } else if (newState == TOKEN_STATE_OWNED) { + this.onGainedOwnership(this); + this.updateLifetimeID = Script.setInterval( + this.updateTokenLifetime.bind(this), this.updateTokenLifetimeEvery); + } else if (newState == TOKEN_STATE_DESTROYED) { + Entities.deleteEntity(this.ownershipTokenID); + } + + debug(this.name, "Info: Switching to state:", newState); + this.state = newState; + }, + updateTokenLifetime: function() { + if (this.state != TOKEN_STATE_OWNED) { + debug(this.name, "Error: Trying to update token while it is unowned"); + return; + } + + debug(this.name, "Updating entity lifetime"); + var age = Entities.getEntityProperties(this.ownershipTokenID, 'age').age; + Entities.editEntity(this.ownershipTokenID, { + lifetime: age + 5 + }); + }, + tryRequestingOwnership: function() { + if (this.state == TOKEN_STATE_REQUESTING_OWNERSHIP || this.state == TOKEN_STATE_OWNED) { + debug(this.name, "We already have or are requesting ownership"); + return; + } + + var ownerID = getOwnershipTokenID(this.parentEntityID); + if (ownerID !== null) { + // Already owned, return + debug(this.name, "Token already owned by another client, return"); + return; + } + + this.ownershipTokenID = createOwnershipToken(this.name, this.parentEntityID); + this.setState(TOKEN_STATE_REQUESTING_OWNERSHIP); + + function checkOwnershipRequest() { + var ownerID = getOwnershipTokenID(this.parentEntityID); + if (ownerID == this.ownershipTokenID) { + debug(this.name, "Info: Obtained ownership"); + this.setState(TOKEN_STATE_OWNED); + } else { + if (ownerID === null) { + debug(this.name, "Warning: Checked ownership request and no tokens existed"); + } + debug(this.name, "Info: Lost ownership request") + this.ownershipTokenID = null; + this.setState(TOKEN_STATE_UNOWNED); + } + } + + Script.setTimeout(checkOwnershipRequest.bind(this), 2000); + }, +}; diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 537931080d..3ccb994ba3 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -30,6 +30,7 @@ Script.include("entityData.js"); Script.include("viveHandsv2.js"); Script.include("lighter/createButaneLighter.js"); +Script.include('ownershipToken.js'); var BASKET_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx"; var BASKET_COLLIDER_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj"; @@ -146,19 +147,19 @@ function spawnWithTag(entityData, transform, tag) { } function deleteEntitiesWithTag(tag) { - print("searching for...:", tag); + print("searching for...:", tag); var entityIDs = findEntitiesWithTag(tag); for (var i = 0; i < entityIDs.length; ++i) { - print("Deleteing:", entityIDs[i]); + //print("Deleteing:", entityIDs[i]); Entities.deleteEntity(entityIDs[i]); } } function editEntitiesWithTag(tag, propertiesOrFn) { - print("Editing:", tag); + //print("Editing:", tag); var entityIDs = findEntitiesWithTag(tag); - print("Editing...", entityIDs); + //print("Editing...", entityIDs); for (var i = 0; i < entityIDs.length; ++i) { - print("Editing...", entityIDs[i]); + //print("Editing...", entityIDs[i]); if (isFunction(propertiesOrFn)) { Entities.editEntity(entityIDs[i], propertiesOrFn(entityIDs[i])); } else { @@ -318,11 +319,11 @@ stepOrient.prototype = { } }; - this.overlay = new StayInFrontOverlay("model", { - url: "http://hifi-content.s3.amazonaws.com/alan/dev/Prompt-Cards/welcome.fbx?11", - ignoreRayIntersection: true, - visible: false - }, 1.5, { x: 0, y: 0.3, z: 0 }); + // this.overlay = new StayInFrontOverlay("model", { + // url: "http://hifi-content.s3.amazonaws.com/alan/dev/Prompt-Cards/welcome.fbx?11", + // ignoreRayIntersection: true, + // visible: false + // }, 1.5, { x: 0, y: 0.3, z: 0 }); // Spawn content set //spawnWithTag(HandsAboveHeadData, defaultTransform, tag); @@ -952,105 +953,115 @@ function hideEntitiesWithTag(tag) { }); } -var STEPS; -var currentStepNum = -1; -var currentStep = null; -function startTutorial() { - currentStepNum = -1; - currentStep = null; - STEPS = [ - new stepDisableControllers("step0"), - new stepOrient("orient"), - //new stepWelcome("welcome"), - new stepRaiseAboveHead("raiseHands"), - new stepNearGrab("nearGrab"), - new stepFarGrab("farGrab"), - new stepEquip("equip"), - new stepTurnAround("turnAround"), - new stepTeleport("teleport"), - new stepFinish("finish"), - ]; - for (var i = 0; i < STEPS.length; ++i) { - STEPS[i].cleanup(); - } - location = "/tutorial_begin"; - //location = "/tutorial"; - MyAvatar.shouldRenderLocally = false; - startNextStep(); -} +TutorialManager = function() { + var STEPS; -function startNextStep() { - if (currentStep) { - currentStep.cleanup(); - } + var currentStepNum = -1; + var currentStep = null; - ++currentStepNum; - - if (currentStepNum >= STEPS.length) { - // Done - print("DONE WITH TUTORIAL"); + this.startTutorial = function() { + currentStepNum = -1; + currentStep = null; + STEPS = [ + new stepDisableControllers("step0"), + new stepOrient("orient"), + //new stepWelcome("welcome"), + new stepRaiseAboveHead("raiseHands"), + new stepNearGrab("nearGrab"), + new stepFarGrab("farGrab"), + new stepEquip("equip"), + new stepTurnAround("turnAround"), + new stepTeleport("teleport"), + new stepFinish("finish"), + ]; + for (var i = 0; i < STEPS.length; ++i) { + STEPS[i].cleanup(); + } + location = "/tutorial_begin"; + //location = "/tutorial"; + MyAvatar.shouldRenderLocally = false; + this.startNextStep(); + } + + this.startNextStep = function() { + if (currentStep) { + currentStep.cleanup(); + } + + ++currentStepNum; + + if (currentStepNum >= STEPS.length) { + // Done + print("DONE WITH TUTORIAL"); + currentStepNum = -1; + currentStep = null; + return false; + } else { + print("Starting step", currentStepNum); + currentStep = STEPS[currentStepNum]; + currentStep.start(this.startNextStep); + return true; + } + }.bind(this); + this.restartStep = function() { + if (currentStep) { + currentStep.cleanup(); + currentStep.start(this.startNextStep); + } + } + + this.stopTutorial = function() { + if (currentStep) { + currentStep.cleanup(); + } currentStepNum = -1; currentStep = null; - return false; - } else { - print("Starting step", currentStepNum); - currentStep = STEPS[currentStepNum]; - currentStep.start(startNextStep); - return true; } } -function restartStep() { - if (currentStep) { - currentStep.cleanup(); - currentStep.start(startNextStep); - } -} - -function skipTutorial() { -} - -function stopTutorial() { - if (currentStep) { - currentStep.cleanup(); - } - currentStepNum = -1; - currentStep = null; -} - -startTutorial(); Script.scriptEnding.connect(function() { Controller.enableMapping('handControllerPointer-click'); }); Controller.disableMapping('handControllerPointer-click'); -//mapping.from([Controller.Standard.RY]).to(noop); - //{ "from": "Vive.LeftApplicationMenu", "to": "Standard.LeftSecondaryThumb" }, -//mapping.from([Controller.Standard.RY]).when("Controller.Application.Grounded").to(noop); -//mapping.from([Controller.Standard.RY]).when(Controller.Application.Grounded).to(noop); +// var entityID = '{be3d10a3-262a-4827-b30c-ec025c4325dc}'; +// var token = new OwnershipToken(Math.random() * 100000, entityID, { +// onGainedOwnership: function(token) { +// //Script.setTimeout(function() { token.destroy() }, 15000); +// Controller.keyReleaseEvent.connect(keyReleaseHandler); +// startTutorial(); +// }, +// onLostOwnership: function(token) { +// Controller.keyReleaseEvent.disconnect(keyReleaseHandler); +// stopTutorial(); +// } +// }); - -Script.scriptEnding.connect(stopTutorial); - - - -Controller.keyReleaseEvent.connect(function (event) { - print(event.text); - if (event.text == ",") { - if (!startNextStep()) { - startTutorial(); - } - } else if (event.text == "F11") { - restartStep(); - } else if (event.text == "F10") { - MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; - } else if (event.text == "r") { - stopTutorial(); - startTutorial(); - } +//tutorialManager = new TutorialManager(); +//tutorialManager.startTutorial(); +//Controller.keyReleaseEvent.connect(keyReleaseHandler); +Script.scriptEnding.connect(function() { + //token.destroy(); + //stopTutorial(); }); +// function keyReleaseHandler(event) { +// print(event.text); +// if (event.text == ",") { +// if (!tutorialManager.startNextStep()) { +// tutorialManager.startTutorial(); +// } +// } else if (event.text == "F11") { +// tutorialManager.restartStep(); +// } else if (event.text == "F10") { +// MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; +// } else if (event.text == "r") { +// tutorialManager.stopTutorial(); +// tutorialManager.startTutorial(); +// } +// } +// // Messages.sendLocalMessage('Controller-Display', JSON.stringify({ // name: "menu", // visible: false, From 5714535fb404f0d44fc4b16a2e7cc0fb7b091ce0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 19 Sep 2016 16:11:40 -0700 Subject: [PATCH 052/109] Disable going to /tutorial when finishing first step in tut --- tutorial/tutorial.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 3ccb994ba3..1d768fe148 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -347,7 +347,7 @@ stepOrient.prototype = { }, cleanup: function() { if (this.active) { - location = "/tutorial"; + //location = "/tutorial"; this.active = false; } if (this.overlay) { From 4efeb928e2ff62b3642e4e2bee79ad2661ab0480 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 20 Sep 2016 08:21:44 -0700 Subject: [PATCH 053/109] Add user activity logging to tutorial --- .../UserActivityLoggerScriptingInterface.cpp | 10 +++++++++ .../UserActivityLoggerScriptingInterface.h | 1 + tutorial/tutorial.js | 22 +++++++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index 8b22b8ff58..de3238f08d 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -24,6 +24,16 @@ void UserActivityLoggerScriptingInterface::toggledAway(bool isAway) { logAction("toggled_away", { { "is_away", isAway } }); } +void UserActivityLoggerScriptingInterface::tutorialProgress(QString stepName, int stepNumber, float secondsToComplete, float tutorialElapsedTime) { + logAction("tutorial_progress", { + { "step", stepName }, + { "stepNumber", stepNumber }, + { "secondsToComplete", secondsToComplete }, + { "tutorial_elapsed_time", tutorialElapsedTime } + }); + +} + void UserActivityLoggerScriptingInterface::logAction(QString action, QJsonObject details) { QMetaObject::invokeMethod(&UserActivityLogger::getInstance(), "logAction", Q_ARG(QString, action), diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index 9d60d666e2..bf3e20a2d7 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -23,6 +23,7 @@ public: Q_INVOKABLE void enabledEdit(); Q_INVOKABLE void openedMarketplace(); Q_INVOKABLE void toggledAway(bool isAway); + Q_INVOKABLE void tutorialProgress(QString stepName, int stepNumber, float secondsToComplete, float tutorialElapsedTime); private: void logAction(QString action, QJsonObject details = {}); diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 1d768fe148..a01d1616e0 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -210,6 +210,7 @@ function playSuccessSound() { /////////////////////////////////////////////////////////////////////////////// var stepDisableControllers = function(name) { this.tag = name; + this.shouldLog = false; } stepDisableControllers.prototype = { start: function(onFinish) { @@ -959,10 +960,15 @@ TutorialManager = function() { var currentStepNum = -1; var currentStep = null; + var startedTutorialAt = 0; + var startedLastStepAt = 0; + + var self = this; this.startTutorial = function() { currentStepNum = -1; currentStep = null; + startedTutorialAt = Date.now(); STEPS = [ new stepDisableControllers("step0"), new stepOrient("orient"), @@ -984,6 +990,17 @@ TutorialManager = function() { this.startNextStep(); } + this.onFinish = function() { + if (currentStep && currentStep.shouldLog !== false) { + var timeToFinishStep = (Date.now() - startedLastStepAt) / 1000; + var tutorialTimeElapsed = (Date.now() - startedTutorialAt) / 1000; + UserActivityLogger.tutorialProgress( + currentStep.tag, currentStepNum, timeToFinishStep, tutorialTimeElapsed); + } + + self.startNextStep(); + } + this.startNextStep = function() { if (currentStep) { currentStep.cleanup(); @@ -1000,14 +1017,15 @@ TutorialManager = function() { } else { print("Starting step", currentStepNum); currentStep = STEPS[currentStepNum]; - currentStep.start(this.startNextStep); + startedLastStepAt = Date.now(); + currentStep.start(this.onFinish); return true; } }.bind(this); this.restartStep = function() { if (currentStep) { currentStep.cleanup(); - currentStep.start(this.startNextStep); + currentStep.start(this.onFinish); } } From 03c01bb9b03a805ec5d3003f42261f4b34352622 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 20 Sep 2016 08:34:39 -0700 Subject: [PATCH 054/109] Add exception handling to callEntityMethod calls --- libraries/script-engine/src/ScriptEngine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 8a0de3e168..7a4265829b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1533,6 +1533,7 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& s #else operation(); #endif + hadUncaughtExceptions(*this, _fileNameString); currentEntityIdentifier = oldIdentifier; currentSandboxURL = oldSandboxURL; From 39e4ad43001e4cff208a01f812eae1bc1021f998 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 20 Sep 2016 13:23:49 -0700 Subject: [PATCH 055/109] Cleanup controllerDisplay.js --- tutorial/controllerDisplay.js | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/tutorial/controllerDisplay.js b/tutorial/controllerDisplay.js index 5626c6897a..8a4f7a8c5d 100644 --- a/tutorial/controllerDisplay.js +++ b/tutorial/controllerDisplay.js @@ -1,6 +1,5 @@ var DEBUG = false; var VISIBLE_BY_DEFAULT = false; -//var PARENT_ID = MyAvatar.sessionUUID; var PARENT_ID = "{00000000-0000-0000-0000-000000000001}"; createControllerDisplay = function(config) { @@ -49,7 +48,6 @@ createControllerDisplay = function(config) { for (var i = 0; i < config.controllers.length; ++i) { var controller = config.controllers[i]; var position = controller.position; - //position = { x: 0, y: 5, z: 5 }; Vec3.print("position", position); print("position", position.x, position.y, position.z); if (controller.naturalPosition) { @@ -158,9 +156,6 @@ createControllerDisplay = function(config) { var partPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition)); var innerRotation = controller.rotation - //Vec3.print("controller", controller.position); - //Vec3.print("part", partPosition); - controllerDisplay.parts[partName] = controller.parts[partName]; var properties = { @@ -170,7 +165,6 @@ createControllerDisplay = function(config) { parentID: PARENT_ID, parentJointIndex: controller.jointIndex, ignoreRayIntersection: true, - //visible: false }; if (part.defaultTextureLayer) { @@ -185,23 +179,16 @@ createControllerDisplay = function(config) { var range = part.maxValue - part.minValue; mapping.from([part.input]).peek().to(function(controller, overlayID, part) { return function(value) { - //print(value); - //print(JSON.stringify(part)); - value = clamp(value, part.minValue, part.maxValue); var pct = (value - part.minValue) / part.maxValue; var angle = pct * part.maxAngle; var rotation = Quat.angleAxis(angle, part.axis); - //print(value, pct, angle); var offset = { x: 0, y: 0, z: 0 }; if (part.origin) { - //print(rotation.x, rotation.y, rotation.z, rotation.w); var offset = Vec3.multiplyQbyV(rotation, part.origin); offset = Vec3.subtract(offset, part.origin); - //Vec3.print('offset', offset); - //partPosition = Vec3.sum(partPosition, offset); } var partPosition = Vec3.sum(controller.position, @@ -230,14 +217,14 @@ createControllerDisplay = function(config) { var xinput = resolveHardware(part.xInput); var yinput = resolveHardware(part.yInput); + // TODO: Touchpad inputs are currently only working for half + // of the touchpad. When that is fixed, it would be useful + // to update these to display the current finger position. mapping.from([visibleInput]).peek().to(function(value) { - //print("visible", value); }); mapping.from([xinput]).peek().to(function(value) { - //print("X", value); }); mapping.from([yinput]).peek().invert().to(function(value) { - //print("Y", value); }); } else if (part.type == "static") { } else { From fec8049226ae9c6ec6825a71b5de3f787f9333c2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 20 Sep 2016 13:24:39 -0700 Subject: [PATCH 056/109] Add proper disabling to advanced movement script --- ...oggleAdvancedMovementForHandControllers.js | 16 +++++++++++ tutorial/tutorial.js | 28 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 21eeb68b96..bd31c5c42e 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -18,6 +18,7 @@ var mappingName, basicMapping, isChecked; var TURN_RATE = 1000; var MENU_ITEM_NAME = "Advanced Movement For Hand Controllers"; var SETTINGS_KEY = 'advancedMovementForHandControllersIsChecked'; +var isDisabled = false; var previousSetting = Settings.getValue(SETTINGS_KEY); if (previousSetting === '' || previousSetting === false || previousSetting === 'false') { previousSetting = false; @@ -146,4 +147,19 @@ HMD.displayModeChanged.connect(function(isHMDMode) { } }); + +var HIFI_ADVANCED_MOVEMENT_DISABLER_CHANNEL = 'Hifi-Advanced-Movement-Disabler'; +function handleMessage(channel, message, sender) { + if (channel == HIFI_ADVANCED_MOVEMENT_DISABLER_CHANNEL) { + if (message == 'disable') { + isDisabled = true; + } else if (message == 'enable') { + isDisabled = false; + } + } +} + +Messages.subscribe(HIFI_ADVANCED_MOVEMENT_DISABLER_CHANNEL); +Messages.messageReceived.connect(handleHandMessages); + }()); // END LOCAL_SCOPE diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index a01d1616e0..4e95bf856a 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -216,6 +216,8 @@ stepDisableControllers.prototype = { start: function(onFinish) { editEntitiesWithTag('door', { visible: true }); Menu.setIsOptionChecked("Overlays", false); + Controller.disableMapping('handControllerPointer-click'); + Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'disable'); Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'both'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ nearGrabEnabled: true, @@ -230,6 +232,30 @@ stepDisableControllers.prototype = { } }; +var stepEnableControllers = function(name) { + this.tag = name; + this.shouldLog = false; +} +stepEnableControllers.prototype = { + start: function(onFinish) { + editEntitiesWithTag('door', { visible: false }); + Menu.setIsOptionChecked("Overlays", true); + Controller.enableMapping('handControllerPointer-click'); + Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'enable'); + Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); + Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ + nearGrabEnabled: true, + holdEnabled: true, + farGrabEnabled: true, + })); + setControllerPartLayer('touchpad', 'blank'); + setControllerPartLayer('tips', 'blank'); + onFinish(); + }, + cleanup: function() { + } +}; + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // @@ -980,6 +1006,7 @@ TutorialManager = function() { new stepTurnAround("turnAround"), new stepTeleport("teleport"), new stepFinish("finish"), + new stepEnableControllers("enableControllers"), ]; for (var i = 0; i < STEPS.length; ++i) { STEPS[i].cleanup(); @@ -1041,7 +1068,6 @@ TutorialManager = function() { Script.scriptEnding.connect(function() { Controller.enableMapping('handControllerPointer-click'); }); -Controller.disableMapping('handControllerPointer-click'); // var entityID = '{be3d10a3-262a-4827-b30c-ec025c4325dc}'; // var token = new OwnershipToken(Math.random() * 100000, entityID, { From fd929a46b31404259ce4a14c9bf43b415badd26d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 20 Sep 2016 13:27:17 -0700 Subject: [PATCH 057/109] Clean up fire.js --- tutorial/firePit/fire.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/tutorial/firePit/fire.js b/tutorial/firePit/fire.js index 0747bc9f14..381676f1ce 100644 --- a/tutorial/firePit/fire.js +++ b/tutorial/firePit/fire.js @@ -1,6 +1,6 @@ -// this script turns an entity into an exploder -- anything that collides with it will be vaporized! -// +// Originally written by James Pollack, modified by Ryan Huffman for the tutorial // +// this script turns an entity into an exploder -- anything that collides with it will be vaporized! (function() { @@ -58,27 +58,20 @@ preload: function(entityID) { this.entityID = entityID; this.EXPLOSION_SOUND = SoundCache.getSound("atp:/firepit/fire_burst.wav"); - print("IN FIRE SCRIPT"); }, collisionWithEntity: function(myID, otherID, collisionInfo) { - print("FIRE SCRIPT: COLLIDED"); var otherProps = Entities.getEntityProperties(otherID); var data = null; - print("FIRE SCRIPT: 2 COLLIDED"); try { - print("parsing.."); data = JSON.parse(otherProps.userData) - print("done parsing.."); } catch (err) { print('ERROR GETTING USERDATA!'); } - print("HERE"); if (data === null || "") { return; } else { if (data.hasOwnProperty('hifiHomeKey')) { if (data.hifiHomeKey.reset === true) { - print('FLAMMABLE THING, EXPLODE IT!'); _this.playSoundAtCurrentPosition(); _this.explodeWithColor(); Entities.deleteEntity(otherID) @@ -90,7 +83,6 @@ } }, explodeWithColor: function() { - print('EXPLODE!') var myProps = Entities.getEntityProperties(this.entityID); var color = colors[Math.floor(Math.random() * colors.length)]; var explosionParticleProperties = { @@ -152,7 +144,6 @@ }; var explosion = Entities.addEntity(explosionParticleProperties); - print('explosion is: ' + explosion) }, playSoundAtCurrentPosition: function() { From e875981a6a25c84c7b8b9eb1f98402fdfa8b787b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 08:51:05 -0700 Subject: [PATCH 058/109] Add tracking of tutorialComplete to tutorial --- tutorial/firePit/flicker.js | 3 ++- tutorial/tutorial.js | 25 ++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/tutorial/firePit/flicker.js b/tutorial/firePit/flicker.js index 43148dabba..f4406286c4 100644 --- a/tutorial/firePit/flicker.js +++ b/tutorial/firePit/flicker.js @@ -1,3 +1,4 @@ +// Originally written for the Home content set. Pulled into the tutorial by Ryan Huffman (function() { var MINIMUM_LIGHT_INTENSITY = 50.0; @@ -48,4 +49,4 @@ return new FlickeringFlame -}); \ No newline at end of file +}); diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 4e95bf856a..59bc88363a 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -226,6 +226,10 @@ stepDisableControllers.prototype = { })); setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('tips', 'blank'); + + hideEntitiesWithTag('finish'); + onFinish(); + onFinish(); }, cleanup: function() { @@ -929,11 +933,25 @@ stepFinish.prototype = { start: function(onFinish) { editEntitiesWithTag('door', { visible: false }); showEntitiesWithTag(this.tag); + Settings.setValue("tutorialComplete", true); + onFinish(); }, cleanup: function() { //Menu.setIsOptionChecked("Overlays", true); - hideEntitiesWithTag(this.tag); - deleteEntitiesWithTag(this.tempTag); + //hideEntitiesWithTag(this.tag); + //deleteEntitiesWithTag(this.tempTag); + } +}; + +var stepCleanupFinish = function() { + this.shouldLog = false; +} +stepCleanupFinish.prototype = { + start: function(onFinish) { + hideEntitiesWithTag('finish'); + onFinish(); + }, + cleanup: function() { } }; @@ -996,6 +1014,7 @@ TutorialManager = function() { currentStep = null; startedTutorialAt = Date.now(); STEPS = [ + //new stepCleanupFinish("finish"); new stepDisableControllers("step0"), new stepOrient("orient"), //new stepWelcome("welcome"), @@ -1011,7 +1030,7 @@ TutorialManager = function() { for (var i = 0; i < STEPS.length; ++i) { STEPS[i].cleanup(); } - location = "/tutorial_begin"; + //location = "/tutorial_begin"; //location = "/tutorial"; MyAvatar.shouldRenderLocally = false; this.startNextStep(); From e3e917b4b7d77d5e58ed7ad20ce73991c00c6573 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 09:23:45 -0700 Subject: [PATCH 059/109] Add tutorial StartZone and Zone entity scripts --- tutorial/tutorialStartZone.js | 29 ++++++++ tutorial/tutorialZone.js | 123 ++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 tutorial/tutorialStartZone.js create mode 100644 tutorial/tutorialZone.js diff --git a/tutorial/tutorialStartZone.js b/tutorial/tutorialStartZone.js new file mode 100644 index 0000000000..5f5fa47b94 --- /dev/null +++ b/tutorial/tutorialStartZone.js @@ -0,0 +1,29 @@ +(function() { + var TutorialStartZone = function() { + }; + + TutorialStartZone.prototype = { + preload: function(entityID) { + this.entityID = entityID; + }, + enterEntity: function() { + // send message to outer zone + print("ENTERED THE TUTORIAL START AREA"); + var parentID = Entities.getEntityProperties(this.entityID, 'parentID').parentID; + print("HERE", parentID); + if (parentID) { + print("HERE2"); + Entities.callEntityMethod(parentID, 'start'); + print("HERE4"); + } else { + print("HERE3"); + print("ERROR: No parent id found on tutorial start zone"); + } + }, + leaveEntity: function() { + print("EXITED THE TUTORIAL START AREA"); + } + }; + + return new TutorialStartZone(); +}); diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js new file mode 100644 index 0000000000..97aa906052 --- /dev/null +++ b/tutorial/tutorialZone.js @@ -0,0 +1,123 @@ +// A user designates ownership of the tutorial by creating a child entity (token) +// of the tutorial zone. The entity should have a short lifetime (5 seconds), and +// should have it's lifetime reset every second. +// +// * When you enter the "tutorial" begin zone +// * If the tutorial is owned +// * Show a "waiting" text, and check for ownership periodically +// * If the tutorial is not owned +// * Create the ownership token, begin tutorial +// * For extra safety, to avoid races, check after 1 second to confirm that +// another user hasn't created a token. If they have, use some method to +// resolve the conflict. +// * Once the user has finished the tutorial, stop creating the token to +// release ownership. +// +// * The tutorial will expose a local message API for controlling the tutorial +// * A special script will be used to: +// * Create a key shortcut to go to the beginning of the tutorial +// * +// + +if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 + // internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function() {}, + fBound = function() { + return fToBind.apply(this instanceof fNOP + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + if (this.prototype) { + // Function.prototype doesn't have a prototype property + fNOP.prototype = this.prototype; + } + fBound.prototype = new fNOP(); + + return fBound; + }; +} + +(function() { + Script.include("file:///C:/Users/Ryan/dev/hifi/tutorial/ownershipToken.js"); + Script.include("file:///C:/Users/Ryan/dev/hifi/tutorial/tutorial.js"); + + var TutorialZone = function() { + this.token = null; + }; + + TutorialZone.prototype = { + keyReleaseHandler: function(event) { + print(event.text); + if (event.text == ",") { + if (!this.tutorialManager.startNextStep()) { + this.tutorialManager.startTutorial(); + } + } else if (event.text == "F11") { + this.tutorialManager.restartStep(); + } else if (event.text == "F10") { + MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; + } else if (event.text == "r") { + this.tutorialManager.stopTutorial(); + this.tutorialManager.startTutorial(); + } + }, + preload: function(entityID) { + this.entityID = entityID; + }, + start: function() { + print("Got start"); + var self = this; + if (!this.token) { + this.token = new OwnershipToken(Math.random() * 100000, this.entityID, { + onGainedOwnership: function(token) { + print("GOT OWNERSHIP"); + if (!self.tutorialManager) { + self.tutorialManager = new TutorialManager(); + } + self.tutorialManager.startTutorial(); + print("making bound release handler"); + self.keyReleaseHandlerBound = self.keyReleaseHandler.bind(self); + print("binding"); + Controller.keyReleaseEvent.connect(self.keyReleaseHandlerBound); + print("done"); + }, + onLostOwnership: function(token) { + print("LOST OWNERSHIP"); + if (self.tutorialManager) { + print("stopping tutorial.."); + self.tutorialManager.stopTutorial(); + print("done"); + Controller.keyReleaseEvent.disconnect(self.keyReleaseHandlerBound); + } else { + print("no tutorial manager..."); + } + } + }); + } + }, + + enterEntity: function() { + print("ENTERED THE TUTORIAL AREA"); + }, + leaveEntity: function() { + print("EXITED THE TUTORIAL AREA"); + if (this.token) { + print("destroying token"); + this.token.destroy(); + this.token = null; + } + } + }; + + return new TutorialZone(); +}); From 9f53652dfb7c2f37833d465d2cf2f05e5a2f6806 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 09:29:40 -0700 Subject: [PATCH 060/109] Add routing of new users to tutorial --- interface/src/Application.cpp | 97 ++++++++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 24 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fcd6c59b63..a81084d853 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1255,6 +1255,79 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : return entityServerNode && !isPhysicsEnabled(); }); + + + // Initialize location + + auto initializeLocation = [this]() { + // Get sandbox content set version, if available + auto acDirPath = PathUtils::getRootDataDirectory() + qApp->organizationName() + "/assignment-client/"; + auto contentVersionPath = acDirPath + "content-version.txt"; + auto contentVersion = 0; + QFile contentVersionFile(contentVersionPath); + if (contentVersionFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + QString line = contentVersionFile.readAll(); + // toInt() returns 0 if the conversion fails, so we don't need to specifically check for failure + contentVersion = line.toInt(); + } + qDebug() << "Server content version: " << contentVersion; + + bool hasTutorialContent = contentVersion >= 1; + + Setting::Handle firstRun { Settings::firstRun, true }; + bool isOnVive = _displayPlugin && _displayPlugin->getName() == "OpenVR (Vive)"; + bool isFirstRun = firstRun.get(); + Setting::Handle tutorialComplete { "tutorialComplete", false }; + + bool shouldGoToTutorial = isOnVive && hasTutorialContent && !tutorialComplete.get(); + qDebug() << "Is on vive " << isOnVive << ", " << _displayPlugin->getName(); + qDebug() << "has tutorial content" << hasTutorialContent; + qDebug() << "tutorial complete" << tutorialComplete.get(); + qDebug() << "should go to tutorial " << shouldGoToTutorial; + + + + if (shouldGoToTutorial) { + DependencyManager::get()->ifLocalSandboxRunningElse([=]() { + qDebug() << "Home sandbox appears to be running, going to Home."; + //DependencyManager::get()->goToLocalSandbox("/tutorial"); + DependencyManager::get()->loadSettings("hifi://sport/tutorial"); + }, [=]() { + qDebug() << "Home sandbox does not appear to be running, going to Entry."; + showHelp(); + DependencyManager::get()->goToEntry(); + }); + } else { + + // when --url in command line, teleport to location + const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; + int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY); + QString addressLookupString; + if (urlIndex != -1) { + addressLookupString = arguments().value(urlIndex + 1); + } + + if (firstRun.get()) { + showHelp(); + } + + if (addressLookupString.isEmpty() && firstRun.get()) { + DependencyManager::get()->ifLocalSandboxRunningElse([=]() { + qDebug() << "Home sandbox appears to be running, going to Home."; + DependencyManager::get()->goToLocalSandbox(); + }, [=]() { + qDebug() << "Home sandbox does not appear to be running, going to Entry."; + DependencyManager::get()->goToEntry(); + }); + } else { + qDebug() << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); + DependencyManager::get()->loadSettings(addressLookupString); + } + } + }; + + initializeLocation(); + // After all of the constructor is completed, then set firstRun to false. Setting::Handle firstRun{ Settings::firstRun, true }; firstRun.set(false); @@ -3279,15 +3352,6 @@ void Application::init() { _timerStart.start(); _lastTimeUpdated.start(); - - // when --url in command line, teleport to location - const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; - int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY); - QString addressLookupString; - if (urlIndex != -1) { - addressLookupString = arguments().value(urlIndex + 1); - } - // when +connect_lobby in command line, join steam lobby const QString STEAM_LOBBY_COMMAND_LINE_KEY = "+connect_lobby"; int lobbyIndex = arguments().indexOf(STEAM_LOBBY_COMMAND_LINE_KEY); @@ -3296,21 +3360,6 @@ void Application::init() { SteamClient::joinLobby(lobbyId); } - Setting::Handle firstRun { Settings::firstRun, true }; - if (addressLookupString.isEmpty() && firstRun.get()) { - qCDebug(interfaceapp) << "First run and no URL passed... attempting to go to Home or Entry..."; - DependencyManager::get()->ifLocalSandboxRunningElse([](){ - qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox(); - }, - [](){ - qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; - DependencyManager::get()->goToEntry(); - }); - } else { - qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); - DependencyManager::get()->loadSettings(addressLookupString); - } qCDebug(interfaceapp) << "Loaded settings"; From 4c3cb83d4745d75621b2169a4c1b95d5dd962b8a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 09:29:58 -0700 Subject: [PATCH 061/109] Set default preferred device to vive and rift --- interface/src/Application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a81084d853..cb25766dbb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5392,6 +5392,8 @@ void Application::initPlugins(const QStringList& arguments) { auto preferredDisplays = parser.value(display).split(',', QString::SkipEmptyParts); qInfo() << "Setting prefered display plugins:" << preferredDisplays; PluginManager::getInstance()->setPreferredDisplayPlugins(preferredDisplays); + } else { + PluginManager::getInstance()->setPreferredDisplayPlugins({ "OpenVR (Vive)", "Oculus Rift" }); } if (parser.isSet(disableDisplays)) { From e24a01d1fec9a27d12deba2c760c1ff8281e26d6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 09:30:47 -0700 Subject: [PATCH 062/109] Add path parameter to goToSandbox --- libraries/networking/src/AddressManager.h | 2 +- libraries/shared/src/PathUtils.cpp | 14 ++++++++++++++ libraries/shared/src/PathUtils.h | 1 + libraries/shared/src/ServerPathUtils.cpp | 12 +++--------- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 248a1ef435..0ab70854eb 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -92,7 +92,7 @@ public slots: void goBack(); void goForward(); - void goToLocalSandbox(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(SANDBOX_HIFI_ADDRESS, trigger); } + void goToLocalSandbox(QString path = "", LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger); } void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(DEFAULT_HIFI_ADDRESS, trigger); } void goToUser(const QString& username); diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 954ed2d75a..016b9ccfd6 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -17,6 +17,7 @@ #include #include #include "PathUtils.h" +#include const QString& PathUtils::resourcesPath() { @@ -29,6 +30,19 @@ const QString& PathUtils::resourcesPath() { return staticResourcePath; } +QString PathUtils::getRootDataDirectory() { + auto dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); + +#ifdef Q_OS_WIN + dataPath += "/AppData/Roaming/"; +#elif defined(Q_OS_OSX) + dataPath += "/Library/Application Support/"; +#else + dataPath += "/.local/share/"; +#endif + + return dataPath; +} QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions) { QString fileNameLowered = fileName.toLower(); diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 9f990815ce..43464fe236 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -22,6 +22,7 @@ class PathUtils : public QObject, public Dependency { Q_PROPERTY(QString resources READ resourcesPath) public: static const QString& resourcesPath(); + static QString getRootDataDirectory(); }; QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions); diff --git a/libraries/shared/src/ServerPathUtils.cpp b/libraries/shared/src/ServerPathUtils.cpp index ca87a28610..cf52875c5f 100644 --- a/libraries/shared/src/ServerPathUtils.cpp +++ b/libraries/shared/src/ServerPathUtils.cpp @@ -15,16 +15,10 @@ #include #include -QString ServerPathUtils::getDataDirectory() { - auto dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); +#include "PathUtils.h" -#ifdef Q_OS_WIN - dataPath += "/AppData/Roaming/"; -#elif defined(Q_OS_OSX) - dataPath += "/Library/Application Support/"; -#else - dataPath += "/.local/share/"; -#endif +QString ServerPathUtils::getDataDirectory() { + auto dataPath = PathUtils::getRootDataDirectory(); dataPath += qApp->organizationName() + "/" + qApp->applicationName(); From c74df965bac643794800da3daefe0b087c3417f7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 09:31:18 -0700 Subject: [PATCH 063/109] Fix misnamed function and duplicate onFinish in tutorial --- .../controllers/toggleAdvancedMovementForHandControllers.js | 2 +- tutorial/tutorial.js | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index bd31c5c42e..b74fe52e42 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -160,6 +160,6 @@ function handleMessage(channel, message, sender) { } Messages.subscribe(HIFI_ADVANCED_MOVEMENT_DISABLER_CHANNEL); -Messages.messageReceived.connect(handleHandMessages); +Messages.messageReceived.connect(handleMessage); }()); // END LOCAL_SCOPE diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 59bc88363a..713b052412 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -228,7 +228,6 @@ stepDisableControllers.prototype = { setControllerPartLayer('tips', 'blank'); hideEntitiesWithTag('finish'); - onFinish(); onFinish(); }, @@ -364,7 +363,7 @@ stepOrient.prototype = { this.checkIntervalID = null; function checkForHandsAboveHead() { - print("Checking for hands above head..."); + print("Orient: Checking for hands above head..."); if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; @@ -431,7 +430,7 @@ stepRaiseAboveHead.prototype = { this.waitTimeoutID = Script.setTimeout(function() { this.checkIntervalID = null; function checkForHandsAboveHead() { - print("Checking for hands above head..."); + print("Raise above head: Checking for hands above head..."); if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; From e9fb11b5f98d2c8444a8bc3cda914510d4a28d63 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 10:06:46 -0700 Subject: [PATCH 064/109] Remove annotation support from ControllerDisplay --- tutorial/controllerDisplay.js | 92 ++++------------------------------- 1 file changed, 10 insertions(+), 82 deletions(-) diff --git a/tutorial/controllerDisplay.js b/tutorial/controllerDisplay.js index 8a4f7a8c5d..f66cd90703 100644 --- a/tutorial/controllerDisplay.js +++ b/tutorial/controllerDisplay.js @@ -2,6 +2,12 @@ var DEBUG = false; var VISIBLE_BY_DEFAULT = false; var PARENT_ID = "{00000000-0000-0000-0000-000000000001}"; +function debug() { + if (DEBUG) { + print.apply(self, arguments); + } +} + createControllerDisplay = function(config) { var controllerDisplay = { overlays: [], @@ -9,14 +15,11 @@ createControllerDisplay = function(config) { }, parts: { }, - annotations: { - }, mappingName: "mapping-display", setPartVisible: function(partName, visible) { - print("Setting part visible", partName, visible); if (partName in this.partOverlays) { - print("FOUND"); + debug("Setting part visible", partName, visible); for (var i = 0; i < this.partOverlays[partName].length; ++i) { Overlays.editOverlay(this.partOverlays[partName][i], { visible: visible @@ -26,8 +29,8 @@ createControllerDisplay = function(config) { }, setLayerForPart: function(partName, layerName) { - print("Setting layer...", partName, layerName); if (partName in this.parts) { + debug("Setting layer...", partName, layerName); var part = this.parts[partName]; if (part.textureLayers && layerName in part.textureLayers) { var layer = part.textureLayers[layerName]; @@ -48,13 +51,12 @@ createControllerDisplay = function(config) { for (var i = 0; i < config.controllers.length; ++i) { var controller = config.controllers[i]; var position = controller.position; - Vec3.print("position", position); - print("position", position.x, position.y, position.z); + if (controller.naturalPosition) { position = Vec3.sum(Vec3.multiplyQbyV( controller.rotation, controller.naturalPosition), position); } - Vec3.print("Got controller position", position); + var overlayID = Overlays.addOverlay("model", { url: controller.modelURL, dimensions: controller.dimensions, @@ -68,79 +70,6 @@ createControllerDisplay = function(config) { controllerDisplay.overlays.push(overlayID); overlayID = null; - if (controller.annotations) { - for (var key in controller.annotations) { - var annotation = controller.annotations[key]; - var annotationPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, annotation.position)); - if (DEBUG) { - overlayID = Overlays.addOverlay("sphere", { - localPosition: annotationPosition, - //localPosition: Vec3.sum(controller.position, annotation.position), - //localPosition: Vec3.sum(position, annotation.position), - color: annotation.color || { red: 255, green: 100, blue: 100 }, - dimensions: { - x: 0.01, - y: 0.01, - z: 0.01 - }, - parentID: PARENT_ID, - parentJointIndex: controller.jointIndex, - }); - controllerDisplay.overlays.push(overlayID); - - } - - var ANNOTATION_TEXT_OFFSET = 0.1; - var sign = annotation.direction == "right" ? 1 : -1; - var textOffset = annotation.direction == "right" ? 0.08 : 0.02; - if (annotation.textOffset) { - var pos = Vec3.sum(annotationPosition, Vec3.multiplyQbyV(controller.rotation, annotation.textOffset)); - } else { - var pos = Vec3.sum(annotationPosition, Vec3.multiplyQbyV(controller.rotation, { x: textOffset, y: 0, z: -0.005 })); - } - var textOverlayID = Overlays.addOverlay("text3d", { - visible: VISIBLE_BY_DEFAULT, - text: key, - localPosition: pos, - localRotation: controller.annotationTextRotation, - lineHeight: annotation.lineHeight ? annotation.lineHeight : 0.01, - leftMargin: 0, - rightMargin: 0, - topMargin: 0, - bottomMargin: 0, - backgroundAlpha: 0, - dimensions: { x: 0.003, y: 0.003, z: 0.003 }, - //localPosition: Vec3.sum(controller.position, annotation.position), - //localPosition: Vec3.sum(position, annotation.position), - color: annotation.textColor || { red: 255, green: 255, blue: 255 }, - parentID: PARENT_ID, - parentJointIndex: controller.jointIndex, - }); - - controllerDisplay.overlays.push(textOverlayID); - if (key in controllerDisplay.annotations) { - controllerDisplay.annotations[key].push(textOverlayID); - } else { - controllerDisplay.annotations[key] = [textOverlayID]; - } - - var ANNOTATION_OFFSET = 0.5; - var offset = { x: 0, y: 0, z: annotation.direction == "right" ? -1 * ANNOTATION_OFFSET : ANNOTATION_OFFSET }; - var lineOverlayID = Overlays.addOverlay("line3d", { - visible: false, - localPosition: annotationPosition, - localStart: { x: 0, y: 0, z: 0 }, - localEnd: offset, - //localPosition: Vec3.sum(controller.position, annotation.position), - //localPosition: Vec3.sum(position, annotation.position), - color: annotation.color || { red: 255, green: 100, blue: 100 }, - parentID: PARENT_ID, - parentJointIndex: controller.jointIndex, - }); - controllerDisplay.overlays.push(lineOverlayID); - } - } - function clamp(value, min, max) { if (value < min) { return min; @@ -204,7 +133,6 @@ createControllerDisplay = function(config) { function resolveHardware(path) { var parts = path.split("."); function resolveInner(base, path, i) { - //print(path[i]); if (i >= path.length) { return base; } From a75894240344698f10d2deef45adf9b7f9b05381 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 10:11:31 -0700 Subject: [PATCH 065/109] Remove annotation data from vive controller config --- tutorial/viveControllerConfiguration.js | 136 ------------------------ 1 file changed, 136 deletions(-) diff --git a/tutorial/viveControllerConfiguration.js b/tutorial/viveControllerConfiguration.js index a33a701641..25146c7382 100644 --- a/tutorial/viveControllerConfiguration.js +++ b/tutorial/viveControllerConfiguration.js @@ -155,69 +155,6 @@ VIVE_CONTROLLER_CONFIGURATION = { naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} }, }, - annotationTextRotation: Quat.fromPitchYawRollDegrees(45, -90, 0), - annotations: { - - left: { - textOffset: { x: -0.035, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - right: { - textOffset: { x: 0.023, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - - - trigger: { - position: { - x: 0.0055, - y: -0.032978, - z: 0.04546 - }, - lineHeight: 0.013, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - menu: { - position: { - x: 0, - y: 0.00770, - z: 0.01979 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - grip: { - position: { - x: 0.01980, - y: -0.01561, - z: 0.08721 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - steam: { - position: { - x: 0, - y: 0.00303, - z: 0.08838 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - }, }, @@ -336,79 +273,6 @@ VIVE_CONTROLLER_CONFIGURATION = { naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} }, }, - - annotationTextRotation: Quat.fromPitchYawRollDegrees(180 + 45, 90, 180), - annotations: { - - left: { - textOffset: { x: -0.035, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - right: { - textOffset: { x: 0.023, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - - trigger: { - position: { - x: -0.075, - y: -0.032978, - z: 0.04546 - }, - lineHeight: 0.013, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - menu: { - position: { - x: 0, - y: 0.00770, - z: 0.01979 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - grip: { - position: { - x: 0.01980, - y: -0.01561, - z: 0.08721 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - teleport: { - textOffset: { x: -0.015, y: 0.004, z: -0.005 }, - position: { - x: 0, - y: 0.00378, - z: 0.04920 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - steam: { - position: { - x: 0, - y: 0.00303, - z: 0.08838 - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - }, - } } ] } From 1d699186a36653c385157c4fc7e7232f9d6f2775 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 10:36:10 -0700 Subject: [PATCH 066/109] Clean up tutorial scripts --- tutorial/controllerDisplay.js | 2 +- tutorial/entityData.js | 415 ----------------------- tutorial/fuse.js | 52 +-- tutorial/fuseCollider.js | 47 +-- tutorial/ownershipToken.js | 6 +- tutorial/spinner.js | 25 +- tutorial/touchControllerConfiguration.js | 10 + tutorial/tutorial.js | 212 +++++------- 8 files changed, 136 insertions(+), 633 deletions(-) diff --git a/tutorial/controllerDisplay.js b/tutorial/controllerDisplay.js index f66cd90703..0485e0bd9f 100644 --- a/tutorial/controllerDisplay.js +++ b/tutorial/controllerDisplay.js @@ -1,7 +1,7 @@ -var DEBUG = false; var VISIBLE_BY_DEFAULT = false; var PARENT_ID = "{00000000-0000-0000-0000-000000000001}"; +var DEBUG = false; function debug() { if (DEBUG) { print.apply(self, arguments); diff --git a/tutorial/entityData.js b/tutorial/entityData.js index 1d36293586..1e38079de5 100644 --- a/tutorial/entityData.js +++ b/tutorial/entityData.js @@ -1,188 +1,3 @@ -Step1EntityData = [ -{ - "clientOnly": 0, - "color": { - "blue": 255, - "green": 0, - "red": 255 - }, - "created": "2016-08-29T22:57:55Z", - "dimensions": { - "x": 0.018359377980232239, - "y": 0.018359377980232239, - "z": 0.018359377980232239 - }, - "id": "{3bb83d9c-11db-4bc1-a61b-36921370cb40}", - "name": "tutorial/box_spawn", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "collisionless": 1, - "position": { - "x": 0, - "y": 0.8, - "z": 0.7790381908416748 - }, - "queryAACube": { - "scale": 0.031799376010894775, - "x": -0.015899688005447388, - "y": 0.79706859588623047, - "z": 0.7631385326385498 - }, - "rotation": { - "w": 1, - "x": 0, - "y": 0, - "z": 0 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"tag\":\"step2\"}", - "visible": 0 -}, -{ - "color": { - "blue": 181, - "green": 181, - "red": 181 - }, - "dimensions": { - "x": 0.37322089076042175, - "y": 0.8015166997909546, - "z": 0.37322089076042175 - }, - "name": "tutorial/pillar2", - //"shapeType": "simple-hull", - "position": { - "x": 0.019208565354347229, - "y": -0.1, - "z": 0.75276124477386475 - }, - "rotation": { - "w": 1, - "x": 0, - "y": 0, - "z": 0 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"tag\":\"step2\"}" -}, -{ - "clientOnly": 0, - "compoundShapeURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj", - "created": "2016-08-29T22:57:55Z", - "dimensions": { - "x": 0.57461458444595337, - "y": 0.35781359672546387, - "z": 0.57461458444595337 - }, - "gravity": { - "x": 0, - "y": -5, - "z": 0 - }, - "id": "{2a8a9cb8-4501-4089-8fb8-6b1b5100db10}", - "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx", - "name": "tutorial/basket", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.022034257650375366, - "y": 0.47968916893005371, - "z": 0 - }, - "queryAACube": { - "scale": 0.88791579008102417, - "x": -0.42192363739013672, - "y": 0.23573127388954163, - "z": -0.44395789504051208 - }, - "rotation": { - "w": 1, - "x": -1.52587890625e-05, - "y": -1.52587890625e-05, - "z": -1.52587890625e-05 - }, - "shapeType": "compound", - "type": "Model", - "userData": "{\"hifiHomeKey\":{\"reset\":true},\"tag\":\"step2\"}" -}, -{ - "clientOnly": 0, - "collisionless": 1, - "color": { - "blue": 255, - "green": 0, - "red": 255 - }, - "created": "2016-08-29T22:57:55Z", - "dimensions": { - "x": 0.43770244717597961, - "y": 0.33723857998847961, - "z": 0.43770244717597961 - }, - "id": "{436aec80-15e8-4fc3-bd74-f173b731a922}", - "ignoreForCollisions": 1, - "name": "tutorial/basket_collider", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.02785143256187439, - "y": 0.50166182518005371, - "z": 0.0017895996570587158 - }, - "queryAACube": { - "scale": 0.70490902662277222, - "x": -0.32460308074951172, - "y": 0.3492073118686676, - "z": -0.35066491365432739 - }, - "rotation": { - "w": 1, - "x": 0, - "y": 0, - "z": 0 - }, - "type": "Sphere", - "userData": "{\"tag\":\"step2\"}", - "visible": 0 -}, -{ - "clientOnly": 0, - "color": { - "blue": 181, - "green": 181, - "red": 181 - }, - "created": "2016-08-29T22:57:55Z", - "dimensions": { - "x": 0.37322089076042175, - "y": 0.8015000104904175, - "z": 0.37322089076042175 - }, - "id": "{221be6c2-e0d6-4a7c-b9d4-a77e6b7d1c9a}", - "name": "tutorial/pillar1", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.019208565354347229, - "y": -0.1, - "z": 0.025902509689331055 - }, - "queryAACube": { - "scale": 1.1320732831954956, - "x": -0.54682809114456177, - "y": -0.5660366415977478, - "z": -0.54013413190841675 - }, - "rotation": { - "w": 1, - "x": 0, - "y": 0, - "z": 0 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"tag\":\"step2\"}" -} -]; - birdFirework1 = { "clientOnly": 0, "collisionsWillMove": 1, @@ -320,233 +135,3 @@ Step1BlockData = { "type": "Box", "userData": JSON.stringify({ hifiHomeKey: { reset: true } }), }; - -StepGunData = [ - { - "clientOnly": 0, - "created": "2016-08-23T22:18:46Z", - "dimensions": { - "x": 2.4929797649383545, - "y": 0.94968640804290771, - "z": 1.0870213508605957 - }, - "id": "{de28363f-d1f8-4001-8e6b-1b5876699f49}", - "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/table2_re-oriented.fbx", - "name": "tutorial/table", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.17360222339630127, - "y": 0, - "z": 0 - }, - "queryAACube": { - "scale": 2.8807060718536377, - "x": -1.2667508125305176, - "y": -1.4403530359268188, - "z": -1.4403530359268188 - }, - "rotation": { - "w": 0.70705735683441162, - "x": -1.52587890625e-05, - "y": -0.70717936754226685, - "z": -1.52587890625e-05 - }, - "shapeType": "static-mesh", - "type": "Model", - "userData": "{\"tag\":\"step4\"}" - }, - { - "clientOnly": 0, - "compoundShapeURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj", - "created": "2016-08-23T22:18:46Z", - "dimensions": { - "x": 0.57461458444595337, - "y": 0.35781359672546387, - "z": 0.57461458444595337 - }, - "gravity": { - "x": 0, - "y": -5, - "z": 0 - }, - "id": "{51e7cf16-e624-44a8-b835-47c35c6ad5f0}", - "modelURL": "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx", - "name": "tutorial/basket", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 3.8134055137634277, - "y": 0.6480712890625, - "z": 0.015498995780944824 - }, - "queryAACube": { - "scale": 0.88791579008102417, - "x": 3.3694477081298828, - "y": 0.20411339402198792, - "z": -0.42845889925956726 - }, - "rotation": { - "w": 1, - "x": -1.52587890625e-05, - "y": -1.52587890625e-05, - "z": -1.52587890625e-05 - }, - "shapeType": "compound", - "type": "Model", - "userData": "{\"hifiHomeKey\":{\"reset\":true},\"tag\":\"step4\"}" - }, - { - "clientOnly": 0, - "collisionless": 1, - "color": { - "blue": 0, - "green": 0, - "red": 255 - }, - "created": "2016-08-23T22:20:57Z", - "dimensions": { - "x": 0.0649842768907547, - "y": 0.0649842768907547, - "z": 0.0649842768907547 - }, - "id": "{264943d2-600f-4d22-ad30-ccd57f7c4424}", - "ignoreForCollisions": 1, - "name": "tutorial/gun_spawn", - visible: false, - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0, - "y": 0.62629544734954834, - "z": 0.028602004051208496 - }, - "queryAACube": { - "scale": 0.11255607008934021, - "x": -0.056278035044670105, - "y": 0.57001739740371704, - "z": -0.027676030993461609 - }, - "shape": "Cube", - "type": "Box" - }, - { - "clientOnly": 0, - "collisionless": 1, - "color": { - "blue": 255, - "green": 0, - "red": 255 - }, - "created": "2016-08-23T22:18:46Z", - "dimensions": { - "x": 0.43770244717597961, - "y": 0.33723857998847961, - "z": 0.43770244717597961 - }, - "id": "{a8944645-3234-484f-aed1-1a63d76aa51c}", - "ignoreForCollisions": 1, - "name": "tutorial/basket_collider", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 3.8192229270935059, - "y": 0.6700439453125, - "z": 0.017288565635681152 - }, - "queryAACube": { - "scale": 0.70490902662277222, - "x": 3.4667685031890869, - "y": 0.31758943200111389, - "z": -0.33516594767570496 - }, - "rotation": { - "w": 1, - "x": 0, - "y": 0, - "z": 0 - }, - "type": "Sphere", - "userData": "{\"tag\":\"step4\"}", - "visible": 0 - } -]; - -GunData = { - "clientOnly": 0, - "collisionsWillMove": 1, - "compoundShapeURL": "http://hifi-production.s3.amazonaws.com/tutorials/pingPongGun/Pingpong-Gun-New.obj", - "created": "2016-08-23T22:12:13Z", - "dimensions": { - "x": 0.125, - "y": 0.38749998807907104, - "z": 0.99309998750686646 - }, - "dynamic": 1, - "gravity": { - "x": 0, - "y": -5, - "z": 0 - }, - - "id": "{8d3fa3f2-8b59-4f47-8bb4-c03574239c9f}", - "modelURL": "http://hifi-production.s3.amazonaws.com/tutorials/pingPongGun/Pingpong-Gun-New.fbx", - "name": "tutorial/gun", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "queryAACube": { - "scale": 3.219977855682373, - "x": -2.5046753883361816, - "y": -1.8901374340057373, - "z": -0.68512386083602905 - }, - velocity: { - x: 0, - y: -1, - z: 0 - }, - "rotation": { - "w": 0.69534718990325928, - "x": -0.13302478194236755, - "y": -0.12684555351734161, - "z": 0.69477111101150513 - }, - "script": "http://hifi-production.s3.amazonaws.com/tutorials/entity_scripts/pingPongGun.js", - "shapeType": "compound", - "type": "Model", - "userData": "{\"grabbableKey\":{\"invertSolidWhileHeld\":true},\"wearable\":{\"joints\":{\"RightHand\":[{\"x\":0.1177130937576294,\"y\":0.12922893464565277,\"z\":0.08307232707738876},{\"x\":0.4934672713279724,\"y\":0.3605862259864807,\"z\":0.6394805908203125,\"w\":-0.4664038419723511}],\"LeftHand\":[{\"x\":0.09151676297187805,\"y\":0.13639454543590546,\"z\":0.09354984760284424},{\"x\":-0.19628101587295532,\"y\":0.6418180465698242,\"z\":0.2830369472503662,\"w\":0.6851521730422974}]}}}" -}; - -HandsAboveHeadData = [ -{ - name: "tutorial/sign", - "backgroundColor": { - "blue": 187, - "green": 242, - "red": 198 - }, - "clientOnly": 0, - "created": "2016-08-23T22:42:48Z", - "dimensions": { - "x": 0.58140444755554199, - "y": 0.38676983118057251, - "z": 0.0099999997764825821 - }, - "id": "{c0ceabcf-501e-41fe-99e9-aca47a44122f}", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "queryAACube": { - "scale": 0.69837099313735962, - "x": -0.34918549656867981, - "y": -0.34918549656867981, - "z": -0.34918549656867981 - }, - "rotation": { - "w": 0.70710676908493042, - "x": 0, - "y": -0.70710670948028564, - "z": 0 - }, - "text": "Put your hands above your head.", - "textColor": { - "blue": 0, - "green": 0, - "red": 0 - }, - "type": "Text" -} -]; diff --git a/tutorial/fuse.js b/tutorial/fuse.js index 48efeb28e6..bd188a741f 100644 --- a/tutorial/fuse.js +++ b/tutorial/fuse.js @@ -1,36 +1,21 @@ +// +// fuse.js +// +// Created by Ryan Huffman on 9/1/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + (function() { - var findEntity = function(properties, searchRadius, filterFn) { - var entities = findEntities(properties, searchRadius, filterFn); - return entities.length > 0 ? entities[0] : null; - } + Script.include('utils.js'); - // Return all entities with properties `properties` within radius `searchRadius` - var findEntities = function(properties, searchRadius, filterFn) { - if (!filterFn) { - filterFn = function(properties, key, value) { - return value == properties[key]; - } + var DEBUG = false; + function debug() { + if (DEBUG) { + print.apply(self, arguments); } - searchRadius = searchRadius ? searchRadius : 100000; - var entities = Entities.findEntities({ x: 0, y: 0, z: 0 }, searchRadius); - var matchedEntities = []; - var keys = Object.keys(properties); - for (var i = 0; i < entities.length; ++i) { - var match = true; - var candidateProperties = Entities.getEntityProperties(entities[i], keys); - for (var key in properties) { - if (!filterFn(properties, key, candidateProperties[key])) { - // This isn't a match, move to next entity - match = false; - break; - } - } - if (match) { - matchedEntities.push(entities[i]); - } - } - - return matchedEntities; } var fuseSound = SoundCache.getSound("https://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Sounds/fuse.wav"); @@ -48,9 +33,8 @@ }; Fuse.prototype = { light: function() { - print("LIT", this.entityID); + debug("LIT", this.entityID); var anim = Entities.getEntityProperties(this.entityID, ['animation']).animation; - print("anim: ", anim.currentFrame, Object.keys(anim)); if (anim.currentFrame < 140) { return; @@ -86,8 +70,8 @@ var self = this; Script.setTimeout(function() { - print("BLOW UP"); - var spinnerID = findEntity({ name: "tutorial/equip/spinner" }, 20); + debug("BLOW UP"); + var spinnerID = Utils.findEntity({ name: "tutorial/equip/spinner" }, 20); Entities.callEntityMethod(spinnerID, "onLit"); injector.stop(); diff --git a/tutorial/fuseCollider.js b/tutorial/fuseCollider.js index dd8195d9b0..0ad5cfb371 100644 --- a/tutorial/fuseCollider.js +++ b/tutorial/fuseCollider.js @@ -1,55 +1,12 @@ (function() { - var findEntity = function(properties, searchRadius, filterFn) { - var entities = findEntities(properties, searchRadius, filterFn); - return entities.length > 0 ? entities[0] : null; - } - - // Return all entities with properties `properties` within radius `searchRadius` - var findEntities = function(properties, searchRadius, filterFn) { - if (!filterFn) { - filterFn = function(properties, key, value) { - return value == properties[key]; - } - } - searchRadius = searchRadius ? searchRadius : 100000; - var entities = Entities.findEntities({ x: 0, y: 0, z: 0 }, searchRadius); - var matchedEntities = []; - var keys = Object.keys(properties); - for (var i = 0; i < entities.length; ++i) { - var match = true; - var candidateProperties = Entities.getEntityProperties(entities[i], keys); - for (var key in properties) { - if (!filterFn(properties, key, candidateProperties[key])) { - // This isn't a match, move to next entity - match = false; - break; - } - } - if (match) { - matchedEntities.push(entities[i]); - } - } - - return matchedEntities; - } - - function getChildProperties(entityID, propertyNames) { - var childEntityIDs = Entities.getChildrenIDs(entityID); - var results = {} - for (var i = 0; i < childEntityIDs.length; ++i) { - var childEntityID = childEntityIDs[i]; - var properties = Entities.getEntityProperties(childEntityID, propertyNames); - results[childEntityID] = properties; - } - return results; - } + Script.include('utils.js'); var Fuse = function() { }; Fuse.prototype = { onLit: function() { print("LIT", this.entityID); - var fuseID = findEntity({ name: "tutorial/equip/fuse" }, 20); + var fuseID = Utils.findEntity({ name: "tutorial/equip/fuse" }, 20); Entities.callEntityMethod(fuseID, "light"); }, preload: function(entityID) { diff --git a/tutorial/ownershipToken.js b/tutorial/ownershipToken.js index ae212baa84..3cfb3f285d 100644 --- a/tutorial/ownershipToken.js +++ b/tutorial/ownershipToken.js @@ -18,7 +18,7 @@ if (!Function.prototype.bind) { if (this.prototype) { // Function.prototype doesn't have a prototype property - fNOP.prototype = this.prototype; + fNOP.prototype = this.prototype; } fBound.prototype = new fNOP(); @@ -43,8 +43,7 @@ function getOwnershipTokenID(parentEntityID) { var childID = childEntityIDs[i]; var properties = Entities.getEntityProperties(childID, ['name', 'userData', 'lifetime', 'age']); var childName = properties.name; - //debug("Owner lifetime: ", properties.lifetime, properties.age); - if (properties.age > 0.5 && childName.indexOf(TOKEN_NAME_PREFIX) == 0) { + if (childName.indexOf(TOKEN_NAME_PREFIX) == 0) { if (ownerID === null || childName < ownerName) { ownerID = childID; ownerName = childName; @@ -89,7 +88,6 @@ OwnershipToken = function(name, parentEntityID, options) { this.checkEverySeconds = getOption(options, 'checkEverySeconds', 1000); this.updateTokenLifetimeEvery = getOption(options, 'updateTokenLifetimeEvery', 2000); - //this.onRequestingOwnership = getOption(options, 'onRequestingOwnership', function() { }); this.onGainedOwnership = getOption(options, 'onGainedOwnership', function() { }); this.onLostOwnership = getOption(options, 'onLostOwnership', function() { }); diff --git a/tutorial/spinner.js b/tutorial/spinner.js index 4d34f31890..b4ff188b44 100644 --- a/tutorial/spinner.js +++ b/tutorial/spinner.js @@ -1,4 +1,21 @@ +// +// spinner.js +// +// Created by Ryan Huffman on 9/1/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + (function() { + var DEBUG = false; + function debug() { + if (DEBUG) { + print.apply(self, arguments); + } + } + var spinnerSound = SoundCache.getSound("http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Sounds/Pinwheel.L.wav"); var Spinner = function() { }; @@ -14,7 +31,7 @@ } Spinner.prototype = { onLit: function() { - print("LIT SPINNER", this.entityID); + debug("LIT SPINNER", this.entityID); Entities.editEntity(this.entityID, { "angularDamping": 0.1, "angularVelocity": { @@ -26,10 +43,9 @@ var injector = Audio.playSound(spinnerSound, { position: Entities.getEntityProperties(this.entityID, 'position').position, volume: 1.0, - loop: false + loop: false }); - print("HERE2"); var childrenProps = getChildProperties(this.entityID, ['type']); for (var childEntityID in childrenProps) { var props = childrenProps[childEntityID]; @@ -43,10 +59,9 @@ var self = this; Script.setTimeout(function() { - print("BLOW UP"); + debug("BLOW UP"); injector.stop(); - print("HERE"); var childrenProps = getChildProperties(self.entityID, ['type']); for (var childEntityID in childrenProps) { var props = childrenProps[childEntityID]; diff --git a/tutorial/touchControllerConfiguration.js b/tutorial/touchControllerConfiguration.js index 644215ce60..a63d6749ad 100644 --- a/tutorial/touchControllerConfiguration.js +++ b/tutorial/touchControllerConfiguration.js @@ -1,3 +1,13 @@ +// +// touchControllerConfiguration.js +// +// Created by Ryan Huffman on 9/1/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + var CONTROLLER_LENGTH_OFFSET = 0.0762; var leftBasePosition = { x: CONTROLLER_LENGTH_OFFSET / 2, diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 713b052412..2db6dfc8df 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -1,3 +1,18 @@ +// +// tutorial.js +// +// Created by Ryan Huffman on 9/1/16. +// Copyright 2016 High Fidelity, Inc. +// +// 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("entityData.js"); +Script.include("viveHandsv2.js"); +Script.include("lighter/createButaneLighter.js"); +Script.include('ownershipToken.js'); + if (!Function.prototype.bind) { Function.prototype.bind = function(oThis) { if (typeof this !== 'function') { @@ -26,15 +41,31 @@ if (!Function.prototype.bind) { }; } -Script.include("entityData.js"); +var DEBUG = false; +function debug() { + if (DEBUG) { + print.apply(self, arguments); + } +} -Script.include("viveHandsv2.js"); -Script.include("lighter/createButaneLighter.js"); -Script.include('ownershipToken.js'); +var INFO = true; +function info() { + if (INFO) { + print.apply(self, arguments); + } +} var BASKET_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx"; var BASKET_COLLIDER_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj"; -//var successSound = SoundCache.getSound(Script.resolvePath("success48.wav")); + +var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn"; +var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn"; +var NEAR_BASKET_COLLIDER_NAME = "tutorial/nearGrab/basket_collider"; +var FAR_BASKET_COLLIDER_NAME = "tutorial/farGrab/basket_collider"; +var GUN_BASKET_COLLIDER_NAME = "tutorial/equip/basket_collider"; +var GUN_SPAWN_NAME = "tutorial/gun_spawn"; +var TELEPORT_PAD_NAME = "tutorial/teleport/pad" + var successSound = SoundCache.getSound("http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Sounds/good_one.L.wav"); function beginsWithFilter(value, key) { @@ -74,8 +105,24 @@ findEntities = function(properties, searchRadius, filterFn) { return matchedEntities; } -// On start tutorial... +function setControllerVisible(name, visible) { + return; + Messages.sendLocalMessage('Controller-Display', JSON.stringify({ + name: name, + visible: visible, + })); +} + +function setControllerPartsVisible(parts) { + Messages.sendLocalMessage('Controller-Display-Parts', JSON.stringify(parts)); +} + +function setControllerPartLayer(part, layer) { + data = {}; + data[part] = layer; + Messages.sendLocalMessage('Controller-Set-Part-Layer', JSON.stringify(data)); +} function triggerHapticPulse() { function scheduleHaptics(delay, strength, duration) { @@ -91,18 +138,8 @@ function triggerHapticPulse() { scheduleHaptics(1200, 0.1, 100); } -// Load assets -var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn"; -var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn"; -var NEAR_BASKET_COLLIDER_NAME = "tutorial/nearGrab/basket_collider"; -var FAR_BASKET_COLLIDER_NAME = "tutorial/farGrab/basket_collider"; -var GUN_BASKET_COLLIDER_NAME = "tutorial/equip/basket_collider"; -var GUN_SPAWN_NAME = "tutorial/gun_spawn"; -var GUN_AMMO_NAME = "Tutorial Ping Pong Ball" -var TELEPORT_PAD_NAME = "tutorial/teleport/pad" - function spawn(entityData, transform, modifyFn) { - print("Creating: ", entityData); + debug("Creating: ", entityData); if (!transform) { transform = { position: { x: 0, y: 0, z: 0 }, @@ -112,7 +149,7 @@ function spawn(entityData, transform, modifyFn) { var ids = []; for (var i = 0; i < entityData.length; ++i) { var data = entityData[i]; - print("Creating: ", data.name); + debug("Creating: ", data.name); data.position = Vec3.sum(transform.position, data.position); data.rotation = Quat.multiply(data.rotation, transform.rotation); if (modifyFn) { @@ -120,7 +157,7 @@ function spawn(entityData, transform, modifyFn) { } var id = Entities.addEntity(data); ids.push(id); - print(id, "data:", JSON.stringify(data)); + debug(id, "data:", JSON.stringify(data)); } return ids; } @@ -140,26 +177,22 @@ function spawnWithTag(entityData, transform, tag) { var userData = parseJSON(data.userData); userData.tag = tag; data.userData = JSON.stringify(userData); - print("In modify", tag, userData, data.userData); + debug("In modify", tag, userData, data.userData); return data; } return spawn(entityData, transform, modifyFn); } function deleteEntitiesWithTag(tag) { - print("searching for...:", tag); + debug("searching for...:", tag); var entityIDs = findEntitiesWithTag(tag); for (var i = 0; i < entityIDs.length; ++i) { - //print("Deleteing:", entityIDs[i]); Entities.deleteEntity(entityIDs[i]); } } function editEntitiesWithTag(tag, propertiesOrFn) { - //print("Editing:", tag); var entityIDs = findEntitiesWithTag(tag); - //print("Editing...", entityIDs); for (var i = 0; i < entityIDs.length; ++i) { - //print("Editing...", entityIDs[i]); if (isFunction(propertiesOrFn)) { Entities.editEntity(entityIDs[i], propertiesOrFn(entityIDs[i])); } else { @@ -349,21 +382,14 @@ stepOrient.prototype = { } }; - // this.overlay = new StayInFrontOverlay("model", { - // url: "http://hifi-content.s3.amazonaws.com/alan/dev/Prompt-Cards/welcome.fbx?11", - // ignoreRayIntersection: true, - // visible: false - // }, 1.5, { x: 0, y: 0.3, z: 0 }); - // Spawn content set - //spawnWithTag(HandsAboveHeadData, defaultTransform, tag); - print("raise hands...", this.tag); + debug("raise hands...", this.tag); editEntitiesWithTag(this.tag, { visible: true }); this.checkIntervalID = null; function checkForHandsAboveHead() { - print("Orient: Checking for hands above head..."); + debug("Orient: Checking for hands above head..."); if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; @@ -421,16 +447,14 @@ stepRaiseAboveHead.prototype = { } }; - // Spawn content set - print("raise hands...", this.tag); + debug("raise hands...", this.tag); editEntitiesWithTag(this.tag, { visible: true }); - // Wait 2 seconds before starting to check for hands this.waitTimeoutID = Script.setTimeout(function() { this.checkIntervalID = null; function checkForHandsAboveHead() { - print("Raise above head: Checking for hands above head..."); + debug("Raise above head: Checking for hands above head..."); if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; @@ -455,24 +479,6 @@ stepRaiseAboveHead.prototype = { } }; -function setControllerVisible(name, visible) { - return; - Messages.sendLocalMessage('Controller-Display', JSON.stringify({ - name: name, - visible: visible, - })); -} - -function setControllerPartsVisible(parts) { - Messages.sendLocalMessage('Controller-Display-Parts', JSON.stringify(parts)); -} - -function setControllerPartLayer(part, layer) { - data = {}; - data[part] = layer; - Messages.sendLocalMessage('Controller-Set-Part-Layer', JSON.stringify(data)); -} - /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -498,13 +504,12 @@ stepNearGrab.prototype = { var tag = this.tag; // Spawn content set - //spawnWithTag(Step1EntityData, null, tag); showEntitiesWithTag(this.tag, { visible: true }); showEntitiesWithTag('bothGrab', { visible: true }); var boxSpawnID = findEntity({ name: NEAR_BOX_SPAWN_NAME }, 10000); if (!boxSpawnID) { - print("Error creating block, cannot find spawn"); + info("Error creating block, cannot find spawn"); return null; } var boxSpawnPosition = Entities.getEntityProperties(boxSpawnID, 'position').position; @@ -530,7 +535,7 @@ stepNearGrab.prototype = { return; } if (channel == "Entity-Exploded") { - print("TUTORIAL: Got entity-exploded message"); + debug("TUTORIAL: Got entity-exploded message"); var data = parseJSON(message); if (this.birdIDs.indexOf(data.entityID) >= 0) { @@ -541,7 +546,7 @@ stepNearGrab.prototype = { } }, cleanup: function() { - print("cleaning up near grab"); + debug("cleaning up near grab"); this.finished = true; setControllerVisible("trigger", false); setControllerPartLayer('tips', 'blank'); @@ -590,7 +595,7 @@ stepFarGrab.prototype = { var boxSpawnID = findEntity({ name: FAR_BOX_SPAWN_NAME }, 10000); if (!boxSpawnID) { - print("Error creating block, cannot find spawn"); + debug("Error creating block, cannot find spawn"); return null; } var boxSpawnPosition = Entities.getEntityProperties(boxSpawnID, 'position').position; @@ -610,7 +615,7 @@ stepFarGrab.prototype = { return; } if (channel == "Entity-Exploded") { - print("TUTORIAL: Got entity-exploded message"); + debug("TUTORIAL: Got entity-exploded message"); var data = parseJSON(message); if (this.birdIDs.indexOf(data.entityID) >= 0) { playSuccessSound(); @@ -694,7 +699,7 @@ stepEquip.prototype = { function createGun() { var boxSpawnID = findEntity({ name: GUN_SPAWN_NAME }, 10000); if (!boxSpawnID) { - print("Error creating block, cannot find spawn"); + info("Error creating block, cannot find spawn"); return null; } @@ -707,12 +712,9 @@ stepEquip.prototype = { } - // Enabled grab - // Create table ? - // Create blocks and basket this.gunID = createGun.bind(this)(); this.startWatchingGun(); - print("Created", this.gunID); + debug("Created", this.gunID); this.onFinish = onFinish; }, startWatchingGun: function() { @@ -737,7 +739,7 @@ stepEquip.prototype = { return; } - print("Got message", channel, message, sender, MyAvatar.sessionUUID); + debug("Got message", channel, message, sender, MyAvatar.sessionUUID); if (channel == "Tutorial-Spinner") { if (this.currentPart == this.PART1 && message == "wasLit") { @@ -753,7 +755,7 @@ stepEquip.prototype = { if (this.currentPart == this.PART2) { var data = parseJSON(message); if (data.action == 'release' && data.grabbedEntity == this.gunID) { - print("got release"); + info("got release"); this.stopWatchingGun(); this.currentPart = this.COMPLETE; playSuccessSound(); @@ -820,17 +822,16 @@ stepTurnAround.prototype = { var dir = Quat.getFront(MyAvatar.orientation); var angle = Math.atan2(dir.z, dir.x); var angleDegrees = ((angle / Math.PI) * 180); - print("CHECK"); if (!hasTurnedAround) { if (Math.abs(angleDegrees) > 140) { hasTurnedAround = true; - print("half way there..."); + info("Half way turned around"); } } else { if (Math.abs(angleDegrees) < 30) { Script.clearInterval(this.interval); this.interval = null; - print("DONE"); + info("Turned around"); playSuccessSound(); onFinish(); } @@ -876,20 +877,21 @@ stepTeleport.prototype = { // Wait until touching teleport pad... var padID = findEntity({ name: TELEPORT_PAD_NAME }, 100); - print(padID); var padProps = Entities.getEntityProperties(padID, ["position", "dimensions"]); - print(Object.keys(padProps)); var xMin = padProps.position.x - padProps.dimensions.x / 2; var xMax = padProps.position.x + padProps.dimensions.x / 2; var zMin = padProps.position.z - padProps.dimensions.z / 2; var zMax = padProps.position.z + padProps.dimensions.z / 2; function checkCollides() { - print("Checking if on pad..."); + debug("Checking if on pad..."); + var pos = MyAvatar.position; - print('x', pos.x, xMin, xMax); - print('z', pos.z, zMin, zMax); + + debug('x', pos.x, xMin, xMax); + debug('z', pos.z, zMin, zMax); + if (pos.x > xMin && pos.x < xMax && pos.z > zMin && pos.z < zMax) { - print("On pad!!"); + debug("On teleport pad"); Script.clearInterval(this.checkCollidesTimer); this.checkCollidesTimer = null; playSuccessSound(); @@ -901,8 +903,6 @@ stepTeleport.prototype = { showEntitiesWithTag(this.tag); }, cleanup: function() { - //setControllerVisible("teleport", false); - setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('tips', 'blank'); @@ -968,7 +968,7 @@ function showEntitiesWithTag(tag) { collisionless = data.collidable === true ? false : true; } if (data.soundKey) { - print("Setting sound key to true"); + debug("Setting sound key to true"); data.soundKey.playing = true; } var newProperties = { @@ -1055,12 +1055,12 @@ TutorialManager = function() { if (currentStepNum >= STEPS.length) { // Done - print("DONE WITH TUTORIAL"); + info("DONE WITH TUTORIAL"); currentStepNum = -1; currentStep = null; return false; } else { - print("Starting step", currentStepNum); + info("Starting step", currentStepNum); currentStep = STEPS[currentStepNum]; startedLastStepAt = Date.now(); currentStep.start(this.onFinish); @@ -1082,49 +1082,3 @@ TutorialManager = function() { currentStep = null; } } - -Script.scriptEnding.connect(function() { - Controller.enableMapping('handControllerPointer-click'); -}); - -// var entityID = '{be3d10a3-262a-4827-b30c-ec025c4325dc}'; -// var token = new OwnershipToken(Math.random() * 100000, entityID, { -// onGainedOwnership: function(token) { -// //Script.setTimeout(function() { token.destroy() }, 15000); -// Controller.keyReleaseEvent.connect(keyReleaseHandler); -// startTutorial(); -// }, -// onLostOwnership: function(token) { -// Controller.keyReleaseEvent.disconnect(keyReleaseHandler); -// stopTutorial(); -// } -// }); - -//tutorialManager = new TutorialManager(); -//tutorialManager.startTutorial(); -//Controller.keyReleaseEvent.connect(keyReleaseHandler); -Script.scriptEnding.connect(function() { - //token.destroy(); - //stopTutorial(); -}); - -// function keyReleaseHandler(event) { -// print(event.text); -// if (event.text == ",") { -// if (!tutorialManager.startNextStep()) { -// tutorialManager.startTutorial(); -// } -// } else if (event.text == "F11") { -// tutorialManager.restartStep(); -// } else if (event.text == "F10") { -// MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; -// } else if (event.text == "r") { -// tutorialManager.stopTutorial(); -// tutorialManager.startTutorial(); -// } -// } -// -// Messages.sendLocalMessage('Controller-Display', JSON.stringify({ -// name: "menu", -// visible: false, -// })); From 1e0772d613c2ad87a2963cbab8fe066101d41a6f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 12:05:42 -0700 Subject: [PATCH 067/109] Remove unused variable --- interface/src/Application.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cb25766dbb..ef5f044cf0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1276,7 +1276,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : Setting::Handle firstRun { Settings::firstRun, true }; bool isOnVive = _displayPlugin && _displayPlugin->getName() == "OpenVR (Vive)"; - bool isFirstRun = firstRun.get(); Setting::Handle tutorialComplete { "tutorialComplete", false }; bool shouldGoToTutorial = isOnVive && hasTutorialContent && !tutorialComplete.get(); From 2ea71f5e0b6a399630fb48742d6bbbc4bc9881aa Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 16:47:25 -0700 Subject: [PATCH 068/109] Fix cleanup of controller in tutorial --- tutorial/controllerDisplay.js | 1 + tutorial/tutorial.js | 87 +++++++++++++------- tutorial/tutorialZone.js | 4 +- tutorial/viveHandsv2.js | 144 +++++++++++++++++----------------- 4 files changed, 134 insertions(+), 102 deletions(-) diff --git a/tutorial/controllerDisplay.js b/tutorial/controllerDisplay.js index 0485e0bd9f..a85d25f562 100644 --- a/tutorial/controllerDisplay.js +++ b/tutorial/controllerDisplay.js @@ -175,6 +175,7 @@ ControllerDisplay = function() { }; deleteControllerDisplay = function(controllerDisplay) { + print("Deleting controller display"); for (var i = 0; i < controllerDisplay.overlays.length; ++i) { Overlays.deleteOverlay(controllerDisplay.overlays[i]); } diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 2db6dfc8df..68dd316360 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -44,14 +44,14 @@ if (!Function.prototype.bind) { var DEBUG = false; function debug() { if (DEBUG) { - print.apply(self, arguments); + print.apply(this, arguments); } } var INFO = true; function info() { if (INFO) { - print.apply(self, arguments); + print.apply(this, arguments); } } @@ -247,7 +247,8 @@ var stepDisableControllers = function(name) { } stepDisableControllers.prototype = { start: function(onFinish) { - editEntitiesWithTag('door', { visible: true }); + controllerDisplayManager = new ControllerDisplayManager(); + editEntitiesWithTag('door', { visible: true, collisionless: false }); Menu.setIsOptionChecked("Overlays", false); Controller.disableMapping('handControllerPointer-click'); Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'disable'); @@ -268,24 +269,39 @@ stepDisableControllers.prototype = { } }; +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: ENABLE CONTROLLERS // +// // +/////////////////////////////////////////////////////////////////////////////// +function reenableEverything() { + editEntitiesWithTag('door', { visible: false, collisionless: true }); + Menu.setIsOptionChecked("Overlays", true); + Controller.enableMapping('handControllerPointer-click'); + Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'enable'); + Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); + Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ + nearGrabEnabled: true, + holdEnabled: true, + farGrabEnabled: true, + })); + setControllerPartLayer('touchpad', 'blank'); + setControllerPartLayer('tips', 'blank'); + MyAvatar.shouldRenderLocally = true; + if (controllerDisplayManager) { + controllerDisplayManager.destroy(); + controllerDisplayManager = null; + } +} + var stepEnableControllers = function(name) { this.tag = name; this.shouldLog = false; } stepEnableControllers.prototype = { start: function(onFinish) { - editEntitiesWithTag('door', { visible: false }); - Menu.setIsOptionChecked("Overlays", true); - Controller.enableMapping('handControllerPointer-click'); - Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'enable'); - Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); - Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ - nearGrabEnabled: true, - holdEnabled: true, - farGrabEnabled: true, - })); - setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('tips', 'blank'); + reenableEverything(); onFinish(); }, cleanup: function() { @@ -433,6 +449,11 @@ stepRaiseAboveHead.prototype = { start: function(onFinish) { var tag = this.tag; + var STATE_START = 0; + var STATE_HANDS_DOWN = 1; + var STATE_HANDS_UP = 2; + this.state = STATE_START; + var defaultTransform = { position: { x: 0.2459, @@ -451,19 +472,24 @@ stepRaiseAboveHead.prototype = { editEntitiesWithTag(this.tag, { visible: true }); // Wait 2 seconds before starting to check for hands - this.waitTimeoutID = Script.setTimeout(function() { - this.checkIntervalID = null; - function checkForHandsAboveHead() { - debug("Raise above head: Checking for hands above head..."); + this.checkIntervalID = null; + function checkForHandsAboveHead() { + debug("Raise above head: Checking hands..."); + if (this.state == STATE_START) { + if (MyAvatar.getLeftPalmPosition().y < (MyAvatar.getHeadPosition().y - 0.1)) { + this.state = STATE_HANDS_DOWN; + } + } else if (this.state == STATE_HANDS_DOWN) { if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { + this.state = STATE_HANDS_UP; Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; playSuccessSound(); onFinish(); } } - this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); - }.bind(this), 2000); + } + this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); }, cleanup: function() { if (this.checkIntervalID) { @@ -675,7 +701,8 @@ var stepEquip = function(name) { this.tempTag = name + "-temporary"; this.PART1 = 0; this.PART2 = 1; - this.COMPLETE = 2; + this.PART3 = 2; + this.COMPLETE = 3; Messages.subscribe('Tutorial-Spinner'); Messages.messageReceived.connect(this.onMessage.bind(this)); @@ -745,6 +772,7 @@ stepEquip.prototype = { if (this.currentPart == this.PART1 && message == "wasLit") { this.currentPart = this.PART2; Script.setTimeout(function() { + this.currentPart = this.PART3; hideEntitiesWithTag(this.tagPart1); showEntitiesWithTag(this.tagPart2); setControllerPartLayer('tips', 'grip'); @@ -752,7 +780,7 @@ stepEquip.prototype = { }.bind(this), 9000); } } else if (channel == "Hifi-Object-Manipulation") { - if (this.currentPart == this.PART2) { + if (this.currentPart == this.PART3) { var data = parseJSON(message); if (data.action == 'release' && data.grabbedEntity == this.gunID) { info("got release"); @@ -930,7 +958,7 @@ var stepFinish = function(name) { } stepFinish.prototype = { start: function(onFinish) { - editEntitiesWithTag('door', { visible: false }); + editEntitiesWithTag('door', { visible: false, collisonless: true }); showEntitiesWithTag(this.tag); Settings.setValue("tutorialComplete", true); onFinish(); @@ -956,9 +984,6 @@ stepCleanupFinish.prototype = { - - - function showEntitiesWithTag(tag) { editEntitiesWithTag(tag, function(entityID) { var userData = Entities.getEntityProperties(entityID, "userData").userData; @@ -975,7 +1000,6 @@ function showEntitiesWithTag(tag) { visible: data.visible == false ? false : true, collisionless: collisionless, userData: JSON.stringify(data), - //collisionless: data.collisionless == true ? true : false, }; Entities.editEntity(entityID, newProperties); }); @@ -1029,8 +1053,6 @@ TutorialManager = function() { for (var i = 0; i < STEPS.length; ++i) { STEPS[i].cleanup(); } - //location = "/tutorial_begin"; - //location = "/tutorial"; MyAvatar.shouldRenderLocally = false; this.startNextStep(); } @@ -1078,7 +1100,12 @@ TutorialManager = function() { if (currentStep) { currentStep.cleanup(); } + reenableEverything(); currentStepNum = -1; currentStep = null; } } + + +var tutorialManager = new TutorialManager(); +tutorialManager.startTutorial(); diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js index 97aa906052..63772ee75d 100644 --- a/tutorial/tutorialZone.js +++ b/tutorial/tutorialZone.js @@ -48,8 +48,8 @@ if (!Function.prototype.bind) { } (function() { - Script.include("file:///C:/Users/Ryan/dev/hifi/tutorial/ownershipToken.js"); - Script.include("file:///C:/Users/Ryan/dev/hifi/tutorial/tutorial.js"); + Script.include("ownershipToken.js"); + Script.include("tutorial.js"); var TutorialZone = function() { this.token = null; diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index 17cf0185fb..bd37e15007 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -14,91 +14,101 @@ var zeroRotation = { x: 0, y: 0, z: 0, w: 1 }; /////////////////////////////////////////////////////////////////////////////// // Management of controller display // /////////////////////////////////////////////////////////////////////////////// +ControllerDisplayManager = function() { + var controllerDisplay = null; + var activeController = null; + var controllerCheckerIntervalID = null; -var controllerDisplay = null; -var activeController = null; -var controllerCheckerIntervalID = null; - -function updateControllers() { - if (HMD.active) { - if ("Vive" in Controller.Hardware) { - if (!activeController) { - debug("Found vive!"); - activeController = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION); + function updateControllers() { + if (HMD.active) { + if ("Vive" in Controller.Hardware) { + if (!activeController) { + debug("Found vive!"); + activeController = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION); + } + // We've found the controllers, we no longer need to look for active controllers + if (controllerCheckerIntervalID) { + Script.clearInterval(controllerCheckerIntervalID); + controllerCheckerIntervalID = null; + } + } else { + debug("HMD active, but no controllers found"); + if (activeController) { + deleteControllerDisplay(activeController); + activeController = null; + } + if (controllerCheckerIntervalID == null) { + controllerCheckerIntervalID = Script.setInterval(updateControllers, 1000); + } } - // We've found the controllers, we no longer need to look for active controllers + } else { + debug("HMD inactive"); + // We aren't in HMD mode, we no longer need to look for active controllers if (controllerCheckerIntervalID) { + debug("Clearing controller checker interval"); Script.clearInterval(controllerCheckerIntervalID); controllerCheckerIntervalID = null; } - } else { - debug("HMD active, but no controllers found"); if (activeController) { + debug("Deleting controller"); deleteControllerDisplay(activeController); activeController = null; } - if (controllerCheckerIntervalID == null) { - controllerCheckerIntervalID = Script.setInterval(updateControllers, 1000); - } - } - } else { - debug("HMD inactive"); - // We aren't in HMD mode, we no longer need to look for active controllers - if (controllerCheckerIntervalID) { - debug("Clearing controller checker interval"); - Script.clearInterval(controllerCheckerIntervalID); - controllerCheckerIntervalID = null; - } - if (activeController) { - debug("Deleting controller"); - deleteControllerDisplay(activeController); - activeController = null; } } -} -HMD.displayModeChanged.connect(updateControllers); + HMD.displayModeChanged.connect(updateControllers); -updateControllers(); + updateControllers(); -Messages.subscribe('Controller-Display'); -var handleMessages = function(channel, message, sender) { - if (!activeController) { - return; - } + Messages.subscribe('Controller-Display'); + var handleMessages = function(channel, message, sender) { + if (!activeController) { + return; + } - if (sender === MyAvatar.sessionUUID) { - if (channel === 'Controller-Display') { - debug('here'); - var data = JSON.parse(message); - var name = data.name; - var visible = data.visible; - //c.setDisplayAnnotation(name, visible); - if (name in activeController.annotations) { - debug("hiding"); - for (var i = 0; i < activeController.annotations[name].length; ++i) { - debug("hiding", i); - Overlays.editOverlay(activeController.annotations[name][i], { visible: visible }); + if (sender === MyAvatar.sessionUUID) { + if (channel === 'Controller-Display') { + debug('here'); + var data = JSON.parse(message); + var name = data.name; + var visible = data.visible; + //c.setDisplayAnnotation(name, visible); + if (name in activeController.annotations) { + debug("hiding"); + for (var i = 0; i < activeController.annotations[name].length; ++i) { + debug("hiding", i); + Overlays.editOverlay(activeController.annotations[name][i], { visible: visible }); + } + } + } else if (channel === 'Controller-Display-Parts') { + debug('here part'); + var data = JSON.parse(message); + for (var name in data) { + var visible = data[name]; + activeController.setPartVisible(name, visible); + } + } else if (channel === 'Controller-Set-Part-Layer') { + var data = JSON.parse(message); + for (var name in data) { + var layer = data[name]; + activeController.setLayerForPart(name, layer); } } - } else if (channel === 'Controller-Display-Parts') { - debug('here part'); - var data = JSON.parse(message); - for (var name in data) { - var visible = data[name]; - activeController.setPartVisible(name, visible); - } - } else if (channel === 'Controller-Set-Part-Layer') { - var data = JSON.parse(message); - for (var name in data) { - var layer = data[name]; - activeController.setLayerForPart(name, layer); - } } } -} -Messages.messageReceived.connect(handleMessages); + Messages.messageReceived.connect(handleMessages); + + this.destroy = function() { + print("Destroying controller display"); + Messages.messageReceived.disconnect(handleMessages); + if (activeController) { + deleteControllerDisplay(activeController); + } + }; + +} //var c = setupController(TOUCH_CONTROLLER_CONFIGURATION); //var c = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION); @@ -112,9 +122,3 @@ Messages.messageReceived.connect(handleMessages); // c.setLayerForPart("touchpad", layers[num]); //}, 2000); // -Script.scriptEnding.connect(function() { - if (activeController) { - deleteControllerDisplay(activeController); - } - //MyAvatar.shouldRenderLocally = true; -}); From d2a1957e085adc22a44b8b5bb52ae097aa8b391f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 16:47:34 -0700 Subject: [PATCH 069/109] Remove extraneous comment --- tutorial/tutorialZone.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js index 63772ee75d..e1e5a76eb0 100644 --- a/tutorial/tutorialZone.js +++ b/tutorial/tutorialZone.js @@ -1,24 +1,3 @@ -// A user designates ownership of the tutorial by creating a child entity (token) -// of the tutorial zone. The entity should have a short lifetime (5 seconds), and -// should have it's lifetime reset every second. -// -// * When you enter the "tutorial" begin zone -// * If the tutorial is owned -// * Show a "waiting" text, and check for ownership periodically -// * If the tutorial is not owned -// * Create the ownership token, begin tutorial -// * For extra safety, to avoid races, check after 1 second to confirm that -// another user hasn't created a token. If they have, use some method to -// resolve the conflict. -// * Once the user has finished the tutorial, stop creating the token to -// release ownership. -// -// * The tutorial will expose a local message API for controlling the tutorial -// * A special script will be used to: -// * Create a key shortcut to go to the beginning of the tutorial -// * -// - if (!Function.prototype.bind) { Function.prototype.bind = function(oThis) { if (typeof this !== 'function') { From 9c7ea6ac7c467ca215ba5a33524e28ad0a542686 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 16:48:17 -0700 Subject: [PATCH 070/109] Update logic for going to tutorial on launch --- interface/src/Application.cpp | 37 +++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ef5f044cf0..7a505c96c0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1275,37 +1275,46 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : bool hasTutorialContent = contentVersion >= 1; Setting::Handle firstRun { Settings::firstRun, true }; - bool isOnVive = _displayPlugin && _displayPlugin->getName() == "OpenVR (Vive)"; + bool hasVive = false; + for (auto& displayPlugin : PluginManager::getInstance()->getDisplayPlugins()) { + if (displayPlugin->getName() == "OpenVR (Vive)") { + hasVive = true; + break; + } + } Setting::Handle tutorialComplete { "tutorialComplete", false }; - bool shouldGoToTutorial = isOnVive && hasTutorialContent && !tutorialComplete.get(); - qDebug() << "Is on vive " << isOnVive << ", " << _displayPlugin->getName(); + bool shouldGoToTutorial = hasVive && hasTutorialContent && !tutorialComplete.get(); + qDebug() << "has vive: " << hasVive << ", current plugin: " << _displayPlugin->getName(); qDebug() << "has tutorial content" << hasTutorialContent; qDebug() << "tutorial complete" << tutorialComplete.get(); qDebug() << "should go to tutorial " << shouldGoToTutorial; + // when --url in command line, teleport to location + const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; + int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY); + QString addressLookupString; + if (urlIndex != -1) { + addressLookupString = arguments().value(urlIndex + 1); + } if (shouldGoToTutorial) { DependencyManager::get()->ifLocalSandboxRunningElse([=]() { qDebug() << "Home sandbox appears to be running, going to Home."; //DependencyManager::get()->goToLocalSandbox("/tutorial"); - DependencyManager::get()->loadSettings("hifi://sport/tutorial"); + DependencyManager::get()->loadSettings("hifi://sport/tutorial_begin"); }, [=]() { qDebug() << "Home sandbox does not appear to be running, going to Entry."; showHelp(); - DependencyManager::get()->goToEntry(); + if (addressLookupString.isEmpty()) { + DependencyManager::get()->goToEntry(); + } else { + DependencyManager::get()->loadSettings(addressLookupString); + } }); } else { - // when --url in command line, teleport to location - const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; - int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY); - QString addressLookupString; - if (urlIndex != -1) { - addressLookupString = arguments().value(urlIndex + 1); - } - if (firstRun.get()) { showHelp(); } @@ -5391,8 +5400,6 @@ void Application::initPlugins(const QStringList& arguments) { auto preferredDisplays = parser.value(display).split(',', QString::SkipEmptyParts); qInfo() << "Setting prefered display plugins:" << preferredDisplays; PluginManager::getInstance()->setPreferredDisplayPlugins(preferredDisplays); - } else { - PluginManager::getInstance()->setPreferredDisplayPlugins({ "OpenVR (Vive)", "Oculus Rift" }); } if (parser.isSet(disableDisplays)) { From 6924040d82d668a732c7b035cacde686baa0e236 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 22:54:57 -0700 Subject: [PATCH 071/109] Update tutorial http references to atp --- tutorial/entityData.js | 4 +-- tutorial/fuse.js | 4 +-- tutorial/lighter/butaneLighter.js | 15 ++++---- tutorial/lighter/createButaneLighter.js | 9 ++--- tutorial/spinner.js | 2 +- tutorial/tutorial.js | 5 +-- tutorial/viveControllerConfiguration.js | 46 ++++++++++++------------- 7 files changed, 38 insertions(+), 47 deletions(-) diff --git a/tutorial/entityData.js b/tutorial/entityData.js index 1e38079de5..76eb4d98ed 100644 --- a/tutorial/entityData.js +++ b/tutorial/entityData.js @@ -20,7 +20,7 @@ birdFirework1 = { "z": 0 }, "id": "{1c4061bc-b2e7-4435-bc47-3fcc39ae6624}", - "modelURL": "http://hifi-content.s3.amazonaws.com/jimi/tutorialroom/birdStatue15.fbx", + "modelURL": "atp:/tutorial_models/birdStatue15.fbx", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "position": { "x": 0.11612319946289062, @@ -66,7 +66,7 @@ birdFirework2 = { "z": 0 }, "id": "{ba067084-8d0f-4eeb-a8a1-c6814527c1bb}", - "modelURL": "http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Models/statuebird4.fbx", + "modelURL": "atp:/tutorial_models/statuebird4.fbx", "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", "position": { "x": 0, diff --git a/tutorial/fuse.js b/tutorial/fuse.js index bd188a741f..842695d85c 100644 --- a/tutorial/fuse.js +++ b/tutorial/fuse.js @@ -18,7 +18,7 @@ } } - var fuseSound = SoundCache.getSound("https://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Sounds/fuse.wav"); + var fuseSound = SoundCache.getSound("atp:/tutorial_sounds/fuse.wav"); function getChildProperties(entityID, propertyNames) { var childEntityIDs = Entities.getChildrenIDs(entityID); var results = {} @@ -44,7 +44,7 @@ currentFrame: 1, lastFrame: 150, running: 1, - url: "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/fuse/fuse.fbx", + url: "atp:/tutorial_models/fuse.fbx", loop: 0 }, }); diff --git a/tutorial/lighter/butaneLighter.js b/tutorial/lighter/butaneLighter.js index 2592d8ec4a..e5400c60f3 100644 --- a/tutorial/lighter/butaneLighter.js +++ b/tutorial/lighter/butaneLighter.js @@ -10,11 +10,11 @@ var _this; function getResourceURL(file) { - return 'http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/' + file; + return 'atp:/' + file; }; - const LIGHTER_ON_SOUND_URL = getResourceURL('Sounds/lighter_on.wav'); - const BUTANE_SOUND_URL = getResourceURL('Sounds/butane.wav'); + const LIGHTER_ON_SOUND_URL = getResourceURL('tutorial_sounds/lighter_on.wav'); + const BUTANE_SOUND_URL = getResourceURL('tutorial_sound/butane.wav'); // TODO: fix this in the client, changing the sound volume while a sound is playing doesn't seem to work right now const DYNAMIC_SOUND_VOLUME = false; @@ -151,11 +151,12 @@ var flameProperties = Entities.getEntityProperties(_this.lighterParticleEntity, ['position', 'rotation']); var pickRay = { origin: flameProperties.position, - direction: Quat.getFront(flameProperties.rotation) + direction: Quat.inverse(Quat.getFront(flameProperties.rotation)) } - var intersection = Entities.findRayIntersection(pickRay, true); - if (intersection.intersects) { - debugPrint(JSON.stringify(intersection)); + var intersection = Entities.findRayIntersection(pickRay, true, [], [_this.entityID, _this.lighterParticleEntity]); + if (intersection.intersects && intersection.distance <= FLAME_LENGTH && intersection.properties.script !== '') { + Entities.callEntityMethod(intersection.properties.id, 'onLit', [_this.triggerValue]); + debugPrint('Light it up! found: ' + intersection.properties.id); } }, releaseEquip: function(entityID, args) { diff --git a/tutorial/lighter/createButaneLighter.js b/tutorial/lighter/createButaneLighter.js index ec305ecb05..caf3188b14 100644 --- a/tutorial/lighter/createButaneLighter.js +++ b/tutorial/lighter/createButaneLighter.js @@ -7,12 +7,7 @@ // const TEST_MODE = false; -const SCRIPT_URL = 'https://dl.dropboxusercontent.com/u/14997455/hifi/butaneLighter/butaneLighter.js?v=' + Date.now(); -//const SCRIPT_URL = Script.resolvePath("butaneLighter.js"); - -function getResourceURL(file) { - return 'http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/' + file; -}; +const SCRIPT_URL = 'atp:/tutorial/lighter/butaneLighter.js'; //Creates an entity and returns a mixed object of the creation properties and the assigned entityID var createEntity = function(entityProperties, parent) { @@ -53,7 +48,7 @@ createButaneLighter = function(transform) { y: -0.01, z: 0 }, - modelURL: getResourceURL('Models/lighterIceCreamSandwich.fbx'), + modelURL: 'atp:/tutorial_models/lighterIceCreamSandwich.fbx', name: 'BrutaneLighter', shapeType: 'simple-compound', type: 'Model', diff --git a/tutorial/spinner.js b/tutorial/spinner.js index b4ff188b44..b50db2704e 100644 --- a/tutorial/spinner.js +++ b/tutorial/spinner.js @@ -16,7 +16,7 @@ } } - var spinnerSound = SoundCache.getSound("http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Sounds/Pinwheel.L.wav"); + var spinnerSound = SoundCache.getSound("atp:/tutorial_sounds/Pinwheel.L.wav"); var Spinner = function() { }; function getChildProperties(entityID, propertyNames) { diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 68dd316360..fd48b63978 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -55,9 +55,6 @@ function info() { } } -var BASKET_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx"; -var BASKET_COLLIDER_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj"; - var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn"; var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn"; var NEAR_BASKET_COLLIDER_NAME = "tutorial/nearGrab/basket_collider"; @@ -66,7 +63,7 @@ var GUN_BASKET_COLLIDER_NAME = "tutorial/equip/basket_collider"; var GUN_SPAWN_NAME = "tutorial/gun_spawn"; var TELEPORT_PAD_NAME = "tutorial/teleport/pad" -var successSound = SoundCache.getSound("http://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Sounds/good_one.L.wav"); +var successSound = SoundCache.getSound("atp:/tutorial_sounds/good_one.L.wav"); function beginsWithFilter(value, key) { return value.indexOf(properties[key]) == 0; diff --git a/tutorial/viveControllerConfiguration.js b/tutorial/viveControllerConfiguration.js index 25146c7382..edabccc3f2 100644 --- a/tutorial/viveControllerConfiguration.js +++ b/tutorial/viveControllerConfiguration.js @@ -1,5 +1,3 @@ -var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive2.fbx"; - var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); @@ -42,8 +40,8 @@ var viveNaturalPosition = { z: 0.06380049744620919 }; -var viveModelURL = "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_body.fbx"; -var viveTipsModelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/Tutorial/Models/vive_tips.fbx" +var viveModelURL = "atp:/controller/vive_body.fbx"; +var viveTipsModelURL = "atp:/controller/vive_tips.fbx" VIVE_CONTROLLER_CONFIGURATION = { name: "Vive", @@ -89,7 +87,7 @@ VIVE_CONTROLLER_CONFIGURATION = { // and swaps in textures based on the thumb position. touchpad: { type: "touchpad", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", + modelURL: "atp:/controller/vive_trackpad.fbx", visibleInput: "Vive.RSTouch", xInput: "Vive.LX", yInput: "Vive.LY", @@ -103,20 +101,20 @@ VIVE_CONTROLLER_CONFIGURATION = { disable_defaultTextureLayer: "blank", disable_textureLayers: { blank: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", + defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", }, teleport: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg", + defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg", }, arrows: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", + defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg", } } }, trigger: { type: "rotational", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trigger.fbx", + modelURL: "atp:/controller/vive_trigger.fbx", input: Controller.Standard.LT, naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, origin: { x: 0, y: -0.015, z: -0.00 }, @@ -128,30 +126,30 @@ VIVE_CONTROLLER_CONFIGURATION = { l_grip: { type: "static", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_l_grip.fbx", + modelURL: "atp:/controller/vive_l_grip.fbx", naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, }, r_grip: { type: "static", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_r_grip.fbx", + modelURL: "atp:/controller/vive_r_grip.fbx", naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, }, sys_button: { type: "static", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_sys_button.fbx", + modelURL: "atp:/controller/vive_sys_button.fbx", naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}, }, button: { type: "static", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + modelURL: "atp:/controller/vive_button.fbx", naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} }, button2: { type: "static", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + modelURL: "atp:/controller/vive_button.fbx", naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} }, }, @@ -207,7 +205,7 @@ VIVE_CONTROLLER_CONFIGURATION = { // and swaps in textures based on the thumb position. touchpad: { type: "touchpad", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx", + modelURL: "atp:/controller/vive_trackpad.fbx", visibleInput: "Vive.RSTouch", xInput: "Vive.RX", yInput: "Vive.RY", @@ -221,20 +219,20 @@ VIVE_CONTROLLER_CONFIGURATION = { disable_defaultTextureLayer: "blank", disable_textureLayers: { blank: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", + defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg", }, teleport: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg", + defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg", }, arrows: { - defaultTextureURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows-active.jpg", + defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows-active.jpg", } } }, trigger: { type: "rotational", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_trigger.fbx", + modelURL: "atp:/controller/vive_trigger.fbx", input: Controller.Standard.RT, naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763}, origin: { x: 0, y: -0.015, z: -0.00 }, @@ -246,30 +244,30 @@ VIVE_CONTROLLER_CONFIGURATION = { l_grip: { type: "static", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_l_grip.fbx", + modelURL: "atp:/controller/vive_l_grip.fbx", naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, }, r_grip: { type: "static", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_r_grip.fbx", + modelURL: "atp:/controller/vive_r_grip.fbx", naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226}, }, sys_button: { type: "static", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_sys_button.fbx", + modelURL: "atp:/controller/vive_sys_button.fbx", naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311}, }, button: { type: "static", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + modelURL: "atp:/controller/vive_button.fbx", naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} }, button2: { type: "static", - modelURL: "https://hifi-public.s3.amazonaws.com/huffman/controllers/vive_button.fbx", + modelURL: "atp:/controller/vive_button.fbx", naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564} }, }, From 0f187dc34a4ac46ee2c773df58c67d0f98991dff Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Sep 2016 23:08:02 -0700 Subject: [PATCH 072/109] Update content set url --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index b8e661ffbd..4f56921b28 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home.tgz"; +const HOME_CONTENT_URL = "https://hifi-public.s3.amazonaws.com/tutorial.tar.gz" function getBuildInfo() { var buildInfoPath = null; From 37096910fc7580ac74b4b809723074820ed9e1bc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Sep 2016 08:59:45 -0700 Subject: [PATCH 073/109] Fix tutorial begin location --- interface/src/Application.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7a505c96c0..2c562d317b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1302,11 +1302,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : if (shouldGoToTutorial) { DependencyManager::get()->ifLocalSandboxRunningElse([=]() { qDebug() << "Home sandbox appears to be running, going to Home."; - //DependencyManager::get()->goToLocalSandbox("/tutorial"); - DependencyManager::get()->loadSettings("hifi://sport/tutorial_begin"); + DependencyManager::get()->goToLocalSandbox("/tutorial_begin"); }, [=]() { qDebug() << "Home sandbox does not appear to be running, going to Entry."; - showHelp(); + if (firstRun.get()) { + showHelp(); + } if (addressLookupString.isEmpty()) { DependencyManager::get()->goToEntry(); } else { From 95d2e2a97a2d4fe54442f541b861aa93149c99d5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Sep 2016 09:34:04 -0700 Subject: [PATCH 074/109] Update tutorial path --- tutorial/tutorial.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index fd48b63978..701ce32269 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -1103,6 +1103,8 @@ TutorialManager = function() { } } - -var tutorialManager = new TutorialManager(); -tutorialManager.startTutorial(); +// To run the tutorial: +// +// var tutorialManager = new TutorialManager(); +// tutorialManager.startTutorial(); +// From 15e00c30a61c8120c2cee4e19d204511af1e429b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Sep 2016 09:43:26 -0700 Subject: [PATCH 075/109] Update tutorial content version --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 4f56921b28..a181f4ba28 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "https://hifi-public.s3.amazonaws.com/tutorial.tar.gz" +const HOME_CONTENT_URL = "https://hifi-public.s3.amazonaws.com/home-tutorial-1.tar.gz"; function getBuildInfo() { var buildInfoPath = null; From 7c791755c07f4a45164b65ee5c6bd5df2278ac7f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Sep 2016 09:59:59 -0700 Subject: [PATCH 076/109] Update home content to version 2 --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index a181f4ba28..f7cb9a53af 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "https://hifi-public.s3.amazonaws.com/home-tutorial-1.tar.gz"; +const HOME_CONTENT_URL = "https://hifi-public.s3.amazonaws.com/home-tutorial-2.tar.gz"; function getBuildInfo() { var buildInfoPath = null; From 1699299de9140afe0d244dc6ba6b36ccd9612528 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Sep 2016 12:33:53 -0700 Subject: [PATCH 077/109] Fix location that server content version is looked for --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2c562d317b..4e9cf57fda 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1261,8 +1261,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : auto initializeLocation = [this]() { // Get sandbox content set version, if available - auto acDirPath = PathUtils::getRootDataDirectory() + qApp->organizationName() + "/assignment-client/"; + auto acDirPath = PathUtils::getRootDataDirectory() + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/"; auto contentVersionPath = acDirPath + "content-version.txt"; + qDebug() << "Checking " << contentVersionPath << " for content version"; auto contentVersion = 0; QFile contentVersionFile(contentVersionPath); if (contentVersionFile.open(QIODevice::ReadOnly | QIODevice::Text)) { From 66a33d020d3082724e1fecc2020c8b7633c90a52 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Sep 2016 14:17:24 -0700 Subject: [PATCH 078/109] Update content version --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index f7cb9a53af..51148ba91b 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "https://hifi-public.s3.amazonaws.com/home-tutorial-2.tar.gz"; +const HOME_CONTENT_URL = "https://hifi-public.s3.amazonaws.com/home-tutorial-3.tar.gz"; function getBuildInfo() { var buildInfoPath = null; From 216cf2b4bcff13e8fe3e6c2cf475162bd9c28a56 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Sep 2016 14:18:25 -0700 Subject: [PATCH 079/109] Update tutorial to work with HMD + Hand Controllers only --- interface/src/Application.cpp | 18 +++++----- .../src/scripting/HMDScriptingInterface.cpp | 12 ++++++- .../src/scripting/HMDScriptingInterface.h | 3 ++ .../src/input-plugins/KeyboardMouseDevice.h | 2 ++ .../src/input-plugins/TouchscreenDevice.h | 2 ++ libraries/plugins/src/plugins/InputPlugin.h | 3 +- libraries/plugins/src/plugins/PluginUtils.cpp | 35 +++++++++++++++++++ libraries/plugins/src/plugins/PluginUtils.h | 17 +++++++++ plugins/hifiNeuron/src/NeuronPlugin.h | 2 ++ plugins/hifiSdl2/src/SDL2Manager.h | 2 ++ plugins/hifiSixense/src/SixenseManager.h | 2 ++ plugins/oculus/src/OculusControllerManager.h | 2 ++ plugins/openvr/src/ViveControllerManager.h | 2 ++ tutorial/tutorialStartZone.js | 21 +++++------ tutorial/tutorialZone.js | 26 ++++++++------ 15 files changed, 116 insertions(+), 33 deletions(-) create mode 100644 libraries/plugins/src/plugins/PluginUtils.cpp create mode 100644 libraries/plugins/src/plugins/PluginUtils.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4e9cf57fda..9cf0e841f9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -87,6 +87,7 @@ #include #include #include +#include #include #include #include @@ -1276,17 +1277,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : bool hasTutorialContent = contentVersion >= 1; Setting::Handle firstRun { Settings::firstRun, true }; - bool hasVive = false; - for (auto& displayPlugin : PluginManager::getInstance()->getDisplayPlugins()) { - if (displayPlugin->getName() == "OpenVR (Vive)") { - hasVive = true; - break; - } - } + bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable() && PluginUtils::isHandControllerAvailable(); Setting::Handle tutorialComplete { "tutorialComplete", false }; - bool shouldGoToTutorial = hasVive && hasTutorialContent && !tutorialComplete.get(); - qDebug() << "has vive: " << hasVive << ", current plugin: " << _displayPlugin->getName(); + bool shouldGoToTutorial = hasHMDAndHandControllers && hasTutorialContent && !tutorialComplete.get(); + + qDebug() << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName(); qDebug() << "has tutorial content" << hasTutorialContent; qDebug() << "tutorial complete" << tutorialComplete.get(); qDebug() << "should go to tutorial " << shouldGoToTutorial; @@ -1300,10 +1296,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : addressLookupString = arguments().value(urlIndex + 1); } + const QString TUTORIAL_PATH = "/tutorial_begin"; + if (shouldGoToTutorial) { DependencyManager::get()->ifLocalSandboxRunningElse([=]() { qDebug() << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox("/tutorial_begin"); + DependencyManager::get()->goToLocalSandbox(TUTORIAL_PATH); }, [=]() { qDebug() << "Home sandbox does not appear to be running, going to Entry."; if (firstRun.get()) { diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index a4676428a9..2c769c37d4 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -13,10 +13,12 @@ #include +#include #include #include #include -#include +#include + #include "Application.h" HMDScriptingInterface::HMDScriptingInterface() { @@ -47,6 +49,14 @@ glm::vec2 HMDScriptingInterface::overlayToSpherical(const glm::vec2 & position) return qApp->getApplicationCompositor().overlayToSpherical(position); } +bool HMDScriptingInterface::isHMDAvailable() { + return PluginUtils::isHMDAvailable(); +} + +bool HMDScriptingInterface::isHandControllerAvailable() { + return PluginUtils::isHandControllerAvailable(); +} + QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { glm::vec3 hudIntersection; auto instance = DependencyManager::get(); diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 2fbdb76198..4148b1cb4a 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -38,6 +38,9 @@ public: Q_INVOKABLE QString preferredAudioInput() const; Q_INVOKABLE QString preferredAudioOutput() const; + Q_INVOKABLE bool isHMDAvailable(); + Q_INVOKABLE bool isHandControllerAvailable(); + Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const; Q_INVOKABLE void disableHandLasers(int hands) const; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 2fdecf0bba..8177c9bcc0 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -68,6 +68,8 @@ public: bool isSupported() const override { return true; } const QString& getName() const override { return NAME; } + bool isHandController() const override { return false; } + void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h index f89f247ce8..7bfaa23be8 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h +++ b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h @@ -39,6 +39,8 @@ public: virtual bool isSupported() const override; virtual const QString& getName() const override { return NAME; } + bool isHandController() const override { return false; } + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; diff --git a/libraries/plugins/src/plugins/InputPlugin.h b/libraries/plugins/src/plugins/InputPlugin.h index 02ae5f58d5..f68be3edf6 100644 --- a/libraries/plugins/src/plugins/InputPlugin.h +++ b/libraries/plugins/src/plugins/InputPlugin.h @@ -19,7 +19,8 @@ namespace controller { class InputPlugin : public Plugin { public: virtual void pluginFocusOutEvent() = 0; - virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) = 0; + + virtual bool isHandController() const = 0; }; diff --git a/libraries/plugins/src/plugins/PluginUtils.cpp b/libraries/plugins/src/plugins/PluginUtils.cpp new file mode 100644 index 0000000000..0a19071210 --- /dev/null +++ b/libraries/plugins/src/plugins/PluginUtils.cpp @@ -0,0 +1,35 @@ +// PluginUtils.cpp +// input-plugins/src/input-plugins +// +// Created by Ryan Huffman on 9/22/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "PluginUtils.h" + +#include "DisplayPlugin.h" +#include "InputPlugin.h" +#include "PluginManager.h" + +bool PluginUtils::isHMDAvailable() { + for (auto& displayPlugin : PluginManager::getInstance()->getDisplayPlugins()) { + if (displayPlugin->isHmd()) { + return true; + break; + } + } + return false; +} + +bool PluginUtils::isHandControllerAvailable() { + for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) { + if (inputPlugin->isHandController()) { + return true; + break; + } + } + return false; +}; diff --git a/libraries/plugins/src/plugins/PluginUtils.h b/libraries/plugins/src/plugins/PluginUtils.h new file mode 100644 index 0000000000..aba5364800 --- /dev/null +++ b/libraries/plugins/src/plugins/PluginUtils.h @@ -0,0 +1,17 @@ +// PluginUtils.h +// input-plugins/src/input-plugins +// +// Created by Ryan Huffman on 9/22/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once + +class PluginUtils { +public: + static bool isHMDAvailable(); + static bool isHandControllerAvailable(); +}; \ No newline at end of file diff --git a/plugins/hifiNeuron/src/NeuronPlugin.h b/plugins/hifiNeuron/src/NeuronPlugin.h index 9ddd79c013..576deb64ae 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.h +++ b/plugins/hifiNeuron/src/NeuronPlugin.h @@ -25,6 +25,8 @@ class NeuronPlugin : public InputPlugin { public: friend void FrameDataReceivedCallback(void* context, void* sender, _BvhDataHeaderEx* header, float* data); + bool isHandController() const override { return false; } + // Plugin functions virtual bool isSupported() const override; virtual const QString& getName() const override { return NAME; } diff --git a/plugins/hifiSdl2/src/SDL2Manager.h b/plugins/hifiSdl2/src/SDL2Manager.h index a597a87aee..44b75abd2f 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.h +++ b/plugins/hifiSdl2/src/SDL2Manager.h @@ -26,6 +26,8 @@ public: bool isSupported() const override; const QString& getName() const override { return NAME; } + bool isHandController() const override { return false; } + void init() override; void deinit() override; diff --git a/plugins/hifiSixense/src/SixenseManager.h b/plugins/hifiSixense/src/SixenseManager.h index 6aec9fd4ad..56d3c6bc4d 100644 --- a/plugins/hifiSixense/src/SixenseManager.h +++ b/plugins/hifiSixense/src/SixenseManager.h @@ -31,6 +31,8 @@ public: virtual const QString& getName() const override { return NAME; } virtual const QString& getID() const override { return HYDRA_ID_STRING; } + bool isHandController() const override { return true; } + virtual bool activate() override; virtual void deactivate() override; diff --git a/plugins/oculus/src/OculusControllerManager.h b/plugins/oculus/src/OculusControllerManager.h index 3c5cdeb7c6..4c236a375d 100644 --- a/plugins/oculus/src/OculusControllerManager.h +++ b/plugins/oculus/src/OculusControllerManager.h @@ -26,6 +26,8 @@ public: bool isSupported() const override; const QString& getName() const override { return NAME; } + bool isHandController() const override { return true; } + bool activate() override; void deactivate() override; diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 95ff2f881a..5f34d70ba8 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -35,6 +35,8 @@ public: bool isSupported() const override; const QString& getName() const override { return NAME; } + bool isHandController() const override { return true; } + bool activate() override; void deactivate() override; diff --git a/tutorial/tutorialStartZone.js b/tutorial/tutorialStartZone.js index 5f5fa47b94..5adad1d00a 100644 --- a/tutorial/tutorialStartZone.js +++ b/tutorial/tutorialStartZone.js @@ -8,20 +8,21 @@ }, enterEntity: function() { // send message to outer zone - print("ENTERED THE TUTORIAL START AREA"); - var parentID = Entities.getEntityProperties(this.entityID, 'parentID').parentID; - print("HERE", parentID); - if (parentID) { - print("HERE2"); - Entities.callEntityMethod(parentID, 'start'); - print("HERE4"); + print("Entered the tutorial start area"); + if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable()) { + var parentID = Entities.getEntityProperties(this.entityID, 'parentID').parentID; + if (parentID) { + Entities.callEntityMethod(parentID, 'start'); + } else { + print("ERROR: No parent id found on tutorial start zone"); + } } else { - print("HERE3"); - print("ERROR: No parent id found on tutorial start zone"); + Window.alert("To proceed with this tutorial, please connect your VR headset and hand controllers."); + location = "/"; } }, leaveEntity: function() { - print("EXITED THE TUTORIAL START AREA"); + print("Exited the tutorial start area"); } }; diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js index e1e5a76eb0..e64d0f2445 100644 --- a/tutorial/tutorialZone.js +++ b/tutorial/tutorialZone.js @@ -27,8 +27,10 @@ if (!Function.prototype.bind) { } (function() { - Script.include("ownershipToken.js"); - Script.include("tutorial.js"); + var ownershipTokenPath = Script.resolvePath("ownershipToken.js"); + var tutorialPath = Script.resolvePath("tutorial.js"); + Script.include(ownershipTokenPath); + Script.include(tutorialPath); var TutorialZone = function() { this.token = null; @@ -37,17 +39,19 @@ if (!Function.prototype.bind) { TutorialZone.prototype = { keyReleaseHandler: function(event) { print(event.text); - if (event.text == ",") { - if (!this.tutorialManager.startNextStep()) { + if (event.isShifted && event.isAlt) { + if (event.text == ",") { + if (!this.tutorialManager.startNextStep()) { + this.tutorialManager.startTutorial(); + } + } else if (event.text == "F11") { + this.tutorialManager.restartStep(); + } else if (event.text == "F10") { + MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; + } else if (event.text == "r") { + this.tutorialManager.stopTutorial(); this.tutorialManager.startTutorial(); } - } else if (event.text == "F11") { - this.tutorialManager.restartStep(); - } else if (event.text == "F10") { - MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; - } else if (event.text == "r") { - this.tutorialManager.stopTutorial(); - this.tutorialManager.startTutorial(); } }, preload: function(entityID) { From f426f1cd4f0f7658771921713bb037171a718d37 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Sep 2016 16:26:32 -0700 Subject: [PATCH 080/109] Update isHandController to temporarily only return true for Vive --- libraries/plugins/src/plugins/PluginUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/plugins/src/plugins/PluginUtils.cpp b/libraries/plugins/src/plugins/PluginUtils.cpp index 0a19071210..9150b89bcd 100644 --- a/libraries/plugins/src/plugins/PluginUtils.cpp +++ b/libraries/plugins/src/plugins/PluginUtils.cpp @@ -26,7 +26,7 @@ bool PluginUtils::isHMDAvailable() { bool PluginUtils::isHandControllerAvailable() { for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) { - if (inputPlugin->isHandController()) { + if (inputPlugin->getName() == "OpenVR") { return true; break; } From 9c6eae36a7b5be5f93ab9e320bb7f20aad7951fb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 23 Sep 2016 09:07:26 -0700 Subject: [PATCH 081/109] Update tutorial progress keys --- .../networking/src/UserActivityLoggerScriptingInterface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index de3238f08d..248811b86a 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -27,9 +27,9 @@ void UserActivityLoggerScriptingInterface::toggledAway(bool isAway) { void UserActivityLoggerScriptingInterface::tutorialProgress(QString stepName, int stepNumber, float secondsToComplete, float tutorialElapsedTime) { logAction("tutorial_progress", { { "step", stepName }, - { "stepNumber", stepNumber }, - { "secondsToComplete", secondsToComplete }, - { "tutorial_elapsed_time", tutorialElapsedTime } + { "step_number", stepNumber }, + { "seconds_to_complete", secondsToComplete }, + { "tutorial_elapsed_seconds", tutorialElapsedTime } }); } From a062e6ab9a7349ec5f85470b063adae2197c1e38 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 23 Sep 2016 18:39:10 -0700 Subject: [PATCH 082/109] Revert change to make tutorial only work for Vive --- libraries/plugins/src/plugins/PluginUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/plugins/src/plugins/PluginUtils.cpp b/libraries/plugins/src/plugins/PluginUtils.cpp index 9150b89bcd..0a19071210 100644 --- a/libraries/plugins/src/plugins/PluginUtils.cpp +++ b/libraries/plugins/src/plugins/PluginUtils.cpp @@ -26,7 +26,7 @@ bool PluginUtils::isHMDAvailable() { bool PluginUtils::isHandControllerAvailable() { for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) { - if (inputPlugin->getName() == "OpenVR") { + if (inputPlugin->isHandController()) { return true; break; } From 8c6ba204cda73fef15a5679aa94be5c9e2583995 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 28 Sep 2016 16:01:00 -0700 Subject: [PATCH 083/109] Update tutorial: away.js toggle, turning based on controller --- tutorial/tutorial.js | 63 +++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 701ce32269..231783716f 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -65,6 +65,13 @@ var TELEPORT_PAD_NAME = "tutorial/teleport/pad" var successSound = SoundCache.getSound("atp:/tutorial_sounds/good_one.L.wav"); + +var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable"; +function setAwayEnabled(value) { + var message = value ? 'enable' : 'disable'; + Messages.sendLocalMessage(CHANNEL_AWAY_ENABLE, message); +} + function beginsWithFilter(value, key) { return value.indexOf(properties[key]) == 0; } @@ -259,6 +266,7 @@ stepDisableControllers.prototype = { setControllerPartLayer('tips', 'blank'); hideEntitiesWithTag('finish'); + setAwayEnabled(false); onFinish(); }, @@ -290,6 +298,7 @@ function reenableEverything() { controllerDisplayManager.destroy(); controllerDisplayManager = null; } + setAwayEnabled(true); } var stepEnableControllers = function(name) { @@ -361,7 +370,10 @@ StayInFrontOverlay.prototype = { }, destroy: function() { Overlays.deleteOverlay(this.overlayID); - Script.update.disconnect(this.boundUpdate); + try { + Script.update.disconnect(this.boundUpdate); + } catch(e) { + } } }; @@ -823,15 +835,8 @@ var stepTurnAround = function(name) { this.tag = name; this.tempTag = name + "-temporary"; - - //var name = "mapping-name"; - //var mapping = Controller.newMapping(name); - //mapping.from([Controller.Actions.StepYaw]).to(function() { - // print("STEPYAW"); - //}); - //Script.scriptEnding.connect(function() { - // Controller.disableMapping(name); - //}); + this.onActionBound = this.onAction.bind(this); + this.numTimesTurnPressed = 0; } stepTurnAround.prototype = { start: function(onFinish) { @@ -842,28 +847,38 @@ stepTurnAround.prototype = { setControllerPartLayer('tips', 'arrows'); showEntitiesWithTag(this.tag); - var hasTurnedAround = false; + + this.numTimesTurnPressed = 0; + Controller.actionEvent.connect(this.onActionBound); + this.interval = Script.setInterval(function() { + var FORWARD_THRESHOLD = 30; + var REQ_NUM_TIMES_PRESSED = 6; + var dir = Quat.getFront(MyAvatar.orientation); var angle = Math.atan2(dir.z, dir.x); var angleDegrees = ((angle / Math.PI) * 180); - if (!hasTurnedAround) { - if (Math.abs(angleDegrees) > 140) { - hasTurnedAround = true; - info("Half way turned around"); - } - } else { - if (Math.abs(angleDegrees) < 30) { - Script.clearInterval(this.interval); - this.interval = null; - info("Turned around"); - playSuccessSound(); - onFinish(); - } + + if (this.numTimesTurnPressed >= REQ_NUM_TIMES_PRESSED && Math.abs(angleDegrees) < FORWARD_THRESHOLD) { + Script.clearInterval(this.interval); + this.interval = null; + playSuccessSound(); + onFinish(); } }.bind(this), 100); }, + onAction: function(action, value) { + var STEP_YAW_ACTION = 6; + if (action == STEP_YAW_ACTION && value != 0) { + this.numTimesTurnPressed += 1; + } + }, cleanup: function() { + try { + Controller.actionEvent.disconnect(this.onActionBound); + } catch (e) { + } + setControllerVisible("left", false); setControllerVisible("right", false); From 20b93a7afa611ffd3229eaa86400f4ee8ce4e07e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 28 Sep 2016 16:38:03 -0700 Subject: [PATCH 084/109] Add redirection to entry on first turn if you don't have Vive --- interface/src/Application.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9cf0e841f9..45c14b2f88 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1277,7 +1277,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : bool hasTutorialContent = contentVersion >= 1; Setting::Handle firstRun { Settings::firstRun, true }; - bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable() && PluginUtils::isHandControllerAvailable(); + bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable("OpenVR (Vive)") && PluginUtils::isHandControllerAvailable(); Setting::Handle tutorialComplete { "tutorialComplete", false }; bool shouldGoToTutorial = hasHMDAndHandControllers && hasTutorialContent && !tutorialComplete.get(); @@ -1320,13 +1320,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } if (addressLookupString.isEmpty() && firstRun.get()) { - DependencyManager::get()->ifLocalSandboxRunningElse([=]() { - qDebug() << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox(); - }, [=]() { - qDebug() << "Home sandbox does not appear to be running, going to Entry."; + if (hasHMDAndHandControllers) { + DependencyManager::get()->ifLocalSandboxRunningElse([=]() { + qDebug() << "Home sandbox appears to be running, going to Home."; + DependencyManager::get()->goToLocalSandbox(); + }, [=]() { + qDebug() << "Home sandbox does not appear to be running, going to Entry."; + DependencyManager::get()->goToEntry(); + }); + } else { DependencyManager::get()->goToEntry(); - }); + } } else { qDebug() << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); DependencyManager::get()->loadSettings(addressLookupString); From e6314a27018eae1bb43ba1d2601d0cfa7a3aeeee Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 28 Sep 2016 16:48:28 -0700 Subject: [PATCH 085/109] Add changes to isHMDAvailable to specify hmd --- libraries/plugins/src/plugins/PluginUtils.cpp | 5 +++-- libraries/plugins/src/plugins/PluginUtils.h | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/plugins/src/plugins/PluginUtils.cpp b/libraries/plugins/src/plugins/PluginUtils.cpp index 0a19071210..f689f6f8bf 100644 --- a/libraries/plugins/src/plugins/PluginUtils.cpp +++ b/libraries/plugins/src/plugins/PluginUtils.cpp @@ -14,9 +14,10 @@ #include "InputPlugin.h" #include "PluginManager.h" -bool PluginUtils::isHMDAvailable() { +bool PluginUtils::isHMDAvailable(const QString& pluginName) { for (auto& displayPlugin : PluginManager::getInstance()->getDisplayPlugins()) { - if (displayPlugin->isHmd()) { + // Temporarily only enable this for Vive + if (displayPlugin->isHmd() && (pluginName.isEmpty() || displayPlugin->getName() == pluginName)) { return true; break; } diff --git a/libraries/plugins/src/plugins/PluginUtils.h b/libraries/plugins/src/plugins/PluginUtils.h index aba5364800..727677ccd3 100644 --- a/libraries/plugins/src/plugins/PluginUtils.h +++ b/libraries/plugins/src/plugins/PluginUtils.h @@ -10,8 +10,10 @@ #pragma once +#include + class PluginUtils { public: - static bool isHMDAvailable(); + static bool isHMDAvailable(const QString& pluginName = ""); static bool isHandControllerAvailable(); -}; \ No newline at end of file +}; From 408cc1cd04620edee7e85439656c2438f1a44765 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 29 Sep 2016 08:52:10 -0700 Subject: [PATCH 086/109] Update default hifi address to dev-welcome --- libraries/networking/src/AddressManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 0ab70854eb..f753bb0548 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -23,7 +23,7 @@ #include "AccountManager.h" const QString HIFI_URL_SCHEME = "hifi"; -const QString DEFAULT_HIFI_ADDRESS = "hifi://entry"; +const QString DEFAULT_HIFI_ADDRESS = "hifi://dev-welcome"; const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost"; const QString SANDBOX_STATUS_URL = "http://localhost:60332/status"; const QString INDEX_PATH = "/"; From 7379e9a03fb74e3aca833a3e7aa09bd7c56cc3c6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 29 Sep 2016 10:23:13 -0700 Subject: [PATCH 087/109] Update server-console content URL --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 51148ba91b..66d05b49dc 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "https://hifi-public.s3.amazonaws.com/home-tutorial-3.tar.gz"; +const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-4.tar.gz"; function getBuildInfo() { var buildInfoPath = null; From 3c18d082708f2f7bb33e3a26ab62be6b12f609c3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 30 Sep 2016 16:54:12 -0700 Subject: [PATCH 088/109] Update firepit to use Local Messages --- tutorial/firePit/fire.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/firePit/fire.js b/tutorial/firePit/fire.js index 381676f1ce..077d79a42a 100644 --- a/tutorial/firePit/fire.js +++ b/tutorial/firePit/fire.js @@ -75,7 +75,7 @@ _this.playSoundAtCurrentPosition(); _this.explodeWithColor(); Entities.deleteEntity(otherID) - Messages.sendMessage('Entity-Exploded', JSON.stringify({ + Messages.sendLocalMessage('Entity-Exploded', JSON.stringify({ entityID: otherID, })); } From 4ba637edf1eaf9a050929b767aa5febd5e17f6da Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 30 Sep 2016 16:55:32 -0700 Subject: [PATCH 089/109] Update tutorial to use static list of entity ids --- tutorial/tutorial.js | 99 ++++++++++------------ tutorial/tutorialEntityIDs.js | 151 ++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 54 deletions(-) create mode 100644 tutorial/tutorialEntityIDs.js diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 231783716f..884420c154 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -11,7 +11,7 @@ Script.include("entityData.js"); Script.include("viveHandsv2.js"); Script.include("lighter/createButaneLighter.js"); -Script.include('ownershipToken.js'); +Script.include("tutorialEntityIDs.js"); if (!Function.prototype.bind) { Function.prototype.bind = function(oThis) { @@ -57,9 +57,6 @@ function info() { var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn"; var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn"; -var NEAR_BASKET_COLLIDER_NAME = "tutorial/nearGrab/basket_collider"; -var FAR_BASKET_COLLIDER_NAME = "tutorial/farGrab/basket_collider"; -var GUN_BASKET_COLLIDER_NAME = "tutorial/equip/basket_collider"; var GUN_SPAWN_NAME = "tutorial/gun_spawn"; var TELEPORT_PAD_NAME = "tutorial/teleport/pad" @@ -218,19 +215,6 @@ function isFunction(functionToCheck) { return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; } - var defaultTransform = { - position: { - x: 0.2459, - y: 0.9011, - z: 0.7266 - }, - rotation: { - x: 0, - y: 0, - z: 0, - w: 1 - } - }; function playSuccessSound() { Audio.playSound(successSound, { position: MyAvatar.position, @@ -393,20 +377,6 @@ stepOrient.prototype = { var tag = this.tag; - var defaultTransform = { - position: { - x: 0.2459, - y: 0.9011, - z: 0.7266 - }, - rotation: { - x: 0, - y: 0, - z: 0, - w: 1 - } - }; - // Spawn content set debug("raise hands...", this.tag); editEntitiesWithTag(this.tag, { visible: true }); @@ -428,7 +398,6 @@ stepOrient.prototype = { }, cleanup: function() { if (this.active) { - //location = "/tutorial"; this.active = false; } if (this.overlay) { @@ -463,20 +432,6 @@ stepRaiseAboveHead.prototype = { var STATE_HANDS_UP = 2; this.state = STATE_START; - var defaultTransform = { - position: { - x: 0.2459, - y: 0.9011, - z: 0.7266 - }, - rotation: { - x: 0, - y: 0, - z: 0, - w: 1 - } - }; - debug("raise hands...", this.tag); editEntitiesWithTag(this.tag, { visible: true }); @@ -554,16 +509,11 @@ stepNearGrab.prototype = { return spawnWithTag([birdFirework1], null, this.tempTag)[0]; } - // Enabled grab - // Create table ? - // Create blocks and basket this.birdIDs = []; this.birdIDs.push(createBlock.bind(this)()); this.birdIDs.push(createBlock.bind(this)()); this.birdIDs.push(createBlock.bind(this)()); this.positionWatcher = new PositionWatcher(this.birdIDs, boxSpawnPosition, -0.4, 4); - - // If block gets too far away or hasn't been touched for X seconds, create a new block and destroy the old block }, onMessage: function(channel, message, seneder) { if (this.finished) { @@ -997,6 +947,29 @@ stepCleanupFinish.prototype = { function showEntitiesWithTag(tag) { + var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; + if (entities) { + for (entityID in entities) { + var data = entities[entityID]; + + var collisionless = data.visible === false ? true : false; + if (data.collidable !== undefined) { + collisionless = data.collidable === true ? false : true; + } + if (data.soundKey) { + data.soundKey.playing = true; + } + var newProperties = { + visible: data.visible == false ? false : true, + collisionless: collisionless, + userData: JSON.stringify(data), + }; + Entities.editEntity(entityID, newProperties); + } + } + + // Dynamic method, suppressed for now + return; editEntitiesWithTag(tag, function(entityID) { var userData = Entities.getEntityProperties(entityID, "userData").userData; var data = parseJSON(userData); @@ -1005,7 +978,6 @@ function showEntitiesWithTag(tag) { collisionless = data.collidable === true ? false : true; } if (data.soundKey) { - debug("Setting sound key to true"); data.soundKey.playing = true; } var newProperties = { @@ -1017,6 +989,26 @@ function showEntitiesWithTag(tag) { }); } function hideEntitiesWithTag(tag) { + var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; + if (entities) { + for (entityID in entities) { + var data = entities[entityID]; + + if (data.soundKey) { + data.soundKey.playing = false; + } + var newProperties = { + visible: false, + collisionless: 1, + ignoreForCollisions: 1, + userData: JSON.stringify(data), + }; + Entities.editEntity(entityID, newProperties); + } + } + + // Dynamic method, suppressed for now + return; editEntitiesWithTag(tag, function(entityID) { var userData = Entities.getEntityProperties(entityID, "userData").userData; var data = parseJSON(userData); @@ -1049,10 +1041,8 @@ TutorialManager = function() { currentStep = null; startedTutorialAt = Date.now(); STEPS = [ - //new stepCleanupFinish("finish"); new stepDisableControllers("step0"), new stepOrient("orient"), - //new stepWelcome("welcome"), new stepRaiseAboveHead("raiseHands"), new stepNearGrab("nearGrab"), new stepFarGrab("farGrab"), @@ -1101,6 +1091,7 @@ TutorialManager = function() { return true; } }.bind(this); + this.restartStep = function() { if (currentStep) { currentStep.cleanup(); diff --git a/tutorial/tutorialEntityIDs.js b/tutorial/tutorialEntityIDs.js new file mode 100644 index 0000000000..38bd06e5ff --- /dev/null +++ b/tutorial/tutorialEntityIDs.js @@ -0,0 +1,151 @@ +TUTORIAL_TAG_TO_ENTITY_IDS_MAP = { + "teleport": { + "{ff064b9e-7fa4-4693-a386-a67b9f92a948}": { + "tag": "teleport" + }, + "{4478f7b5-d3ac-4213-9a7b-ad8cd69575b8}": { + "tag": "teleport" + } + }, + "finish": { + "{340e05b5-88df-4b2b-b43c-756dd714d6d8}": { + "tag": "finish" + } + }, + "door": { + "{9c5b0fee-e695-4516-94cd-153371e3857b}": { + "tag": "door" + } + }, + "farGrab": { + "{70fcd96c-cd59-4f23-9ca5-a167f2f85680}": { + "visible": false, + "tag": "farGrab" + }, + "{ff7b9793-0d94-4f18-bc09-4ab589126e60}": { + "tag": "farGrab" + }, + "{fdd77d2c-af36-41c1-ba57-74b7ae79d996}": { + "tag": "farGrab" + }, + "{e11700f6-bc9a-411f-9ddc-bf265d4e3ccf}": { + "tag": "farGrab" + }, + "{95850c56-cd1c-42b9-ab6b-a163a6f2878f}": { + "tag": "farGrab" + } + }, + "nearGrab": { + "{55c861ef-60ca-4722-a6c5-9c6967966ec5}": { + "tag": "nearGrab" + }, + "{644d655b-ae66-43b1-9bab-a44b9a8ad632}": { + "tag": "nearGrab" + }, + "{88221a22-b710-4d35-852b-5257b0aa77dc}": { + "tag": "nearGrab" + }, + "{8bf0baa1-88d0-448a-a782-100d4413bd82}": { + "tag": "nearGrab" + }, + "{5cf22b9c-fb22-4854-8821-554422980b24}": { + "visible": false, + "tag": "nearGrab" + } + }, + "equip-part1": { + "{d73822ca-0a34-4cf4-a530-3258ac459a14}": { + "tag": "equip-part1" + }, + "{97ced5e7-fc81-40f9-a9e8-f85b4b30f24c}": { + "tag": "equip-part1" + }, + "{8572d991-5777-45df-97bf-7243d7b12f81}": { + "tag": "equip-part1" + }, + "{da5ea72e-54b6-41ac-b711-742b062b6968}": { + "tag": "equip-part1" + }, + "{c8944a13-9acb-4d77-b1ee-851845e98357}": { + "tag": "equip-part1" + }, + "{e9481c78-1a21-43f7-b54c-58f2efdf3c8f}": { + "tag": "equip-part1" + }, + "{ca3c28f3-15fc-4349-a85e-eaca0fad6434}": { + "tag": "equip-part1" + }, + "{09ddcb94-52a7-4f50-a5a2-db9db28fc519}": { + "tag": "equip-part1" + }, + "{dd13fcd5-616f-4749-ab28-2e1e8bc512e9}": { + "tag": "equip-part1" + } + }, + "equip-part2": { + "{8b92eec5-aeed-4368-bce0-432cc9ad4c51}": { + "tag": "equip-part2" + }, + "{6307cd16-dd1d-4988-a339-578178436b45}": { + "tag": "equip-part2" + } + }, + "turnAround": { + "{ce74b3ca-d1c7-4980-bd98-2d488095a39e}": { + "tag": "turnAround" + } + }, + "bothGrab": { + "{14792a6e-dc6f-4e7a-843f-4b109b06b5a4}": { + "visible": false, + "tag": "bothGrab", + "collidable": true + }, + "{215dcd14-88fc-4604-9033-cbd2a660178a}": { + "tag": "bothGrab" + }, + "{fbc2e40d-0633-45ac-b1c9-97fc8465f93b}": { + "tag": "bothGrab" + }, + "{6752dad6-109d-4dc5-aef7-dc8509468cf4}": { + "tag": "bothGrab" + }, + "{178e2c71-dff5-4231-8d28-df47fddf4709}": { + "soundKey": { + "playbackGapRange": 0, + "url": "atp:/sounds/crackling_fire.L.wav", + "volume": 0.5, + "playbackGap": 5, + "playing": false, + "loop": true + }, + "tag": "bothGrab" + }, + "{52445ac5-8730-4457-827e-6c076d2c609c}": { + "tag": "bothGrab" + } + }, + "raiseHands": { + "{7139e45d-25cf-470b-b133-c0fda0099d2b}": { + "tag": "raiseHands" + } + }, + "equip": { + "{e7897c9c-f4fa-4989-a383-28af56c2e544}": { + "visible": false, + "tag": "equip" + }, + "{9df518da-9e65-4b76-8a79-eeefdb0b7310}": { + "visible": false, + "tag": "equip" + }, + "{1a77c20e-5d9b-4b54-bf20-1416141a7ca8}": { + "tag": "equip" + } + }, + "orient": { + "{95d233ab-ed0a-46e1-b047-1c542688ef3f}": { + "tag": "orient" + } + } +} From a463a3e608d9214656b05d8524d4d103cb56d2f7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 30 Sep 2016 16:56:53 -0700 Subject: [PATCH 090/109] Update viveHandsv2 to hide on equip --- tutorial/controllerDisplay.js | 13 +- tutorial/viveControllerConfiguration.js | 10 +- tutorial/viveHandsv2.js | 197 ++++++++++++++++++------ 3 files changed, 172 insertions(+), 48 deletions(-) diff --git a/tutorial/controllerDisplay.js b/tutorial/controllerDisplay.js index a85d25f562..3bc0e05a96 100644 --- a/tutorial/controllerDisplay.js +++ b/tutorial/controllerDisplay.js @@ -17,12 +17,23 @@ createControllerDisplay = function(config) { }, mappingName: "mapping-display", + setVisible: function(visible) { + print("CONTROLLER_DISPLAY::Setting visible", this.overlays.length); + for (var i = 0; i < this.overlays.length; ++i) { + print("i", i, this.overlays[i]); + Overlays.editOverlay(this.overlays[i], { + visible: visible + }); + } + }, + setPartVisible: function(partName, visible) { + return; if (partName in this.partOverlays) { debug("Setting part visible", partName, visible); for (var i = 0; i < this.partOverlays[partName].length; ++i) { Overlays.editOverlay(this.partOverlays[partName][i], { - visible: visible + //visible: visible }); } } diff --git a/tutorial/viveControllerConfiguration.js b/tutorial/viveControllerConfiguration.js index edabccc3f2..b8197d6cd2 100644 --- a/tutorial/viveControllerConfiguration.js +++ b/tutorial/viveControllerConfiguration.js @@ -43,7 +43,7 @@ var viveNaturalPosition = { var viveModelURL = "atp:/controller/vive_body.fbx"; var viveTipsModelURL = "atp:/controller/vive_tips.fbx" -VIVE_CONTROLLER_CONFIGURATION = { +VIVE_CONTROLLER_CONFIGURATION_LEFT = { name: "Vive", controllers: [ { @@ -154,10 +154,14 @@ VIVE_CONTROLLER_CONFIGURATION = { }, }, }, + ] +}; - +VIVE_CONTROLLER_CONFIGURATION_RIGHT = { + name: "Vive Right", + controllers: [ { modelURL: viveModelURL, jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), @@ -273,5 +277,5 @@ VIVE_CONTROLLER_CONFIGURATION = { }, } ] -} +}; diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index bd37e15007..e365a689c1 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -1,3 +1,33 @@ +if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 + // internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function() {}, + fBound = function() { + return fToBind.apply(this instanceof fNOP + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + if (this.prototype) { + // Function.prototype doesn't have a prototype property + fNOP.prototype = this.prototype; + } + fBound.prototype = new fNOP(); + + return fBound; + }; +} + +Script.setTimeout(function() { print('timeout') }, 100); + Script.include("controllerDisplay.js"); Script.include("viveControllerConfiguration.js"); @@ -15,16 +45,37 @@ var zeroRotation = { x: 0, y: 0, z: 0, w: 1 }; // Management of controller display // /////////////////////////////////////////////////////////////////////////////// ControllerDisplayManager = function() { - var controllerDisplay = null; - var activeController = null; + var self = this; + var controllerLeft = null; + var controllerRight = null; var controllerCheckerIntervalID = null; + this.setLeftVisible = function(visible) { + print("settings controller display to visible"); + if (controllerLeft) { + print("doign it...", visible); + controllerLeft.setVisible(visible); + } + }; + + this.setRightVisible = function(visible) { + print("settings controller display to visible"); + if (controllerRight) { + print("doign it...", visible); + controllerRight.setVisible(visible); + } + }; + function updateControllers() { if (HMD.active) { if ("Vive" in Controller.Hardware) { - if (!activeController) { - debug("Found vive!"); - activeController = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION); + if (!controllerLeft) { + debug("Found vive left!"); + controllerLeft = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION_LEFT); + } + if (!controllerRight) { + debug("Found vive right!"); + controllerRight = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION_RIGHT); } // We've found the controllers, we no longer need to look for active controllers if (controllerCheckerIntervalID) { @@ -33,10 +84,7 @@ ControllerDisplayManager = function() { } } else { debug("HMD active, but no controllers found"); - if (activeController) { - deleteControllerDisplay(activeController); - activeController = null; - } + self.deleteControllerDisplays(); if (controllerCheckerIntervalID == null) { controllerCheckerIntervalID = Script.setInterval(updateControllers, 1000); } @@ -49,36 +97,38 @@ ControllerDisplayManager = function() { Script.clearInterval(controllerCheckerIntervalID); controllerCheckerIntervalID = null; } - if (activeController) { - debug("Deleting controller"); - deleteControllerDisplay(activeController); - activeController = null; - } + self.deleteControllerDisplays(); } } - HMD.displayModeChanged.connect(updateControllers); - - updateControllers(); - Messages.subscribe('Controller-Display'); var handleMessages = function(channel, message, sender) { - if (!activeController) { + if (!controllerLeft && !controllerRight) { return; } if (sender === MyAvatar.sessionUUID) { if (channel === 'Controller-Display') { - debug('here'); var data = JSON.parse(message); var name = data.name; var visible = data.visible; //c.setDisplayAnnotation(name, visible); - if (name in activeController.annotations) { - debug("hiding"); - for (var i = 0; i < activeController.annotations[name].length; ++i) { - debug("hiding", i); - Overlays.editOverlay(activeController.annotations[name][i], { visible: visible }); + if (controllerLeft) { + if (name in controllerLeft.annotations) { + debug("hiding"); + for (var i = 0; i < controllerLeft.annotations[name].length; ++i) { + debug("hiding", i); + Overlays.editOverlay(controllerLeft.annotations[name][i], { visible: visible }); + } + } + } + if (controllerRight) { + if (name in controllerRight.annotations) { + debug("hiding"); + for (var i = 0; i < controllerRight.annotations[name].length; ++i) { + debug("hiding", i); + Overlays.editOverlay(controllerRight.annotations[name][i], { visible: visible }); + } } } } else if (channel === 'Controller-Display-Parts') { @@ -86,39 +136,98 @@ ControllerDisplayManager = function() { var data = JSON.parse(message); for (var name in data) { var visible = data[name]; - activeController.setPartVisible(name, visible); + if (controllerLeft) { + controllerLeft.setPartVisible(name, visible); + } + if (controllerRight) { + controllerRight.setPartVisible(name, visible); + } } } else if (channel === 'Controller-Set-Part-Layer') { var data = JSON.parse(message); for (var name in data) { var layer = data[name]; - activeController.setLayerForPart(name, layer); + if (controllerLeft) { + controllerLeft.setLayerForPart(name, layer); + } + if (controllerRight) { + controllerRight.setLayerForPart(name, layer); + } } + } else if (channel == 'Hifi-Object-Manipulation') {// && sender == MyAvatar.sessionUUID) { + //print("got manip"); + var data = JSON.parse(message); + //print("post data", data); + var visible = data.action != 'equip'; + //print("Calling..."); + if (data.joint == "LeftHand") { + self.setLeftVisible(visible); + } else if (data.joint == "RightHand") { + self.setRightVisible(visible); + } } } } Messages.messageReceived.connect(handleMessages); - this.destroy = function() { - print("Destroying controller display"); - Messages.messageReceived.disconnect(handleMessages); - if (activeController) { - deleteControllerDisplay(activeController); + this.deleteControllerDisplays = function() { + if (controllerLeft) { + deleteControllerDisplay(controllerLeft); + controllerLeft = null; + } + if (controllerRight) { + deleteControllerDisplay(controllerRight); + controllerRight = null; } }; + this.destroy = function() { + print("Destroying controller display"); + Messages.messageReceived.disconnect(handleMessages); + self.deleteControllerDisplays(); + }; + + HMD.displayModeChanged.connect(updateControllers); + + updateControllers(); } -//var c = setupController(TOUCH_CONTROLLER_CONFIGURATION); -//var c = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION); -//c.setPartVisible("touchpad", false); -//c.setPartVisible("touchpad_teleport", false); -//layers = ["blank", "teleport", 'arrows']; -//num = 0; -//Script.setInterval(function() { -// print('num', num); -// num = (num + 1) % layers.length; -// c.setLayerForPart("touchpad", layers[num]); -//}, 2000); -// +/* +var controllerDisplayManager = new ControllerDisplayManager(); + +Messages.subscribe('Hifi-Object-Manipulation'); +function onMessageReceived(channel, message, sender) { + print(channel, message, sender); + if (channel == 'Hifi-Object-Manipulation') {// && sender == MyAvatar.sessionUUID) { + print("got manip"); + var data = JSON.parse(message); + print("post data", data); + var visible = data.action != 'equip'; + print("Calling..."); + if (data.joint == "LeftHand") { + controllerDisplayManager.setLeftVisible(visible); + } else if (data.joint == "RightHand") { + controllerDisplayManager.setRightVisible(visible); + } + } +} +Messages.messageReceived.connect(onMessageReceived); + +var visible = false; +print("SETTING IT UP"); +print("done"); +function toggleVis() { + print("toggling"); + visible = !visible; + controllerDisplayManager.setVisible(visible); +} +//toggleVis(); +//Script.setInterval(toggleVis, 1000); + +Script.scriptEnding.connect(function() { + print("destorying..."); + controllerDisplayManager.destroy(); + controllerDisplayManager = null; +}); +*/ From 0e5f670cceeb41d008724a57ead7abc99afc0633 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Sun, 2 Oct 2016 21:53:30 -0700 Subject: [PATCH 091/109] Add extra debug to ownershipToken.js --- tutorial/ownershipToken.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tutorial/ownershipToken.js b/tutorial/ownershipToken.js index 3cfb3f285d..745eee44e4 100644 --- a/tutorial/ownershipToken.js +++ b/tutorial/ownershipToken.js @@ -108,6 +108,7 @@ OwnershipToken.prototype = { } if (this.updateLifetimeID) { + debug(this.name, "Clearing update lifetime interval"); Script.clearInterval(this.updateLifetimeID); this.updateLifetimeID = null; } From c7ead55380d2ccc49e0bd79dd9dc816ba4cc635e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Sun, 2 Oct 2016 22:14:44 -0700 Subject: [PATCH 092/109] Update default content set --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 66d05b49dc..04ed7b0938 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-4.tar.gz"; +const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-5.tar.gz"; function getBuildInfo() { var buildInfoPath = null; From 63c72eed659564aa150780ba640f62ccf8a1db29 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 09:31:30 -0700 Subject: [PATCH 093/109] Update content set for server console --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 04ed7b0938..7b1ece922a 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-5.tar.gz"; +const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-6.tar.gz"; function getBuildInfo() { var buildInfoPath = null; From 5f765e94f0f59f3159d4addb6476492e8d5197bf Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 10:33:27 -0700 Subject: [PATCH 094/109] Fix butane lighter sound url --- tutorial/lighter/butaneLighter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial/lighter/butaneLighter.js b/tutorial/lighter/butaneLighter.js index e5400c60f3..574c2be6c6 100644 --- a/tutorial/lighter/butaneLighter.js +++ b/tutorial/lighter/butaneLighter.js @@ -14,7 +14,7 @@ }; const LIGHTER_ON_SOUND_URL = getResourceURL('tutorial_sounds/lighter_on.wav'); - const BUTANE_SOUND_URL = getResourceURL('tutorial_sound/butane.wav'); + const BUTANE_SOUND_URL = getResourceURL('tutorial_sounds/butane.wav'); // TODO: fix this in the client, changing the sound volume while a sound is playing doesn't seem to work right now const DYNAMIC_SOUND_VOLUME = false; From 92a7a638d4b59f00bb7f94dd2dbacc65a3c83499 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 11:23:15 -0700 Subject: [PATCH 095/109] Update content set to version 7 --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 7b1ece922a..ab6d900dd6 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-6.tar.gz"; +const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-7.tar.gz"; function getBuildInfo() { var buildInfoPath = null; From 9ddeda389ee1d123e5bb411d29215125e9f4f238 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 13:24:19 -0700 Subject: [PATCH 096/109] Update initial run logic --- interface/src/Application.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 45c14b2f88..f5b78ce8ca 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1283,10 +1283,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : bool shouldGoToTutorial = hasHMDAndHandControllers && hasTutorialContent && !tutorialComplete.get(); qDebug() << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName(); - qDebug() << "has tutorial content" << hasTutorialContent; - qDebug() << "tutorial complete" << tutorialComplete.get(); - qDebug() << "should go to tutorial " << shouldGoToTutorial; - + qDebug() << "Has tutorial content: " << hasTutorialContent; + qDebug() << "Tutorial complete: " << tutorialComplete.get(); + qDebug() << "Should go to tutorial: " << shouldGoToTutorial; // when --url in command line, teleport to location const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; @@ -1315,11 +1314,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : }); } else { - if (firstRun.get()) { + bool isFirstRun = firstRun.get(); + + if (isFirstRun) { showHelp(); } - if (addressLookupString.isEmpty() && firstRun.get()) { + // If this is a first run we short-circuit the address passed in + if (isFirstRun) { if (hasHMDAndHandControllers) { DependencyManager::get()->ifLocalSandboxRunningElse([=]() { qDebug() << "Home sandbox appears to be running, going to Home."; From 09d9cd8311ab697d9927fb7e7b75bec13618d00e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 16:03:43 -0700 Subject: [PATCH 097/109] Update content version to 8 --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index ab6d900dd6..017ca2e151 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-7.tar.gz"; +const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-8.tar.gz"; function getBuildInfo() { var buildInfoPath = null; From 504295cdcf4cfff98c844072b61a3c3af9f4d149 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 4 Oct 2016 11:03:42 -0700 Subject: [PATCH 098/109] Add extra logging to tutorial zones --- tutorial/tutorialStartZone.js | 28 +++++++++++++++++++++------- tutorial/tutorialZone.js | 27 +++++++++++++++------------ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/tutorial/tutorialStartZone.js b/tutorial/tutorialStartZone.js index 5adad1d00a..8dec47b7ac 100644 --- a/tutorial/tutorialStartZone.js +++ b/tutorial/tutorialStartZone.js @@ -1,28 +1,42 @@ (function() { var TutorialStartZone = function() { + print("TutorialStartZone | Creating"); }; TutorialStartZone.prototype = { preload: function(entityID) { + print("TutorialStartZone | Preload"); this.entityID = entityID; + this.sendStartIntervalID = null; }, enterEntity: function() { + var self = this; // send message to outer zone - print("Entered the tutorial start area"); + print("TutorialStartZone | Entered the tutorial start area"); if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable()) { - var parentID = Entities.getEntityProperties(this.entityID, 'parentID').parentID; - if (parentID) { - Entities.callEntityMethod(parentID, 'start'); - } else { - print("ERROR: No parent id found on tutorial start zone"); + function sendStart() { + print("TutorialStartZone | Checking parent ID"); + var parentID = Entities.getEntityProperties(self.entityID, 'parentID').parentID; + print("TutorialStartZone | Parent ID is: ", parentID); + if (parentID) { + print("TutorialStartZone | Entered the tutorial start area"); + Entities.callEntityMethod(parentID, 'start'); + } else { + print("TutorialStartZone | ERROR: No parent id found on tutorial start zone"); + } } + this.sendStartIntervalID = Script.setInterval(sendStart, 1500); } else { + print("TutorialStartZone | User tried to go to tutorial with HMD and hand controllers, sending back to /"); Window.alert("To proceed with this tutorial, please connect your VR headset and hand controllers."); location = "/"; } }, leaveEntity: function() { - print("Exited the tutorial start area"); + print("TutorialStartZone | Exited the tutorial start area"); + if (this.sendStartIntervalID) { + Script.clearInterval(this.sendStartIntervalID); + } } }; diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js index e64d0f2445..db7306a529 100644 --- a/tutorial/tutorialZone.js +++ b/tutorial/tutorialZone.js @@ -33,6 +33,7 @@ if (!Function.prototype.bind) { Script.include(tutorialPath); var TutorialZone = function() { + print("TutorialZone | Creating"); this.token = null; }; @@ -55,34 +56,36 @@ if (!Function.prototype.bind) { } }, preload: function(entityID) { + print("TutorialZone | Preload"); this.entityID = entityID; }, start: function() { - print("Got start"); + print("TutorialZone | Got start"); var self = this; if (!this.token) { + print("TutorialZone | Creating token"); this.token = new OwnershipToken(Math.random() * 100000, this.entityID, { onGainedOwnership: function(token) { - print("GOT OWNERSHIP"); + print("TutorialZone | GOT OWNERSHIP"); if (!self.tutorialManager) { self.tutorialManager = new TutorialManager(); } self.tutorialManager.startTutorial(); - print("making bound release handler"); + print("TutorialZone | making bound release handler"); self.keyReleaseHandlerBound = self.keyReleaseHandler.bind(self); - print("binding"); + print("TutorialZone | binding"); Controller.keyReleaseEvent.connect(self.keyReleaseHandlerBound); - print("done"); + print("TutorialZone | done"); }, onLostOwnership: function(token) { - print("LOST OWNERSHIP"); + print("TutorialZone | LOST OWNERSHIP"); if (self.tutorialManager) { - print("stopping tutorial.."); + print("TutorialZone | stopping tutorial.."); self.tutorialManager.stopTutorial(); - print("done"); + print("TutorialZone | done"); Controller.keyReleaseEvent.disconnect(self.keyReleaseHandlerBound); } else { - print("no tutorial manager..."); + print("TutorialZone | no tutorial manager..."); } } }); @@ -90,12 +93,12 @@ if (!Function.prototype.bind) { }, enterEntity: function() { - print("ENTERED THE TUTORIAL AREA"); + print("TutorialZone | ENTERED THE TUTORIAL AREA"); }, leaveEntity: function() { - print("EXITED THE TUTORIAL AREA"); + print("TutorialZone | EXITED THE TUTORIAL AREA"); if (this.token) { - print("destroying token"); + print("TutorialZone | Destroying token"); this.token.destroy(); this.token = null; } From 1592394d6af811b0aec006d58c65ed6ddfc68486 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 4 Oct 2016 11:19:44 -0700 Subject: [PATCH 099/109] Update tutorialStartZone to send start to parent immediately --- tutorial/tutorialStartZone.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tutorial/tutorialStartZone.js b/tutorial/tutorialStartZone.js index 8dec47b7ac..5cff1a4e99 100644 --- a/tutorial/tutorialStartZone.js +++ b/tutorial/tutorialStartZone.js @@ -19,13 +19,14 @@ var parentID = Entities.getEntityProperties(self.entityID, 'parentID').parentID; print("TutorialStartZone | Parent ID is: ", parentID); if (parentID) { - print("TutorialStartZone | Entered the tutorial start area"); + print("TutorialStartZone | Sending start"); Entities.callEntityMethod(parentID, 'start'); } else { print("TutorialStartZone | ERROR: No parent id found on tutorial start zone"); } } this.sendStartIntervalID = Script.setInterval(sendStart, 1500); + sendStart(); } else { print("TutorialStartZone | User tried to go to tutorial with HMD and hand controllers, sending back to /"); Window.alert("To proceed with this tutorial, please connect your VR headset and hand controllers."); From 79ea7c40750625fd867fb705979588ae2cdda13c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 4 Oct 2016 11:20:07 -0700 Subject: [PATCH 100/109] Update content set version to 9 --- server-console/src/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 017ca2e151..92bb549fbb 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; -const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-8.tar.gz"; +const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-9.tar.gz"; function getBuildInfo() { var buildInfoPath = null; From d589812c5257c556f8cd804e2ffd37ee571c7bc0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 4 Oct 2016 21:35:55 -0700 Subject: [PATCH 101/109] Update hand controller detection --- plugins/hifiSixense/src/SixenseManager.h | 4 +++- plugins/oculus/src/OculusControllerManager.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/hifiSixense/src/SixenseManager.h b/plugins/hifiSixense/src/SixenseManager.h index 56d3c6bc4d..c77569474e 100644 --- a/plugins/hifiSixense/src/SixenseManager.h +++ b/plugins/hifiSixense/src/SixenseManager.h @@ -31,7 +31,9 @@ public: virtual const QString& getName() const override { return NAME; } virtual const QString& getID() const override { return HYDRA_ID_STRING; } - bool isHandController() const override { return true; } + // Sixense always seems to initialize even if the hydras are not present. Is there + // a way we can properly detect whether the hydras are present? + bool isHandController() const override { return false; } virtual bool activate() override; virtual void deactivate() override; diff --git a/plugins/oculus/src/OculusControllerManager.h b/plugins/oculus/src/OculusControllerManager.h index 4c236a375d..234acd7db2 100644 --- a/plugins/oculus/src/OculusControllerManager.h +++ b/plugins/oculus/src/OculusControllerManager.h @@ -26,7 +26,7 @@ public: bool isSupported() const override; const QString& getName() const override { return NAME; } - bool isHandController() const override { return true; } + bool isHandController() const override { return _touch != nullptr; } bool activate() override; void deactivate() override; From b839926396083762cad20be4a32e336a6d61e304 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 6 Oct 2016 13:32:47 -0700 Subject: [PATCH 102/109] Remove unnecessary lambda for location initialization on startup --- interface/src/Application.cpp | 132 +++++++++++++++++----------------- 1 file changed, 64 insertions(+), 68 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f5b78ce8ca..388fe936c7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1258,87 +1258,83 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : - // Initialize location + // Get sandbox content set version, if available + auto acDirPath = PathUtils::getRootDataDirectory() + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/"; + auto contentVersionPath = acDirPath + "content-version.txt"; + qDebug() << "Checking " << contentVersionPath << " for content version"; + auto contentVersion = 0; + QFile contentVersionFile(contentVersionPath); + if (contentVersionFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + QString line = contentVersionFile.readAll(); + // toInt() returns 0 if the conversion fails, so we don't need to specifically check for failure + contentVersion = line.toInt(); + } + qDebug() << "Server content version: " << contentVersion; - auto initializeLocation = [this]() { - // Get sandbox content set version, if available - auto acDirPath = PathUtils::getRootDataDirectory() + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/"; - auto contentVersionPath = acDirPath + "content-version.txt"; - qDebug() << "Checking " << contentVersionPath << " for content version"; - auto contentVersion = 0; - QFile contentVersionFile(contentVersionPath); - if (contentVersionFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - QString line = contentVersionFile.readAll(); - // toInt() returns 0 if the conversion fails, so we don't need to specifically check for failure - contentVersion = line.toInt(); - } - qDebug() << "Server content version: " << contentVersion; + bool hasTutorialContent = contentVersion >= 1; - bool hasTutorialContent = contentVersion >= 1; + Setting::Handle firstRun { Settings::firstRun, true }; + bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable("OpenVR (Vive)") && PluginUtils::isHandControllerAvailable(); + Setting::Handle tutorialComplete { "tutorialComplete", false }; - Setting::Handle firstRun { Settings::firstRun, true }; - bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable("OpenVR (Vive)") && PluginUtils::isHandControllerAvailable(); - Setting::Handle tutorialComplete { "tutorialComplete", false }; + bool shouldGoToTutorial = hasHMDAndHandControllers && hasTutorialContent && !tutorialComplete.get(); - bool shouldGoToTutorial = hasHMDAndHandControllers && hasTutorialContent && !tutorialComplete.get(); + qDebug() << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName(); + qDebug() << "Has tutorial content: " << hasTutorialContent; + qDebug() << "Tutorial complete: " << tutorialComplete.get(); + qDebug() << "Should go to tutorial: " << shouldGoToTutorial; - qDebug() << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName(); - qDebug() << "Has tutorial content: " << hasTutorialContent; - qDebug() << "Tutorial complete: " << tutorialComplete.get(); - qDebug() << "Should go to tutorial: " << shouldGoToTutorial; + // when --url in command line, teleport to location + const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; + int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY); + QString addressLookupString; + if (urlIndex != -1) { + addressLookupString = arguments().value(urlIndex + 1); + } - // when --url in command line, teleport to location - const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; - int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY); - QString addressLookupString; - if (urlIndex != -1) { - addressLookupString = arguments().value(urlIndex + 1); - } + const QString TUTORIAL_PATH = "/tutorial_begin"; - const QString TUTORIAL_PATH = "/tutorial_begin"; - - if (shouldGoToTutorial) { - DependencyManager::get()->ifLocalSandboxRunningElse([=]() { - qDebug() << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox(TUTORIAL_PATH); - }, [=]() { - qDebug() << "Home sandbox does not appear to be running, going to Entry."; - if (firstRun.get()) { - showHelp(); - } - if (addressLookupString.isEmpty()) { - DependencyManager::get()->goToEntry(); - } else { - DependencyManager::get()->loadSettings(addressLookupString); - } - }); - } else { - - bool isFirstRun = firstRun.get(); - - if (isFirstRun) { + if (shouldGoToTutorial) { + DependencyManager::get()->ifLocalSandboxRunningElse([=]() { + qDebug() << "Home sandbox appears to be running, going to Home."; + DependencyManager::get()->goToLocalSandbox(TUTORIAL_PATH); + }, [=]() { + qDebug() << "Home sandbox does not appear to be running, going to Entry."; + if (firstRun.get()) { showHelp(); } - - // If this is a first run we short-circuit the address passed in - if (isFirstRun) { - if (hasHMDAndHandControllers) { - DependencyManager::get()->ifLocalSandboxRunningElse([=]() { - qDebug() << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox(); - }, [=]() { - qDebug() << "Home sandbox does not appear to be running, going to Entry."; - DependencyManager::get()->goToEntry(); - }); - } else { - DependencyManager::get()->goToEntry(); - } + if (addressLookupString.isEmpty()) { + DependencyManager::get()->goToEntry(); } else { - qDebug() << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); DependencyManager::get()->loadSettings(addressLookupString); } + }); + } else { + + bool isFirstRun = firstRun.get(); + + if (isFirstRun) { + showHelp(); } - }; + + // If this is a first run we short-circuit the address passed in + if (isFirstRun) { + if (hasHMDAndHandControllers) { + DependencyManager::get()->ifLocalSandboxRunningElse([=]() { + qDebug() << "Home sandbox appears to be running, going to Home."; + DependencyManager::get()->goToLocalSandbox(); + }, [=]() { + qDebug() << "Home sandbox does not appear to be running, going to Entry."; + DependencyManager::get()->goToEntry(); + }); + } else { + DependencyManager::get()->goToEntry(); + } + } else { + qDebug() << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); + DependencyManager::get()->loadSettings(addressLookupString); + } + } initializeLocation(); From a5e3fa45c3ee2fedcfdbbf43f9ec4f751102cc45 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 6 Oct 2016 13:44:51 -0700 Subject: [PATCH 103/109] Fix advanced movement being disabled --- .../toggleAdvancedMovementForHandControllers.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index b74fe52e42..46464dc2e1 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -55,7 +55,9 @@ function registerBasicMapping() { mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random(); basicMapping = Controller.newMapping(mappingName); basicMapping.from(Controller.Standard.LY).to(function(value) { - return; + if (isDisabled) { + return; + } var stick = Controller.getValue(Controller.Standard.LS); if (value === 1 && Controller.Hardware.OculusTouch !== undefined) { rotate180(); @@ -72,7 +74,9 @@ function registerBasicMapping() { }); basicMapping.from(Controller.Standard.LX).to(Controller.Standard.RX); basicMapping.from(Controller.Standard.RY).to(function(value) { - return; + if (isDisabled) { + return; + } var stick = Controller.getValue(Controller.Standard.RS); if (value === 1 && Controller.Hardware.OculusTouch !== undefined) { rotate180(); From 56a5d82f4b4497ab4f85388523f9ac1a8a3dbd04 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 6 Oct 2016 13:53:21 -0700 Subject: [PATCH 104/109] Cleanup tutorial.js and remove unused code --- tutorial/tutorial.js | 91 +++++++++++++------------------------------- 1 file changed, 26 insertions(+), 65 deletions(-) diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 884420c154..c8b3bd38c1 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -236,21 +236,7 @@ var stepDisableControllers = function(name) { stepDisableControllers.prototype = { start: function(onFinish) { controllerDisplayManager = new ControllerDisplayManager(); - editEntitiesWithTag('door', { visible: true, collisionless: false }); - Menu.setIsOptionChecked("Overlays", false); - Controller.disableMapping('handControllerPointer-click'); - Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'disable'); - Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'both'); - Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ - nearGrabEnabled: true, - holdEnabled: false, - farGrabEnabled: false, - })); - setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('tips', 'blank'); - - hideEntitiesWithTag('finish'); - setAwayEnabled(false); + disableEverything(); onFinish(); }, @@ -258,12 +244,24 @@ stepDisableControllers.prototype = { } }; -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: ENABLE CONTROLLERS // -// // -/////////////////////////////////////////////////////////////////////////////// +function disableEverything() { + editEntitiesWithTag('door', { visible: true, collisionless: false }); + Menu.setIsOptionChecked("Overlays", false); + Controller.disableMapping('handControllerPointer-click'); + Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'disable'); + Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'both'); + Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ + nearGrabEnabled: true, + holdEnabled: false, + farGrabEnabled: false, + })); + setControllerPartLayer('touchpad', 'blank'); + setControllerPartLayer('tips', 'blank'); + + hideEntitiesWithTag('finish'); + setAwayEnabled(false); +} + function reenableEverything() { editEntitiesWithTag('door', { visible: false, collisionless: true }); Menu.setIsOptionChecked("Overlays", true); @@ -285,6 +283,13 @@ function reenableEverything() { setAwayEnabled(true); } +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// // +// STEP: ENABLE CONTROLLERS // +// // +/////////////////////////////////////////////////////////////////////////////// + var stepEnableControllers = function(name) { this.tag = name; this.shouldLog = false; @@ -321,45 +326,6 @@ stepWelcome.prototype = { } }; -function StayInFrontOverlay(type, properties, distance, positionOffset) { - this.currentOrientation = MyAvatar.orientation; - this.currentPosition = MyAvatar.position; - this.distance = distance; - this.positionOffset = positionOffset; - - var forward = Vec3.multiply(this.distance, Quat.getFront(this.currentOrientation)); - - properties.rotation = this.currentOrientation; - properties.position = Vec3.sum(Vec3.sum(this.currentPosition, forward), this.positionOffset); - this.overlayID = Overlays.addOverlay(type, properties); - - - this.distance = distance; - - this.boundUpdate = this.update.bind(this); - Script.update.connect(this.boundUpdate); -} -StayInFrontOverlay.prototype = { - update: function(dt) { - var targetOrientation = MyAvatar.orientation; - var targetPosition = MyAvatar.position; - this.currentOrientation = Quat.slerp(this.currentOrientation, targetOrientation, 0.05); - this.currentPosition = Vec3.mix(this.currentPosition, targetPosition, 0.05); - - var forward = Vec3.multiply(this.distance, Quat.getFront(this.currentOrientation)); - Overlays.editOverlay(this.overlayID, { - position: Vec3.sum(Vec3.sum(this.currentPosition, forward), this.positionOffset), - rotation: this.currentOrientation, - }); - }, - destroy: function() { - Overlays.deleteOverlay(this.overlayID); - try { - Script.update.disconnect(this.boundUpdate); - } catch(e) { - } - } -}; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -858,8 +824,6 @@ var stepTeleport = function(name) { } stepTeleport.prototype = { start: function(onFinish) { - //setControllerVisible("teleport", true); - setControllerPartLayer('touchpad', 'teleport'); setControllerPartLayer('tips', 'teleport'); @@ -926,9 +890,6 @@ stepFinish.prototype = { onFinish(); }, cleanup: function() { - //Menu.setIsOptionChecked("Overlays", true); - //hideEntitiesWithTag(this.tag); - //deleteEntitiesWithTag(this.tempTag); } }; From c995e511281d4d01fc3f48ca69eb08546308fff9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 6 Oct 2016 14:12:13 -0700 Subject: [PATCH 105/109] Fix compilation bugs in Application --- interface/src/Application.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 388fe936c7..03bb2249d4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1336,10 +1336,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } } - initializeLocation(); - // After all of the constructor is completed, then set firstRun to false. - Setting::Handle firstRun{ Settings::firstRun, true }; firstRun.set(false); } From 8a4cfd473597453658817dca0463813b9b96a31d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 7 Oct 2016 13:37:52 -0700 Subject: [PATCH 106/109] Remove unreachable code in PluginUtils --- libraries/plugins/src/plugins/PluginUtils.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/plugins/src/plugins/PluginUtils.cpp b/libraries/plugins/src/plugins/PluginUtils.cpp index f689f6f8bf..bc53e8166a 100644 --- a/libraries/plugins/src/plugins/PluginUtils.cpp +++ b/libraries/plugins/src/plugins/PluginUtils.cpp @@ -19,7 +19,6 @@ bool PluginUtils::isHMDAvailable(const QString& pluginName) { // Temporarily only enable this for Vive if (displayPlugin->isHmd() && (pluginName.isEmpty() || displayPlugin->getName() == pluginName)) { return true; - break; } } return false; @@ -29,7 +28,6 @@ bool PluginUtils::isHandControllerAvailable() { for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) { if (inputPlugin->isHandController()) { return true; - break; } } return false; From de14fdffb7a570f5007d9b200f20ed677564c809 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 7 Oct 2016 13:38:18 -0700 Subject: [PATCH 107/109] Remove commented log line --- plugins/openvr/src/OpenVrHelpers.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 4429eb274f..f5e36492bd 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -296,8 +296,6 @@ controller::Pose openVrControllerPoseToHandPose(bool isLeftHand, const mat4& mat auto translationOffset = (isLeftHand ? leftTranslationOffset : rightTranslationOffset); auto rotationOffset = (isLeftHand ? leftRotationOffset : rightRotationOffset); - //qDebug() << "TRANSLATION OFFSET: " << isLeftHand << ", " << translationOffset.x << ", " << translationOffset.y << ", " << translationOffset.z; - glm::vec3 position = extractTranslation(mat); glm::quat rotation = glm::normalize(glm::quat_cast(mat)); From 0f0ea5f73f867cb194e4c9754a73b83f62854460 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 7 Oct 2016 13:40:35 -0700 Subject: [PATCH 108/109] Remove dead code --- tutorial/viveHandsv2.js | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/tutorial/viveHandsv2.js b/tutorial/viveHandsv2.js index e365a689c1..890a5e1588 100644 --- a/tutorial/viveHandsv2.js +++ b/tutorial/viveHandsv2.js @@ -192,42 +192,3 @@ ControllerDisplayManager = function() { updateControllers(); } - -/* -var controllerDisplayManager = new ControllerDisplayManager(); - -Messages.subscribe('Hifi-Object-Manipulation'); -function onMessageReceived(channel, message, sender) { - print(channel, message, sender); - if (channel == 'Hifi-Object-Manipulation') {// && sender == MyAvatar.sessionUUID) { - print("got manip"); - var data = JSON.parse(message); - print("post data", data); - var visible = data.action != 'equip'; - print("Calling..."); - if (data.joint == "LeftHand") { - controllerDisplayManager.setLeftVisible(visible); - } else if (data.joint == "RightHand") { - controllerDisplayManager.setRightVisible(visible); - } - } -} -Messages.messageReceived.connect(onMessageReceived); - -var visible = false; -print("SETTING IT UP"); -print("done"); -function toggleVis() { - print("toggling"); - visible = !visible; - controllerDisplayManager.setVisible(visible); -} -//toggleVis(); -//Script.setInterval(toggleVis, 1000); - -Script.scriptEnding.connect(function() { - print("destorying..."); - controllerDisplayManager.destroy(); - controllerDisplayManager = null; -}); -*/ From abaa75245ee12640fc3ef744ad0f53c0f3b2b34a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 7 Oct 2016 13:41:23 -0700 Subject: [PATCH 109/109] Temporarily remov touch controller configuration --- tutorial/touchControllerConfiguration.js | 152 ----------------------- 1 file changed, 152 deletions(-) delete mode 100644 tutorial/touchControllerConfiguration.js diff --git a/tutorial/touchControllerConfiguration.js b/tutorial/touchControllerConfiguration.js deleted file mode 100644 index a63d6749ad..0000000000 --- a/tutorial/touchControllerConfiguration.js +++ /dev/null @@ -1,152 +0,0 @@ -// -// touchControllerConfiguration.js -// -// Created by Ryan Huffman on 9/1/16. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var CONTROLLER_LENGTH_OFFSET = 0.0762; -var leftBasePosition = { - x: CONTROLLER_LENGTH_OFFSET / 2, - y: CONTROLLER_LENGTH_OFFSET * 2, - z: CONTROLLER_LENGTH_OFFSET / 2 -}; -var rightBasePosition = { - x: -CONTROLLER_LENGTH_OFFSET / 2, - y: CONTROLLER_LENGTH_OFFSET * 2, - z: CONTROLLER_LENGTH_OFFSET / 2 -}; - - -var touchLeftBaseRotation = Quat.multiply( - Quat.fromPitchYawRollDegrees(0, 0, 0), - Quat.multiply( - Quat.fromPitchYawRollDegrees(0, 0, -45), - Quat.multiply( - Quat.fromPitchYawRollDegrees(180, 0, 0), - Quat.fromPitchYawRollDegrees(0, -90, 0) - ) - ) -); - -var touchRightBaseRotation = Quat.multiply( - Quat.fromPitchYawRollDegrees(0, 0, 45), - Quat.multiply( - Quat.fromPitchYawRollDegrees(180, 0, 0), - Quat.fromPitchYawRollDegrees(0, 90, 0) - ) -); - -var TOUCH_CONTROLLER_CONFIGURATION = { - name: "Touch", - controllers: [ - { - modelURL: "C:/Users/Ryan/Assets/controller/oculus_touch_l.fbx", - naturalPosition: { - x: 0.016486000269651413, - y: -0.035518500953912735, - z: -0.018527504056692123 - }, - jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"), - rotation: touchLeftBaseRotation, - position: leftBasePosition, - - annotationTextRotation: Quat.fromPitchYawRollDegrees(20, -90, 0), - annotations: { - - buttonX: { - position: { - x: -0.00931, - y: 0.00212, - z: -0.01259, - }, - direction: "left", - color: { red: 100, green: 100, blue: 100 }, - }, - buttonY: { - position: { - x: -0.01617, - y: 0.00216, - z: 0.00177, - }, - direction: "left", - color: { red: 100, green: 255, blue: 100 }, - }, - bumper: { - position: { - x: 0.00678, - y: -0.02740, - z: -0.02537, - }, - direction: "left", - color: { red: 100, green: 100, blue: 255 }, - }, - trigger: { - position: { - x: -0.01275, - y: -0.01992, - z: 0.02314, - }, - direction: "left", - color: { red: 255, green: 100, blue: 100 }, - } - }, - }, - { - modelURL: "C:/Users/Ryan/Assets/controller/oculus_touch_r.fbx", - naturalPosition: { - x: -0.016486000269651413, - y: -0.035518500953912735, - z: -0.018527504056692123 - }, - jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"), - rotation: touchRightBaseRotation, - position: rightBasePosition, - - annotationTextRotation: Quat.fromPitchYawRollDegrees(20, 90, 0), - annotations: { - - buttonA: { - position: { - x: 0.00931, - y: 0.00212, - z: -0.01259, - }, - direction: "right", - color: { red: 100, green: 100, blue: 100 }, - }, - buttonB: { - position: { - x: 0.01617, - y: 0.00216, - z: 0.00177, - }, - direction: "right", - color: { red: 100, green: 255, blue: 100 }, - }, - bumper: { - position: { - x: 0.00678, - y: -0.02740, - z: -0.02537, - }, - direction: "right", - color: { red: 100, green: 100, blue: 255 }, - }, - trigger: { - position: { - x: 0.01275, - y: -0.01992, - z: 0.02314, - }, - direction: "right", - color: { red: 255, green: 100, blue: 100 }, - } - }, - } - ] -} -