From c54d0bc9938dcd4106953c05e38620d6eb62b7aa Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Tue, 12 Apr 2016 12:28:12 -0700
Subject: [PATCH 01/17] Don't expose raw pointer to the display plugin

---
 interface/src/Application.cpp                 | 23 ++++++++-----------
 interface/src/Application.h                   |  5 ++--
 interface/src/PluginContainerProxy.cpp        |  2 +-
 interface/src/PluginContainerProxy.h          |  2 +-
 .../plugins/src/plugins/PluginContainer.h     |  2 +-
 plugins/openvr/src/ViveControllerManager.cpp  |  6 ++++-
 tests/controllers/src/main.cpp                |  2 +-
 7 files changed, 20 insertions(+), 22 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index c2a4088dcc..7e8c96f5af 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -4957,24 +4957,19 @@ qreal Application::getDevicePixelRatio() {
     return (_window && _window->windowHandle()) ? _window->windowHandle()->devicePixelRatio() : 1.0;
 }
 
-DisplayPlugin* Application::getActiveDisplayPlugin() {
-    DisplayPlugin* result = nullptr;
-    if (QThread::currentThread() == thread()) {
-        if (nullptr == _displayPlugin) {
-            updateDisplayMode();
-            Q_ASSERT(_displayPlugin);
-        }
-        result = _displayPlugin.get();
-    } else {
+DisplayPluginPointer Application::getActiveDisplayPlugin() const {
+    if (QThread::currentThread() != thread()) {
         std::unique_lock<std::mutex> lock(_displayPluginLock);
-        result = _displayPlugin.get();
+        return _displayPlugin;
     }
-    return result;
+
+    if (!_displayPlugin) {
+        const_cast<Application*>(this)->updateDisplayMode();
+        Q_ASSERT(_displayPlugin);
+    }
+    return _displayPlugin;
 }
 
-const DisplayPlugin* Application::getActiveDisplayPlugin() const {
-    return const_cast<Application*>(this)->getActiveDisplayPlugin();
-}
 
 static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
     auto menu = Menu::getInstance();
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 2911d42b65..fdd5e42006 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -177,8 +177,7 @@ public:
 
     void setActiveDisplayPlugin(const QString& pluginName);
 
-    DisplayPlugin* getActiveDisplayPlugin();
-    const DisplayPlugin* getActiveDisplayPlugin() const;
+    DisplayPluginPointer getActiveDisplayPlugin() const;
 
     FileLogger* getLogger() const { return _logger; }
 
@@ -388,7 +387,7 @@ private:
 
     OffscreenGLCanvas* _offscreenContext { nullptr };
     DisplayPluginPointer _displayPlugin;
-    std::mutex _displayPluginLock;
+    mutable std::mutex _displayPluginLock;
     InputPluginList _activeInputPlugins;
 
     bool _activatingDisplayPlugin { false };
diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp
index c3d186f190..b651a1520d 100644
--- a/interface/src/PluginContainerProxy.cpp
+++ b/interface/src/PluginContainerProxy.cpp
@@ -54,7 +54,7 @@ QOpenGLContext* PluginContainerProxy::getPrimaryContext() {
     return qApp->_glWidget->context()->contextHandle();
 }
 
-const DisplayPlugin* PluginContainerProxy::getActiveDisplayPlugin() const {
+const DisplayPluginPointer PluginContainerProxy::getActiveDisplayPlugin() const {
     return qApp->getActiveDisplayPlugin();
 }
 
diff --git a/interface/src/PluginContainerProxy.h b/interface/src/PluginContainerProxy.h
index 3ec2489c54..a04a1b2977 100644
--- a/interface/src/PluginContainerProxy.h
+++ b/interface/src/PluginContainerProxy.h
@@ -24,7 +24,7 @@ class PluginContainerProxy : public QObject, PluginContainer {
     virtual ui::Menu* getPrimaryMenu() override;
     virtual QOpenGLContext* getPrimaryContext() override;
     virtual bool isForeground() override;
-    virtual const DisplayPlugin* getActiveDisplayPlugin() const override;
+    virtual const DisplayPluginPointer getActiveDisplayPlugin() const override;
 
     friend class Application;
 
diff --git a/libraries/plugins/src/plugins/PluginContainer.h b/libraries/plugins/src/plugins/PluginContainer.h
index feaabf83e1..e1d1a212e2 100644
--- a/libraries/plugins/src/plugins/PluginContainer.h
+++ b/libraries/plugins/src/plugins/PluginContainer.h
@@ -64,7 +64,7 @@ public:
     virtual MainWindow* getPrimaryWindow() = 0;
     virtual QOpenGLContext* getPrimaryContext() = 0;
     virtual bool isForeground() = 0;
-    virtual const DisplayPlugin* getActiveDisplayPlugin() const = 0;
+    virtual const DisplayPluginPointer getActiveDisplayPlugin() const = 0;
 
     /// settings interface
     bool getBoolSetting(const QString& settingName, bool defaultValue);
diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp
index c1ed5aa880..4c469ccefa 100644
--- a/plugins/openvr/src/ViveControllerManager.cpp
+++ b/plugins/openvr/src/ViveControllerManager.cpp
@@ -11,6 +11,8 @@
 
 #include "ViveControllerManager.h"
 
+#include <QtCore/QProcessEnvironment>
+
 #include <PerfStat.h>
 #include <PathUtils.h>
 #include <GeometryCache.h>
@@ -48,9 +50,11 @@ static const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME;
 static const QString RENDER_CONTROLLERS = "Render Hand Controllers";
 
 const QString ViveControllerManager::NAME = "OpenVR";
+static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR");
+static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
 
 bool ViveControllerManager::isSupported() const {
-    return !isOculusPresent() && vr::VR_IsHmdPresent();
+    return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent();
 }
 
 bool ViveControllerManager::activate() {
diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp
index 81acf2594d..e978dd9a38 100644
--- a/tests/controllers/src/main.cpp
+++ b/tests/controllers/src/main.cpp
@@ -91,7 +91,7 @@ public:
     virtual QOpenGLContext* getPrimaryContext() override { return nullptr; }
     virtual ui::Menu* getPrimaryMenu() { return nullptr; }
     virtual bool isForeground() override { return true; }
-    virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr;  }
+    virtual const DisplayPluginPointer getActiveDisplayPlugin() const override { return DisplayPluginPointer();  }
 };
 
 class MyControllerScriptingInterface : public controller::ScriptingInterface {

From 641e1526998fe5fcf57f35816c7a5675133d330f Mon Sep 17 00:00:00 2001
From: "Anthony J. Thibault" <tony@highfidelity.io>
Date: Fri, 6 May 2016 18:55:09 -0700
Subject: [PATCH 02/17] Eye tracking bug fix and debug rendering improvement

* Bug fix for eye tracking in HMD, the "up" orientation of your eyes now match your head.
* DebugDraw: added drawRay method.
* Application: Renamed preRender to postUpdate
* AvatarManager: added postUpdate method that iterates over all avatars.
* MyAvatar: Renamed preRender to preDisplaySide
* MyAvatar: split preRender code into postUpdate and preDisplaySide.
* Removed "Show who is looking at me", "Render focus indicator" and "Render lookat target" debug draw.
* Split "Show Look At Vectors" into "Show My Look At Vectors" and "Show Other Look At Vectors", to make it easier to debug eye tracking.
* "Show Look at Vectors" now draws the right eye red and the left eye blue.
* Removed Avatar and MyAvatar renderBody
* Removed look at rendering from head.
* GLMHelpers: Bugfix for generateBasisVectors when up primary and secondary axis were orthogonal
---
 interface/src/Application.cpp                 |  27 +++--
 interface/src/Application.h                   |   6 +-
 interface/src/Menu.cpp                        |   6 +-
 interface/src/Menu.h                          |   6 +-
 interface/src/avatar/Avatar.cpp               | 105 ++++++------------
 interface/src/avatar/Avatar.h                 |   7 +-
 interface/src/avatar/AvatarManager.cpp        |   9 ++
 interface/src/avatar/AvatarManager.h          |   2 +
 interface/src/avatar/Head.cpp                 |  46 --------
 interface/src/avatar/Head.h                   |  32 ++----
 interface/src/avatar/MyAvatar.cpp             |  45 ++------
 interface/src/avatar/MyAvatar.h               |   4 +-
 interface/src/avatar/SkeletonModel.cpp        |   2 +-
 libraries/animation/src/Rig.cpp               |  18 ++-
 .../src/AbstractViewStateInterface.h          |   4 +-
 libraries/render-utils/src/AnimDebugDraw.cpp  |  18 +++
 libraries/render-utils/src/Model.cpp          |   2 +-
 libraries/shared/src/DebugDraw.cpp            |   5 +
 libraries/shared/src/DebugDraw.h              |  12 +-
 libraries/shared/src/GLMHelpers.cpp           |  20 +++-
 20 files changed, 159 insertions(+), 217 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index c2a4088dcc..ea088135bf 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -45,11 +45,13 @@
 #include <ResourceScriptingInterface.h>
 #include <AccountManager.h>
 #include <AddressManager.h>
+#include <AnimDebugDraw.h>
 #include <BuildInfo.h>
 #include <AssetClient.h>
 #include <AutoUpdater.h>
 #include <AudioInjectorManager.h>
 #include <CursorManager.h>
+#include <DebugDraw.h>
 #include <DeferredLightingEffect.h>
 #include <display-plugins/DisplayPlugin.h>
 #include <EntityScriptingInterface.h>
@@ -101,7 +103,7 @@
 #include <Preferences.h>
 #include <display-plugins/CompositorHelper.h>
 
-#include "AnimDebugDraw.h"
+
 #include "AudioClient.h"
 #include "audio/AudioScope.h"
 #include "avatar/AvatarManager.h"
@@ -3033,9 +3035,9 @@ void Application::updateLOD() const {
     }
 }
 
-void Application::pushPreRenderLambda(void* key, std::function<void()> func) {
-    std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
-    _preRenderLambdas[key] = func;
+void Application::pushPostUpdateLambda(void* key, std::function<void()> func) {
+    std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
+    _postUpdateLambdas[key] = func;
 }
 
 // Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone.
@@ -3553,15 +3555,19 @@ void Application::update(float deltaTime) {
         }
     }
 
+    avatarManager->postUpdate(deltaTime);
+
     {
         PROFILE_RANGE_EX("PreRenderLambdas", 0xffff0000, (uint64_t)0);
 
-        std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
-        for (auto& iter : _preRenderLambdas) {
+        std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
+        for (auto& iter : _postUpdateLambdas) {
             iter.second();
         }
-        _preRenderLambdas.clear();
+        _postUpdateLambdas.clear();
     }
+
+    AnimDebugDraw::getInstance().update();
 }
 
 
@@ -4006,13 +4012,10 @@ namespace render {
 
 void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool selfAvatarOnly) {
 
-    // FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering.
+    // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering.
     // Then we can move this logic into the Avatar::simulate call.
     auto myAvatar = getMyAvatar();
-    myAvatar->preRender(renderArgs);
-
-    // Update animation debug draw renderer
-    AnimDebugDraw::getInstance().update();
+    myAvatar->preDisplaySide(renderArgs);
 
     activeRenderingThread = QThread::currentThread();
     PROFILE_RANGE(__FUNCTION__);
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 2911d42b65..303cb3fb1d 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -212,7 +212,7 @@ public:
     render::EnginePointer getRenderEngine() override { return _renderEngine; }
     gpu::ContextPointer getGPUContext() const { return _gpuContext; }
 
-    virtual void pushPreRenderLambda(void* key, std::function<void()> func) override;
+    virtual void pushPostUpdateLambda(void* key, std::function<void()> func) override;
 
     const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
 
@@ -513,8 +513,8 @@ private:
 
     QThread* _deadlockWatchdogThread;
 
-    std::map<void*, std::function<void()>> _preRenderLambdas;
-    std::mutex _preRenderLambdasLock;
+    std::map<void*, std::function<void()>> _postUpdateLambdas;
+    std::mutex _postUpdateLambdasLock;
 
     std::atomic<uint32_t> _fullSceneReceivedCounter { 0 }; // how many times have we received a full-scene octree stats packet
     uint32_t _fullSceneCounterAtLastPhysicsCheck { 0 }; // _fullSceneReceivedCounter last time we checked physics ready
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index c5fa52e49d..4436e86931 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -480,10 +480,8 @@ Menu::Menu() {
         avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
 
     addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
-    addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
-    addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtTargets, 0, false);
-    addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
-    addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
+    addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderMyLookAtVectors, 0, false);
+    addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderOtherLookAtVectors, 0, false);
     addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
     addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false,
         avatar, SLOT(setEnableDebugDrawDefaultPose(bool)));
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index 5796575780..39c3a6fe1f 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -147,9 +147,8 @@ namespace MenuOption {
     const QString ReloadAllScripts = "Reload All Scripts";
     const QString ReloadContent = "Reload Content (Clears all caches)";
     const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes";
-    const QString RenderFocusIndicator = "Show Eye Focus";
-    const QString RenderLookAtTargets = "Show Look-at Targets";
-    const QString RenderLookAtVectors = "Show Look-at Vectors";
+    const QString RenderMyLookAtVectors = "Show My Eye Vectors";
+    const QString RenderOtherLookAtVectors = "Show Other Eye Vectors";
     const QString RenderMaxTextureMemory = "Maximum Texture Memory";
     const QString RenderMaxTextureAutomatic = "Automatic Texture Memory";
     const QString RenderMaxTexture64MB = "64 MB";
@@ -174,7 +173,6 @@ namespace MenuOption {
     const QString ShowDSConnectTable = "Show Domain Connection Timing";
     const QString ShowBordersEntityNodes = "Show Entity Nodes";
     const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
-    const QString ShowWhosLookingAtMe = "Show Who's Looking at Me";
     const QString StandingHMDSensorMode = "Standing HMD Sensor Mode";
     const QString SimulateEyeTracking = "Simulate";
     const QString SMIEyeTracking = "SMI Eye Tracking";
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 2cacb81ce4..14385ddfcb 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -30,6 +30,7 @@
 #include <SharedUtil.h>
 #include <TextRenderer3D.h>
 #include <TextureCache.h>
+#include <DebugDraw.h>
 
 #include "Application.h"
 #include "Avatar.h"
@@ -65,11 +66,6 @@ namespace render {
     }
     template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args) {
         auto avatarPtr = static_pointer_cast<Avatar>(avatar);
-        bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtVectors);
-        avatarPtr->setDisplayingLookatVectors(renderLookAtVectors);
-        bool renderLookAtTarget = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtTargets);
-        avatarPtr->setDisplayingLookatTarget(renderLookAtTarget);
-
         if (avatarPtr->isInitialized() && args) {
             PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload");
             avatarPtr->render(args, qApp->getCamera()->getPosition());
@@ -318,6 +314,39 @@ void Avatar::updateRenderItem(render::PendingChanges& pendingChanges) {
     }
 }
 
+void Avatar::postUpdate(float deltaTime) {
+
+    bool renderLookAtVectors;
+    if (isMyAvatar()) {
+        renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderMyLookAtVectors);
+    } else {
+        renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderOtherLookAtVectors);
+    }
+
+    if (renderLookAtVectors) {
+        const float EYE_RAY_LENGTH = 10.0;
+        const glm::vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
+        const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
+
+        int leftEyeJoint = getJointIndex("LeftEye");
+        glm::vec3 leftEyePosition;
+        glm::quat leftEyeRotation;
+
+        if (_skeletonModel->getJointPositionInWorldFrame(leftEyeJoint, leftEyePosition) &&
+            _skeletonModel->getJointRotationInWorldFrame(leftEyeJoint, leftEyeRotation)) {
+            DebugDraw::getInstance().drawRay(leftEyePosition, leftEyePosition + leftEyeRotation * Vectors::UNIT_Z * EYE_RAY_LENGTH, BLUE);
+        }
+
+        int rightEyeJoint = getJointIndex("RightEye");
+        glm::vec3 rightEyePosition;
+        glm::quat rightEyeRotation;
+        if (_skeletonModel->getJointPositionInWorldFrame(rightEyeJoint, rightEyePosition) &&
+            _skeletonModel->getJointRotationInWorldFrame(rightEyeJoint, rightEyeRotation)) {
+            DebugDraw::getInstance().drawRay(rightEyePosition, rightEyePosition + rightEyeRotation * Vectors::UNIT_Z * EYE_RAY_LENGTH, RED);
+        }
+    }
+}
+
 void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
     auto& batch = *renderArgs->_batch;
     PROFILE_RANGE_BATCH(batch, __FUNCTION__);
@@ -412,8 +441,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
                       ? 1.0f
                       : GLOW_FROM_AVERAGE_LOUDNESS;
 
-        // render body
-        renderBody(renderArgs, frustum, glowLevel);
+        fixupModelsInScene();
 
         if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) {
             // add local lights
@@ -437,64 +465,6 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
             const float BOUNDING_SHAPE_ALPHA = 0.7f;
             _skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA);
         }
-
-        // If this is the avatar being looked at, render a little ball above their head
-        if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) {
-            static const float INDICATOR_OFFSET = 0.22f;
-            static const float INDICATOR_RADIUS = 0.03f;
-            static const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
-            glm::vec3 avatarPosition = getPosition();
-            glm::vec3 position = glm::vec3(avatarPosition.x, getDisplayNamePosition().y + INDICATOR_OFFSET, avatarPosition.z);
-            PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderFocusIndicator");
-            Transform transform;
-            transform.setTranslation(position);
-            transform.postScale(INDICATOR_RADIUS);
-            batch.setModelTransform(transform);
-            DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch, LOOK_AT_INDICATOR_COLOR);
-        }
-
-        // If the avatar is looking at me, indicate that they are
-        if (getHead()->isLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) {
-            PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderLookingAtMe");
-            const glm::vec3 LOOKING_AT_ME_COLOR = { 1.0f, 1.0f, 1.0f };
-            const float LOOKING_AT_ME_ALPHA_START = 0.8f;
-            const float LOOKING_AT_ME_DURATION = 0.5f;  // seconds
-            quint64 now = usecTimestampNow();
-            float alpha = LOOKING_AT_ME_ALPHA_START
-                * (1.0f - ((float)(now - getHead()->getLookingAtMeStarted()))
-                / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND));
-            if (alpha > 0.0f) {
-                if (_skeletonModel->isLoaded()) {
-                    const auto& geometry = _skeletonModel->getFBXGeometry();
-                    const float DEFAULT_EYE_DIAMETER = 0.048f;  // Typical human eye
-                    const float RADIUS_INCREMENT = 0.005f;
-                    batch.setModelTransform(Transform());
-
-                    glm::vec3 position = getHead()->getLeftEyePosition();
-                    Transform transform;
-                    transform.setTranslation(position);
-                    float eyeDiameter = geometry.leftEyeSize;
-                    if (eyeDiameter == 0.0f) {
-                        eyeDiameter = DEFAULT_EYE_DIAMETER;
-                    }
-
-                    batch.setModelTransform(Transform(transform).postScale(eyeDiameter * getUniformScale() / 2.0f + RADIUS_INCREMENT));
-                    DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch,
-                                                                            glm::vec4(LOOKING_AT_ME_COLOR, alpha));
-
-                    position = getHead()->getRightEyePosition();
-                    transform.setTranslation(position);
-                    eyeDiameter = geometry.rightEyeSize;
-                    if (eyeDiameter == 0.0f) {
-                        eyeDiameter = DEFAULT_EYE_DIAMETER;
-                    }
-                    batch.setModelTransform(Transform(transform).postScale(eyeDiameter * getUniformScale() / 2.0f + RADIUS_INCREMENT));
-                    DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch,
-                                                                            glm::vec4(LOOKING_AT_ME_COLOR, alpha));
-
-                }
-            }
-        }
     }
 
     const float DISPLAYNAME_DISTANCE = 20.0f;
@@ -553,11 +523,6 @@ void Avatar::fixupModelsInScene() {
     scene->enqueuePendingChanges(pendingChanges);
 }
 
-void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) {
-    fixupModelsInScene();
-    getHead()->renderLookAts(renderArgs);
-}
-
 bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
     return true;
 }
diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index cb35fbb5eb..2580ac1d37 100644
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -77,9 +77,9 @@ public:
 
     void updateRenderItem(render::PendingChanges& pendingChanges);
 
+    virtual void postUpdate(float deltaTime);
+
     //setters
-    void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
-    void setDisplayingLookatTarget(bool displayingLookatTarget) { getHead()->setRenderLookatTarget(displayingLookatTarget); }
     void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; }
     bool getIsLookAtTarget() const { return _isLookAtTarget; }
     //getters
@@ -232,7 +232,6 @@ protected:
 
     Transform calculateDisplayNameTransform(const ViewFrustum& view, const glm::vec3& textPosition) const;
     void renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const;
-    virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f);
     virtual bool shouldRenderHead(const RenderArgs* renderArgs) const;
     virtual void fixupModelsInScene();
 
@@ -251,7 +250,7 @@ private:
     bool _initialized;
     bool _shouldAnimate { true };
     bool _shouldSkipRender { false };
-    bool _isLookAtTarget;
+    bool _isLookAtTarget { false };
 
     float getBoundingRadius() const;
 
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 402245d0c3..7ff2976cd9 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -156,6 +156,15 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
     simulateAvatarFades(deltaTime);
 }
 
+void AvatarManager::postUpdate(float deltaTime) {
+    auto hashCopy = getHashCopy();
+    AvatarHash::iterator avatarIterator = hashCopy.begin();
+    for (avatarIterator = hashCopy.begin(); avatarIterator != hashCopy.end(); avatarIterator++) {
+        auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
+        avatar->postUpdate(deltaTime);
+    }
+}
+
 void AvatarManager::simulateAvatarFades(float deltaTime) {
     QVector<AvatarSharedPointer>::iterator fadingIterator = _avatarFades.begin();
 
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index 94a66782f2..1cd295d69f 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -44,6 +44,8 @@ public:
     void updateMyAvatar(float deltaTime);
     void updateOtherAvatars(float deltaTime);
 
+    void postUpdate(float deltaTime);
+
     void clearOtherAvatars();
     void clearAllAvatars();
 
diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp
index b7247334fc..3af8b8a423 100644
--- a/interface/src/avatar/Head.cpp
+++ b/interface/src/avatar/Head.cpp
@@ -46,8 +46,6 @@ Head::Head(Avatar* owningAvatar) :
     _mouth3(0.0f),
     _mouth4(0.0f),
     _mouthTime(0.0f),
-    _renderLookatVectors(false),
-    _renderLookatTarget(false),
     _saccade(0.0f, 0.0f, 0.0f),
     _saccadeTarget(0.0f, 0.0f, 0.0f),
     _leftEyeBlinkVelocity(0.0f),
@@ -316,22 +314,6 @@ void Head::relaxLean(float deltaTime) {
     _deltaLeanForward *= relaxationFactor;
 }
 
-void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum) {
-}
-
-void Head::renderLookAts(RenderArgs* renderArgs) {
-    renderLookAts(renderArgs, _leftEyePosition, _rightEyePosition);
-}
-
-void Head::renderLookAts(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition) {
-    if (_renderLookatVectors) {
-        renderLookatVectors(renderArgs, leftEyePosition, rightEyePosition, getCorrectedLookAtPosition());
-    }
-    if (_renderLookatTarget) {
-        renderLookatTarget(renderArgs, getCorrectedLookAtPosition());
-    }
-}
-
 void Head::setScale (float scale) {
     if (_scale == scale) {
         return;
@@ -442,31 +424,3 @@ void Head::addLeanDeltas(float sideways, float forward) {
     _deltaLeanSideways += sideways;
     _deltaLeanForward += forward;
 }
-
-void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
-    auto& batch = *renderArgs->_batch;
-    auto transform = Transform{};
-    batch.setModelTransform(transform);
-    // FIXME: THe line width of 2.0f is not supported anymore, we ll need a workaround
-
-    glm::vec4 startColor(0.2f, 0.2f, 0.2f, 1.0f);
-    glm::vec4 endColor(1.0f, 1.0f, 1.0f, 0.0f);
-    
-    auto geometryCache = DependencyManager::get<GeometryCache>();
-    geometryCache->bindSimpleProgram(batch);
-    geometryCache->renderLine(batch, leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID);
-    geometryCache->renderLine(batch, rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID);
-}
-
-void Head::renderLookatTarget(RenderArgs* renderArgs, glm::vec3 lookatPosition) {
-    auto& batch = *renderArgs->_batch;
-    auto transform = Transform{};
-    transform.setTranslation(lookatPosition);
-
-    auto geometryCache = DependencyManager::get<GeometryCache>();
-    const float LOOK_AT_TARGET_RADIUS = 0.075f;
-    transform.postScale(LOOK_AT_TARGET_RADIUS);
-    const glm::vec4 LOOK_AT_TARGET_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
-    batch.setModelTransform(transform);
-    geometryCache->renderSolidSphereInstance(batch, LOOK_AT_TARGET_COLOR);
-}
diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h
index d0bd4fdb77..e4b8fefea5 100644
--- a/interface/src/avatar/Head.h
+++ b/interface/src/avatar/Head.h
@@ -28,29 +28,24 @@ class Avatar;
 class Head : public HeadData {
 public:
     explicit Head(Avatar* owningAvatar);
-    
+
     void init();
     void reset();
     void simulate(float deltaTime, bool isMine, bool billboard = false);
-    void render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum);
     void setScale(float scale);
     void setPosition(glm::vec3 position) { _position = position; }
     void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
     void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; }
-    void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; }
-    void setRenderLookatTarget(bool onOff) { _renderLookatTarget = onOff; }
-    void renderLookAts(RenderArgs* renderArgs);
-    void renderLookAts(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition);
 
     /// \return orientationBase+Delta
     glm::quat getFinalOrientationInLocalFrame() const;
-    
+
     /// \return orientationBody * (orientationBase+Delta)
     glm::quat getFinalOrientationInWorldFrame() const;
 
     /// \return orientationBody * orientationBasePitch
     glm::quat getCameraOrientation () const;
-    
+
     void setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition);
     glm::vec3 getCorrectedLookAtPosition();
     void clearCorrectedLookAtPosition() { _isLookingAtMe = false; }
@@ -66,9 +61,9 @@ public:
     glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
     float getFinalLeanSideways() const { return _leanSideways + _deltaLeanSideways; }
     float getFinalLeanForward() const { return _leanForward + _deltaLeanForward; }
-    
+
     glm::quat getEyeRotation(const glm::vec3& eyePosition) const;
-    
+
     const glm::vec3& getRightEyePosition() const { return _rightEyePosition; }
     const glm::vec3& getLeftEyePosition() const { return _leftEyePosition; }
     glm::vec3 getRightEarPosition() const { return _rightEyePosition + (getRightDirection() * EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); }
@@ -85,10 +80,10 @@ public:
 
     void setDeltaYaw(float yaw) { _deltaYaw = yaw; }
     float getDeltaYaw() const { return _deltaYaw; }
-    
+
     void setDeltaRoll(float roll) { _deltaRoll = roll; }
     float getDeltaRoll() const { return _deltaRoll; }
-    
+
     virtual void setFinalYaw(float finalYaw);
     virtual void setFinalPitch(float finalPitch);
     virtual void setFinalRoll(float finalRoll);
@@ -100,7 +95,7 @@ public:
     void addLeanDeltas(float sideways, float forward);
 
     float getTimeWithoutTalking() const { return _timeWithoutTalking; }
-    
+
 private:
     glm::vec3 calculateAverageEyePosition() const { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * 0.5f; }
 
@@ -114,7 +109,7 @@ private:
     glm::vec3 _leftEyePosition;
     glm::vec3 _rightEyePosition;
     glm::vec3 _eyePosition;
-    
+
     float _scale;
     float _lastLoudness;
     float _longTermAverageLoudness;
@@ -125,8 +120,7 @@ private:
     float _mouth3;
     float _mouth4;
     float _mouthTime;
-    bool _renderLookatVectors;
-    bool _renderLookatTarget;
+
     glm::vec3 _saccade;
     glm::vec3 _saccadeTarget;
     float _leftEyeBlinkVelocity;
@@ -146,15 +140,13 @@ private:
     bool _isLookingAtMe;
     quint64 _lookingAtMeStarted;
     quint64 _wasLastLookingAtMe;
-    
+
     glm::vec3 _correctedLookAtPosition;
 
     int _leftEyeLookAtID;
     int _rightEyeLookAtID;
-    
+
     // private methods
-    void renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
-    void renderLookatTarget(RenderArgs* renderArgs, glm::vec3 lookatPosition);
     void calculateMouthShapes();
     void applyEyelidOffset(glm::quat headOrientation);
 };
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index bad60643ec..b2d653d573 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -1259,40 +1259,6 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName,
     Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved);
 }
 
-void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) {
-
-    if (!_skeletonModel->isRenderable()) {
-        return; // wait until all models are loaded
-    }
-
-    fixupModelsInScene();
-
-    //  Render head so long as the camera isn't inside it
-    if (shouldRenderHead(renderArgs)) {
-        getHead()->render(renderArgs, 1.0f, renderFrustum);
-    }
-
-    // This is drawing the lookat vectors from our avatar to wherever we're looking.
-    if (qApp->isHMDMode()) {
-        glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
-
-        glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose();
-        glm::mat4 leftEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Left);
-        leftEyePose = leftEyePose * headPose;
-        glm::vec3 leftEyePosition = extractTranslation(leftEyePose);
-        glm::mat4 rightEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Right);
-        rightEyePose = rightEyePose * headPose;
-        glm::vec3 rightEyePosition = extractTranslation(rightEyePose);
-        glm::vec3 headPosition = extractTranslation(headPose);
-
-        getHead()->renderLookAts(renderArgs,
-            cameraPosition + getOrientation() * (leftEyePosition - headPosition),
-            cameraPosition + getOrientation() * (rightEyePosition - headPosition));
-    } else {
-        getHead()->renderLookAts(renderArgs);
-    }
-}
-
 void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) {
     if (model->isActive() && model->isRenderable()) {
         model->setVisibleInScene(visible, scene);
@@ -1349,11 +1315,11 @@ void MyAvatar::destroyAnimGraph() {
     _rig->destroyAnimGraph();
 }
 
-void MyAvatar::preRender(RenderArgs* renderArgs) {
+void MyAvatar::postUpdate(float deltaTime) {
+
+    Avatar::postUpdate(deltaTime);
 
     render::ScenePointer scene = qApp->getMain3DScene();
-    const bool shouldDrawHead = shouldRenderHead(renderArgs);
-
     if (_skeletonModel->initWhenReady(scene)) {
         initHeadBones();
         _skeletonModel->setCauterizeBoneSet(_headBoneSet);
@@ -1403,7 +1369,12 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
 
     DebugDraw::getInstance().updateMyAvatarPos(getPosition());
     DebugDraw::getInstance().updateMyAvatarRot(getOrientation());
+}
 
+void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
+
+    // toggle using the cauterizedBones depending on where the camera is and the rendering pass type.
+    const bool shouldDrawHead = shouldRenderHead(renderArgs);
     if (shouldDrawHead != _prevShouldDrawHead) {
         _skeletonModel->setCauterizeBones(!shouldDrawHead);
     }
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index fee1a9add3..949b4fdfb3 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -95,7 +95,8 @@ public:
 
     Q_INVOKABLE void reset(bool andRecenter = false);
     void update(float deltaTime);
-    void preRender(RenderArgs* renderArgs);
+    virtual void postUpdate(float deltaTime) override;
+    void preDisplaySide(RenderArgs* renderArgs);
 
     const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; }
     const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; }
@@ -306,7 +307,6 @@ private:
     void simulate(float deltaTime);
     void updateFromTrackers(float deltaTime);
     virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
-    virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override;
     virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
     void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); }
     bool getShouldRenderLocally() const { return _shouldRender; }
diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp
index 0fe1a59bb2..5deeb545a1 100644
--- a/interface/src/avatar/SkeletonModel.cpp
+++ b/interface/src/avatar/SkeletonModel.cpp
@@ -13,6 +13,7 @@
 #include <QMultiMap>
 
 #include <recording/Deck.h>
+#include <DebugDraw.h>
 
 #include "Application.h"
 #include "Avatar.h"
@@ -92,7 +93,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
 
     Head* head = _owningAvatar->getHead();
 
-
     // make sure lookAt is not too close to face (avoid crosseyes)
     glm::vec3 lookAt = _owningAvatar->isMyAvatar() ?  head->getLookAtPosition() : head->getCorrectedLookAtPosition();
     glm::vec3 focusOffset = lookAt - _owningAvatar->getHead()->getEyePosition();
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index a7115199a2..67dfbec24a 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -1057,20 +1057,30 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
 }
 
 void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) {
+
+    // TODO: does not properly handle avatar scale.
+
     if (isIndexValid(index)) {
         glm::mat4 rigToWorld = createMatFromQuatAndPos(modelRotation, modelTranslation);
         glm::mat4 worldToRig = glm::inverse(rigToWorld);
-        glm::vec3 zAxis = glm::normalize(_internalPoseSet._absolutePoses[index].trans - transformPoint(worldToRig, lookAtSpot));
+        glm::vec3 lookAtVector = glm::normalize(transformPoint(worldToRig, lookAtSpot) - _internalPoseSet._absolutePoses[index].trans);
 
-        glm::quat desiredQuat = rotationBetween(IDENTITY_FRONT, zAxis);
-        glm::quat headQuat;
         int headIndex = indexOfJoint("Head");
+        glm::quat headQuat;
         if (headIndex >= 0) {
             headQuat = _internalPoseSet._absolutePoses[headIndex].rot;
         }
+
+        glm::vec3 headUp = headQuat * Vectors::UNIT_Y;
+        glm::vec3 z, y, x;
+        generateBasisVectors(lookAtVector, headUp, z, y, x);
+        glm::mat3 m(glm::cross(y, z), y, z);
+        glm::quat desiredQuat = glm::normalize(glm::quat_cast(m));
+
         glm::quat deltaQuat = desiredQuat * glm::inverse(headQuat);
 
-        // limit rotation
+        // limit swing rotation of the deltaQuat by a 30 degree cone.
+        // TODO: use swing twist decomposition constraint instead, for off axis rotation clamping.
         const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
         if (fabsf(glm::angle(deltaQuat)) > MAX_ANGLE) {
             deltaQuat = glm::angleAxis(glm::clamp(glm::angle(deltaQuat), -MAX_ANGLE, MAX_ANGLE), glm::axis(deltaQuat));
diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h
index 7c7c263562..51a030567d 100644
--- a/libraries/render-utils/src/AbstractViewStateInterface.h
+++ b/libraries/render-utils/src/AbstractViewStateInterface.h
@@ -35,7 +35,7 @@ public:
     virtual ViewFrustum* getShadowViewFrustum() = 0;
 
     virtual QThread* getMainThread() = 0;
-    
+
     virtual PickRay computePickRay(float x, float y) const = 0;
 
     virtual glm::vec3 getAvatarPosition() const = 0;
@@ -46,7 +46,7 @@ public:
     virtual render::ScenePointer getMain3DScene() = 0;
     virtual render::EnginePointer getRenderEngine() = 0;
 
-    virtual void pushPreRenderLambda(void* key, std::function<void()> func) = 0;
+    virtual void pushPostUpdateLambda(void* key, std::function<void()> func) = 0;
 
     // FIXME - we shouldn't assume that there's a single instance of an AbstractViewStateInterface
     static AbstractViewStateInterface* instance();
diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp
index e2abc226e5..220b673591 100644
--- a/libraries/render-utils/src/AnimDebugDraw.cpp
+++ b/libraries/render-utils/src/AnimDebugDraw.cpp
@@ -307,6 +307,16 @@ static void addLink(const AnimPose& rootPose, const AnimPose& pose, const AnimPo
     }
 }
 
+static void addLine(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, Vertex*& v) {
+    uint32_t colorInt = toRGBA(color);
+    v->pos = start;
+    v->rgba = colorInt;
+    v++;
+    v->pos = end;
+    v->rgba = colorInt;
+    v++;
+}
+
 void AnimDebugDraw::update() {
 
     render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
@@ -319,6 +329,7 @@ void AnimDebugDraw::update() {
 
         const size_t VERTICES_PER_BONE = (6 + (NUM_CIRCLE_SLICES * 2) * 3);
         const size_t VERTICES_PER_LINK = 8 * 2;
+        const size_t VERTICES_PER_RAY = 2;
 
         const float BONE_RADIUS = 0.01f; // 1 cm
         const float POSE_RADIUS = 0.1f; // 10 cm
@@ -342,6 +353,7 @@ void AnimDebugDraw::update() {
         numVerts += (int)markerMap.size() * VERTICES_PER_BONE;
         auto myAvatarMarkerMap = DebugDraw::getInstance().getMyAvatarMarkerMap();
         numVerts += (int)myAvatarMarkerMap.size() * VERTICES_PER_BONE;
+        numVerts += (int)DebugDraw::getInstance().getRays().size() * VERTICES_PER_RAY;
 
         // allocate verts!
         data._vertexBuffer->resize(sizeof(Vertex) * numVerts);
@@ -390,6 +402,12 @@ void AnimDebugDraw::update() {
             addBone(myAvatarPose, AnimPose(glm::vec3(1), rot, pos), radius, v);
         }
 
+        // draw rays from shared DebugDraw singleton
+        for (auto& iter : DebugDraw::getInstance().getRays()) {
+            addLine(std::get<0>(iter), std::get<1>(iter), std::get<2>(iter), v);
+        }
+        DebugDraw::getInstance().clearRays();
+
         assert(numVerts == (v - verts));
 
         render::Item::Bound theBound;
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index eda856342c..2fe4427333 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -132,7 +132,7 @@ void Model::updateRenderItems() {
     // the application will ensure only the last lambda is actually invoked.
     void* key = (void*)this;
     std::weak_ptr<Model> weakSelf = shared_from_this();
-    AbstractViewStateInterface::instance()->pushPreRenderLambda(key, [weakSelf]() {
+    AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() {
 
         // do nothing, if the model has already been destroyed.
         auto self = weakSelf.lock();
diff --git a/libraries/shared/src/DebugDraw.cpp b/libraries/shared/src/DebugDraw.cpp
index 557889cab0..04759e6187 100644
--- a/libraries/shared/src/DebugDraw.cpp
+++ b/libraries/shared/src/DebugDraw.cpp
@@ -23,6 +23,11 @@ DebugDraw::~DebugDraw() {
 
 }
 
+// world space line, drawn only once
+void DebugDraw::drawRay(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color) {
+    _rays.push_back(Ray(start, end, color));
+}
+
 void DebugDraw::addMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color) {
     _markers[key] = MarkerInfo(rotation, position, color);
 }
diff --git a/libraries/shared/src/DebugDraw.h b/libraries/shared/src/DebugDraw.h
index 2f62f17abf..2d30618afe 100644
--- a/libraries/shared/src/DebugDraw.h
+++ b/libraries/shared/src/DebugDraw.h
@@ -23,16 +23,21 @@ public:
     DebugDraw();
     ~DebugDraw();
 
-    // world space maker
+    // world space line, drawn only once
+    void drawRay(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color);
+
+    // world space maker, marker drawn every frame until it is removed.
     void addMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color);
     void removeMarker(const std::string& key);
 
-    // myAvatar relative marker
+    // myAvatar relative marker, maker is drawn every frame until it is removed.
     void addMyAvatarMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color);
     void removeMyAvatarMarker(const std::string& key);
 
     using MarkerInfo = std::tuple<glm::quat, glm::vec3, glm::vec4>;
     using MarkerMap = std::unordered_map<std::string, MarkerInfo>;
+    using Ray = std::tuple<glm::vec3, glm::vec3, glm::vec4>;
+    using Rays = std::vector<Ray>;
 
     //
     // accessors used by renderer
@@ -44,12 +49,15 @@ public:
     const glm::vec3& getMyAvatarPos() const { return _myAvatarPos; }
     void updateMyAvatarRot(const glm::quat& rot) { _myAvatarRot = rot; }
     const glm::quat& getMyAvatarRot() const { return _myAvatarRot; }
+    const Rays getRays() const { return _rays; }
+    void clearRays() { _rays.clear(); }
 
 protected:
     MarkerMap _markers;
     MarkerMap _myAvatarMarkers;
     glm::quat _myAvatarRot;
     glm::vec3 _myAvatarPos;
+    Rays _rays;
 };
 
 #endif // hifi_DebugDraw_h
diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp
index 53abb3827d..5762122707 100644
--- a/libraries/shared/src/GLMHelpers.cpp
+++ b/libraries/shared/src/GLMHelpers.cpp
@@ -431,13 +431,23 @@ glm::vec3 transformVectorFull(const glm::mat4& m, const glm::vec3& v) {
 void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& secondaryAxis,
                           glm::vec3& uAxisOut, glm::vec3& vAxisOut, glm::vec3& wAxisOut) {
 
+    // primaryAxis & secondaryAxis must not be zero.
+    assert(fabsf(glm::length2(primaryAxis) > 0.01f));
+    assert(fabsf(glm::length2(secondaryAxis) > 0.01f));
+
     uAxisOut = glm::normalize(primaryAxis);
-    wAxisOut = glm::cross(uAxisOut, secondaryAxis);
-    if (glm::length(wAxisOut) > 0.0f) {
-        wAxisOut = glm::normalize(wAxisOut);
-    } else {
-        wAxisOut = glm::normalize(glm::cross(uAxisOut, glm::vec3(0, 1, 0)));
+    glm::vec3 normSecondary = glm::normalize(secondaryAxis);
+
+    // if secondaryAxis is parallel with the primaryAxis, pick another axis.
+    if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > 0.001f) {
+        // pick a better secondaryAxis.
+        normSecondary = glm::vec3(1.0f, 0.0f, 0.0f);
+        if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > 0.001f) {
+            normSecondary = glm::vec3(0.0f, 1.0f, 0.0f);
+        }
     }
+
+    wAxisOut = glm::normalize(glm::cross(uAxisOut, secondaryAxis));
     vAxisOut = glm::cross(wAxisOut, uAxisOut);
 }
 

From cb5c7a35591f6416a37ac631ec0d2acf950a2d13 Mon Sep 17 00:00:00 2001
From: Anthony Thibault <ajt@hyperlogic.org>
Date: Sun, 8 May 2016 17:14:14 -0700
Subject: [PATCH 03/17] compile fix for macosx

---
 libraries/shared/src/DebugDraw.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libraries/shared/src/DebugDraw.h b/libraries/shared/src/DebugDraw.h
index 2d30618afe..f77e281e06 100644
--- a/libraries/shared/src/DebugDraw.h
+++ b/libraries/shared/src/DebugDraw.h
@@ -13,6 +13,7 @@
 #include <unordered_map>
 #include <tuple>
 #include <string>
+#include <vector>
 #include <glm/glm.hpp>
 #include <glm/gtc/quaternion.hpp>
 

From 87d513b5f8c4af2c39c9d0dba1d8f4cc14eb9abb Mon Sep 17 00:00:00 2001
From: Anthony Thibault <ajt@hyperlogic.org>
Date: Sun, 8 May 2016 18:14:18 -0700
Subject: [PATCH 04/17] fixed unused variable warnings

---
 interface/src/avatar/Avatar.cpp | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 14385ddfcb..a78c6635f9 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -427,20 +427,6 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
     float distanceToTarget = glm::length(toTarget);
 
     {
-        // glow when moving far away
-        const float GLOW_DISTANCE = 20.0f;
-        const float GLOW_MAX_LOUDNESS = 2500.0f;
-        const float MAX_GLOW = 0.5f;
-
-        float GLOW_FROM_AVERAGE_LOUDNESS = ((this == DependencyManager::get<AvatarManager>()->getMyAvatar())
-                                            ? 0.0f
-                                            : MAX_GLOW * getHeadData()->getAudioLoudness() / GLOW_MAX_LOUDNESS);
-        GLOW_FROM_AVERAGE_LOUDNESS = 0.0f;
-
-        float glowLevel = _moving && distanceToTarget > GLOW_DISTANCE && renderArgs->_renderMode == RenderArgs::NORMAL_RENDER_MODE
-                      ? 1.0f
-                      : GLOW_FROM_AVERAGE_LOUDNESS;
-
         fixupModelsInScene();
 
         if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) {

From 7f5296f5664b343d73cdc6c434ce334d0c324844 Mon Sep 17 00:00:00 2001
From: "Anthony J. Thibault" <tony@highfidelity.io>
Date: Mon, 9 May 2016 11:53:09 -0700
Subject: [PATCH 05/17] Increased tolerance for near zero length vectors

Also, replaced some magic numbers with constants.
---
 libraries/shared/src/GLMHelpers.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp
index 5762122707..1f15541758 100644
--- a/libraries/shared/src/GLMHelpers.cpp
+++ b/libraries/shared/src/GLMHelpers.cpp
@@ -432,17 +432,19 @@ void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& seconda
                           glm::vec3& uAxisOut, glm::vec3& vAxisOut, glm::vec3& wAxisOut) {
 
     // primaryAxis & secondaryAxis must not be zero.
-    assert(fabsf(glm::length2(primaryAxis) > 0.01f));
-    assert(fabsf(glm::length2(secondaryAxis) > 0.01f));
+    const float MIN_LENGTH_SQUARED = 1.0e-6f;
+    assert(fabsf(glm::length2(primaryAxis) > MIN_LENGTH_SQUARED));
+    assert(fabsf(glm::length2(secondaryAxis) > MIN_LENGTH_SQUARED));
 
     uAxisOut = glm::normalize(primaryAxis);
     glm::vec3 normSecondary = glm::normalize(secondaryAxis);
 
     // if secondaryAxis is parallel with the primaryAxis, pick another axis.
-    if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > 0.001f) {
+    const float EPSILON = 1.0e-4f;
+    if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > EPSILON) {
         // pick a better secondaryAxis.
         normSecondary = glm::vec3(1.0f, 0.0f, 0.0f);
-        if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > 0.001f) {
+        if (fabsf(fabsf(glm::dot(uAxisOut, secondaryAxis)) - 1.0f) > EPSILON) {
             normSecondary = glm::vec3(0.0f, 1.0f, 0.0f);
         }
     }

From 9821c276adcb83c2b732490a393ccbba9005be53 Mon Sep 17 00:00:00 2001
From: "Anthony J. Thibault" <tony@highfidelity.io>
Date: Mon, 9 May 2016 13:56:56 -0700
Subject: [PATCH 06/17] warning fix in release

---
 libraries/shared/src/GLMHelpers.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp
index 1f15541758..bd8bffefd2 100644
--- a/libraries/shared/src/GLMHelpers.cpp
+++ b/libraries/shared/src/GLMHelpers.cpp
@@ -432,7 +432,9 @@ void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& seconda
                           glm::vec3& uAxisOut, glm::vec3& vAxisOut, glm::vec3& wAxisOut) {
 
     // primaryAxis & secondaryAxis must not be zero.
+#ifndef NDEBUG
     const float MIN_LENGTH_SQUARED = 1.0e-6f;
+#endif
     assert(fabsf(glm::length2(primaryAxis) > MIN_LENGTH_SQUARED));
     assert(fabsf(glm::length2(secondaryAxis) > MIN_LENGTH_SQUARED));
 

From 8b517467885f4082fc50a13f25ed43c1090ca6c4 Mon Sep 17 00:00:00 2001
From: Brad Hefta-Gaub <brad@highfidelity.io>
Date: Mon, 9 May 2016 18:49:29 -0700
Subject: [PATCH 07/17] don't show help or login on first run

---
 interface/src/Application.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 5b0d5c65ce..14df366def 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -702,9 +702,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
     // The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
     UserActivityLogger::getInstance().launch(applicationVersion(), _previousSessionCrashed, sessionRunTime.get());
 
-    // once the event loop has started, check and signal for an access token
-    QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
-
     auto addressManager = DependencyManager::get<AddressManager>();
 
     // use our MyAvatar position and quat for address manager path
@@ -1327,8 +1324,6 @@ void Application::initializeGL() {
 
     // update before the first render
     update(0);
-
-    InfoView::show(INFO_HELP_PATH, true);
 }
 
 FrameTimingsScriptingInterface _frameTimingsScriptingInterface;

From 22f4beab1acfdc90d1ac23f90352c6389705cbd1 Mon Sep 17 00:00:00 2001
From: Brad Hefta-Gaub <brad@highfidelity.io>
Date: Tue, 10 May 2016 10:45:57 -0700
Subject: [PATCH 08/17] on first run, go to home or entry

---
 interface/src/Application.cpp               |  9 ++++++-
 libraries/networking/src/AddressManager.cpp | 30 +++++++++++++++++++++
 libraries/networking/src/AddressManager.h   |  4 +++
 3 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 50e09fb77c..ef5a7bef2f 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -2950,7 +2950,14 @@ void Application::init() {
         addressLookupString = arguments().value(urlIndex + 1);
     }
 
-    DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
+    Setting::Handle<bool> firstRun { "firstRun", true };
+    if (addressLookupString.isEmpty() && firstRun.get()) {
+        qDebug() << "First run and no URL passed... attempting to Go Home...";
+        DependencyManager::get<AddressManager>()->goHomeOrElsewhere();
+    } else {
+        qDebug() << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
+        DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
+    }
 
     qCDebug(interfaceapp) << "Loaded settings";
 
diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index 03b33eae38..f6f1416d2f 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -629,3 +629,33 @@ void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) {
         }
     }
 }
+
+void AddressManager::goHomeOrElsewhere(QString elsewhere, LookupTrigger trigger) {
+    QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+    QNetworkRequest sandboxStatus(QString("http://localhost:60332/status"));
+    sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
+    QNetworkReply* reply = networkAccessManager.get(sandboxStatus);
+
+    connect(reply, &QNetworkReply::finished, this, [this, elsewhere, trigger]() {
+        QNetworkReply* sender = qobject_cast<QNetworkReply*>(QObject::sender());
+        auto statusData = sender->readAll();
+        auto statusJson = QJsonDocument::fromJson(statusData);
+        if (!statusJson.isEmpty()) {
+            auto statusObject = statusJson.object();
+            auto serversValue = statusObject.value("servers");
+            if (!serversValue.isUndefined() && serversValue.isObject()) {
+                auto serversObject = serversValue.toObject();
+                auto serversCount = serversObject.size();
+                const int MINIMUM_EXPECTED_SERVER_COUNT = 6;
+                if (serversCount >= MINIMUM_EXPECTED_SERVER_COUNT) {
+                    qDebug() << "Home sandbox is running, going to " << SANDBOX_HIFI_ADDRESS;
+                    handleUrl(SANDBOX_HIFI_ADDRESS, trigger);
+                    return;
+                }
+            }
+        }
+        qDebug() << "Home sandbox was not running, going to " << elsewhere << "instead.";
+        handleUrl(elsewhere, trigger);
+    });
+}
+
diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index e0b54e4072..1b6b04a055 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -24,6 +24,7 @@
 
 const QString HIFI_URL_SCHEME = "hifi";
 const QString DEFAULT_HIFI_ADDRESS = "hifi://entry";
+const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost";
 const QString INDEX_PATH = "/";
 
 const QString GET_PLACE = "/api/v1/places/%1";
@@ -65,6 +66,9 @@ public:
     const QStack<QUrl>& getBackStack() const { return _backStack; }
     const QStack<QUrl>& getForwardStack() const { return _forwardStack; }
 
+    void goHomeOrElsewhere(QString elsewhere = DEFAULT_HIFI_ADDRESS, 
+                           LookupTrigger trigger = LookupTrigger::StartupFromSettings);
+
 public slots:
     void handleLookupString(const QString& lookupString);
 

From 33c5cb519a094f82b37ee585d5a1bf8c70fd0ea0 Mon Sep 17 00:00:00 2001
From: Brad Hefta-Gaub <brad@highfidelity.io>
Date: Tue, 10 May 2016 11:32:29 -0700
Subject: [PATCH 09/17] cleanup firstRun with a common setting

---
 interface/src/Application.cpp                 | 7 ++++++-
 libraries/script-engine/src/ScriptEngines.cpp | 4 ++--
 libraries/script-engine/src/ScriptEngines.h   | 2 --
 libraries/shared/src/SettingHandle.cpp        | 1 +
 libraries/shared/src/SettingHandle.h          | 2 ++
 libraries/shared/src/SettingInterface.h       | 1 +
 6 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index ef5a7bef2f..8a950fabd2 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1091,6 +1091,11 @@ void Application::checkChangeCursor() {
 
         _cursorNeedsChanging = false;
     }
+
+
+    // After all of the constructor is completed, then set firstRun to false.
+    Setting::Handle<bool> firstRun{ Settings::firstRun, true };
+    firstRun.set(false);
 }
 
 void Application::showCursor(const QCursor& cursor) {
@@ -2950,7 +2955,7 @@ void Application::init() {
         addressLookupString = arguments().value(urlIndex + 1);
     }
 
-    Setting::Handle<bool> firstRun { "firstRun", true };
+    Setting::Handle<bool> firstRun { Settings::firstRun, true };
     if (addressLookupString.isEmpty() && firstRun.get()) {
         qDebug() << "First run and no URL passed... attempting to Go Home...";
         DependencyManager::get<AddressManager>()->goHomeOrElsewhere();
diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp
index c6070e0598..370e310c8d 100644
--- a/libraries/script-engine/src/ScriptEngines.cpp
+++ b/libraries/script-engine/src/ScriptEngines.cpp
@@ -270,12 +270,12 @@ void ScriptEngines::loadOneScript(const QString& scriptFilename) {
 
 void ScriptEngines::loadScripts() {
     // check first run...
-    if (_firstRun.get()) {
+    Setting::Handle<bool> firstRun { Settings::firstRun, true };
+    if (firstRun.get()) {
         qCDebug(scriptengine) << "This is a first run...";
         // clear the scripts, and set out script to our default scripts
         clearScripts();
         loadDefaultScripts();
-        _firstRun.set(false);
         return;
     }
 
diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h
index 0963b21600..6522aa9bb3 100644
--- a/libraries/script-engine/src/ScriptEngines.h
+++ b/libraries/script-engine/src/ScriptEngines.h
@@ -87,8 +87,6 @@ protected:
     void onScriptEngineError(const QString& scriptFilename);
     void launchScriptEngine(ScriptEngine* engine);
 
-
-    Setting::Handle<bool> _firstRun { "firstRun", true };
     QReadWriteLock _scriptEnginesHashLock;
     QHash<QUrl, ScriptEngine*> _scriptEnginesHash;
     QSet<ScriptEngine*> _allKnownScriptEngines;
diff --git a/libraries/shared/src/SettingHandle.cpp b/libraries/shared/src/SettingHandle.cpp
index 951d004318..d2e84d8b75 100644
--- a/libraries/shared/src/SettingHandle.cpp
+++ b/libraries/shared/src/SettingHandle.cpp
@@ -13,6 +13,7 @@
 
 #include <math.h>
 
+const QString Settings::firstRun { "firstRun" };
 
 void Settings::getFloatValueIfValid(const QString& name, float& floatValue) {
     const QVariant badDefaultValue = NAN;
diff --git a/libraries/shared/src/SettingHandle.h b/libraries/shared/src/SettingHandle.h
index c803efaa71..bef2daf84c 100644
--- a/libraries/shared/src/SettingHandle.h
+++ b/libraries/shared/src/SettingHandle.h
@@ -26,6 +26,8 @@
 // TODO: remove
 class Settings : public QSettings {
 public:
+    static const QString firstRun;
+
     void getFloatValueIfValid(const QString& name, float& floatValue);
     void getBoolValue(const QString& name, bool& boolValue);
 
diff --git a/libraries/shared/src/SettingInterface.h b/libraries/shared/src/SettingInterface.h
index 3aad048dbe..2b814ca85e 100644
--- a/libraries/shared/src/SettingInterface.h
+++ b/libraries/shared/src/SettingInterface.h
@@ -16,6 +16,7 @@
 #include <QVariant>
 
 namespace Setting {
+
     void preInit();
     void init();
     void cleanupSettings();

From df34b49b41e309c08eeb0a2170b2f92fe045b925 Mon Sep 17 00:00:00 2001
From: Brad Hefta-Gaub <brad@highfidelity.io>
Date: Tue, 10 May 2016 11:34:11 -0700
Subject: [PATCH 10/17] fix white space

---
 libraries/shared/src/SettingInterface.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libraries/shared/src/SettingInterface.h b/libraries/shared/src/SettingInterface.h
index 2b814ca85e..3aad048dbe 100644
--- a/libraries/shared/src/SettingInterface.h
+++ b/libraries/shared/src/SettingInterface.h
@@ -16,7 +16,6 @@
 #include <QVariant>
 
 namespace Setting {
-
     void preInit();
     void init();
     void cleanupSettings();

From 7958020f4ec764c2d283224619ac9f5b785e9754 Mon Sep 17 00:00:00 2001
From: Brad Hefta-Gaub <brad@highfidelity.io>
Date: Tue, 10 May 2016 12:07:21 -0700
Subject: [PATCH 11/17] only expect the 5 essential servers

---
 libraries/networking/src/AddressManager.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index f6f1416d2f..4b8017455c 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -646,7 +646,7 @@ void AddressManager::goHomeOrElsewhere(QString elsewhere, LookupTrigger trigger)
             if (!serversValue.isUndefined() && serversValue.isObject()) {
                 auto serversObject = serversValue.toObject();
                 auto serversCount = serversObject.size();
-                const int MINIMUM_EXPECTED_SERVER_COUNT = 6;
+                const int MINIMUM_EXPECTED_SERVER_COUNT = 5;
                 if (serversCount >= MINIMUM_EXPECTED_SERVER_COUNT) {
                     qDebug() << "Home sandbox is running, going to " << SANDBOX_HIFI_ADDRESS;
                     handleUrl(SANDBOX_HIFI_ADDRESS, trigger);

From 0eb2388f69f2fed02d96d38af338a45fd33d7a28 Mon Sep 17 00:00:00 2001
From: Brad Hefta-Gaub <brad@highfidelity.io>
Date: Tue, 10 May 2016 15:00:50 -0700
Subject: [PATCH 12/17] fix unix build buster

---
 libraries/networking/src/AddressManager.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index 4b8017455c..c27081ec28 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -636,9 +636,8 @@ void AddressManager::goHomeOrElsewhere(QString elsewhere, LookupTrigger trigger)
     sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
     QNetworkReply* reply = networkAccessManager.get(sandboxStatus);
 
-    connect(reply, &QNetworkReply::finished, this, [this, elsewhere, trigger]() {
-        QNetworkReply* sender = qobject_cast<QNetworkReply*>(QObject::sender());
-        auto statusData = sender->readAll();
+    connect(reply, &QNetworkReply::finished, this, [this, elsewhere, reply, trigger]() {
+        auto statusData = reply->readAll();
         auto statusJson = QJsonDocument::fromJson(statusData);
         if (!statusJson.isEmpty()) {
             auto statusObject = statusJson.object();

From a17acc6f6b98a4c7ddd0008180fc4d5e6c3814e5 Mon Sep 17 00:00:00 2001
From: Brad Hefta-Gaub <brad@highfidelity.io>
Date: Tue, 10 May 2016 15:52:07 -0700
Subject: [PATCH 13/17] rename functions

---
 interface/src/Application.cpp               | 11 +++++++++--
 libraries/networking/src/AddressManager.cpp | 14 +++++++-------
 libraries/networking/src/AddressManager.h   |  9 +++++++--
 3 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 8a950fabd2..0cf135db7c 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -2957,8 +2957,15 @@ void Application::init() {
 
     Setting::Handle<bool> firstRun { Settings::firstRun, true };
     if (addressLookupString.isEmpty() && firstRun.get()) {
-        qDebug() << "First run and no URL passed... attempting to Go Home...";
-        DependencyManager::get<AddressManager>()->goHomeOrElsewhere();
+        qDebug() << "First run and no URL passed... attempting to go to Home or Entry...";
+        DependencyManager::get<AddressManager>()->ifLocalSandboxRunningElse([](){
+            qDebug() << "Home sandbox appears to be running, going to Home.";
+            DependencyManager::get<AddressManager>()->goToLocalSandbox();
+        }, 
+        [](){
+            qDebug() << "Home sandbox does not appear to be running, going to Entry.";
+            DependencyManager::get<AddressManager>()->goToEntry();
+        });
     } else {
         qDebug() << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
         DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index c27081ec28..757e145c9a 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -630,13 +630,15 @@ void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) {
     }
 }
 
-void AddressManager::goHomeOrElsewhere(QString elsewhere, LookupTrigger trigger) {
+void AddressManager::ifLocalSandboxRunningElse(std::function<void()> localSandboxRunningDoThis,
+                                               std::function<void()> localSandboxNotRunningDoThat) {
+
     QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
-    QNetworkRequest sandboxStatus(QString("http://localhost:60332/status"));
+    QNetworkRequest sandboxStatus(SANDBOX_STATUS_URL);
     sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
     QNetworkReply* reply = networkAccessManager.get(sandboxStatus);
 
-    connect(reply, &QNetworkReply::finished, this, [this, elsewhere, reply, trigger]() {
+    connect(reply, &QNetworkReply::finished, this, [reply, localSandboxRunningDoThis, localSandboxNotRunningDoThat]() {
         auto statusData = reply->readAll();
         auto statusJson = QJsonDocument::fromJson(statusData);
         if (!statusJson.isEmpty()) {
@@ -647,14 +649,12 @@ void AddressManager::goHomeOrElsewhere(QString elsewhere, LookupTrigger trigger)
                 auto serversCount = serversObject.size();
                 const int MINIMUM_EXPECTED_SERVER_COUNT = 5;
                 if (serversCount >= MINIMUM_EXPECTED_SERVER_COUNT) {
-                    qDebug() << "Home sandbox is running, going to " << SANDBOX_HIFI_ADDRESS;
-                    handleUrl(SANDBOX_HIFI_ADDRESS, trigger);
+                    localSandboxRunningDoThis();
                     return;
                 }
             }
         }
-        qDebug() << "Home sandbox was not running, going to " << elsewhere << "instead.";
-        handleUrl(elsewhere, trigger);
+        localSandboxNotRunningDoThat();
     });
 }
 
diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index 1b6b04a055..d746442376 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -25,6 +25,7 @@
 const QString HIFI_URL_SCHEME = "hifi";
 const QString DEFAULT_HIFI_ADDRESS = "hifi://entry";
 const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost";
+const QString SANDBOX_STATUS_URL = "http://localhost:60332/status";
 const QString INDEX_PATH = "/";
 
 const QString GET_PLACE = "/api/v1/places/%1";
@@ -66,8 +67,10 @@ public:
     const QStack<QUrl>& getBackStack() const { return _backStack; }
     const QStack<QUrl>& getForwardStack() const { return _forwardStack; }
 
-    void goHomeOrElsewhere(QString elsewhere = DEFAULT_HIFI_ADDRESS, 
-                           LookupTrigger trigger = LookupTrigger::StartupFromSettings);
+    /// determines if the local sandbox is likely running. It does not account for custom setups but is only 
+    /// intended to detect the standard sandbox install.
+    void ifLocalSandboxRunningElse(std::function<void()> localSandboxRunningDoThis,
+                                   std::function<void()> localSandboxNotRunningDoThat);
 
 public slots:
     void handleLookupString(const QString& lookupString);
@@ -78,6 +81,8 @@ public slots:
 
     void goBack();
     void goForward();
+    void goToLocalSandbox(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(SANDBOX_HIFI_ADDRESS, trigger); }
+    void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(DEFAULT_HIFI_ADDRESS, trigger); }
 
     void goToUser(const QString& username);
 

From 62b3c12d0f418b3fe757b9795bb35a4fdefc3d5b Mon Sep 17 00:00:00 2001
From: Brad Hefta-Gaub <brad@highfidelity.io>
Date: Tue, 10 May 2016 15:53:10 -0700
Subject: [PATCH 14/17] fix comment

---
 libraries/networking/src/AddressManager.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index d746442376..c0ba69018c 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -67,8 +67,8 @@ public:
     const QStack<QUrl>& getBackStack() const { return _backStack; }
     const QStack<QUrl>& getForwardStack() const { return _forwardStack; }
 
-    /// determines if the local sandbox is likely running. It does not account for custom setups but is only 
-    /// intended to detect the standard sandbox install.
+    /// determines if the local sandbox is likely running. It does not account for custom setups, and is only 
+    /// intended to detect the standard local sandbox install.
     void ifLocalSandboxRunningElse(std::function<void()> localSandboxRunningDoThis,
                                    std::function<void()> localSandboxNotRunningDoThat);
 

From 09a4e0eaa88b668d4fc76f4e71a1fb4b082c3162 Mon Sep 17 00:00:00 2001
From: "Anthony J. Thibault" <tony@highfidelity.io>
Date: Tue, 10 May 2016 19:32:08 -0700
Subject: [PATCH 15/17] Fix for vive controllers sometimes not working

* Fixed bug with input devices that where added, removed then re-added.
  The default mappings were being ignored on the second add.
* Fixed potential crash when hardware inputPlugin device poses were polled from the JavaScript thread
  by taking the UserInputManager lock during pluginUpdate.
* Renamed Controller.Hardware.Vive.LB & RB to LeftGrip and RightGrip, to better match Oculus touch.
* Updated resource/controller/vive.json to reflect this new mapping.
* Exposed touch pad capacitive touch events to JavaScript as
  Controller.Hardware.Vive.LSTouch and RSTouch.
* Added viveTouchpadTest.js script to test LSTouch and RSTouch events.
---
 interface/resources/controllers/vive.json     |  4 +-
 .../src/controllers/UserInputMapper.cpp       | 33 ++++++--
 .../src/controllers/UserInputMapper.h         | 11 ++-
 .../src/input-plugins/KeyboardMouseDevice.cpp |  8 +-
 plugins/hifiNeuron/src/NeuronPlugin.cpp       |  7 +-
 plugins/hifiSixense/src/SixenseManager.cpp    |  7 +-
 plugins/openvr/src/ViveControllerManager.cpp  | 59 ++++++++++-----
 plugins/openvr/src/ViveControllerManager.h    |  2 +-
 scripts/developer/tests/viveTouchpadTest.js   | 75 +++++++++++++++++++
 9 files changed, 172 insertions(+), 34 deletions(-)
 create mode 100644 scripts/developer/tests/viveTouchpadTest.js

diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json
index 1f71658946..e534c16b50 100644
--- a/interface/resources/controllers/vive.json
+++ b/interface/resources/controllers/vive.json
@@ -5,14 +5,14 @@
         { "from": "Vive.LX",  "when": "Vive.LS", "to": "Standard.LX" },
 
         { "from": "Vive.LT", "to": "Standard.LT" },
-        { "from": "Vive.LB", "to": "Standard.LB" },
+        { "from": "Vive.LeftGrip", "to": "Standard.LB" },
         { "from": "Vive.LS", "to": "Standard.LS" },
 
         { "from": "Vive.RY",  "when": "Vive.RS", "filters": "invert", "to": "Standard.RY" },
         { "from": "Vive.RX",  "when": "Vive.RS", "to": "Standard.RX" },
 
         { "from": "Vive.RT", "to": "Standard.RT" },
-        { "from": "Vive.RB", "to": "Standard.RB" },
+        { "from": "Vive.RightGrip", "to": "Standard.RB" },
         { "from": "Vive.RS", "to": "Standard.RS" },
 
         { "from": "Vive.LeftApplicationMenu", "to": "Standard.Back" },
diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp
index 2b7d837aa5..c0d3ff40c0 100755
--- a/libraries/controllers/src/controllers/UserInputMapper.cpp
+++ b/libraries/controllers/src/controllers/UserInputMapper.cpp
@@ -58,7 +58,7 @@ controller::UserInputMapper::UserInputMapper() {
 
 namespace controller {
 
-    
+
 UserInputMapper::~UserInputMapper() {
 }
 
@@ -80,6 +80,7 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) {
     recordDeviceOfType(device->getName());
 
     qCDebug(controllers) << "Registered input device <" << device->getName() << "> deviceID = " << deviceID;
+
     for (const auto& inputMapping : device->getAvailableInputs()) {
         const auto& input = inputMapping.first;
         // Ignore aliases
@@ -102,6 +103,7 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) {
     }
 
     _registeredDevices[deviceID] = device;
+
     auto mapping = loadMappings(device->getDefaultMappingConfigs());
     if (mapping) {
         _mappingsByDevice[deviceID] = mapping;
@@ -111,15 +113,21 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) {
     emit hardwareChanged();
 }
 
-// FIXME remove the associated device mappings
 void UserInputMapper::removeDevice(int deviceID) {
+
     Locker locker(_lock);
     auto proxyEntry = _registeredDevices.find(deviceID);
+
     if (_registeredDevices.end() == proxyEntry) {
         qCWarning(controllers) << "Attempted to remove unknown device " << deviceID;
         return;
     }
-    auto proxy = proxyEntry->second;
+
+    auto device = proxyEntry->second;
+    qCDebug(controllers) << "Unregistering input device <" << device->getName() << "> deviceID = " << deviceID;
+
+    unloadMappings(device->getDefaultMappingConfigs());
+
     auto mappingsEntry = _mappingsByDevice.find(deviceID);
     if (_mappingsByDevice.end() != mappingsEntry) {
         disableMapping(mappingsEntry->second);
@@ -244,7 +252,7 @@ void UserInputMapper::update(float deltaTime) {
     for (auto& channel : _actionStates) {
         channel = 0.0f;
     }
-    
+
     for (auto& channel : _poseStates) {
         channel = Pose();
     }
@@ -705,11 +713,10 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile, bool enab
         return Mapping::Pointer();
     }
     // Each mapping only needs to be loaded once
-    static QSet<QString> loaded;
-    if (loaded.contains(jsonFile)) {
+    if (_loadedRouteJsonFiles.contains(jsonFile)) {
         return Mapping::Pointer();
     }
-    loaded.insert(jsonFile);
+    _loadedRouteJsonFiles.insert(jsonFile);
     QString json;
     {
         QFile file(jsonFile);
@@ -741,6 +748,18 @@ MappingPointer UserInputMapper::loadMappings(const QStringList& jsonFiles) {
     return result;
 }
 
+void UserInputMapper::unloadMappings(const QStringList& jsonFiles) {
+    for (const QString& jsonFile : jsonFiles) {
+        unloadMapping(jsonFile);
+    }
+}
+
+void UserInputMapper::unloadMapping(const QString& jsonFile) {
+    auto entry = _loadedRouteJsonFiles.find(jsonFile);
+    if (entry != _loadedRouteJsonFiles.end()) {
+        _loadedRouteJsonFiles.erase(entry);
+    }
+}
 
 static const QString JSON_NAME = QStringLiteral("name");
 static const QString JSON_CHANNELS = QStringLiteral("channels");
diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h
index 95cc629c73..9c79415b6e 100644
--- a/libraries/controllers/src/controllers/UserInputMapper.h
+++ b/libraries/controllers/src/controllers/UserInputMapper.h
@@ -111,9 +111,18 @@ namespace controller {
 
         void loadDefaultMapping(uint16 deviceID);
         void enableMapping(const QString& mappingName, bool enable = true);
+
+        void unloadMappings(const QStringList& jsonFiles);
+        void unloadMapping(const QString& jsonFile);
+
         float getValue(const Input& input) const;
         Pose getPose(const Input& input) const;
 
+        // perform an action when the UserInputMapper mutex is acquired.
+        using Locker = std::unique_lock<std::recursive_mutex>;
+        template <typename F>
+        void withLock(F&& f) { Locker locker(_lock); f(); }
+
     signals:
         void actionEvent(int action, float state);
         void inputEvent(int input, float state);
@@ -177,7 +186,7 @@ namespace controller {
         RouteList _deviceRoutes;
         RouteList _standardRoutes;
 
-        using Locker = std::unique_lock<std::recursive_mutex>;
+        QSet<QString> _loadedRouteJsonFiles;
 
         mutable std::recursive_mutex _lock;
     };
diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp
index 0bf398cdec..4c0240eaf7 100644
--- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp
+++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp
@@ -20,8 +20,12 @@
 
 const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";
 
-void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { 
-    _inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured); 
+void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
+
+    auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
+    userInputMapper->withLock([&, this]() {
+        _inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
+    });
 
     // For touch event, we need to check that the last event is not too long ago
     // Maybe it's a Qt issue, but the touch event sequence (begin, update, end) is not always called properly
diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp
index f2b8c04827..6e2f744173 100644
--- a/plugins/hifiNeuron/src/NeuronPlugin.cpp
+++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp
@@ -509,7 +509,12 @@ void NeuronPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrat
         std::lock_guard<std::mutex> guard(_jointsMutex);
         joints = _joints;
     }
-    _inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints);
+
+    auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
+    userInputMapper->withLock([&, this]() {
+        _inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints);
+    });
+
     _prevJoints = joints;
 }
 
diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp
index eb55d84664..fdb7bb17fe 100644
--- a/plugins/hifiSixense/src/SixenseManager.cpp
+++ b/plugins/hifiSixense/src/SixenseManager.cpp
@@ -136,7 +136,12 @@ void SixenseManager::setSixenseFilter(bool filter) {
 
 void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
     BAIL_IF_NOT_LOADED
-    _inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
+
+    auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
+    userInputMapper->withLock([&, this]() {
+        _inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
+    });
+
     if (_inputDevice->_requestReset) {
         _container->requestReset();
         _inputDevice->_requestReset = false;
diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp
index c1ed5aa880..04abd7b398 100644
--- a/plugins/openvr/src/ViveControllerManager.cpp
+++ b/plugins/openvr/src/ViveControllerManager.cpp
@@ -211,9 +211,13 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch&
 
 
 void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
-    _inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
     auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
 
+    // because update mutates the internal state we need to lock
+    userInputMapper->withLock([&, this]() {
+        _inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
+    });
+
     if (_inputDevice->_trackedControllers == 0 && _registeredWithInputMapper) {
         userInputMapper->removeDevice(_inputDevice->_deviceID);
         _registeredWithInputMapper = false;
@@ -270,7 +274,8 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u
             for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) {
                 auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i);
                 bool pressed = 0 != (controllerState.ulButtonPressed & mask);
-                handleButtonEvent(deltaTime, i, pressed, isLeftHand);
+                bool touched = 0 != (controllerState.ulButtonTouched & mask);
+                handleButtonEvent(deltaTime, i, pressed, touched, isLeftHand);
             }
 
             // process each axis
@@ -314,20 +319,26 @@ enum ViveButtonChannel {
 
 
 // These functions do translation from the Steam IDs to the standard controller IDs
-void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool isLeftHand) {
-    if (!pressed) {
-        return;
-    }
+void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool touched, bool isLeftHand) {
 
     using namespace controller;
-    if (button == vr::k_EButton_ApplicationMenu) {
-        _buttonPressedMap.insert(isLeftHand ? LEFT_APP_MENU : RIGHT_APP_MENU);
-    } else if (button == vr::k_EButton_Grip) {
-        _buttonPressedMap.insert(isLeftHand ? LB : RB);
-    } else if (button == vr::k_EButton_SteamVR_Trigger) {
-        _buttonPressedMap.insert(isLeftHand ? LT : RT);
-    } else if (button == vr::k_EButton_SteamVR_Touchpad) {
-        _buttonPressedMap.insert(isLeftHand ? LS : RS);
+
+    if (pressed) {
+        if (button == vr::k_EButton_ApplicationMenu) {
+            _buttonPressedMap.insert(isLeftHand ? LEFT_APP_MENU : RIGHT_APP_MENU);
+        } else if (button == vr::k_EButton_Grip) {
+            _buttonPressedMap.insert(isLeftHand ? LEFT_GRIP : RIGHT_GRIP);
+        } else if (button == vr::k_EButton_SteamVR_Trigger) {
+            _buttonPressedMap.insert(isLeftHand ? LT : RT);
+        } else if (button == vr::k_EButton_SteamVR_Touchpad) {
+            _buttonPressedMap.insert(isLeftHand ? LS : RS);
+        }
+    }
+
+    if (touched) {
+         if (button == vr::k_EButton_SteamVR_Touchpad) {
+             _buttonPressedMap.insert(isLeftHand ? LS_TOUCH : RS_TOUCH);
+        }
     }
 }
 
@@ -424,18 +435,28 @@ controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableI
         makePair(LY, "LY"),
         makePair(RX, "RX"),
         makePair(RY, "RY"),
-        // trigger analogs
+
+        // capacitive touch on the touch pad
+        makePair(LS_TOUCH, "LSTouch"),
+        makePair(RS_TOUCH, "RSTouch"),
+
+        // touch pad press
+        makePair(LS, "LS"),
+        makePair(RS, "RS"),
+
+        // triggers
         makePair(LT, "LT"),
         makePair(RT, "RT"),
 
-        makePair(LB, "LB"),
-        makePair(RB, "RB"),
+        // low profile side grip button.
+        makePair(LEFT_GRIP, "LeftGrip"),
+        makePair(RIGHT_GRIP, "RightGrip"),
 
-        makePair(LS, "LS"),
-        makePair(RS, "RS"),
+        // 3d location of controller
         makePair(LEFT_HAND, "LeftHand"),
         makePair(RIGHT_HAND, "RightHand"),
 
+        // app button above trackpad.
         Input::NamedPair(Input(_deviceID, LEFT_APP_MENU, ChannelType::BUTTON), "LeftApplicationMenu"),
         Input::NamedPair(Input(_deviceID, RIGHT_APP_MENU, ChannelType::BUTTON), "RightApplicationMenu"),
     };
diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h
index d3645304c5..d55d4e726c 100644
--- a/plugins/openvr/src/ViveControllerManager.h
+++ b/plugins/openvr/src/ViveControllerManager.h
@@ -59,7 +59,7 @@ private:
         virtual void focusOutEvent() override;
 
         void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand);
-        void handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool isLeftHand);
+        void handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool touched, bool isLeftHand);
         void handleAxisEvent(float deltaTime, uint32_t axis, float x, float y, bool isLeftHand);
         void handlePoseEvent(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const mat4& mat,
                              const vec3& linearVelocity, const vec3& angularVelocity, bool isLeftHand);
diff --git a/scripts/developer/tests/viveTouchpadTest.js b/scripts/developer/tests/viveTouchpadTest.js
new file mode 100644
index 0000000000..52b33f02a4
--- /dev/null
+++ b/scripts/developer/tests/viveTouchpadTest.js
@@ -0,0 +1,75 @@
+//
+//  viveTouchpadTest.js
+//
+//  Anthony J. Thibault
+//  Copyright 2016 High Fidelity, Inc.
+//
+//  An example of reading touch and move events from the vive controller touch pad.
+//
+//  It will spawn a gray cube in front of you, then as you use the right touch pad,
+//  the cube should turn green and respond to the motion of your thumb on the pad.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+var GRAY = {red: 57, green: 57, blue: 57};
+var GREEN = {red: 0, green: 255, blue: 0};
+var ZERO = {x: 0, y: 0, z: 0};
+var Y_AXIS = {x: 0, y: 1, x: 0};
+var ROT_Y_90 = Quat.angleAxis(Y_AXIS, 90.0);
+
+var boxEntity;
+var boxPosition;
+var boxZAxis, boxYAxis;
+var prevThumbDown = false;
+
+function init() {
+    boxPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
+    var front = Quat.getFront(Camera.getOrientation());
+    boxZAxis = Vec3.normalize(Vec3.cross(front, Y_AXIS));
+    boxYAxis = Vec3.normalize(Vec3.cross(boxZAxis, front));
+
+    boxEntity = Entities.addEntity({
+        type: "Box",
+        position: boxPosition,
+        dimentions: {x: 0.25, y: 0.25, z: 0.25},
+        color: GRAY,
+        gravity: ZERO,
+        visible: true,
+        locked: false,
+        lifetime: 60000
+    });
+}
+
+function shutdown() {
+    Entities.deleteEntity(boxEntity);
+}
+Script.scriptEnding.connect(shutdown);
+
+prevPose = Controller.getPoseValue(Controller.Hardware.Vive.LeftHand);
+
+function update(dt) {
+    var thumbDown = Controller.getValue(Controller.Hardware.Vive.RSTouch);
+    if (thumbDown) {
+        var x = Controller.getValue(Controller.Hardware.Vive.RX);
+        var y = Controller.getValue(Controller.Hardware.Vive.RY);
+        var xOffset = Vec3.multiply(boxZAxis, x);
+        var yOffset = Vec3.multiply(boxYAxis, y);
+        var offset = Vec3.sum(xOffset, yOffset);
+        Entities.editEntity(boxEntity, {position: Vec3.sum(boxPosition, offset)});
+    }
+    if (thumbDown && !prevThumbDown) {
+        Entities.editEntity(boxEntity, {color: GREEN});
+    }
+    if (!thumbDown && prevThumbDown) {
+        Entities.editEntity(boxEntity, {color: GRAY});
+    }
+    prevThumbDown = thumbDown;
+}
+
+Script.update.connect(update);
+
+init();
+
+
+

From c91455a291476ead2f85d436d2bfb68ac24eec47 Mon Sep 17 00:00:00 2001
From: "Anthony J. Thibault" <tony@highfidelity.io>
Date: Wed, 11 May 2016 10:36:55 -0700
Subject: [PATCH 16/17] Added mapping from Vive LSTouch and RSTouch to
 Standard.

---
 interface/resources/controllers/vive.json | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json
index e534c16b50..a793e1a6cb 100644
--- a/interface/resources/controllers/vive.json
+++ b/interface/resources/controllers/vive.json
@@ -7,6 +7,7 @@
         { "from": "Vive.LT", "to": "Standard.LT" },
         { "from": "Vive.LeftGrip", "to": "Standard.LB" },
         { "from": "Vive.LS", "to": "Standard.LS" },
+        { "from": "Vive.LSTouch", "to": "Standard.LSTouch" },
 
         { "from": "Vive.RY",  "when": "Vive.RS", "filters": "invert", "to": "Standard.RY" },
         { "from": "Vive.RX",  "when": "Vive.RS", "to": "Standard.RX" },
@@ -14,6 +15,7 @@
         { "from": "Vive.RT", "to": "Standard.RT" },
         { "from": "Vive.RightGrip", "to": "Standard.RB" },
         { "from": "Vive.RS", "to": "Standard.RS" },
+        { "from": "Vive.RSTouch", "to": "Standard.RSTouch" },
 
         { "from": "Vive.LeftApplicationMenu", "to": "Standard.Back" },
         { "from": "Vive.RightApplicationMenu", "to": "Standard.Start" },

From 95ef543f257d6f8ccb525b25b0621cad35418bd2 Mon Sep 17 00:00:00 2001
From: "Anthony J. Thibault" <tony@highfidelity.io>
Date: Wed, 11 May 2016 13:35:17 -0700
Subject: [PATCH 17/17] viveTouchpadTest.js: don't throw if there is no Vive
 controller attached.

---
 scripts/developer/tests/viveTouchpadTest.js | 36 ++++++++++++---------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/scripts/developer/tests/viveTouchpadTest.js b/scripts/developer/tests/viveTouchpadTest.js
index 52b33f02a4..913da5888d 100644
--- a/scripts/developer/tests/viveTouchpadTest.js
+++ b/scripts/developer/tests/viveTouchpadTest.js
@@ -46,25 +46,29 @@ function shutdown() {
 }
 Script.scriptEnding.connect(shutdown);
 
-prevPose = Controller.getPoseValue(Controller.Hardware.Vive.LeftHand);
+function viveIsConnected() {
+    return Controller.Hardware.Vive;
+}
 
 function update(dt) {
-    var thumbDown = Controller.getValue(Controller.Hardware.Vive.RSTouch);
-    if (thumbDown) {
-        var x = Controller.getValue(Controller.Hardware.Vive.RX);
-        var y = Controller.getValue(Controller.Hardware.Vive.RY);
-        var xOffset = Vec3.multiply(boxZAxis, x);
-        var yOffset = Vec3.multiply(boxYAxis, y);
-        var offset = Vec3.sum(xOffset, yOffset);
-        Entities.editEntity(boxEntity, {position: Vec3.sum(boxPosition, offset)});
+    if (viveIsConnected()) {
+        var thumbDown = Controller.getValue(Controller.Hardware.Vive.RSTouch);
+        if (thumbDown) {
+            var x = Controller.getValue(Controller.Hardware.Vive.RX);
+            var y = Controller.getValue(Controller.Hardware.Vive.RY);
+            var xOffset = Vec3.multiply(boxZAxis, x);
+            var yOffset = Vec3.multiply(boxYAxis, y);
+            var offset = Vec3.sum(xOffset, yOffset);
+            Entities.editEntity(boxEntity, {position: Vec3.sum(boxPosition, offset)});
+        }
+        if (thumbDown && !prevThumbDown) {
+            Entities.editEntity(boxEntity, {color: GREEN});
+        }
+        if (!thumbDown && prevThumbDown) {
+            Entities.editEntity(boxEntity, {color: GRAY});
+        }
+        prevThumbDown = thumbDown;
     }
-    if (thumbDown && !prevThumbDown) {
-        Entities.editEntity(boxEntity, {color: GREEN});
-    }
-    if (!thumbDown && prevThumbDown) {
-        Entities.editEntity(boxEntity, {color: GRAY});
-    }
-    prevThumbDown = thumbDown;
 }
 
 Script.update.connect(update);