From 0f7f9990c2d70b59414c60d5eff28e7cc4d8def2 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Wed, 27 Jul 2016 17:58:24 -0700 Subject: [PATCH 1/7] Tuned haptic pulses for grab, release, equip, de-quip --- .../system/controllers/handControllerGrab.js | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index bfdb26a2bb..5815e8129f 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -38,6 +38,15 @@ var BUMPER_ON_VALUE = 0.5; var THUMB_ON_VALUE = 0.5; +var HAPTIC_PULSE_STRENGTH = 1.0; +var HAPTIC_PULSE_DURATION = 13.0; +var HAPTIC_TEXTURE_STRENGTH = 0.1; +var HAPTIC_TEXTURE_DURATION = 3.0; +var HAPTIC_TEXTURE_DISTANCE = 0.002; +var HAPTIC_DEQUIP_STRENGTH = 0.75; +var HAPTIC_DEQUIP_DURATION = 50.0; + + var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only use head for search/move. var PICK_WITH_HAND_RAY = true; @@ -934,7 +943,7 @@ function MyController(hand) { entityPropertiesCache.addEntities(candidateEntities); var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); if (!this.waitForTriggerRelease) { - this.updateEquipHaptics(potentialEquipHotspot); + this.updateEquipHaptics(potentialEquipHotspot, this.getHandPosition()); } var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); @@ -948,11 +957,15 @@ function MyController(hand) { this.prevPotentialEquipHotspot = null; }; - this.updateEquipHaptics = function(potentialEquipHotspot) { + this.updateEquipHaptics = function(potentialEquipHotspot, currentLocation) { if (potentialEquipHotspot && !this.prevPotentialEquipHotspot || !potentialEquipHotspot && this.prevPotentialEquipHotspot) { - Controller.triggerShortHapticPulse(0.5, this.hand); - } + Controller.triggerHapticPulse(HAPTIC_TEXTURE_STRENGTH, HAPTIC_TEXTURE_DURATION, this.hand); + this.lastHapticPulseLocation = currentLocation; + } else if (potentialEquipHotspot && Vec3.distance(this.lastHapticPulseLocation, currentLocation) > HAPTIC_TEXTURE_DISTANCE) { + Controller.triggerHapticPulse(HAPTIC_TEXTURE_STRENGTH, HAPTIC_TEXTURE_DURATION, this.hand); + this.lastHapticPulseLocation = currentLocation; + } this.prevPotentialEquipHotspot = potentialEquipHotspot; }; @@ -1337,7 +1350,7 @@ function MyController(hand) { } } - this.updateEquipHaptics(potentialEquipHotspot); + this.updateEquipHaptics(potentialEquipHotspot, handPosition); var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); @@ -1422,6 +1435,8 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("startDistanceGrab"); } + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + this.turnOffVisualizations(); this.previousRoomControllerPosition = roomControllerPosition; @@ -1611,7 +1626,7 @@ function MyController(hand) { if (handIsUpsideDown != this.prevHandIsUpsideDown) { this.prevHandIsUpsideDown = handIsUpsideDown; - Controller.triggerShortHapticPulse(0.5, this.hand); + Controller.triggerHapticPulse(HAPTIC_DEQUIP_STRENGTH, HAPTIC_DEQUIP_DURATION, this.hand); } return handIsUpsideDown; @@ -1625,7 +1640,7 @@ function MyController(hand) { this.dropGestureReset(); this.clearEquipHaptics(); - Controller.triggerShortHapticPulse(1.0, this.hand); + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); if (this.entityActivated) { var saveGrabbedID = this.grabbedEntity; @@ -1987,6 +2002,8 @@ function MyController(hand) { joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + this.grabbedEntity = null; this.grabbedHotspot = null; From f37570dc9ac574db3cb28565abbbc482915e094c Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 28 Jul 2016 08:23:45 -0700 Subject: [PATCH 2/7] only pulse on release when held --- scripts/system/controllers/handControllerGrab.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 5815e8129f..bd618c5a44 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1965,6 +1965,9 @@ function MyController(hand) { var noVelocity = false; if (this.grabbedEntity !== null) { + // Make a small release haptic pulse if we really were holding something + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + // If this looks like the release after adjusting something still held in the other hand, print the position // and rotation of the held thing to help content creators set the userData. var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {}); @@ -2002,8 +2005,6 @@ function MyController(hand) { joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); - Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); - this.grabbedEntity = null; this.grabbedHotspot = null; From 516780934230e1d9ec4914b1e4ad2f607873cf15 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 28 Jul 2016 08:51:11 -0700 Subject: [PATCH 3/7] release near and far grab at trigger click detent --- scripts/system/controllers/handControllerGrab.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index bd618c5a44..c023278a3b 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1443,7 +1443,8 @@ function MyController(hand) { }; this.distanceHolding = function(deltaTime, timestamp) { - if (this.triggerSmoothedReleased()) { + + if (!this.triggerClicked) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); return; @@ -1761,7 +1762,7 @@ function MyController(hand) { this.nearGrabbing = function(deltaTime, timestamp) { - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + if (this.state == STATE_NEAR_GRABBING && !this.triggerClicked) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); return; From 755989b8ffe1d3afe8683d552f5f89d47ca973d5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 28 Jul 2016 10:01:05 -0700 Subject: [PATCH 4/7] don't allow punished scripts to slow stop or shutdown --- libraries/script-engine/src/ScriptEngine.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 21eae99307..cb82974f4f 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -845,14 +845,24 @@ void ScriptEngine::run() { // it means our script is taking too long in it's updates, and we want to punish the // script a little bit. So we will force the sleepUntil to be at least our averageUpdate // time into the future. - auto wouldSleep = (sleepUntil - clock::now()); + auto wouldSleep = (sleepUntil - beforeSleep); auto avgerageUpdate = totalUpdates / thisFrame; if (wouldSleep < avgerageUpdate) { sleepUntil = beforeSleep + avgerageUpdate; } - std::this_thread::sleep_until(sleepUntil); + // We don't want to actually sleep for too long, because it causes our scripts to hang + // on shutdown and stop... so we want to loop and sleep until we've spent our time in + // purgatory, constantly checking to see if our script was asked to end + while (!_isFinished && clock::now() < sleepUntil) { + auto wouldSleepSlice = (sleepUntil - clock::now()); + auto thisSleepUntil = sleepUntil; + if (wouldSleepSlice > FRAME_DURATION) { + thisSleepUntil = clock::now() + FRAME_DURATION; + } + std::this_thread::sleep_until(thisSleepUntil); + } #ifdef SCRIPT_DELAY_DEBUG { From 22619a66bad13b3aca9c3c03332036283f26194c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 28 Jul 2016 10:51:17 -0700 Subject: [PATCH 5/7] process events also --- libraries/script-engine/src/ScriptEngine.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index cb82974f4f..2dea38cfae 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -855,13 +855,18 @@ void ScriptEngine::run() { // We don't want to actually sleep for too long, because it causes our scripts to hang // on shutdown and stop... so we want to loop and sleep until we've spent our time in // purgatory, constantly checking to see if our script was asked to end + bool firstTime = true; while (!_isFinished && clock::now() < sleepUntil) { + if (!firstTime) { + QCoreApplication::processEvents(); // before we sleep again, give events a chance to process + } auto wouldSleepSlice = (sleepUntil - clock::now()); auto thisSleepUntil = sleepUntil; if (wouldSleepSlice > FRAME_DURATION) { thisSleepUntil = clock::now() + FRAME_DURATION; } std::this_thread::sleep_until(thisSleepUntil); + firstTime = false; } #ifdef SCRIPT_DELAY_DEBUG From 74a3835a1e3d4639ab7fadaf5dd1c19e7c569932 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 28 Jul 2016 15:15:33 -0700 Subject: [PATCH 6/7] CR feedback --- libraries/script-engine/src/ScriptEngine.cpp | 24 +++++--------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 2dea38cfae..f98b07478b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -839,34 +839,22 @@ void ScriptEngine::run() { // calculate a sleepUntil to be the time from our start time until the original target // sleepUntil for this frame. const std::chrono::microseconds FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1); - clock::time_point sleepUntil(startTime + thisFrame++ * FRAME_DURATION); + clock::time_point targetSleepUntil(startTime + thisFrame++ * FRAME_DURATION); // However, if our sleepUntil is not at least our average update time into the future // it means our script is taking too long in it's updates, and we want to punish the // script a little bit. So we will force the sleepUntil to be at least our averageUpdate // time into the future. - auto wouldSleep = (sleepUntil - beforeSleep); - auto avgerageUpdate = totalUpdates / thisFrame; - - if (wouldSleep < avgerageUpdate) { - sleepUntil = beforeSleep + avgerageUpdate; - } + auto averageUpdate = totalUpdates / thisFrame; + auto sleepUntil = std::max(targetSleepUntil, beforeSleep + averageUpdate); // We don't want to actually sleep for too long, because it causes our scripts to hang // on shutdown and stop... so we want to loop and sleep until we've spent our time in // purgatory, constantly checking to see if our script was asked to end - bool firstTime = true; while (!_isFinished && clock::now() < sleepUntil) { - if (!firstTime) { - QCoreApplication::processEvents(); // before we sleep again, give events a chance to process - } - auto wouldSleepSlice = (sleepUntil - clock::now()); - auto thisSleepUntil = sleepUntil; - if (wouldSleepSlice > FRAME_DURATION) { - thisSleepUntil = clock::now() + FRAME_DURATION; - } + QCoreApplication::processEvents(); // before we sleep again, give events a chance to process + auto thisSleepUntil = std::min(sleepUntil, clock::now() + FRAME_DURATION); std::this_thread::sleep_until(thisSleepUntil); - firstTime = false; } #ifdef SCRIPT_DELAY_DEBUG @@ -880,7 +868,7 @@ void ScriptEngine::run() { qCDebug(scriptengine) << "Frame:" << thisFrame << "Slept (us):" << std::chrono::duration_cast(actuallySleptUntil - beforeSleep).count() << - "Avg Updates (us):" << avgerageUpdate.count() << + "Avg Updates (us):" << averageUpdate.count() << "FPS:" << fps; } } From e879f22c952772658276153fb3bd8690affe31fe Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 28 Jul 2016 15:15:52 -0700 Subject: [PATCH 7/7] fix behavior with no entity server --- 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 e294ae38ad..5d50a1c9fe 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4685,7 +4685,8 @@ void Application::packetSent(quint64 length) { void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) { scriptEngine->setEmitScriptUpdatesFunction([this]() { - return isPhysicsEnabled(); + SharedNodePointer entityServerNode = DependencyManager::get()->soloNodeOfType(NodeType::EntityServer); + return !entityServerNode || isPhysicsEnabled(); }); // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so