From a83616a7dcb911204bcde7decf3c44db4d384db3 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Thu, 30 Apr 2015 10:32:19 -0700
Subject: [PATCH 01/88] Don't prevent building on Visual Studio 2015

---
 CMakeLists.txt                             |  2 +-
 cmake/macros/SetupExternalsBinaryDir.cmake | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b271664c35..24deac8393 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,7 +51,7 @@ elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter -ggdb")
 endif(WIN32)
 
-if (NOT MSVC12)
+if ((NOT MSVC12) AND (NOT MSVC14))
   include(CheckCXXCompilerFlag)
   CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
   CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
diff --git a/cmake/macros/SetupExternalsBinaryDir.cmake b/cmake/macros/SetupExternalsBinaryDir.cmake
index a118dcf543..118df9c8fa 100644
--- a/cmake/macros/SetupExternalsBinaryDir.cmake
+++ b/cmake/macros/SetupExternalsBinaryDir.cmake
@@ -14,12 +14,12 @@ macro(SETUP_EXTERNALS_BINARY_DIR)
   # get a short name for the generator to use in the path
   STRING(REGEX REPLACE " " "-" CMAKE_GENERATOR_FOLDER_NAME ${CMAKE_GENERATOR})
 
-  if (MSVC12)
+  if (MSVC12) 
     set(CMAKE_GENERATOR_FOLDER_NAME "vc12")
-  else ()
-    if (CMAKE_GENERATOR_FOLDER_NAME STREQUAL "Unix-Makefiles")
-      set(CMAKE_GENERATOR_FOLDER_NAME "makefiles")
-    endif ()
+  elseif (MSVC14)
+    set(CMAKE_GENERATOR_FOLDER_NAME "vc14")
+  elseif(CMAKE_GENERATOR_FOLDER_NAME STREQUAL "Unix-Makefiles")
+    set(CMAKE_GENERATOR_FOLDER_NAME "makefiles")
   endif ()
 
   set(EXTERNALS_BINARY_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/ext")

From 984d449bf9a391d6385ec9fd239b002e4cf5b8ad Mon Sep 17 00:00:00 2001
From: Sam Gondelman <samuel_gondelman@brown.edu>
Date: Thu, 4 Jun 2015 16:29:58 -0700
Subject: [PATCH 02/88] exposed input key bindings to js to enable key
 remapping

---
 interface/src/Application.h                   |   1 +
 interface/src/devices/KeyboardMouseDevice.cpp |  18 ++-
 .../ControllerScriptingInterface.cpp          | 117 +++++++++++++++++-
 .../scripting/ControllerScriptingInterface.h  |  14 ++-
 interface/src/ui/UserInputMapper.cpp          |  96 +++++++++++++-
 interface/src/ui/UserInputMapper.h            |  56 +++++++--
 .../AbstractControllerScriptingInterface.h    |   2 +
 libraries/script-engine/src/ScriptEngine.cpp  |   1 +
 8 files changed, 289 insertions(+), 16 deletions(-)

diff --git a/interface/src/Application.h b/interface/src/Application.h
index b6efb6420b..9b815cb51a 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -148,6 +148,7 @@ public:
     static glm::quat getOrientationForPath() { return getInstance()->_myAvatar->getOrientation(); }
     static glm::vec3 getPositionForAudio() { return getInstance()->_myAvatar->getHead()->getPosition(); }
     static glm::quat getOrientationForAudio() { return getInstance()->_myAvatar->getHead()->getFinalOrientationInWorldFrame(); }
+    static UserInputMapper* getUserInputMapper() { return &getInstance()->_userInputMapper; }
     static void initPlugins();
     static void shutdownPlugins();
 
diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp
index 7b0f8c056c..a7e85d28e1 100755
--- a/interface/src/devices/KeyboardMouseDevice.cpp
+++ b/interface/src/devices/KeyboardMouseDevice.cpp
@@ -159,9 +159,25 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
     // Grab the current free device ID
     _deviceID = mapper.getFreeDeviceID();
 
-    auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy());
+    auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Keyboard"));
     proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); };
     proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); };
+    proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
+        QVector<UserInputMapper::InputPair> availableInputs;
+        for (int i = 0; i <= 9; i++) {
+            availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(48 + i)), QKeySequence(Qt::Key(48 + i)).toString()));
+        }
+        for (int i = 0; i < 26; i++) {
+            availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(65 + i)), QKeySequence(Qt::Key(65 + i)).toString()));
+        }
+        availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
+        return availableInputs;
+    };
+    proxy->resetDeviceBindings = [this, &_mapper = mapper, &device = _deviceID] () -> bool {
+        _mapper.removeAllInputChannelsForDevice(device);
+        this->assignDefaultInputMapping(_mapper);
+        return true;
+    };
     mapper.registerDevice(_deviceID, proxy);
 }
 
diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp
index 5f12d73b37..fe2eda12df 100644
--- a/interface/src/scripting/ControllerScriptingInterface.cpp
+++ b/interface/src/scripting/ControllerScriptingInterface.cpp
@@ -28,6 +28,90 @@ ControllerScriptingInterface::ControllerScriptingInterface() :
 {
 
 }
+static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>();
+static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>();
+static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>();
+static int inputPairMetaTypeId = qRegisterMetaType<UserInputMapper::InputPair>();
+
+QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input);
+void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input);
+QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel);
+void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel);
+QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action);
+void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action);
+QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair);
+void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair);
+
+QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("device", input.getDevice());
+    obj.setProperty("channel", input.getChannel());
+    obj.setProperty("type", input._type);
+    obj.setProperty("id", input.getID());
+    return obj;
+}
+
+void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) {
+    input._device = object.property("device").toUInt16();
+    input._channel = object.property("channel").toUInt16();
+    input._type = object.property("type").toUInt16();
+    input._id = object.property("id").toInt32();
+}
+
+QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("input", inputToScriptValue(engine, inputChannel._input));
+    obj.setProperty("modifier", inputToScriptValue(engine, inputChannel._modifier));
+    obj.setProperty("action", inputChannel._action);
+    obj.setProperty("scale", inputChannel._scale);
+    return obj;
+}
+
+void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) {
+    inputFromScriptValue(object.property("input"), inputChannel._input);
+    inputFromScriptValue(object.property("modifier"), inputChannel._modifier);
+    inputChannel._action = UserInputMapper::Action(object.property("action").toVariant().toInt());
+    inputChannel._scale = object.property("scale").toVariant().toFloat();
+}
+
+QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) {
+    QScriptValue obj = engine->newObject();
+    QVector<UserInputMapper::InputChannel> inputChannels = Application::getUserInputMapper()->getInputChannelsForAction(action);
+    QScriptValue _inputChannels = engine->newArray(inputChannels.size());
+    for (int i = 0; i < inputChannels.size(); i++) {
+        _inputChannels.setProperty(i, inputChannelToScriptValue(engine, inputChannels[i]));
+    }
+    obj.setProperty("action", (int) action);
+    obj.setProperty("actionName", Application::getUserInputMapper()->getActionName(action));
+    obj.setProperty("inputChannels", _inputChannels);
+    return obj;
+}
+
+void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) {
+    action = UserInputMapper::Action(object.property("action").toVariant().toInt());
+}
+
+QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) {
+    QScriptValue obj = engine->newObject();
+    obj.setProperty("input", inputToScriptValue(engine, inputPair.first));
+    obj.setProperty("inputName", inputPair.second);
+    return obj;
+}
+
+void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) {
+    inputFromScriptValue(object.property("input"), inputPair.first);
+    inputPair.second = QString(object.property("inputName").toVariant().toString());
+}
+
+void ControllerScriptingInterface::registerControllerTypes(QScriptEngine* engine) {
+    qScriptRegisterSequenceMetaType<QVector<UserInputMapper::Action> >(engine);
+    qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputChannel> >(engine);
+    qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputPair> >(engine);
+    qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue);
+    qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue);
+    qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue);
+    qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue);
+}
 
 void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
     if (event->type() == HFActionEvent::startType()) {
@@ -337,6 +421,37 @@ void ControllerScriptingInterface::updateInputControllers() {
     }
 }
 
+QVector<UserInputMapper::Action> ControllerScriptingInterface::getAllActions() {
+    return Application::getUserInputMapper()->getAllActions();
+}
+
+QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) {
+    return Application::getUserInputMapper()->getInputChannelsForAction(action);
+}
+
+QString ControllerScriptingInterface::getDeviceName(unsigned int device) {
+    return Application::getUserInputMapper()->getDeviceName((unsigned short) device);
+}
+
+QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getAllInputsForDevice(unsigned int device) {
+    return Application::getUserInputMapper()->getAllInputsForDevice(device);
+}
+
+bool ControllerScriptingInterface::addInputChannel(UserInputMapper::InputChannel inputChannel) {
+    return Application::getUserInputMapper()->addInputChannel(inputChannel._action, inputChannel._input, inputChannel._modifier, inputChannel._scale);
+}
+
+bool ControllerScriptingInterface::removeInputChannel(UserInputMapper::InputChannel inputChannel) {
+    return Application::getUserInputMapper()->removeInputChannel(inputChannel);
+}
+
+QVector<UserInputMapper::InputPair> ControllerScriptingInterface::getAvailableInputs(unsigned int device) {
+    return Application::getUserInputMapper()->getAvailableInputs((unsigned short) device);
+}
+
+void ControllerScriptingInterface::resetAllDeviceBindings() {
+    Application::getUserInputMapper()->resetAllDeviceBindings();
+}
 
 InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
     AbstractInputController(),
@@ -373,4 +488,4 @@ const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16;
 
 InputController::Key InputController::getKey() const {
     return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);
-}
+}
\ No newline at end of file
diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h
index c088dd6c9a..9414dc887b 100644
--- a/interface/src/scripting/ControllerScriptingInterface.h
+++ b/interface/src/scripting/ControllerScriptingInterface.h
@@ -14,10 +14,11 @@
 
 #include <QtCore/QObject>
 
+#include "ui/UserInputMapper.h"
+
 #include <AbstractControllerScriptingInterface.h>
 class PalmData;
 
-
 class InputController : public  AbstractInputController {
     Q_OBJECT
 
@@ -54,6 +55,9 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface
 
 public:    
     ControllerScriptingInterface();
+    
+    virtual void registerControllerTypes(QScriptEngine* engine);
+    
     void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
     void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
     
@@ -79,6 +83,14 @@ public:
     void updateInputControllers();
 
 public slots:
+    Q_INVOKABLE virtual QVector<UserInputMapper::Action> getAllActions();
+    Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
+    Q_INVOKABLE virtual QString getDeviceName(unsigned int device);
+    Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getAllInputsForDevice(unsigned int device);
+    Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel);
+    Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel);
+    Q_INVOKABLE virtual QVector<UserInputMapper::InputPair> getAvailableInputs(unsigned int device);
+    Q_INVOKABLE virtual void resetAllDeviceBindings();
     virtual bool isPrimaryButtonPressed() const;
     virtual glm::vec2 getPrimaryJoystickPosition() const;
 
diff --git a/interface/src/ui/UserInputMapper.cpp b/interface/src/ui/UserInputMapper.cpp
index 892ab6a9b6..e994b3cf30 100755
--- a/interface/src/ui/UserInputMapper.cpp
+++ b/interface/src/ui/UserInputMapper.cpp
@@ -13,7 +13,15 @@
 
 
 // UserInputMapper Class
+
+// Default contruct allocate the poutput size with the current hardcoded action channels
+UserInputMapper::UserInputMapper() {
+    assignDefaulActionScales();
+    createActionNames();
+}
+
 bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){
+    proxy->_name += " (" + QString::number(deviceID) + ")";
     _registeredDevices[deviceID] = proxy;
     return true;
 }
@@ -27,6 +35,12 @@ UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Inpu
     }
 }
 
+void UserInputMapper::resetAllDeviceBindings() {
+    for (auto device : _registeredDevices) {
+        device.second->resetDeviceBindings();
+    }
+}
+
 bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) {
     return addInputChannel(action, input, Input(), scale);
 }
@@ -37,7 +51,7 @@ bool UserInputMapper::addInputChannel(Action action, const Input& input, const I
         qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped.";
         return false;
     }
-
+    
     auto inputChannel = InputChannel(input, modifier, action, scale);
 
     // Insert or replace the input to modifiers
@@ -61,6 +75,37 @@ int UserInputMapper::addInputChannels(const InputChannels& channels) {
     return nbAdded;
 }
 
+bool UserInputMapper::removeInputChannel(InputChannel inputChannel) {
+    // Remove from Input to Modifiers map
+    if (inputChannel.hasModifier()) {
+        _inputToModifiersMap.erase(inputChannel._input.getID());
+    }
+    
+    // Remove from Action to Inputs map
+    std::pair<ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret;
+    ret = _actionToInputsMap.equal_range(inputChannel._action);
+    for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) {
+        if (it->second == inputChannel) {
+            _actionToInputsMap.erase(it);
+            return true;
+        }
+    }
+    
+    return false;
+}
+
+void UserInputMapper::removeAllInputChannels() {
+    _inputToModifiersMap.clear();
+    _actionToInputsMap.clear();
+}
+
+void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) {
+    QVector<InputChannel> channels = getAllInputsForDevice(device);
+    for (auto& channel : channels) {
+        removeInputChannel(channel);
+    }
+}
+
 int UserInputMapper::getInputChannels(InputChannels& channels) const {
     for (auto& channel : _actionToInputsMap) {
         channels.push_back(channel.second);
@@ -69,6 +114,20 @@ int UserInputMapper::getInputChannels(InputChannels& channels) const {
     return _actionToInputsMap.size();
 }
 
+QVector<UserInputMapper::InputChannel> UserInputMapper::getAllInputsForDevice(uint16 device) {
+    InputChannels allChannels;
+    getInputChannels(allChannels);
+    
+    QVector<InputChannel> channels;
+    for (InputChannel inputChannel : allChannels) {
+        if (inputChannel._input._device == device) {
+            channels.push_back(inputChannel);
+        }
+    }
+    
+    return channels;
+}
+
 void UserInputMapper::update(float deltaTime) {
 
     // Reset the axis state for next loop
@@ -130,6 +189,24 @@ void UserInputMapper::update(float deltaTime) {
     }
 }
 
+QVector<UserInputMapper::Action> UserInputMapper::getAllActions() {
+    QVector<Action> actions;
+    for (auto i = 0; i < NUM_ACTIONS; i++) {
+        actions.append(Action(i));
+    }
+    return actions;
+}
+
+QVector<UserInputMapper::InputChannel> UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) {
+    QVector<InputChannel> inputChannels;
+    std::pair <ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret;
+    ret = _actionToInputsMap.equal_range(action);
+    for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) {
+        inputChannels.append(it->second);
+    }
+    return inputChannels;
+}
+
 void UserInputMapper::assignDefaulActionScales() {
     _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit
     _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit
@@ -144,3 +221,20 @@ void UserInputMapper::assignDefaulActionScales() {
     _actionScales[BOOM_IN] = 1.0f; // 1m per unit
     _actionScales[BOOM_OUT] = 1.0f; // 1m per unit
 }
+
+// This is only necessary as long as the actions are hardcoded
+// Eventually you can just add the string when you add the action
+void UserInputMapper::createActionNames() {
+    _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD";
+    _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD";
+    _actionNames[LATERAL_LEFT] = "LATERAL_LEFT";
+    _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT";
+    _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN";
+    _actionNames[VERTICAL_UP] = "VERTICAL_UP";
+    _actionNames[YAW_LEFT] = "YAW_LEFT";
+    _actionNames[YAW_RIGHT] = "YAW_RIGHT";
+    _actionNames[PITCH_DOWN] = "PITCH_DOWN";
+    _actionNames[PITCH_UP] = "PITCH_UP";
+    _actionNames[BOOM_IN] = "BOOM_IN";
+    _actionNames[BOOM_OUT] = "BOOM_OUT";
+}
\ No newline at end of file
diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h
index ab63bdbef7..34188ae3f5 100755
--- a/interface/src/ui/UserInputMapper.h
+++ b/interface/src/ui/UserInputMapper.h
@@ -21,6 +21,7 @@
 
 class UserInputMapper : public QObject {
     Q_OBJECT
+    Q_ENUMS(Action)
 public:
     typedef unsigned short uint16;
     typedef unsigned int uint32;
@@ -64,6 +65,7 @@ public:
         explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {}
         Input(const Input& src) : _id(src._id) {}
         Input& operator = (const Input& src) { _id = src._id; return (*this); }
+        bool operator ==(const Input& right) const { return _id == right._id; }
         bool operator < (const Input& src) const { return _id < src._id; }
     };
 
@@ -83,22 +85,32 @@ public:
     typedef std::function<bool (const Input& input, int timestamp)> ButtonGetter;
     typedef std::function<float (const Input& input, int timestamp)> AxisGetter;
     typedef std::function<JointValue (const Input& input, int timestamp)> JointGetter;
+    typedef QPair<Input, QString> InputPair;
+    typedef std::function<QVector<InputPair> ()> AvailableInputGetter;
+    typedef std::function<bool ()> ResetBindings;
+    
+    typedef QVector<InputPair> AvailableInput;
 
    class DeviceProxy {
     public:
-        DeviceProxy() {}
-        
-        ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
-        AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; };
-        JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); };
-        
-        typedef std::shared_ptr<DeviceProxy> Pointer;
+       DeviceProxy(QString name) { _name = name; }
+       
+       QString _name;
+       ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
+       AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; };
+       JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); };
+       AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector<InputPair>(); };
+       ResetBindings resetDeviceBindings = [] () -> bool { return true; };
+       
+       typedef std::shared_ptr<DeviceProxy> Pointer;
     };
     // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
     uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
     bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device);
     DeviceProxy::Pointer getDeviceProxy(const Input& input);
-
+    QString getDeviceName(uint16 deviceID) { return _registeredDevices[deviceID]->_name; }
+    QVector<InputPair> getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); }
+    void resetAllDeviceBindings();
 
     // Actions are the output channels of the Mapper, that's what the InputChannel map to
     // For now the Actions are hardcoded, this is bad, but we will fix that in the near future
@@ -123,8 +135,14 @@ public:
 
         NUM_ACTIONS,
     };
+    
+    std::vector<QString> _actionNames = std::vector<QString>(NUM_ACTIONS);
+    void createActionNames();
 
+    QVector<Action> getAllActions();
+    QString getActionName(Action action) { return UserInputMapper::_actionNames[(int) action]; }
     float getActionState(Action action) const { return _actionStates[action]; }
+//    QVector<InputChannel>
     void assignDefaulActionScales();
 
     // Add input channel to the mapper and check that all the used channels are registered.
@@ -146,21 +164,27 @@ public:
             _input(input), _modifier(modifier), _action(action), _scale(scale) {}
         InputChannel(const InputChannel& src) : InputChannel(src._input, src._modifier, src._action, src._scale) {}
         InputChannel& operator = (const InputChannel& src) { _input = src._input; _modifier = src._modifier; _action = src._action; _scale = src._scale; return (*this); }
-    
+        bool operator ==(const InputChannel& right) const { return _input == right._input && _modifier == right._modifier && _action == right._action && _scale == right._scale; }
         bool hasModifier() { return _modifier.isValid(); }
     };
     typedef std::vector< InputChannel > InputChannels;
 
     // Add a bunch of input channels, return the true number of channels that successfully were added
     int addInputChannels(const InputChannels& channels);
+    // Remove the first found instance of the input channel from the input mapper, true if found
+    bool removeInputChannel(InputChannel channel);
+    void removeAllInputChannels();
+    void removeAllInputChannelsForDevice(uint16 device);
     //Grab all the input channels currently in use, return the number
     int getInputChannels(InputChannels& channels) const;
+    QVector<InputChannel> getAllInputsForDevice(uint16 device);
+    QVector<InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
+    std::multimap<Action, InputChannel> getActionToInputsMap() { return _actionToInputsMap; }
 
     // Update means go grab all the device input channels and update the output channel values
     void update(float deltaTime);
-
-    // Default contruct allocate the poutput size with the current hardcoded action channels
-    UserInputMapper() { assignDefaulActionScales(); }
+    
+    UserInputMapper();
 
 protected:
     typedef std::map<int, DeviceProxy::Pointer> DevicesMap;
@@ -177,4 +201,12 @@ protected:
     std::vector<float> _actionScales = std::vector<float>(NUM_ACTIONS, 1.0f);
 };
 
+Q_DECLARE_METATYPE(UserInputMapper::InputPair)
+Q_DECLARE_METATYPE(QVector<UserInputMapper::InputPair>)
+Q_DECLARE_METATYPE(UserInputMapper::Input)
+Q_DECLARE_METATYPE(UserInputMapper::InputChannel)
+Q_DECLARE_METATYPE(QVector<UserInputMapper::InputChannel>)
+Q_DECLARE_METATYPE(UserInputMapper::Action)
+Q_DECLARE_METATYPE(QVector<UserInputMapper::Action>)
+
 #endif // hifi_UserInputMapper_h
diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h
index 11755add8e..43076039a9 100644
--- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h
+++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h
@@ -52,6 +52,8 @@ class AbstractControllerScriptingInterface : public QObject {
     Q_OBJECT
 
 public slots:
+    virtual void registerControllerTypes(QScriptEngine* engine) = 0;
+    
     virtual bool isPrimaryButtonPressed() const = 0;
     virtual glm::vec2 getPrimaryJoystickPosition() const = 0;
 
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 30c3fbe8e4..182a0aea8d 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -317,6 +317,7 @@ void ScriptEngine::init() {
     registerAnimationTypes(this);
     registerAvatarTypes(this);
     registerAudioMetaTypes(this);
+    _controllerScriptingInterface->registerControllerTypes(this);
 
     qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly);
     qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);

From dd12e4740e036895929ddd42e63eab9caab6fab0 Mon Sep 17 00:00:00 2001
From: Sam Gondelman <samuel_gondelman@brown.edu>
Date: Fri, 5 Jun 2015 10:14:13 -0700
Subject: [PATCH 03/88] example script for controller scripting, input mapping

---
 .../scripts/controllerScriptingExamples.js    | 94 +++++++++++++++++++
 1 file changed, 94 insertions(+)
 create mode 100644 examples/example/scripts/controllerScriptingExamples.js

diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js
new file mode 100644
index 0000000000..3e693c42ea
--- /dev/null
+++ b/examples/example/scripts/controllerScriptingExamples.js
@@ -0,0 +1,94 @@
+//
+//  controllerScriptingExamples.js
+//  examples
+//
+//  Created by Sam Gondelman on 6/2/15
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+// Resets every device to its default key bindings:
+Controller.resetAllDeviceBindings()
+
+// Query all actions
+print("All Actions: \n" + Controller.getAllActions())
+
+// Each action stores:
+// action: int representation of enum
+print("Action 5 int: \n" + Controller.getAllActions()[5].action)
+
+// actionName: string representation of enum
+print("Action 5 name: \n" + Controller.getAllActions()[5].actionName)
+
+// inputChannels: list of all inputchannels that control that action
+print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n")
+
+
+// Each input channel stores:
+// action: Action that this InputChannel maps to
+print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action)
+
+// scale: sensitivity of input
+print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale)
+
+// input and modifier: Inputs
+print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n")
+
+
+// Each Input stores:
+// device: device of input
+print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device)
+
+// channel: channel of input
+print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel)
+
+// type: type of input (Unknown, Button, Axis, Joint)
+print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type)
+
+// id: id of input
+print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n")
+
+
+// You can get the name of a device from its id
+print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id))
+
+// You can also get all of a devices input channels
+print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n")
+
+
+// Modifying properties:
+// The following code will switch the "w" and "s" key functionality and adjust their scales
+var s = Controller.getAllActions()[0].inputChannels[0]
+var w = Controller.getAllActions()[1].inputChannels[0]
+
+// You must remove an input controller before modifying it so the old input controller isn't registered anymore
+// removeInputChannel and addInputChannel return true if successful, false otherwise
+Controller.removeInputChannel(s)
+Controller.removeInputChannel(w)
+print(s.scale)
+s.action = 1
+s.scale = .01
+
+w.action = 0
+w.scale = 10000
+Controller.addInputChannel(s)
+Controller.addInputChannel(w)
+print(s.scale)
+
+// You can get all the available inputs for any device
+// Each AvailableInput has:
+// input: the Input itself
+// inputName: string representing the input
+var availableInputs = Controller.getAvailableInputs(1)
+for (i = 0; i < availableInputs.length; i++) {
+	print(availableInputs[i].inputName);
+}
+
+// You can modify key bindings by using these avaiable inputs
+// This will replace e (up) with 6
+var e = Controller.getAllActions()[5].inputChannels[0]
+Controller.removeInputChannel(e)
+e.input = availableInputs[6].input
+Controller.addInputChannel(e)
\ No newline at end of file

From f51feca37d54ddbd0202738090925da412ee0457 Mon Sep 17 00:00:00 2001
From: David Rowe <david@ctrlaltstudio.com>
Date: Mon, 8 Jun 2015 11:29:35 -0700
Subject: [PATCH 04/88] Add directory button

---
 examples/defaultScripts.js |  1 +
 examples/directory.js      | 86 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+)
 create mode 100644 examples/directory.js

diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js
index 8f32b80bba..61bed8d9b1 100644
--- a/examples/defaultScripts.js
+++ b/examples/defaultScripts.js
@@ -18,3 +18,4 @@ Script.load("notifications.js");
 Script.load("users.js");
 Script.load("grab.js");
 Script.load("pointer.js");
+Script.load("directory.js");
diff --git a/examples/directory.js b/examples/directory.js
new file mode 100644
index 0000000000..965abf1453
--- /dev/null
+++ b/examples/directory.js
@@ -0,0 +1,86 @@
+//
+//  directory.js
+//  examples
+//
+//  Created by David Rowe on 8 Jun 2015
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+Script.include("libraries/globals.js");
+
+var directory = (function () {
+
+    var DIRECTORY_URL = "https://metaverse.highfidelity.com/directory",
+        directoryWindow,
+        DIRECTORY_BUTTON_URL = HIFI_PUBLIC_BUCKET + "images/tools/directory.svg",
+        BUTTON_WIDTH = 50,
+        BUTTON_HEIGHT = 50,
+        BUTTON_ALPHA = 0.9,
+        BUTTON_MARGIN = 8,
+        directoryButton,
+        viewport;
+
+    function updateButtonPosition() {
+        Overlays.editOverlay(directoryButton, {
+            x: viewport.x - BUTTON_WIDTH - BUTTON_MARGIN,
+            y: BUTTON_MARGIN
+        });
+    }
+
+    function onMousePressEvent(event) {
+        var clickedOverlay;
+
+        clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
+
+        if (clickedOverlay === directoryButton) {
+            if (directoryWindow.url !== DIRECTORY_URL) {
+                directoryWindow.setURL(DIRECTORY_URL);
+            }
+            directoryWindow.setVisible(true);
+            directoryWindow.raise();
+        }
+    }
+
+    function onScriptUpdate() {
+        var oldViewport = viewport;
+
+        viewport = Controller.getViewportDimensions();
+
+        if (viewport.x !== oldViewport.x || viewport.y !== oldViewport.y) {
+            updateButtonPosition();
+        }
+    }
+
+    function setUp() {
+        viewport = Controller.getViewportDimensions();
+
+        directoryWindow = new WebWindow('Directory', DIRECTORY_URL, 900, 700, false);
+        directoryWindow.setVisible(false);
+
+        directoryButton = Overlays.addOverlay("image", {
+            imageURL: DIRECTORY_BUTTON_URL,
+            width: BUTTON_WIDTH,
+            height: BUTTON_HEIGHT,
+            x: viewport.x - BUTTON_WIDTH - BUTTON_MARGIN,
+            y: BUTTON_MARGIN,
+            alpha: BUTTON_ALPHA,
+            visible: true
+        });
+
+        updateButtonPosition();
+
+        Controller.mousePressEvent.connect(onMousePressEvent);
+
+        Script.update.connect(onScriptUpdate);
+    }
+
+    function tearDown() {
+        Overlays.deleteOverlay(directoryButton);
+    }
+
+    setUp();
+    Script.scriptEnding.connect(tearDown);
+}());
\ No newline at end of file

From cd931282365b27bf31d878f1a81140c32f721a1a Mon Sep 17 00:00:00 2001
From: Niraj Venkat <venkatn93@gmail.com>
Date: Mon, 8 Jun 2015 13:29:54 -0700
Subject: [PATCH 05/88] [ERRORS] Starting on hyperlink entity and properties

---
 libraries/entities/src/EntityItemProperties.h |   6 +
 libraries/entities/src/Hyperlink.h            |   5 +
 .../entities/src/HyperlinkEntityItem.cpp      | 174 ++++++++++++++++++
 libraries/entities/src/HyperlinkEntityItem.h  |  95 ++++++++++
 4 files changed, 280 insertions(+)
 create mode 100644 libraries/entities/src/Hyperlink.h
 create mode 100644 libraries/entities/src/HyperlinkEntityItem.cpp
 create mode 100644 libraries/entities/src/HyperlinkEntityItem.h

diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index 3c8133af8f..42d4555e2e 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -55,6 +55,7 @@ class EntityItemProperties {
     friend class WebEntityItem; // TODO: consider removing this friend relationship and use public methods
     friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods
     friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods
+    friend class HyperlinkEntityItem; // TODO: consider removing this friend relationship and use public methods
 public:
     EntityItemProperties();
     virtual ~EntityItemProperties();
@@ -148,6 +149,9 @@ public:
     DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString);
     DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float);
     DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector<glm::vec3>);
+    DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString);
+    DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString);
+
 
     static QString getBackgroundModeString(BackgroundMode mode);
 
@@ -295,6 +299,8 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, "");
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, "");
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, "");
+    DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, "");
+    DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, "");
     
     properties.getStage().debugDump();
     properties.getAtmosphere().debugDump();
diff --git a/libraries/entities/src/Hyperlink.h b/libraries/entities/src/Hyperlink.h
new file mode 100644
index 0000000000..7e2e2261e7
--- /dev/null
+++ b/libraries/entities/src/Hyperlink.h
@@ -0,0 +1,5 @@
+
+
+
+
+entityItem
\ No newline at end of file
diff --git a/libraries/entities/src/HyperlinkEntityItem.cpp b/libraries/entities/src/HyperlinkEntityItem.cpp
new file mode 100644
index 0000000000..af9a4003b1
--- /dev/null
+++ b/libraries/entities/src/HyperlinkEntityItem.cpp
@@ -0,0 +1,174 @@
+//
+//  HyperlinkEntityItem.cpp
+//  libraries/entities/src
+//
+//  Created by Niraj Venkat on 6/8/15.
+//  Copyright 2013 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+
+#include <glm/gtx/transform.hpp>
+
+#include <QDebug>
+
+#include <ByteCountCoding.h>
+#include <PlaneShape.h>
+
+#include "EntityTree.h"
+#include "EntityTreeElement.h"
+#include "EntitiesLogging.h"
+#include "HyperlinkEntityItem.h"
+
+
+const QString HyperlinkEntityItem::DEFAULT_HREF("");
+const QString HyperlinkEntityItem::DEFAULT_DESCRIPTION("");
+
+const float HyperlinkEntityItem::DEFAULT_LINE_HEIGHT = 0.1f;
+const xColor HyperlinkEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 };
+const xColor HyperlinkEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 };
+
+EntityItemPointer HyperlinkEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+    return EntityItemPointer(new HyperlinkEntityItem(entityID, properties));
+}
+
+HyperlinkEntityItem::HyperlinkEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
+EntityItem(entityItemID)
+{
+    _type = EntityTypes::Text;
+    _created = properties.getCreated();
+    setProperties(properties);
+}
+
+const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
+
+void HyperlinkEntityItem::setDimensions(const glm::vec3& value) {
+    // NOTE: Text Entities always have a "depth" of 1cm.
+    _dimensions = glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH);
+}
+
+EntityItemProperties HyperlinkEntityItem::getProperties() const {
+    EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
+
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColorX);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColorX);
+    return properties;
+}
+
+bool HyperlinkEntityItem::setProperties(const EntityItemProperties& properties) {
+    bool somethingChanged = false;
+    somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
+
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(textColor, setTextColor);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundColor, setBackgroundColor);
+
+    if (somethingChanged) {
+        bool wantDebug = false;
+        if (wantDebug) {
+            uint64_t now = usecTimestampNow();
+            int elapsed = now - getLastEdited();
+            qCDebug(entities) << "HyperlinkEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
+                "now=" << now << " getLastEdited()=" << getLastEdited();
+        }
+        setLastEdited(properties._lastEdited);
+    }
+
+    return somethingChanged;
+}
+
+int HyperlinkEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
+    ReadBitstreamToTreeParams& args,
+    EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
+
+    int bytesRead = 0;
+    const unsigned char* dataAt = data;
+
+    READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
+    READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
+    READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight);
+    READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, rgbColor, setTextColor);
+    READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, rgbColor, setBackgroundColor);
+
+    return bytesRead;
+}
+
+
+// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
+EntityPropertyFlags HyperlinkEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
+    EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
+    requestedProperties += PROP_HREF;
+    requestedProperties += PROP_DESCRIPTION;
+    requestedProperties += PROP_LINE_HEIGHT;
+    requestedProperties += PROP_TEXT_COLOR;
+    requestedProperties += PROP_BACKGROUND_COLOR;
+    return requestedProperties;
+}
+
+void HyperlinkEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
+    EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
+    EntityPropertyFlags& requestedProperties,
+    EntityPropertyFlags& propertyFlags,
+    EntityPropertyFlags& propertiesDidntFit,
+    int& propertyCount,
+    OctreeElement::AppendState& appendState) const {
+
+    bool successPropertyFits = true;
+
+    APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
+    APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
+    APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight());
+    APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, getTextColor());
+    APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, getBackgroundColor());
+}
+
+
+bool HyperlinkEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
+    bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
+    void** intersectedObject, bool precisionPicking) const {
+
+    RayIntersectionInfo rayInfo;
+    rayInfo._rayStart = origin;
+    rayInfo._rayDirection = direction;
+    rayInfo._rayLength = std::numeric_limits<float>::max();
+
+    PlaneShape plane;
+
+    const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
+    glm::vec3 normal = _rotation * UNROTATED_NORMAL;
+    plane.setNormal(normal);
+    plane.setPoint(getPosition()); // the position is definitely a point on our plane
+
+    bool intersects = plane.findRayIntersection(rayInfo);
+
+    if (intersects) {
+        glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance);
+        // now we know the point the ray hit our plane
+
+        glm::mat4 rotation = glm::mat4_cast(getRotation());
+        glm::mat4 translation = glm::translate(getPosition());
+        glm::mat4 entityToWorldMatrix = translation * rotation;
+        glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
+
+        glm::vec3 dimensions = getDimensions();
+        glm::vec3 registrationPoint = getRegistrationPoint();
+        glm::vec3 corner = -(dimensions * registrationPoint);
+        AABox entityFrameBox(corner, dimensions);
+
+        glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f));
+
+        intersects = entityFrameBox.contains(entityFrameHitAt);
+    }
+
+    if (intersects) {
+        distance = rayInfo._hitDistance;
+    }
+    return intersects;
+}
diff --git a/libraries/entities/src/HyperlinkEntityItem.h b/libraries/entities/src/HyperlinkEntityItem.h
new file mode 100644
index 0000000000..2d522d6cfb
--- /dev/null
+++ b/libraries/entities/src/HyperlinkEntityItem.h
@@ -0,0 +1,95 @@
+//
+//  HyperlinkEntityItem.h
+//  libraries/entities/src
+//
+//  Created by Niraj Venkat on 6/8/15.
+//  Copyright 2013 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_HyperlinkEntityItem_h
+#define hifi_HyperlinkEntityItem_h
+
+#include "EntityItem.h" 
+
+class HyperlinkEntityItem : public EntityItem {
+public:
+    static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
+
+    HyperlinkEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
+
+    ALLOW_INSTANTIATION // This class can be instantiated
+
+        /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
+        virtual void setDimensions(const glm::vec3& value);
+    virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
+
+    // methods for getting/setting all properties of an entity
+    virtual EntityItemProperties getProperties() const;
+    virtual bool setProperties(const EntityItemProperties& properties);
+
+    // TODO: eventually only include properties changed since the params.lastViewFrustumSent time
+    virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
+
+    virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
+        EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
+        EntityPropertyFlags& requestedProperties,
+        EntityPropertyFlags& propertyFlags,
+        EntityPropertyFlags& propertiesDidntFit,
+        int& propertyCount,
+        OctreeElement::AppendState& appendState) const;
+
+    virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
+        ReadBitstreamToTreeParams& args,
+        EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
+
+    virtual bool supportsDetailedRayIntersection() const { return true; }
+    virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
+        bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
+        void** intersectedObject, bool precisionPicking) const;
+
+    static const QString DEFAULT_HREF;
+    void setHref(const QString& value) { _href = value; }
+    const QString& getHref() const { return _href; }
+
+    static const QString DEFAULT_DESCRIPTION;
+    void setDescription(const QString& value) { _description = value; }
+    const QString& getDescription() const { return _description; }
+
+    static const float DEFAULT_LINE_HEIGHT;
+    void setLineHeight(float value) { _lineHeight = value; }
+    float getLineHeight() const { return _lineHeight; }
+
+    static const xColor DEFAULT_TEXT_COLOR;
+    const rgbColor& getTextColor() const { return _textColor; }
+    xColor getTextColorX() const { xColor color = { _textColor[RED_INDEX], _textColor[GREEN_INDEX], _textColor[BLUE_INDEX] }; return color; }
+
+    void setTextColor(const rgbColor& value) { memcpy(_textColor, value, sizeof(_textColor)); }
+    void setTextColor(const xColor& value) {
+        _textColor[RED_INDEX] = value.red;
+        _textColor[GREEN_INDEX] = value.green;
+        _textColor[BLUE_INDEX] = value.blue;
+    }
+
+    static const xColor DEFAULT_BACKGROUND_COLOR;
+    const rgbColor& getBackgroundColor() const { return _backgroundColor; }
+    xColor getBackgroundColorX() const { xColor color = { _backgroundColor[RED_INDEX], _backgroundColor[GREEN_INDEX], _backgroundColor[BLUE_INDEX] }; return color; }
+
+    void setBackgroundColor(const rgbColor& value) { memcpy(_backgroundColor, value, sizeof(_backgroundColor)); }
+    void setBackgroundColor(const xColor& value) {
+        _backgroundColor[RED_INDEX] = value.red;
+        _backgroundColor[GREEN_INDEX] = value.green;
+        _backgroundColor[BLUE_INDEX] = value.blue;
+    }
+
+protected:
+    QString _href;
+    QString _description;
+    float _lineHeight;
+    rgbColor _textColor;
+    rgbColor _backgroundColor;
+};
+
+#endif // hifi_HyperlinkEntityItem_h

From 1e858d8bc5bbc3e135c5463dc3b38bb69ffb9c18 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Mon, 8 Jun 2015 14:16:03 -0700
Subject: [PATCH 06/88] start on spring action

---
 .../entities/src/EntityActionInterface.cpp    |  62 +++++++++-
 .../entities/src/EntityActionInterface.h      |   4 +-
 libraries/physics/src/ObjectActionSpring.cpp  | 107 ++++++++++++++++++
 libraries/physics/src/ObjectActionSpring.h    |  37 ++++++
 .../physics/src/PhysicalEntitySimulation.cpp  |   4 +
 5 files changed, 208 insertions(+), 6 deletions(-)
 create mode 100644 libraries/physics/src/ObjectActionSpring.cpp
 create mode 100644 libraries/physics/src/ObjectActionSpring.h

diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp
index f26dd006ff..d92771fce1 100644
--- a/libraries/entities/src/EntityActionInterface.cpp
+++ b/libraries/entities/src/EntityActionInterface.cpp
@@ -22,6 +22,9 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS
     if (normalizedActionTypeString == "pulltopoint") {
         return ACTION_TYPE_PULL_TO_POINT;
     }
+    if (normalizedActionTypeString == "spring") {
+        return ACTION_TYPE_SPRING;
+    }
 
     qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString;
     return ACTION_TYPE_NONE;
@@ -33,6 +36,8 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
             return "none";
         case ACTION_TYPE_PULL_TO_POINT:
             return "pullToPoint";
+        case ACTION_TYPE_SPRING:
+            return "spring";
     }
     assert(false);
     return "none";
@@ -43,21 +48,21 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian
     if (!arguments.contains(argumentName)) {
         qDebug() << objectName << "requires argument:" << argumentName;
         ok = false;
-        return vec3();
+        return glm::vec3();
     }
 
     QVariant resultV = arguments[argumentName];
     if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
         qDebug() << objectName << "argument" << argumentName << "must be a map";
         ok = false;
-        return vec3();
+        return glm::vec3();
     }
 
     QVariantMap resultVM = resultV.toMap();
     if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
         qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z";
         ok = false;
-        return vec3();
+        return glm::vec3();
     }
 
     QVariant xV = resultVM["x"];
@@ -73,13 +78,60 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian
     if (!xOk || !yOk || !zOk) {
         qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z and values of type float.";
         ok = false;
-        return vec3();
+        return glm::vec3();
     }
 
-    return vec3(x, y, z);
+    return glm::vec3(x, y, z);
 }
 
 
+glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
+                                                     QString argumentName, bool& ok) {
+    if (!arguments.contains(argumentName)) {
+        qDebug() << objectName << "requires argument:" << argumentName;
+        ok = false;
+        return glm::quat();
+    }
+
+    QVariant resultV = arguments[argumentName];
+    if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
+        qDebug() << objectName << "argument" << argumentName << "must be a map";
+        ok = false;
+        return glm::quat();
+    }
+
+    QVariantMap resultVM = resultV.toMap();
+    if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
+        qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z";
+        ok = false;
+        return glm::quat();
+    }
+
+    QVariant xV = resultVM["x"];
+    QVariant yV = resultVM["y"];
+    QVariant zV = resultVM["z"];
+    QVariant wV = resultVM["w"];
+
+    bool xOk = true;
+    bool yOk = true;
+    bool zOk = true;
+    bool wOk = true;
+    float x = xV.toFloat(&xOk);
+    float y = yV.toFloat(&yOk);
+    float z = zV.toFloat(&zOk);
+    float w = wV.toFloat(&wOk);
+    if (!xOk || !yOk || !zOk || !wOk) {
+        qDebug() << objectName << "argument" << argumentName
+                 << "must be a map with keys of x, y, z, w and values of type float.";
+        ok = false;
+        return glm::quat();
+    }
+
+    return glm::quat(x, y, z, w);
+}
+
+
+
 float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
                                                   QString argumentName, bool& ok) {
     if (!arguments.contains(argumentName)) {
diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h
index 74efae3239..3baee06c3e 100644
--- a/libraries/entities/src/EntityActionInterface.h
+++ b/libraries/entities/src/EntityActionInterface.h
@@ -19,7 +19,8 @@ class EntitySimulation;
 enum EntityActionType {
     // keep these synchronized with actionTypeFromString and actionTypeToString
     ACTION_TYPE_NONE,
-    ACTION_TYPE_PULL_TO_POINT
+    ACTION_TYPE_PULL_TO_POINT,
+    ACTION_TYPE_SPRING
 };
 
 
@@ -41,6 +42,7 @@ public:
 protected:
 
     static glm::vec3 extractVec3Argument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
+    static glm::quat extractQuatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
     static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
 };
 
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
new file mode 100644
index 0000000000..8a436096a8
--- /dev/null
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -0,0 +1,107 @@
+//
+//  ObjectActionSpring.cpp
+//  libraries/physics/src
+//
+//  Created by Seth Alves 2015-6-5
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "ObjectMotionState.h"
+#include "BulletUtil.h"
+
+#include "ObjectActionSpring.h"
+
+ObjectActionSpring::ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity) :
+    ObjectAction(id, ownerEntity) {
+    #if WANT_DEBUG
+    qDebug() << "ObjectActionSpring::ObjectActionSpring";
+    #endif
+}
+
+ObjectActionSpring::~ObjectActionSpring() {
+    #if WANT_DEBUG
+    qDebug() << "ObjectActionSpring::~ObjectActionSpring";
+    #endif
+}
+
+void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
+    if (!tryLockForRead()) {
+        // don't risk hanging the thread running the physics simulation
+        return;
+    }
+    void* physicsInfo = _ownerEntity->getPhysicsInfo();
+
+    if (_active && physicsInfo) {
+        ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
+        btRigidBody* rigidBody = motionState->getRigidBody();
+        if (rigidBody) {
+            glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition());
+
+
+            // btQuaternion getOrientation() const;
+            // const btTransform& getCenterOfMassTransform() const;
+
+            float offsetLength = glm::length(offset);
+            float speed = offsetLength; // XXX use _positionalSpringConstant
+
+            float interpolation_value = 0.5; // XXX
+            const glm::quat slerped_quat = glm::slerp(bulletToGLM(rigidBody->getOrientation()),
+                                                      _rotationalTarget,
+                                                      interpolation_value);
+
+            if (offsetLength > IGNORE_POSITION_DELTA) {
+                glm::vec3 newVelocity = glm::normalize(offset) * speed;
+                rigidBody->setLinearVelocity(glmToBullet(newVelocity));
+                // void setAngularVelocity (const btVector3 &ang_vel);
+                rigidBody->activate();
+            } else {
+                rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
+            }
+        }
+    }
+    unlock();
+}
+
+
+bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
+    // targets are required, spring-constants are optional
+    bool ok = true;
+    glm::vec3 positionalTarget =
+        EntityActionInterface::extractVec3Argument("spring action", arguments, "positionalTarget", ok);
+    bool pscOK = true;
+    float positionalSpringConstant =
+        EntityActionInterface::extractFloatArgument("spring action", arguments, "positionalSpringConstant", pscOK);
+
+    glm::quat rotationalTarget =
+        EntityActionInterface::extractQuatArgument("spring action", arguments, "rotationalTarget", ok);
+    bool rscOK = true;
+    float rotationalSpringConstant =
+        EntityActionInterface::extractFloatArgument("spring action", arguments, "rotationalSpringConstant", rscOK);
+
+    if (!ok) {
+        return false;
+    }
+
+    lockForWrite();
+
+    _positionalTarget = positionalTarget;
+    if (pscOK) {
+        _positionalSpringConstant = positionalSpringConstant;
+    } else {
+        _positionalSpringConstant = 0.5; // XXX pick a good default;
+    }
+
+    _rotationalTarget = rotationalTarget;
+    if (rscOK) {
+        _rotationalSpringConstant = rotationalSpringConstant;
+    } else {
+        _rotationalSpringConstant = 0.5; // XXX pick a good default;
+    }
+
+    _active = true;
+    unlock();
+    return true;
+}
diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h
new file mode 100644
index 0000000000..77502e8544
--- /dev/null
+++ b/libraries/physics/src/ObjectActionSpring.h
@@ -0,0 +1,37 @@
+//
+//  ObjectActionSpring.h
+//  libraries/physics/src
+//
+//  Created by Seth Alves 2015-6-5
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_ObjectActionSpring_h
+#define hifi_ObjectActionSpring_h
+
+#include <QUuid>
+
+#include <EntityItem.h>
+#include "ObjectAction.h"
+
+class ObjectActionSpring : public ObjectAction {
+public:
+    ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity);
+    virtual ~ObjectActionSpring();
+
+    virtual bool updateArguments(QVariantMap arguments);
+    virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
+
+private:
+
+    glm::vec3 _positionalTarget;
+    float _positionalSpringConstant;
+
+    glm::quat _rotationalTarget;
+    float _rotationalSpringConstant;
+};
+
+#endif // hifi_ObjectActionSpring_h
diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index 711c5e49da..56d497f8a1 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -13,6 +13,7 @@
 #include "PhysicsLogging.h"
 #include "ShapeManager.h"
 #include "ObjectActionPullToPoint.h"
+#include "ObjectActionSpring.h"
 
 #include "PhysicalEntitySimulation.h"
 
@@ -245,6 +246,9 @@ EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType typ
         case ACTION_TYPE_PULL_TO_POINT:
             action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity);
             break;
+        case ACTION_TYPE_SPRING:
+            action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
+            break;
     }
 
     bool ok = action->updateArguments(arguments);

From 63665d720a8c83f3b9ce35de6407b415c30a1005 Mon Sep 17 00:00:00 2001
From: Niraj Venkat <venkatn93@gmail.com>
Date: Mon, 8 Jun 2015 15:32:21 -0700
Subject: [PATCH 07/88] Fixed bug by adding hyperlink props to flags. Safe to
 merge

---
 libraries/entities/src/EntityPropertyFlags.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h
index 8eb09fece0..f1ebdb8a1f 100644
--- a/libraries/entities/src/EntityPropertyFlags.h
+++ b/libraries/entities/src/EntityPropertyFlags.h
@@ -117,6 +117,10 @@ enum EntityPropertyList {
     //for lines
     PROP_LINE_WIDTH,
     PROP_LINE_POINTS,
+
+    // used by hyperlinks
+    PROP_HREF,
+    PROP_DESCRIPTION,
     
     ////////////////////////////////////////////////////////////////////////////////////////////////////
     // ATTENTION: add new properties ABOVE this line

From dbb447c9b5a4ea60263f40955aa782034c0b567a Mon Sep 17 00:00:00 2001
From: Niraj Venkat <venkatn93@gmail.com>
Date: Mon, 8 Jun 2015 16:09:06 -0700
Subject: [PATCH 08/88] Exposing Hyperlink type to javascript

---
 libraries/entities/src/EntityTypes.cpp | 2 ++
 libraries/entities/src/EntityTypes.h   | 3 ++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp
index a41cc22d2e..5689cfd11a 100644
--- a/libraries/entities/src/EntityTypes.cpp
+++ b/libraries/entities/src/EntityTypes.cpp
@@ -28,6 +28,7 @@
 #include "ZoneEntityItem.h"
 #include "LineEntityItem.h"
 #include "PolyVoxEntityItem.h"
+#include "HyperlinkEntityItem.h"
 
 QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
 QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
@@ -47,6 +48,7 @@ REGISTER_ENTITY_TYPE(ParticleEffect)
 REGISTER_ENTITY_TYPE(Zone)
 REGISTER_ENTITY_TYPE(Line)
 REGISTER_ENTITY_TYPE(PolyVox)
+REGISTER_ENTITY_TYPE(Hyperlink)
 
 const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
     QMap<EntityType, QString>::iterator matchedTypeName = _typeToNameMap.find(entityType);
diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h
index 323a4eb92b..89bd57b253 100644
--- a/libraries/entities/src/EntityTypes.h
+++ b/libraries/entities/src/EntityTypes.h
@@ -46,7 +46,8 @@ public:
         Web,
         Line,
         PolyVox,
-        LAST = PolyVox
+        Hyperlink,
+        LAST = Hyperlink
     } EntityType;
 
     static const QString& getEntityTypeName(EntityType entityType);

From b3bc9c3ef05d871b5b6dfc0cc0b2378b2192636e Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Mon, 8 Jun 2015 17:10:13 -0700
Subject: [PATCH 09/88] first attempt at getting spring-action to handle
 rotation

---
 .../entities/src/EntityActionInterface.cpp    | 20 ++--
 .../entities/src/EntityActionInterface.h      |  9 +-
 libraries/physics/src/ObjectActionSpring.cpp  | 96 +++++++++++--------
 libraries/physics/src/ObjectActionSpring.h    |  6 +-
 4 files changed, 80 insertions(+), 51 deletions(-)

diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp
index d92771fce1..b3e774df96 100644
--- a/libraries/entities/src/EntityActionInterface.cpp
+++ b/libraries/entities/src/EntityActionInterface.cpp
@@ -44,9 +44,11 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
 }
 
 glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments,
-                                                     QString argumentName, bool& ok) {
+                                                     QString argumentName, bool& ok, bool required) {
     if (!arguments.contains(argumentName)) {
-        qDebug() << objectName << "requires argument:" << argumentName;
+        if (required) {
+            qDebug() << objectName << "requires argument:" << argumentName;
+        }
         ok = false;
         return glm::vec3();
     }
@@ -86,16 +88,18 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian
 
 
 glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
-                                                     QString argumentName, bool& ok) {
+                                                     QString argumentName, bool& ok, bool required) {
     if (!arguments.contains(argumentName)) {
-        qDebug() << objectName << "requires argument:" << argumentName;
+        if (required) {
+            qDebug() << objectName << "requires argument:" << argumentName;
+        }
         ok = false;
         return glm::quat();
     }
 
     QVariant resultV = arguments[argumentName];
     if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
-        qDebug() << objectName << "argument" << argumentName << "must be a map";
+        qDebug() << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName();
         ok = false;
         return glm::quat();
     }
@@ -133,9 +137,11 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian
 
 
 float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
-                                                  QString argumentName, bool& ok) {
+                                                  QString argumentName, bool& ok, bool required) {
     if (!arguments.contains(argumentName)) {
-        qDebug() << objectName << "requires argument:" << argumentName;
+        if (required) {
+            qDebug() << objectName << "requires argument:" << argumentName;
+        }
         ok = false;
         return 0.0f;
     }
diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h
index 3baee06c3e..a0a3db9b68 100644
--- a/libraries/entities/src/EntityActionInterface.h
+++ b/libraries/entities/src/EntityActionInterface.h
@@ -41,9 +41,12 @@ public:
 
 protected:
 
-    static glm::vec3 extractVec3Argument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
-    static glm::quat extractQuatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
-    static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
+    static glm::vec3 extractVec3Argument (QString objectName, QVariantMap arguments,
+                                          QString argumentName, bool& ok, bool required = true);
+    static glm::quat extractQuatArgument (QString objectName, QVariantMap arguments,
+                                          QString argumentName, bool& ok, bool required = true);
+    static float extractFloatArgument(QString objectName, QVariantMap arguments,
+                                      QString argumentName, bool& ok, bool required = true);
 };
 
 typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
index 8a436096a8..665ec352fe 100644
--- a/libraries/physics/src/ObjectActionSpring.cpp
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -38,27 +38,30 @@ void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar
         ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
         btRigidBody* rigidBody = motionState->getRigidBody();
         if (rigidBody) {
-            glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition());
+            // handle the linear part
+            if (_positionalTargetSet) {
+                glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition());
+                float offsetLength = glm::length(offset);
+                float speed = offsetLength / _linearTimeScale;
 
+                if (offsetLength > IGNORE_POSITION_DELTA) {
+                    glm::vec3 newVelocity = glm::normalize(offset) * speed;
+                    rigidBody->setLinearVelocity(glmToBullet(newVelocity));
+                    // void setAngularVelocity (const btVector3 &ang_vel);
+                    rigidBody->activate();
+                } else {
+                    rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
+                }
+            }
 
-            // btQuaternion getOrientation() const;
-            // const btTransform& getCenterOfMassTransform() const;
-
-            float offsetLength = glm::length(offset);
-            float speed = offsetLength; // XXX use _positionalSpringConstant
-
-            float interpolation_value = 0.5; // XXX
-            const glm::quat slerped_quat = glm::slerp(bulletToGLM(rigidBody->getOrientation()),
-                                                      _rotationalTarget,
-                                                      interpolation_value);
-
-            if (offsetLength > IGNORE_POSITION_DELTA) {
-                glm::vec3 newVelocity = glm::normalize(offset) * speed;
-                rigidBody->setLinearVelocity(glmToBullet(newVelocity));
-                // void setAngularVelocity (const btVector3 &ang_vel);
-                rigidBody->activate();
-            } else {
-                rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
+            // handle rotation
+            if (_rotationalTargetSet) {
+                glm::quat qZeroInverse = glm::inverse(bulletToGLM(rigidBody->getOrientation()));
+                glm::quat deltaQ = _rotationalTarget * qZeroInverse;
+                glm::vec3 axis = glm::axis(deltaQ);
+                float angle = glm::angle(deltaQ);
+                glm::vec3 newAngularVelocity = (-angle / _angularTimeScale) * glm::normalize(axis);
+                rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity));
             }
         }
     }
@@ -68,37 +71,52 @@ void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar
 
 bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
     // targets are required, spring-constants are optional
-    bool ok = true;
+    bool ptOk = true;
     glm::vec3 positionalTarget =
-        EntityActionInterface::extractVec3Argument("spring action", arguments, "positionalTarget", ok);
-    bool pscOK = true;
-    float positionalSpringConstant =
-        EntityActionInterface::extractFloatArgument("spring action", arguments, "positionalSpringConstant", pscOK);
+        EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false);
+    bool pscOk = true;
+    float linearTimeScale =
+        EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", pscOk, false);
+    if (ptOk && pscOk && linearTimeScale <= 0.0f) {
+        qDebug() << "spring action -- linearTimeScale must be greater than zero.";
+        return false;
+    }
 
+    bool rtOk = true;
     glm::quat rotationalTarget =
-        EntityActionInterface::extractQuatArgument("spring action", arguments, "rotationalTarget", ok);
-    bool rscOK = true;
-    float rotationalSpringConstant =
-        EntityActionInterface::extractFloatArgument("spring action", arguments, "rotationalSpringConstant", rscOK);
+        EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false);
+    bool rscOk = true;
+    float angularTimeScale =
+        EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", rscOk, false);
 
-    if (!ok) {
+    if (!ptOk && !rtOk) {
+        qDebug() << "spring action requires either targetPosition or targetRotation argument";
         return false;
     }
 
     lockForWrite();
 
-    _positionalTarget = positionalTarget;
-    if (pscOK) {
-        _positionalSpringConstant = positionalSpringConstant;
-    } else {
-        _positionalSpringConstant = 0.5; // XXX pick a good default;
+    _positionalTargetSet = _rotationalTargetSet = false;
+
+    if (ptOk) {
+        _positionalTarget = positionalTarget;
+        _positionalTargetSet = true;
+
+        if (pscOk) {
+            _linearTimeScale = linearTimeScale;
+        } else {
+            _linearTimeScale = 0.1;
+        }
     }
 
-    _rotationalTarget = rotationalTarget;
-    if (rscOK) {
-        _rotationalSpringConstant = rotationalSpringConstant;
-    } else {
-        _rotationalSpringConstant = 0.5; // XXX pick a good default;
+    if (rtOk) {
+        _rotationalTarget = rotationalTarget;
+
+        if (rscOk) {
+            _angularTimeScale = angularTimeScale;
+        } else {
+            _angularTimeScale = 0.1;
+        }
     }
 
     _active = true;
diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h
index 77502e8544..b211259866 100644
--- a/libraries/physics/src/ObjectActionSpring.h
+++ b/libraries/physics/src/ObjectActionSpring.h
@@ -28,10 +28,12 @@ public:
 private:
 
     glm::vec3 _positionalTarget;
-    float _positionalSpringConstant;
+    float _linearTimeScale;
+    bool _positionalTargetSet;
 
     glm::quat _rotationalTarget;
-    float _rotationalSpringConstant;
+    float _angularTimeScale;
+    bool _rotationalTargetSet;
 };
 
 #endif // hifi_ObjectActionSpring_h

From 14a45e83492e411b66726d31fd8287b46e68b1b9 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Mon, 8 Jun 2015 18:25:58 -0700
Subject: [PATCH 10/88] convert grab.js to use spring action.  rotation doesn't
 work right, yet

---
 examples/grab.js                              | 95 +++++++++++--------
 .../physics/src/ObjectActionPullToPoint.cpp   |  4 +
 libraries/physics/src/ObjectActionSpring.cpp  |  5 +
 3 files changed, 65 insertions(+), 39 deletions(-)

diff --git a/examples/grab.js b/examples/grab.js
index 306af86c68..d04e46b17b 100644
--- a/examples/grab.js
+++ b/examples/grab.js
@@ -4,7 +4,7 @@
 //  Created by Eric Levin on May 1, 2015
 //  Copyright 2015 High Fidelity, Inc.
 //
-//  Grab's physically moveable entities with the mouse, by applying a spring force. 
+//  Grab's physically moveable entities with the mouse, by applying a spring force.
 //
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -20,6 +20,7 @@ var ANGULAR_DAMPING_RATE = 0.40;
 // NOTE: to improve readability global variable names start with 'g'
 var gIsGrabbing = false;
 var gGrabbedEntity = null;
+var gActionID = null;
 var gPrevMouse = {x: 0, y: 0};
 var gEntityProperties;
 var gStartPosition;
@@ -31,20 +32,20 @@ var gPlaneNormal = ZERO_VEC3;
 // gMaxGrabDistance is a function of the size of the object.
 var gMaxGrabDistance;
 
-// gGrabMode defines the degrees of freedom of the grab target positions 
-// relative to gGrabStartPosition options include: 
+// gGrabMode defines the degrees of freedom of the grab target positions
+// relative to gGrabStartPosition options include:
 //     xzPlane  (default)
 //     verticalCylinder  (SHIFT)
 //     rotate  (CONTROL)
 // Modes to eventually support?:
-//     xyPlane 
-//     yzPlane 
+//     xyPlane
+//     yzPlane
 //     polar
 //     elevationAzimuth
-var gGrabMode = "xzplane"; 
+var gGrabMode = "xzplane";
 
-// gGrabOffset allows the user to grab an object off-center.  It points from ray's intersection 
-// with the move-plane to object center (at the moment the grab is initiated).  Future target positions 
+// gGrabOffset allows the user to grab an object off-center.  It points from ray's intersection
+// with the move-plane to object center (at the moment the grab is initiated).  Future target positions
 // are relative to the ray's intersection by the same offset.
 var gGrabOffset = { x: 0, y: 0, z: 0 };
 
@@ -162,7 +163,7 @@ function computeNewGrabPlane() {
     var xzOffset = Vec3.subtract(gPointOnPlane, Camera.getPosition());
     xzOffset.y = 0;
     gXzDistanceToGrab = Vec3.length(xzOffset);
-    
+
     if (gGrabMode !== "rotate" && maybeResetMousePosition) {
         // we reset the mouse position whenever we stop rotating
         Window.setCursorPosition(gMouseAtRotateStart.x, gMouseAtRotateStart.y);
@@ -193,6 +194,7 @@ function mousePressEvent(event) {
     var cameraPosition = Camera.getPosition();
 
     gBeaconHeight = Vec3.length(entityProperties.dimensions);
+    print("gBeaconHeight = " + gBeaconHeight);
     gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE;
     if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) {
         // don't allow grabs of things far away
@@ -231,6 +233,8 @@ function mouseReleaseEvent() {
         }
 
         gIsGrabbing = false
+        Entities.deleteAction(grabbedEntity, gActionID);
+        gActionID = null;
 
         Overlays.editOverlay(gBeacon, { visible: false });
 
@@ -250,6 +254,8 @@ function mouseMoveEvent(event) {
         gOriginalGravity = entityProperties.gravity;
     }
 
+    var actionArgs;
+
     if (gGrabMode === "rotate") {
         var deltaMouse = { x: 0, y: 0 };
         var dx = event.x - gPreviousMouse.x;
@@ -259,9 +265,12 @@ function mouseMoveEvent(event) {
         var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation));
         dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation)));
         var axis = Vec3.cross(dragOffset, Quat.getFront(orientation));
-        var axis = Vec3.normalize(axis);
-        var ROTATE_STRENGTH = 8.0; // magic number tuned by hand
-        gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis);
+        // var axis = Vec3.normalize(axis);
+        // var ROTATE_STRENGTH = 8.0; // magic number tuned by hand
+        // gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis);
+
+        var targetRotation = Quat.angleAxis(Vec3.length(axis), Vec3.normalize(axis));
+        actionArgs = {targetRotation: targetRotation, angularTimeScale: 1.0};
     } else {
         var newTargetPosition;
         if (gGrabMode === "verticalCylinder") {
@@ -284,9 +293,18 @@ function mouseMoveEvent(event) {
             }
         }
         gTargetPosition = Vec3.sum(newTargetPosition, gGrabOffset);
+        actionArgs = {targetPosition: gTargetPosition, linearTimeScale: 0.1};
     }
     gPreviousMouse = { x: event.x, y: event.y };
     gMouseCursorLocation = { x: Window.getCursorPositionX(), y: Window.getCursorPositionY() };
+
+    if (!gActionID) {
+        gActionID = Entities.addAction("spring", gGrabbedEntity, actionArgs);
+    } else {
+        Entities.updateAction(gGrabbedEntity, gActionID, actionArgs);
+    }
+
+    updateDropLine(gTargetPosition);
 }
 
 function keyReleaseEvent(event) {
@@ -309,38 +327,37 @@ function keyPressEvent(event) {
     computeNewGrabPlane();
 }
 
-function update(deltaTime) {
-    if (!gIsGrabbing) {
-        return;
-    }
+// function update(deltaTime) {
+//     if (!gIsGrabbing) {
+//         return;
+//     }
 
-    var entityProperties = Entities.getEntityProperties(gGrabbedEntity);
-    gCurrentPosition = entityProperties.position;
-    if (gGrabMode === "rotate") {
-        gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE));
-        Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, });
-    } 
+//     var entityProperties = Entities.getEntityProperties(gGrabbedEntity);
+//     gCurrentPosition = entityProperties.position;
+//     if (gGrabMode === "rotate") {
+//         gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE));
+//         Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, });
+//     }
 
-    // always push toward linear grab position, even when rotating
-    var newVelocity = ZERO_VEC3;
-    var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition);
-    var delta = Vec3.length(dPosition);
-    if (delta > CLOSE_ENOUGH) {
-        var MAX_POSITION_DELTA = 4.0;
-        if (delta > MAX_POSITION_DELTA) {
-            dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta);
-        }
-        // desired speed is proportional to displacement by the inverse of timescale
-        // (for critically damped motion)
-        newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE);
-    }
-    Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, });
-    updateDropLine(gTargetPosition);
-}
+//     // always push toward linear grab position, even when rotating
+//     var newVelocity = ZERO_VEC3;
+//     var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition);
+//     var delta = Vec3.length(dPosition);
+//     if (delta > CLOSE_ENOUGH) {
+//         var MAX_POSITION_DELTA = 4.0;
+//         if (delta > MAX_POSITION_DELTA) {
+//             dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta);
+//         }
+//         // desired speed is proportional to displacement by the inverse of timescale
+//         // (for critically damped motion)
+//         newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE);
+//     }
+//     Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, });
+// }
 
 Controller.mouseMoveEvent.connect(mouseMoveEvent);
 Controller.mousePressEvent.connect(mousePressEvent);
 Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
 Controller.keyPressEvent.connect(keyPressEvent);
 Controller.keyReleaseEvent.connect(keyReleaseEvent);
-Script.update.connect(update);
+// Script.update.connect(update);
diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp
index 78f202a24f..28c0e08bd9 100644
--- a/libraries/physics/src/ObjectActionPullToPoint.cpp
+++ b/libraries/physics/src/ObjectActionPullToPoint.cpp
@@ -28,6 +28,10 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() {
 }
 
 void ObjectActionPullToPoint::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
+    if (!_ownerEntity) {
+        qDebug() << "ObjectActionPullToPoint::updateAction no owner entity";
+        return;
+    }
     if (!tryLockForRead()) {
         // don't risk hanging the thread running the physics simulation
         return;
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
index 665ec352fe..f8b8d92ad5 100644
--- a/libraries/physics/src/ObjectActionSpring.cpp
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -28,6 +28,10 @@ ObjectActionSpring::~ObjectActionSpring() {
 }
 
 void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
+    if (!_ownerEntity) {
+        qDebug() << "ObjectActionSpring::updateAction no owner entity";
+        return;
+    }
     if (!tryLockForRead()) {
         // don't risk hanging the thread running the physics simulation
         return;
@@ -111,6 +115,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
 
     if (rtOk) {
         _rotationalTarget = rotationalTarget;
+        _rotationalTargetSet = true;
 
         if (rscOk) {
             _angularTimeScale = angularTimeScale;

From 2fb64aad91cab4a1ec3089a88cc6abed90be79b0 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Mon, 8 Jun 2015 20:31:35 -0700
Subject: [PATCH 11/88] fix variable name

---
 examples/grab.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/grab.js b/examples/grab.js
index d04e46b17b..f2c74bb252 100644
--- a/examples/grab.js
+++ b/examples/grab.js
@@ -233,7 +233,7 @@ function mouseReleaseEvent() {
         }
 
         gIsGrabbing = false
-        Entities.deleteAction(grabbedEntity, gActionID);
+        Entities.deleteAction(gGrabbedEntity, gActionID);
         gActionID = null;
 
         Overlays.editOverlay(gBeacon, { visible: false });

From 873f73ffb4929c5fa44a7574e9777d0c629e9abf Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Tue, 9 Jun 2015 00:39:49 -0700
Subject: [PATCH 12/88] Working on cursor manager

---
 libraries/ui/src/CursorManager.cpp | 32 ++++++++++++++++++++++++++++++
 libraries/ui/src/CursorManager.h   | 32 ++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+)
 create mode 100644 libraries/ui/src/CursorManager.cpp
 create mode 100644 libraries/ui/src/CursorManager.h

diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp
new file mode 100644
index 0000000000..8b318d549a
--- /dev/null
+++ b/libraries/ui/src/CursorManager.cpp
@@ -0,0 +1,32 @@
+//
+//  Created by Bradley Austin Davis on 2015/06/08
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "CursorManager.h"
+
+namespace Cursor {
+    enum class Source {
+        MOUSE,
+        LEFT_HAND,
+        RIGHT_HAND,
+        UNKNOWN,
+    };
+
+    class Instance {
+        Source type;
+    };
+
+    class Manager {
+    public:
+        static Manager& instance();
+
+        uint8_t getCount();
+        Instance
+    };
+}
+
+
diff --git a/libraries/ui/src/CursorManager.h b/libraries/ui/src/CursorManager.h
new file mode 100644
index 0000000000..a2d26efc58
--- /dev/null
+++ b/libraries/ui/src/CursorManager.h
@@ -0,0 +1,32 @@
+//
+//  Created by Bradley Austin Davis on 2015/06/08
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#pragma once
+
+namespace Cursor {
+    enum class Source {
+        MOUSE,
+        LEFT_HAND,
+        RIGHT_HAND,
+        UNKNOWN,
+    };
+
+    class Instance {
+        Source type;
+    };
+
+    class Manager {
+    public:
+        static Manager& instance();
+
+        uint8_t getCount();
+        Instance
+    };
+}
+
+

From 9e3ce344fbdd9b5419d14404bb3945c379aba40d Mon Sep 17 00:00:00 2001
From: David Rowe <david@ctrlaltstudio.com>
Date: Tue, 9 Jun 2015 07:26:18 -0700
Subject: [PATCH 13/88] Display directory button just above edit toobar

---
 examples/directory.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/examples/directory.js b/examples/directory.js
index 965abf1453..03d352e024 100644
--- a/examples/directory.js
+++ b/examples/directory.js
@@ -21,12 +21,13 @@ var directory = (function () {
         BUTTON_ALPHA = 0.9,
         BUTTON_MARGIN = 8,
         directoryButton,
+        EDIT_TOOLBAR_BUTTONS = 10,  // Number of buttons in edit.js toolbar
         viewport;
 
     function updateButtonPosition() {
         Overlays.editOverlay(directoryButton, {
             x: viewport.x - BUTTON_WIDTH - BUTTON_MARGIN,
-            y: BUTTON_MARGIN
+            y: (viewport.y - (EDIT_TOOLBAR_BUTTONS + 1) * (BUTTON_HEIGHT + BUTTON_MARGIN) - BUTTON_MARGIN) / 2 - 1
         });
     }
 

From b1b2d1f85cf54f9d0bf65a6674bb4c4c3fdc0fb6 Mon Sep 17 00:00:00 2001
From: Sam Gondelman <samuel_gondelman@brown.edu>
Date: Tue, 9 Jun 2015 09:45:19 -0700
Subject: [PATCH 14/88] ;) (added semicolons to js example)

---
 .../scripts/controllerScriptingExamples.js    | 64 ++++++++++---------
 1 file changed, 33 insertions(+), 31 deletions(-)

diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js
index 3e693c42ea..26a1999bbb 100644
--- a/examples/example/scripts/controllerScriptingExamples.js
+++ b/examples/example/scripts/controllerScriptingExamples.js
@@ -9,86 +9,88 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+// Assumes you only have the default keyboard connected
+
 // Resets every device to its default key bindings:
-Controller.resetAllDeviceBindings()
+Controller.resetAllDeviceBindings();
 
 // Query all actions
-print("All Actions: \n" + Controller.getAllActions())
+print("All Actions: \n" + Controller.getAllActions());
 
 // Each action stores:
 // action: int representation of enum
-print("Action 5 int: \n" + Controller.getAllActions()[5].action)
+print("Action 5 int: \n" + Controller.getAllActions()[5].action);
 
 // actionName: string representation of enum
-print("Action 5 name: \n" + Controller.getAllActions()[5].actionName)
+print("Action 5 name: \n" + Controller.getAllActions()[5].actionName);
 
 // inputChannels: list of all inputchannels that control that action
-print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n")
+print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n");
 
 
 // Each input channel stores:
 // action: Action that this InputChannel maps to
-print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action)
+print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action);
 
 // scale: sensitivity of input
-print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale)
+print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale);
 
 // input and modifier: Inputs
-print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n")
+print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n");
 
 
 // Each Input stores:
 // device: device of input
-print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device)
+print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device);
 
 // channel: channel of input
-print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel)
+print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel);
 
 // type: type of input (Unknown, Button, Axis, Joint)
-print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type)
+print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type);
 
 // id: id of input
-print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n")
+print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n");
 
 
 // You can get the name of a device from its id
-print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id))
+print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id));
 
 // You can also get all of a devices input channels
-print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n")
+print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n");
 
 
 // Modifying properties:
 // The following code will switch the "w" and "s" key functionality and adjust their scales
-var s = Controller.getAllActions()[0].inputChannels[0]
-var w = Controller.getAllActions()[1].inputChannels[0]
+var s = Controller.getAllActions()[0].inputChannels[0];
+var w = Controller.getAllActions()[1].inputChannels[0];
 
 // You must remove an input controller before modifying it so the old input controller isn't registered anymore
 // removeInputChannel and addInputChannel return true if successful, false otherwise
-Controller.removeInputChannel(s)
-Controller.removeInputChannel(w)
-print(s.scale)
-s.action = 1
-s.scale = .01
+Controller.removeInputChannel(s);
+Controller.removeInputChannel(w);
+print(s.scale);
+s.action = 1;
+s.scale = .01;
 
-w.action = 0
-w.scale = 10000
-Controller.addInputChannel(s)
-Controller.addInputChannel(w)
-print(s.scale)
+w.action = 0;
+w.scale = 10000;
+Controller.addInputChannel(s);
+Controller.addInputChannel(w);
+print(s.scale);
 
 // You can get all the available inputs for any device
 // Each AvailableInput has:
 // input: the Input itself
 // inputName: string representing the input
-var availableInputs = Controller.getAvailableInputs(1)
+var availableInputs = Controller.getAvailableInputs(1);
 for (i = 0; i < availableInputs.length; i++) {
 	print(availableInputs[i].inputName);
 }
 
 // You can modify key bindings by using these avaiable inputs
 // This will replace e (up) with 6
-var e = Controller.getAllActions()[5].inputChannels[0]
-Controller.removeInputChannel(e)
-e.input = availableInputs[6].input
-Controller.addInputChannel(e)
\ No newline at end of file
+var e = Controller.getAllActions()[5].inputChannels[0];
+Controller.removeInputChannel(e);
+e.input = availableInputs[6].input;
+Controller.addInputChannel(e);
\ No newline at end of file

From d3a4eec5c5a935bfacd56221672829f246c18e2f Mon Sep 17 00:00:00 2001
From: Sam Gondelman <samuel_gondelman@brown.edu>
Date: Tue, 9 Jun 2015 09:56:40 -0700
Subject: [PATCH 15/88] removed dead code, magic numbers

---
 interface/src/devices/KeyboardMouseDevice.cpp | 8 ++++----
 interface/src/ui/UserInputMapper.h            | 1 -
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp
index a7e85d28e1..d0f04e5636 100755
--- a/interface/src/devices/KeyboardMouseDevice.cpp
+++ b/interface/src/devices/KeyboardMouseDevice.cpp
@@ -164,11 +164,11 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
     proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); };
     proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
         QVector<UserInputMapper::InputPair> availableInputs;
-        for (int i = 0; i <= 9; i++) {
-            availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(48 + i)), QKeySequence(Qt::Key(48 + i)).toString()));
+        for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) {
+            availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
         }
-        for (int i = 0; i < 26; i++) {
-            availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(65 + i)), QKeySequence(Qt::Key(65 + i)).toString()));
+        for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) {
+            availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
         }
         availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
         return availableInputs;
diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h
index 34188ae3f5..9019a1de48 100755
--- a/interface/src/ui/UserInputMapper.h
+++ b/interface/src/ui/UserInputMapper.h
@@ -142,7 +142,6 @@ public:
     QVector<Action> getAllActions();
     QString getActionName(Action action) { return UserInputMapper::_actionNames[(int) action]; }
     float getActionState(Action action) const { return _actionStates[action]; }
-//    QVector<InputChannel>
     void assignDefaulActionScales();
 
     // Add input channel to the mapper and check that all the used channels are registered.

From 0769593ca89c74a0b84f38e8066e2b43cfa5e42f Mon Sep 17 00:00:00 2001
From: Sam Gondelman <samuel_gondelman@brown.edu>
Date: Tue, 9 Jun 2015 10:20:36 -0700
Subject: [PATCH 16/88] added getters/setters for Inputs and InputChannels

---
 .../ControllerScriptingInterface.cpp          | 30 +++++++++++--------
 interface/src/ui/UserInputMapper.h            | 17 ++++++++++-
 2 files changed, 33 insertions(+), 14 deletions(-)

diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp
index fe2eda12df..2c747195a7 100644
--- a/interface/src/scripting/ControllerScriptingInterface.cpp
+++ b/interface/src/scripting/ControllerScriptingInterface.cpp
@@ -46,32 +46,36 @@ QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::In
     QScriptValue obj = engine->newObject();
     obj.setProperty("device", input.getDevice());
     obj.setProperty("channel", input.getChannel());
-    obj.setProperty("type", input._type);
+    obj.setProperty("type", (unsigned short) input.getType());
     obj.setProperty("id", input.getID());
     return obj;
 }
 
 void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) {
-    input._device = object.property("device").toUInt16();
-    input._channel = object.property("channel").toUInt16();
-    input._type = object.property("type").toUInt16();
-    input._id = object.property("id").toInt32();
+    input.setDevice(object.property("device").toUInt16());
+    input.setChannel(object.property("channel").toUInt16());
+    input.setType(object.property("type").toUInt16());
+    input.setID(object.property("id").toInt32());
 }
 
 QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) {
     QScriptValue obj = engine->newObject();
-    obj.setProperty("input", inputToScriptValue(engine, inputChannel._input));
-    obj.setProperty("modifier", inputToScriptValue(engine, inputChannel._modifier));
-    obj.setProperty("action", inputChannel._action);
-    obj.setProperty("scale", inputChannel._scale);
+    obj.setProperty("input", inputToScriptValue(engine, inputChannel.getInput()));
+    obj.setProperty("modifier", inputToScriptValue(engine, inputChannel.getModifier()));
+    obj.setProperty("action", inputChannel.getAction());
+    obj.setProperty("scale", inputChannel.getScale());
     return obj;
 }
 
 void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) {
-    inputFromScriptValue(object.property("input"), inputChannel._input);
-    inputFromScriptValue(object.property("modifier"), inputChannel._modifier);
-    inputChannel._action = UserInputMapper::Action(object.property("action").toVariant().toInt());
-    inputChannel._scale = object.property("scale").toVariant().toFloat();
+    UserInputMapper::Input input;
+    UserInputMapper::Input modifier;
+    inputFromScriptValue(object.property("input"), input);
+    inputChannel.setInput(input);
+    inputFromScriptValue(object.property("modifier"), modifier);
+    inputChannel.setModifier(modifier);
+    inputChannel.setAction(UserInputMapper::Action(object.property("action").toVariant().toInt()));
+    inputChannel.setScale(object.property("scale").toVariant().toFloat());
 }
 
 QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) {
diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h
index 9019a1de48..0a08e277db 100755
--- a/interface/src/ui/UserInputMapper.h
+++ b/interface/src/ui/UserInputMapper.h
@@ -52,8 +52,13 @@ public:
         uint16 getDevice() const { return _device; }
         uint16 getChannel() const { return _channel; }
         uint32 getID() const { return _id; }
-
         ChannelType getType() const { return (ChannelType) _type; }
+        
+        void setDevice(uint16 device) { _device = device; }
+        void setChannel(uint16 channel) { _channel = channel; }
+        void setType(uint16 type) { _type = type; }
+        void setID(uint32 ID) { _id = ID; }
+
         bool isButton() const { return getType() == ChannelType::BUTTON; }
         bool isAxis() const { return getType() == ChannelType::AXIS; }
         bool isJoint() const { return getType() == ChannelType::JOINT; }
@@ -157,6 +162,16 @@ public:
         Input _modifier = Input(); // make it invalid by default, meaning no modifier
         Action _action = LONGITUDINAL_BACKWARD;
         float _scale = 0.0f;
+        
+        Input getInput() const { return _input; }
+        Input getModifier() const { return _modifier; }
+        Action getAction() const { return _action; }
+        float getScale() const { return _scale; }
+        
+        void setInput(Input input) { _input = input; }
+        void setModifier(Input modifier) { _modifier = modifier; }
+        void setAction(Action action) { _action = action; }
+        void setScale(float scale) { _scale = scale; }
 
         InputChannel() {}
         InputChannel(const Input& input, const Input& modifier, Action action, float scale = 1.0f) :

From 0004b1a81cb808744f595508f31ebd18f13973d4 Mon Sep 17 00:00:00 2001
From: Niraj Venkat <venkatn93@gmail.com>
Date: Tue, 9 Jun 2015 10:32:25 -0700
Subject: [PATCH 17/88] Reversing previous entity work and creation of property
 group

---
 libraries/entities/src/EntityItemProperties.h |  6 +-
 libraries/entities/src/EntityTypes.cpp        |  1 -
 libraries/entities/src/EntityTypes.h          |  3 +-
 .../entities/src/HyperlinkEntityItem.cpp      |  2 +-
 .../entities/src/HyperlinkPropertyGroup.h     | 88 +++++++++++++++++++
 5 files changed, 92 insertions(+), 8 deletions(-)
 create mode 100644 libraries/entities/src/HyperlinkPropertyGroup.h

diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index 42d4555e2e..e7d9a7ad18 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -36,6 +36,7 @@
 #include "EntityPropertyFlags.h"
 #include "SkyboxPropertyGroup.h"
 #include "StagePropertyGroup.h"
+#include "HyperlinkPropertyGroup.h"
 
 const quint64 UNKNOWN_CREATED_TIME = 0;
 
@@ -149,8 +150,7 @@ public:
     DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString);
     DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float);
     DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector<glm::vec3>);
-    DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString);
-    DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString);
+    DEFINE_PROPERTY_GROUP(Hyperlink, hyperlink, HyperlinkPropertyGroup)
 
 
     static QString getBackgroundModeString(BackgroundMode mode);
@@ -299,8 +299,6 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, "");
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, "");
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, "");
-    DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, "");
-    DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, "");
     
     properties.getStage().debugDump();
     properties.getAtmosphere().debugDump();
diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp
index 5689cfd11a..b20ee28e5a 100644
--- a/libraries/entities/src/EntityTypes.cpp
+++ b/libraries/entities/src/EntityTypes.cpp
@@ -48,7 +48,6 @@ REGISTER_ENTITY_TYPE(ParticleEffect)
 REGISTER_ENTITY_TYPE(Zone)
 REGISTER_ENTITY_TYPE(Line)
 REGISTER_ENTITY_TYPE(PolyVox)
-REGISTER_ENTITY_TYPE(Hyperlink)
 
 const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
     QMap<EntityType, QString>::iterator matchedTypeName = _typeToNameMap.find(entityType);
diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h
index 89bd57b253..323a4eb92b 100644
--- a/libraries/entities/src/EntityTypes.h
+++ b/libraries/entities/src/EntityTypes.h
@@ -46,8 +46,7 @@ public:
         Web,
         Line,
         PolyVox,
-        Hyperlink,
-        LAST = Hyperlink
+        LAST = PolyVox
     } EntityType;
 
     static const QString& getEntityTypeName(EntityType entityType);
diff --git a/libraries/entities/src/HyperlinkEntityItem.cpp b/libraries/entities/src/HyperlinkEntityItem.cpp
index af9a4003b1..4dee124dc1 100644
--- a/libraries/entities/src/HyperlinkEntityItem.cpp
+++ b/libraries/entities/src/HyperlinkEntityItem.cpp
@@ -37,7 +37,7 @@ EntityItemPointer HyperlinkEntityItem::factory(const EntityItemID& entityID, con
 HyperlinkEntityItem::HyperlinkEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
 EntityItem(entityItemID)
 {
-    _type = EntityTypes::Text;
+    _type = EntityTypes::Hyperlink;
     _created = properties.getCreated();
     setProperties(properties);
 }
diff --git a/libraries/entities/src/HyperlinkPropertyGroup.h b/libraries/entities/src/HyperlinkPropertyGroup.h
new file mode 100644
index 0000000000..b6bd40c91f
--- /dev/null
+++ b/libraries/entities/src/HyperlinkPropertyGroup.h
@@ -0,0 +1,88 @@
+//
+//  HyperlinkPropertyGroup.h
+//  libraries/entities/src
+//
+//  Created by Niraj Venkat on 6/9/15.
+//  Copyright 2013 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_HyperlinkPropertyGroup_h
+#define hifi_HyperlinkPropertyGroup_h
+
+#include <QtScript/QScriptEngine>
+
+#include "PropertyGroup.h"
+#include "EntityItemPropertiesMacros.h"
+
+class EntityItemProperties;
+class EncodeBitstreamParams;
+class OctreePacketData;
+class EntityTreeElementExtraEncodeData;
+class ReadBitstreamToTreeParams;
+
+#include <stdint.h>
+#include <glm/glm.hpp>
+
+
+
+class HyperlinkPropertyGroup : public PropertyGroup {
+public:
+    HyperlinkPropertyGroup();
+    virtual ~HyperlinkPropertyGroup() {}
+
+    // EntityItemProperty related helpers
+    virtual void copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const;
+    virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings);
+    virtual void debugDump() const;
+
+    virtual bool appentToEditPacket(OctreePacketData* packetData,
+        EntityPropertyFlags& requestedProperties,
+        EntityPropertyFlags& propertyFlags,
+        EntityPropertyFlags& propertiesDidntFit,
+        int& propertyCount,
+        OctreeElement::AppendState& appendState) const;
+
+    virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt, int& processedBytes);
+    virtual void markAllChanged();
+    virtual EntityPropertyFlags getChangedProperties() const;
+
+    // EntityItem related helpers
+    // methods for getting/setting all properties of an entity
+    virtual void getProperties(EntityItemProperties& propertiesOut) const;
+
+    /// returns true if something changed
+    virtual bool setProperties(const EntityItemProperties& properties);
+
+    virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
+
+    virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
+        EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData,
+        EntityPropertyFlags& requestedProperties,
+        EntityPropertyFlags& propertyFlags,
+        EntityPropertyFlags& propertiesDidntFit,
+        int& propertyCount,
+        OctreeElement::AppendState& appendState) const;
+
+    virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
+        ReadBitstreamToTreeParams& args,
+        EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
+
+    /*
+    DEFINE_PROPERTY_REF(PROP_Hyperlink_CENTER, Center, center, glm::vec3);
+    DEFINE_PROPERTY(PROP_Hyperlink_INNER_RADIUS, InnerRadius, innerRadius, float);
+    DEFINE_PROPERTY(PROP_Hyperlink_OUTER_RADIUS, OuterRadius, outerRadius, float);
+    DEFINE_PROPERTY(PROP_Hyperlink_MIE_SCATTERING, MieScattering, mieScattering, float);
+    DEFINE_PROPERTY(PROP_Hyperlink_RAYLEIGH_SCATTERING, RayleighScattering, rayleighScattering, float);
+    DEFINE_PROPERTY_REF(PROP_Hyperlink_SCATTERING_WAVELENGTHS, ScatteringWavelengths, scatteringWavelengths, glm::vec3);
+    DEFINE_PROPERTY(PROP_Hyperlink_HAS_STARS, HasStars, hasStars, bool);
+    */
+
+    DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString);
+    DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString);
+
+};
+
+#endif // hifi_HyperlinkPropertyGroup_h

From 1bf0b15bb0d76f3f72637e935f3fd4d8aecdbb36 Mon Sep 17 00:00:00 2001
From: Niraj Venkat <venkatn93@gmail.com>
Date: Tue, 9 Jun 2015 10:33:05 -0700
Subject: [PATCH 18/88] Removing unnecessary files

---
 libraries/entities/src/Hyperlink.h            |   5 -
 .../entities/src/HyperlinkPropertyGroup.cpp   | 136 ++++++++++++++++++
 2 files changed, 136 insertions(+), 5 deletions(-)
 delete mode 100644 libraries/entities/src/Hyperlink.h
 create mode 100644 libraries/entities/src/HyperlinkPropertyGroup.cpp

diff --git a/libraries/entities/src/Hyperlink.h b/libraries/entities/src/Hyperlink.h
deleted file mode 100644
index 7e2e2261e7..0000000000
--- a/libraries/entities/src/Hyperlink.h
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-entityItem
\ No newline at end of file
diff --git a/libraries/entities/src/HyperlinkPropertyGroup.cpp b/libraries/entities/src/HyperlinkPropertyGroup.cpp
new file mode 100644
index 0000000000..bc94a8d5d3
--- /dev/null
+++ b/libraries/entities/src/HyperlinkPropertyGroup.cpp
@@ -0,0 +1,136 @@
+//
+//  HyperlinkPropertyGroup.cpp
+//  libraries/entities/src
+//
+//  Created by Niraj Venkat on 6/9/15.
+//  Copyright 2013 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include <OctreePacketData.h>
+
+#include "HyperlinkPropertyGroup.h"
+#include "EntityItemProperties.h"
+#include "EntityItemPropertiesMacros.h"
+
+HyperlinkPropertyGroup::HyperlinkPropertyGroup() {
+    const QString DEFAULT_HREF = QString("");
+    const QString DEFAULT_DESCRIPTION = QString("");
+
+    _href = DEFAULT_HREF;
+    _description = DEFAULT_DESCRIPTION;
+}
+
+void HyperlinkPropertyGroup::copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
+    COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Hyperlink, hyperlink, Href, href);
+    COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Hyperlink, hyperlink, Description, description);
+}
+
+void HyperlinkPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
+    COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(Hyperlink, href, QString, setHref);
+    COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(Hyperlink, description, QString, setDescription);
+}
+
+void HyperlinkPropertyGroup::debugDump() const {
+    qDebug() << "   HyperlinkPropertyGroup: ---------------------------------------------";
+    qDebug() << "       Href:" << getHref() << " has changed:" << hrefChanged();
+    qDebug() << "       Description:" << getDescription() << " has changed:" << descriptionChanged();
+}
+
+bool HyperlinkPropertyGroup::appentToEditPacket(OctreePacketData* packetData,
+    EntityPropertyFlags& requestedProperties,
+    EntityPropertyFlags& propertyFlags,
+    EntityPropertyFlags& propertiesDidntFit,
+    int& propertyCount,
+    OctreeElement::AppendState& appendState) const {
+
+    bool successPropertyFits = true;
+
+    APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
+    APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
+
+    return true;
+}
+
+
+bool HyperlinkPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt, int& processedBytes) {
+
+    int bytesRead = 0;
+    bool overwriteLocalData = true;
+
+    READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
+    READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
+
+    DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HREF, Href);
+    DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_DESCRIPTION, Description);
+
+    processedBytes += bytesRead;
+
+    return true;
+}
+
+void HyperlinkPropertyGroup::markAllChanged() {
+    _hrefChanged = true;
+    _descriptionChanged = true;
+}
+
+EntityPropertyFlags HyperlinkPropertyGroup::getChangedProperties() const {
+    EntityPropertyFlags changedProperties;
+
+    CHECK_PROPERTY_CHANGE(PROP_HREF, href);
+    CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description);
+
+    return changedProperties;
+}
+
+void HyperlinkPropertyGroup::getProperties(EntityItemProperties& properties) const {
+    COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Hyperlink, Href, getHref);
+    COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Hyperlink, Description, getDescription);
+}
+
+bool HyperlinkPropertyGroup::setProperties(const EntityItemProperties& properties) {
+    bool somethingChanged = false;
+
+    SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Hyperlink, Href, href, setHref);
+    SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Hyperlink, Description, description, setDescription);
+
+    return somethingChanged;
+}
+
+EntityPropertyFlags HyperlinkPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const {
+    EntityPropertyFlags requestedProperties;
+
+    requestedProperties += PROP_HREF;
+    requestedProperties += PROP_DESCRIPTION;
+
+    return requestedProperties;
+}
+
+void HyperlinkPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
+    EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData,
+    EntityPropertyFlags& requestedProperties,
+    EntityPropertyFlags& propertyFlags,
+    EntityPropertyFlags& propertiesDidntFit,
+    int& propertyCount,
+    OctreeElement::AppendState& appendState) const {
+
+    bool successPropertyFits = true;
+
+    APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
+    APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
+}
+
+int HyperlinkPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
+    ReadBitstreamToTreeParams& args,
+    EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
+
+    int bytesRead = 0;
+    const unsigned char* dataAt = data;
+
+    READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setHref);
+    READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
+
+    return bytesRead;
+}

From ccb2f17b33f48d46cfa3d1c51325ab04402ff2ec Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Tue, 9 Jun 2015 11:21:13 -0700
Subject: [PATCH 19/88] grab.js rotation sort-of works now

---
 examples/grab.js                               | 18 +++++++++---------
 .../entities/src/EntityActionInterface.cpp     |  2 +-
 libraries/physics/src/ObjectActionSpring.cpp   |  3 ++-
 .../physics/src/PhysicalEntitySimulation.cpp   |  6 +++---
 4 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/examples/grab.js b/examples/grab.js
index f2c74bb252..8f733e9c9d 100644
--- a/examples/grab.js
+++ b/examples/grab.js
@@ -60,7 +60,7 @@ var gMouseAtRotateStart = { x: 0, y: 0 };
 
 var gBeaconHeight = 0.10;
 
-var gAngularVelocity = ZERO_VEC3;
+// var gAngularVelocity = ZERO_VEC3;
 
 // TODO: play sounds again when we aren't leaking AudioInjector threads
 // var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav");
@@ -194,7 +194,6 @@ function mousePressEvent(event) {
     var cameraPosition = Camera.getPosition();
 
     gBeaconHeight = Vec3.length(entityProperties.dimensions);
-    print("gBeaconHeight = " + gBeaconHeight);
     gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE;
     if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) {
         // don't allow grabs of things far away
@@ -254,7 +253,7 @@ function mouseMoveEvent(event) {
         gOriginalGravity = entityProperties.gravity;
     }
 
-    var actionArgs;
+    var actionArgs = {};
 
     if (gGrabMode === "rotate") {
         var deltaMouse = { x: 0, y: 0 };
@@ -265,12 +264,13 @@ function mouseMoveEvent(event) {
         var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation));
         dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation)));
         var axis = Vec3.cross(dragOffset, Quat.getFront(orientation));
-        // var axis = Vec3.normalize(axis);
-        // var ROTATE_STRENGTH = 8.0; // magic number tuned by hand
-        // gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis);
-
-        var targetRotation = Quat.angleAxis(Vec3.length(axis), Vec3.normalize(axis));
-        actionArgs = {targetRotation: targetRotation, angularTimeScale: 1.0};
+        axis = Vec3.normalize(axis);
+        var ROTATE_STRENGTH = 8.0; // magic number tuned by hand
+        var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy));
+        var deltaQ = Quat.angleAxis(angle, axis);
+        var qZero = entityProperties.rotation;
+        var qOne = Quat.multiply(deltaQ, qZero);
+        actionArgs = {targetRotation: qOne, angularTimeScale: 0.1};
     } else {
         var newTargetPosition;
         if (gGrabMode === "verticalCylinder") {
diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp
index b3e774df96..399c529aa2 100644
--- a/libraries/entities/src/EntityActionInterface.cpp
+++ b/libraries/entities/src/EntityActionInterface.cpp
@@ -131,7 +131,7 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian
         return glm::quat();
     }
 
-    return glm::quat(x, y, z, w);
+    return glm::quat(w, x, y, z);
 }
 
 
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
index f8b8d92ad5..5badc91f6a 100644
--- a/libraries/physics/src/ObjectActionSpring.cpp
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -64,8 +64,9 @@ void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar
                 glm::quat deltaQ = _rotationalTarget * qZeroInverse;
                 glm::vec3 axis = glm::axis(deltaQ);
                 float angle = glm::angle(deltaQ);
-                glm::vec3 newAngularVelocity = (-angle / _angularTimeScale) * glm::normalize(axis);
+                glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis);
                 rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity));
+                rigidBody->activate();
             }
         }
     }
diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index 56d497f8a1..a4d2113be6 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -236,9 +236,9 @@ void PhysicalEntitySimulation::handleCollisionEvents(CollisionEvents& collisionE
 }
 
 EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType type,
-                                                               QUuid id,
-                                                               EntityItemPointer ownerEntity,
-                                                               QVariantMap arguments) {
+                                                            QUuid id,
+                                                            EntityItemPointer ownerEntity,
+                                                            QVariantMap arguments) {
     EntityActionPointer action = nullptr;
     switch (type) {
         case ACTION_TYPE_NONE:

From a577d7955c56cb07a31c8df6c8146f47b078346d Mon Sep 17 00:00:00 2001
From: Niraj Venkat <venkatn93@gmail.com>
Date: Tue, 9 Jun 2015 11:27:57 -0700
Subject: [PATCH 20/88] Removed classes and the "hyperlink entity"

---
 libraries/entities/src/EntityTypes.cpp        |   1 -
 .../entities/src/HyperlinkEntityItem.cpp      | 174 ------------------
 libraries/entities/src/HyperlinkEntityItem.h  |  95 ----------
 3 files changed, 270 deletions(-)
 delete mode 100644 libraries/entities/src/HyperlinkEntityItem.cpp
 delete mode 100644 libraries/entities/src/HyperlinkEntityItem.h

diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp
index b20ee28e5a..a41cc22d2e 100644
--- a/libraries/entities/src/EntityTypes.cpp
+++ b/libraries/entities/src/EntityTypes.cpp
@@ -28,7 +28,6 @@
 #include "ZoneEntityItem.h"
 #include "LineEntityItem.h"
 #include "PolyVoxEntityItem.h"
-#include "HyperlinkEntityItem.h"
 
 QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
 QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
diff --git a/libraries/entities/src/HyperlinkEntityItem.cpp b/libraries/entities/src/HyperlinkEntityItem.cpp
deleted file mode 100644
index 4dee124dc1..0000000000
--- a/libraries/entities/src/HyperlinkEntityItem.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-//
-//  HyperlinkEntityItem.cpp
-//  libraries/entities/src
-//
-//  Created by Niraj Venkat on 6/8/15.
-//  Copyright 2013 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-
-#include <glm/gtx/transform.hpp>
-
-#include <QDebug>
-
-#include <ByteCountCoding.h>
-#include <PlaneShape.h>
-
-#include "EntityTree.h"
-#include "EntityTreeElement.h"
-#include "EntitiesLogging.h"
-#include "HyperlinkEntityItem.h"
-
-
-const QString HyperlinkEntityItem::DEFAULT_HREF("");
-const QString HyperlinkEntityItem::DEFAULT_DESCRIPTION("");
-
-const float HyperlinkEntityItem::DEFAULT_LINE_HEIGHT = 0.1f;
-const xColor HyperlinkEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 };
-const xColor HyperlinkEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 };
-
-EntityItemPointer HyperlinkEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
-    return EntityItemPointer(new HyperlinkEntityItem(entityID, properties));
-}
-
-HyperlinkEntityItem::HyperlinkEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
-EntityItem(entityItemID)
-{
-    _type = EntityTypes::Hyperlink;
-    _created = properties.getCreated();
-    setProperties(properties);
-}
-
-const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
-
-void HyperlinkEntityItem::setDimensions(const glm::vec3& value) {
-    // NOTE: Text Entities always have a "depth" of 1cm.
-    _dimensions = glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH);
-}
-
-EntityItemProperties HyperlinkEntityItem::getProperties() const {
-    EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
-
-    COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
-    COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription);
-    COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight);
-    COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColorX);
-    COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColorX);
-    return properties;
-}
-
-bool HyperlinkEntityItem::setProperties(const EntityItemProperties& properties) {
-    bool somethingChanged = false;
-    somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
-
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight);
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(textColor, setTextColor);
-    SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundColor, setBackgroundColor);
-
-    if (somethingChanged) {
-        bool wantDebug = false;
-        if (wantDebug) {
-            uint64_t now = usecTimestampNow();
-            int elapsed = now - getLastEdited();
-            qCDebug(entities) << "HyperlinkEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
-                "now=" << now << " getLastEdited()=" << getLastEdited();
-        }
-        setLastEdited(properties._lastEdited);
-    }
-
-    return somethingChanged;
-}
-
-int HyperlinkEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
-    ReadBitstreamToTreeParams& args,
-    EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
-
-    int bytesRead = 0;
-    const unsigned char* dataAt = data;
-
-    READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
-    READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
-    READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight);
-    READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, rgbColor, setTextColor);
-    READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, rgbColor, setBackgroundColor);
-
-    return bytesRead;
-}
-
-
-// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
-EntityPropertyFlags HyperlinkEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
-    EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
-    requestedProperties += PROP_HREF;
-    requestedProperties += PROP_DESCRIPTION;
-    requestedProperties += PROP_LINE_HEIGHT;
-    requestedProperties += PROP_TEXT_COLOR;
-    requestedProperties += PROP_BACKGROUND_COLOR;
-    return requestedProperties;
-}
-
-void HyperlinkEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
-    EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
-    EntityPropertyFlags& requestedProperties,
-    EntityPropertyFlags& propertyFlags,
-    EntityPropertyFlags& propertiesDidntFit,
-    int& propertyCount,
-    OctreeElement::AppendState& appendState) const {
-
-    bool successPropertyFits = true;
-
-    APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
-    APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
-    APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight());
-    APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, getTextColor());
-    APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, getBackgroundColor());
-}
-
-
-bool HyperlinkEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
-    bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
-    void** intersectedObject, bool precisionPicking) const {
-
-    RayIntersectionInfo rayInfo;
-    rayInfo._rayStart = origin;
-    rayInfo._rayDirection = direction;
-    rayInfo._rayLength = std::numeric_limits<float>::max();
-
-    PlaneShape plane;
-
-    const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
-    glm::vec3 normal = _rotation * UNROTATED_NORMAL;
-    plane.setNormal(normal);
-    plane.setPoint(getPosition()); // the position is definitely a point on our plane
-
-    bool intersects = plane.findRayIntersection(rayInfo);
-
-    if (intersects) {
-        glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance);
-        // now we know the point the ray hit our plane
-
-        glm::mat4 rotation = glm::mat4_cast(getRotation());
-        glm::mat4 translation = glm::translate(getPosition());
-        glm::mat4 entityToWorldMatrix = translation * rotation;
-        glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
-
-        glm::vec3 dimensions = getDimensions();
-        glm::vec3 registrationPoint = getRegistrationPoint();
-        glm::vec3 corner = -(dimensions * registrationPoint);
-        AABox entityFrameBox(corner, dimensions);
-
-        glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f));
-
-        intersects = entityFrameBox.contains(entityFrameHitAt);
-    }
-
-    if (intersects) {
-        distance = rayInfo._hitDistance;
-    }
-    return intersects;
-}
diff --git a/libraries/entities/src/HyperlinkEntityItem.h b/libraries/entities/src/HyperlinkEntityItem.h
deleted file mode 100644
index 2d522d6cfb..0000000000
--- a/libraries/entities/src/HyperlinkEntityItem.h
+++ /dev/null
@@ -1,95 +0,0 @@
-//
-//  HyperlinkEntityItem.h
-//  libraries/entities/src
-//
-//  Created by Niraj Venkat on 6/8/15.
-//  Copyright 2013 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_HyperlinkEntityItem_h
-#define hifi_HyperlinkEntityItem_h
-
-#include "EntityItem.h" 
-
-class HyperlinkEntityItem : public EntityItem {
-public:
-    static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
-
-    HyperlinkEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
-
-    ALLOW_INSTANTIATION // This class can be instantiated
-
-        /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
-        virtual void setDimensions(const glm::vec3& value);
-    virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
-
-    // methods for getting/setting all properties of an entity
-    virtual EntityItemProperties getProperties() const;
-    virtual bool setProperties(const EntityItemProperties& properties);
-
-    // TODO: eventually only include properties changed since the params.lastViewFrustumSent time
-    virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
-
-    virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
-        EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
-        EntityPropertyFlags& requestedProperties,
-        EntityPropertyFlags& propertyFlags,
-        EntityPropertyFlags& propertiesDidntFit,
-        int& propertyCount,
-        OctreeElement::AppendState& appendState) const;
-
-    virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
-        ReadBitstreamToTreeParams& args,
-        EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
-
-    virtual bool supportsDetailedRayIntersection() const { return true; }
-    virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
-        bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
-        void** intersectedObject, bool precisionPicking) const;
-
-    static const QString DEFAULT_HREF;
-    void setHref(const QString& value) { _href = value; }
-    const QString& getHref() const { return _href; }
-
-    static const QString DEFAULT_DESCRIPTION;
-    void setDescription(const QString& value) { _description = value; }
-    const QString& getDescription() const { return _description; }
-
-    static const float DEFAULT_LINE_HEIGHT;
-    void setLineHeight(float value) { _lineHeight = value; }
-    float getLineHeight() const { return _lineHeight; }
-
-    static const xColor DEFAULT_TEXT_COLOR;
-    const rgbColor& getTextColor() const { return _textColor; }
-    xColor getTextColorX() const { xColor color = { _textColor[RED_INDEX], _textColor[GREEN_INDEX], _textColor[BLUE_INDEX] }; return color; }
-
-    void setTextColor(const rgbColor& value) { memcpy(_textColor, value, sizeof(_textColor)); }
-    void setTextColor(const xColor& value) {
-        _textColor[RED_INDEX] = value.red;
-        _textColor[GREEN_INDEX] = value.green;
-        _textColor[BLUE_INDEX] = value.blue;
-    }
-
-    static const xColor DEFAULT_BACKGROUND_COLOR;
-    const rgbColor& getBackgroundColor() const { return _backgroundColor; }
-    xColor getBackgroundColorX() const { xColor color = { _backgroundColor[RED_INDEX], _backgroundColor[GREEN_INDEX], _backgroundColor[BLUE_INDEX] }; return color; }
-
-    void setBackgroundColor(const rgbColor& value) { memcpy(_backgroundColor, value, sizeof(_backgroundColor)); }
-    void setBackgroundColor(const xColor& value) {
-        _backgroundColor[RED_INDEX] = value.red;
-        _backgroundColor[GREEN_INDEX] = value.green;
-        _backgroundColor[BLUE_INDEX] = value.blue;
-    }
-
-protected:
-    QString _href;
-    QString _description;
-    float _lineHeight;
-    rgbColor _textColor;
-    rgbColor _backgroundColor;
-};
-
-#endif // hifi_HyperlinkEntityItem_h

From ba0cecb3561c6b47be665354d082a6918b3df034 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Tue, 9 Jun 2015 12:32:37 -0700
Subject: [PATCH 21/88] remove attachments from Model - only supported at
 avatar layer

---
 libraries/fbx/src/FBXReader.cpp      | 28 -------------
 libraries/fbx/src/FBXReader.h        | 15 +------
 libraries/fbx/src/FSTReader.cpp      |  4 +-
 libraries/fbx/src/OBJReader.cpp      |  1 -
 libraries/render-utils/src/Model.cpp | 60 ----------------------------
 libraries/render-utils/src/Model.h   |  2 -
 6 files changed, 4 insertions(+), 106 deletions(-)

diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp
index 464deb1059..76108730e8 100644
--- a/libraries/fbx/src/FBXReader.cpp
+++ b/libraries/fbx/src/FBXReader.cpp
@@ -2646,34 +2646,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
         }
     }
     geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
-
-    // process attachments
-    QVariantHash attachments = mapping.value("attach").toHash();
-    for (QVariantHash::const_iterator it = attachments.constBegin(); it != attachments.constEnd(); it++) {
-        FBXAttachment attachment;
-        attachment.jointIndex = modelIDs.indexOf(processID(it.key()));
-        attachment.scale = glm::vec3(1.0f, 1.0f, 1.0f);
-
-        QVariantList properties = it->toList();
-        if (properties.isEmpty()) {
-            attachment.url = it->toString();
-        } else {
-            attachment.url = properties.at(0).toString();
-
-            if (properties.size() >= 2) {
-                attachment.translation = parseVec3(properties.at(1).toString());
-
-                if (properties.size() >= 3) {
-                    attachment.rotation = glm::quat(glm::radians(parseVec3(properties.at(2).toString())));
-
-                    if (properties.size() >= 4) {
-                        attachment.scale = parseVec3(properties.at(3).toString());
-                    }
-                }
-            }
-        }
-        geometry.attachments.append(attachment);
-    }
     
     // Add sitting points
     QVariantHash sittingPoints = mapping.value("sit").toHash();
diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h
index 08ac0e308c..200cd4a121 100644
--- a/libraries/fbx/src/FBXReader.h
+++ b/libraries/fbx/src/FBXReader.h
@@ -189,17 +189,6 @@ public:
 Q_DECLARE_METATYPE(FBXAnimationFrame)
 Q_DECLARE_METATYPE(QVector<FBXAnimationFrame>)
 
-/// An attachment to an FBX document.
-class FBXAttachment {
-public:
-    
-    int jointIndex;
-    QUrl url;
-    glm::vec3 translation;
-    glm::quat rotation;
-    glm::vec3 scale;
-};
-
 /// A point where an avatar can sit
 class SittingPoint {
 public:
@@ -256,9 +245,7 @@ public:
     Extents meshExtents;
     
     QVector<FBXAnimationFrame> animationFrames;
-    
-    QVector<FBXAttachment> attachments;
-    
+        
     int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; }
     QStringList getJointNames() const;
     
diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp
index 32be82b392..a62c0fcea2 100644
--- a/libraries/fbx/src/FSTReader.cpp
+++ b/libraries/fbx/src/FSTReader.cpp
@@ -124,7 +124,9 @@ FSTReader::ModelType FSTReader::getTypeFromName(const QString& name) {
         _namesToTypes["head"] = HEAD_MODEL ;
         _namesToTypes["body"] = BODY_ONLY_MODEL;
         _namesToTypes["body+head"] = HEAD_AND_BODY_MODEL;
-        _namesToTypes["attachment"] = ATTACHMENT_MODEL;
+        
+        // NOTE: this is not yet implemented, but will be used to allow you to attach fully independent models to your avatar
+        _namesToTypes["attachment"] = ATTACHMENT_MODEL; 
     }
     return _namesToTypes[name];
 }
diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp
index 4a8a2fc53d..f0d3ecf517 100644
--- a/libraries/fbx/src/OBJReader.cpp
+++ b/libraries/fbx/src/OBJReader.cpp
@@ -544,7 +544,6 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) {
     qCDebug(modelformat) << "---------------- fbxGeometry ----------------";
     qCDebug(modelformat) << "  hasSkeletonJoints =" << fbxgeo.hasSkeletonJoints;
     qCDebug(modelformat) << "  offset =" << fbxgeo.offset;
-    qCDebug(modelformat) << "  attachments.count() = " << fbxgeo.attachments.count();
     qCDebug(modelformat) << "  meshes.count() =" << fbxgeo.meshes.count();
     foreach (FBXMesh mesh, fbxgeo.meshes) {
         qCDebug(modelformat) << "    vertices.count() =" << mesh.vertices.count();
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index c578015b9f..824f100d46 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -405,9 +405,6 @@ void Model::reset() {
     if (_jointStates.isEmpty()) {
         return;
     }
-    foreach (Model* attachment, _attachments) {
-        attachment->reset();
-    }
     const FBXGeometry& geometry = _geometry->getFBXGeometry();
     for (int i = 0; i < _jointStates.size(); i++) {
         _jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f);
@@ -419,14 +416,7 @@ void Model::reset() {
 }
 
 bool Model::updateGeometry() {
-    // NOTE: this is a recursive call that walks all attachments, and their attachments
     bool needFullUpdate = false;
-    for (int i = 0; i < _attachments.size(); i++) {
-        Model* model = _attachments.at(i);
-        if (model->updateGeometry()) {
-            needFullUpdate = true;
-        }
-    }
 
     bool needToRebuild = false;
     if (_nextGeometry) {
@@ -499,12 +489,6 @@ bool Model::updateGeometry() {
             }
             _blendedVertexBuffers.push_back(buffer);
         }
-        foreach (const FBXAttachment& attachment, fbxGeometry.attachments) {
-            Model* model = new Model(this);
-            model->init();
-            model->setURL(attachment.url);
-            _attachments.append(model);
-        }
         needFullUpdate = true;
     }
     return needFullUpdate;
@@ -913,12 +897,6 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
 
     bool somethingAdded = false;
 
-    // allow the attachments to add to scene
-    foreach (Model* attachment, _attachments) {
-        bool attachementSomethingAdded = attachment->addToScene(scene, pendingChanges);
-        somethingAdded = somethingAdded || attachementSomethingAdded;
-    }
-    
     foreach (auto renderItem, _transparentRenderItems) {
         auto item = scene->allocateID();
         auto renderData = TransparentMeshPart::Pointer(renderItem);
@@ -942,11 +920,6 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
 }
 
 void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
-    // allow the attachments to remove to scene
-    foreach (Model* attachment, _attachments) {
-        attachment->removeFromScene(scene, pendingChanges);
-    }
-
     foreach (auto item, _renderItems.keys()) {
         pendingChanges.removeItem(item);
     }
@@ -958,10 +931,6 @@ bool Model::render(RenderArgs* renderArgs, float alpha) {
     return true; //
     PROFILE_RANGE(__FUNCTION__);
 
-    // render the attachments
-    foreach (Model* attachment, _attachments) {
-        attachment->render(renderArgs, alpha);
-    }
     if (_meshStates.isEmpty()) {
         return false;
     }
@@ -1623,7 +1592,6 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
 }
 
 void Model::simulateInternal(float deltaTime) {
-    // NOTE: this is a recursive call that walks all attachments, and their attachments
     // update the world space transforms for all joints
     
     // update animations
@@ -1640,31 +1608,7 @@ void Model::simulateInternal(float deltaTime) {
 
     _shapesAreDirty = !_shapes.isEmpty();
     
-    // update the attachment transforms and simulate them
     const FBXGeometry& geometry = _geometry->getFBXGeometry();
-    for (int i = 0; i < _attachments.size(); i++) {
-        const FBXAttachment& attachment = geometry.attachments.at(i);
-        Model* model = _attachments.at(i);
-        
-        glm::vec3 jointTranslation = _translation;
-        glm::quat jointRotation = _rotation;
-        if (_showTrueJointTransforms) {
-            getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
-            getJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
-        } else {
-            getVisibleJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
-            getVisibleJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
-        }
-        
-        model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale);
-        model->setRotation(jointRotation * attachment.rotation);
-        model->setScale(_scale * attachment.scale);
-        
-        if (model->isActive()) {
-            model->simulateInternal(deltaTime);
-        }
-    }
-    
     glm::mat4 modelToWorld = glm::mat4_cast(_rotation);
     for (int i = 0; i < _meshStates.size(); i++) {
         MeshState& state = _meshStates[i];
@@ -2002,10 +1946,6 @@ void Model::applyNextGeometry() {
 }
 
 void Model::deleteGeometry() {
-    foreach (Model* attachment, _attachments) {
-        delete attachment;
-    }
-    _attachments.clear();
     _blendedVertexBuffers.clear();
     _jointStates.clear();
     _meshStates.clear();
diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h
index 6f751a5f8d..043b7e659b 100644
--- a/libraries/render-utils/src/Model.h
+++ b/libraries/render-utils/src/Model.h
@@ -350,8 +350,6 @@ private:
 
     QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
     
-    QVector<Model*> _attachments;
-
     QSet<WeakAnimationHandlePointer> _animationHandles;
 
     QList<AnimationHandlePointer> _runningAnimations;

From 01b1e9c4ed29d1c92aebb8330ade9592984814d1 Mon Sep 17 00:00:00 2001
From: David Rowe <david@ctrlaltstudio.com>
Date: Tue, 9 Jun 2015 12:33:14 -0700
Subject: [PATCH 22/88] Signal domain changes for those handled by ICE as well
 as not

---
 interface/src/scripting/WindowScriptingInterface.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp
index 3aae6a4d4a..6be67a7261 100644
--- a/interface/src/scripting/WindowScriptingInterface.cpp
+++ b/interface/src/scripting/WindowScriptingInterface.cpp
@@ -31,7 +31,7 @@ WindowScriptingInterface::WindowScriptingInterface() :
     _formResult(QDialog::Rejected) 
 {
     const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
-    connect(&domainHandler, &DomainHandler::hostnameChanged, this, &WindowScriptingInterface::domainChanged);
+    connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged);
     connect(Application::getInstance(), &Application::svoImportRequested, this, &WindowScriptingInterface::svoImportRequested);
     connect(Application::getInstance(), &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
 }

From a4885ff38ce896f2d71ee57b33009856c5045f02 Mon Sep 17 00:00:00 2001
From: David Rowe <david@ctrlaltstudio.com>
Date: Tue, 9 Jun 2015 12:33:30 -0700
Subject: [PATCH 23/88] Close directory window after domain change

---
 examples/directory.js | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/examples/directory.js b/examples/directory.js
index 03d352e024..b1fac19e8b 100644
--- a/examples/directory.js
+++ b/examples/directory.js
@@ -45,6 +45,10 @@ var directory = (function () {
         }
     }
 
+    function onDomainChanged() {
+        directoryWindow.setVisible(false);
+    }
+
     function onScriptUpdate() {
         var oldViewport = viewport;
 
@@ -74,6 +78,7 @@ var directory = (function () {
         updateButtonPosition();
 
         Controller.mousePressEvent.connect(onMousePressEvent);
+        Window.domainChanged.connect(onDomainChanged);
 
         Script.update.connect(onScriptUpdate);
     }

From e0adb8e38ac1d899509f39f21721f650281e21f4 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Tue, 9 Jun 2015 12:57:04 -0700
Subject: [PATCH 24/88] render avatar attachments as model payload items

---
 interface/src/avatar/Avatar.cpp   | 31 ++++++++++++++-------------
 interface/src/avatar/Avatar.h     |  1 -
 interface/src/avatar/MyAvatar.cpp | 35 ++++++-------------------------
 interface/src/avatar/MyAvatar.h   |  3 ---
 4 files changed, 23 insertions(+), 47 deletions(-)

diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 8708a2b2b0..022c0262e0 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -298,12 +298,22 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene>
     pendingChanges.resetItem(_renderItemID, avatarPayloadPointer);
     _skeletonModel.addToScene(scene, pendingChanges);
     getHead()->getFaceModel().addToScene(scene, pendingChanges);
+
+    for (auto attachmentModel : _attachmentModels) {
+        attachmentModel->addToScene(scene, pendingChanges);
+    }
+
     return true;
 }
 
 void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
     pendingChanges.removeItem(_renderItemID);
     _skeletonModel.removeFromScene(scene, pendingChanges);
+    getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
+    for (auto attachmentModel : _attachmentModels) {
+        attachmentModel->removeFromScene(scene, pendingChanges);
+    }
+
 }
 
 void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, bool postLighting) {
@@ -529,6 +539,12 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool
         getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
         getHead()->getFaceModel().addToScene(scene, pendingChanges);
     }
+    for (auto attachmentModel : _attachmentModels) {
+        if (attachmentModel->needsFixupInScene()) {
+            attachmentModel->removeFromScene(scene, pendingChanges);
+            attachmentModel->addToScene(scene, pendingChanges);
+        }
+    }
     scene->enqueuePendingChanges(pendingChanges);
 
     {
@@ -544,10 +560,6 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool
 
         if (postLighting) {
             getHand()->render(renderArgs, false);
-        } else {
-            // NOTE: we no longer call this here, because we've added all the model parts as renderable items in the scene
-            //_skeletonModel.render(renderArgs, 1.0f);
-            renderAttachments(renderArgs);
         }
     }
     getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting);
@@ -577,16 +589,6 @@ void Avatar::simulateAttachments(float deltaTime) {
     }
 }
 
-void Avatar::renderAttachments(RenderArgs* args) {
- //   RenderArgs::RenderMode modelRenderMode = (renderMode == RenderArgs::SHADOW_RENDER_MODE) ?
-  //      RenderArgs::SHADOW_RENDER_MODE : RenderArgs::DEFAULT_RENDER_MODE;
-    /*
-    foreach (Model* model, _attachmentModels) {
-        model->render(args, 1.0f);
-    }
-    */
-}
-
 void Avatar::updateJointMappings() {
     // no-op; joint mappings come from skeleton model
 }
@@ -949,6 +951,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
         _attachmentModels.append(model);
     }
     while (_attachmentModels.size() > attachmentData.size()) {
+        // NOTE: what's really going to happen here? This seems dangerous... has the model been removed from the scene?
         delete _attachmentModels.takeLast();
     }
 
diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index dbc59f7d9c..1113496080 100644
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -236,7 +236,6 @@ protected:
     virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const;
 
     void simulateAttachments(float deltaTime);
-    virtual void renderAttachments(RenderArgs* args);
 
     virtual void updateJointMappings();
 
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 8a49d69129..afe0311a29 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -1188,6 +1188,12 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo
         getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
         getHead()->getFaceModel().addToScene(scene, pendingChanges);
     }
+    for (auto attachmentModel : _attachmentModels) {
+        if (attachmentModel->needsFixupInScene()) {
+            attachmentModel->removeFromScene(scene, pendingChanges);
+            attachmentModel->addToScene(scene, pendingChanges);
+        }
+    }
     scene->enqueuePendingChanges(pendingChanges);
 
     Camera *camera = Application::getInstance()->getCamera();
@@ -1208,14 +1214,6 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo
         }
     }*/
 
-    //  Render the body's voxels and head
-    if (!postLighting) {
-    
-        // NOTE: we no longer call this here, because we've added all the model parts as renderable items in the scene
-        //_skeletonModel.render(renderArgs, 1.0f);
-        renderAttachments(renderArgs);
-    }
-    
     //  Render head so long as the camera isn't inside it
     if (shouldRenderHead(renderArgs, cameraPos)) {
         getHead()->render(renderArgs, 1.0f, renderFrustum, postLighting);
@@ -1571,27 +1569,6 @@ void MyAvatar::updateMotionBehavior() {
     _feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations);
 }
 
-void MyAvatar::renderAttachments(RenderArgs* args) {
-    if (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON || args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
-        Avatar::renderAttachments(args);
-        return;
-    }
-    const FBXGeometry& geometry = _skeletonModel.getGeometry()->getFBXGeometry();
-    QString headJointName = (geometry.headJointIndex == -1) ? QString() : geometry.joints.at(geometry.headJointIndex).name;
- //   RenderArgs::RenderMode modelRenderMode = (renderMode == RenderArgs::SHADOW_RENDER_MODE) ?
-  //      RenderArgs::SHADOW_RENDER_MODE : RenderArgs::DEFAULT_RENDER_MODE;
-    
-    // FIX ME - attachments need to be added to scene too...
-    /*
-    for (int i = 0; i < _attachmentData.size(); i++) {
-        const QString& jointName = _attachmentData.at(i).jointName;
-        if (jointName != headJointName && jointName != "Head") {
-            _attachmentModels.at(i)->render(args, 1.0f);
-        }
-    }
-    */
-}
-
 //Renders sixense laser pointers for UI selection with controllers
 void MyAvatar::renderLaserPointers() {
     const float PALM_TIP_ROD_RADIUS = 0.002f;
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index c8d16e8cb0..a3dc34e6e0 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -197,9 +197,6 @@ public slots:
 signals:
     void transformChanged();
 
-protected:
-    virtual void renderAttachments(RenderArgs* args);
-    
 private:
 
     // These are made private for MyAvatar so that you will use the "use" methods instead

From 2c6ebcb06a90b8f4e9d82a9ad94c2c60ec7785f7 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Tue, 9 Jun 2015 12:57:14 -0700
Subject: [PATCH 25/88] remove a bunch of cruft from Model

---
 libraries/render-utils/src/Model.cpp | 422 ---------------------------
 libraries/render-utils/src/Model.h   |  19 +-
 2 files changed, 1 insertion(+), 440 deletions(-)

diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 824f100d46..3026efceb2 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -927,206 +927,6 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin
     _readyWhenAdded = false;
 }
 
-bool Model::render(RenderArgs* renderArgs, float alpha) {
-    return true; //
-    PROFILE_RANGE(__FUNCTION__);
-
-    if (_meshStates.isEmpty()) {
-        return false;
-    }
-
-    renderSetup(renderArgs);
-    return renderCore(renderArgs, alpha);
-}
-
-bool Model::renderCore(RenderArgs* args, float alpha) {
-  return true;
-  
-    PROFILE_RANGE(__FUNCTION__);
-    if (!_viewState) {
-        return false;
-    }
-
-    auto mode = args->_renderMode;
-
-    // Let's introduce a gpu::Batch to capture all the calls to the graphics api
-    _renderBatch.clear();
-    gpu::Batch& batch = _renderBatch;
-
-    // Setup the projection matrix
-    if (args && args->_viewFrustum) {
-        glm::mat4 proj;
-        // If for easier debug depending on the pass
-        if (mode == RenderArgs::SHADOW_RENDER_MODE) {
-            args->_viewFrustum->evalProjectionMatrix(proj); 
-        } else {
-            args->_viewFrustum->evalProjectionMatrix(proj); 
-        }
-        batch.setProjectionTransform(proj);
-    }
-
-    // Capture the view matrix once for the rendering of this model
-    if (_transforms.empty()) {
-        _transforms.push_back(Transform());
-    }
-
-    _transforms[0] = _viewState->getViewTransform();
-
-    // apply entity translation offset to the viewTransform  in one go (it's a preTranslate because viewTransform goes from world to eye space)
-    _transforms[0].preTranslate(-_translation);
-
-    batch.setViewTransform(_transforms[0]);
-
-    /*DependencyManager::get<TextureCache>()->setPrimaryDrawBuffers(
-        mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE,
-        mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::NORMAL_RENDER_MODE,
-        mode == RenderArgs::DEFAULT_RENDER_MODE);
-        */
-     /*if (mode != RenderArgs::SHADOW_RENDER_MODE)*/ {
-        GLenum buffers[3];
-        int bufferCount = 0;
-
-       // if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE) {
-        if (mode != RenderArgs::SHADOW_RENDER_MODE) {
-            buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
-        }
-     //   if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::NORMAL_RENDER_MODE) {
-        if (mode != RenderArgs::SHADOW_RENDER_MODE) {
-            buffers[bufferCount++] = GL_COLOR_ATTACHMENT1;
-        }
-       // if (mode == RenderArgs::DEFAULT_RENDER_MODE) {
-        if (mode != RenderArgs::SHADOW_RENDER_MODE) {
-            buffers[bufferCount++] = GL_COLOR_ATTACHMENT2;
-        }
-        GLBATCH(glDrawBuffers)(bufferCount, buffers);
-      //  batch.setFramebuffer(DependencyManager::get<TextureCache>()->getPrimaryOpaqueFramebuffer());
-    }
-
-    const float DEFAULT_ALPHA_THRESHOLD = 0.5f;
-    
-
-    //renderMeshes(batch, mode, translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned, args, forceRenderMeshes);
-    int opaqueMeshPartsRendered = 0;
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, true, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, true, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, true, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, true, false, args, true);
-
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, false, false, args, true);
-    opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, false, false, args, true);
-
-    // render translucent meshes afterwards
-    //DependencyManager::get<TextureCache>()->setPrimaryDrawBuffers(false, true, true);
-    {
-        GLenum buffers[2];
-        int bufferCount = 0;
-        buffers[bufferCount++] = GL_COLOR_ATTACHMENT1;
-        buffers[bufferCount++] = GL_COLOR_ATTACHMENT2;
-        GLBATCH(glDrawBuffers)(bufferCount, buffers);
-    }
-
-    int translucentMeshPartsRendered = 0;
-    const float MOSTLY_OPAQUE_THRESHOLD = 0.75f;
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, false, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, true, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, false, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, true, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, false, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, true, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, false, false, args, true);
-    translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, true, false, args, true);
-
-    {
-        GLenum buffers[1];
-        int bufferCount = 0;
-        buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
-        GLBATCH(glDrawBuffers)(bufferCount, buffers);
-    }
-
-   // if (mode == RenderArgs::DEFAULT_RENDER_MODE || mode == RenderArgs::DIFFUSE_RENDER_MODE) {
-    if (mode != RenderArgs::SHADOW_RENDER_MODE) {
-    //    batch.setFramebuffer(DependencyManager::get<TextureCache>()->getPrimaryTransparentFramebuffer());
-
-        const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f;
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, false, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, true, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, false, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, true, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, false, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, true, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, false, false, args, true);
-        translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, true, false, args, true);
-
-   //     batch.setFramebuffer(DependencyManager::get<TextureCache>()->getPrimaryOpaqueFramebuffer());
-    }
-
-    GLBATCH(glDepthMask)(true);
-    GLBATCH(glDepthFunc)(GL_LESS);
-    GLBATCH(glDisable)(GL_CULL_FACE);
-    
-    if (mode == RenderArgs::SHADOW_RENDER_MODE) {
-        GLBATCH(glCullFace)(GL_BACK);
-    }
-
-    GLBATCH(glActiveTexture)(GL_TEXTURE0 + 1);
-    GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-    GLBATCH(glActiveTexture)(GL_TEXTURE0 + 2);
-    GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-    GLBATCH(glActiveTexture)(GL_TEXTURE0 + 3);
-    GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-    GLBATCH(glActiveTexture)(GL_TEXTURE0);
-    GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-
-    // deactivate vertex arrays after drawing
-    GLBATCH(glDisableClientState)(GL_NORMAL_ARRAY);
-    GLBATCH(glDisableClientState)(GL_VERTEX_ARRAY);
-    GLBATCH(glDisableClientState)(GL_TEXTURE_COORD_ARRAY);
-    GLBATCH(glDisableClientState)(GL_COLOR_ARRAY);
-    GLBATCH(glDisableVertexAttribArray)(gpu::Stream::TANGENT);
-    GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_INDEX);
-    GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_WEIGHT);
-    
-    // bind with 0 to switch back to normal operation
-    GLBATCH(glBindBuffer)(GL_ARRAY_BUFFER, 0);
-    GLBATCH(glBindBuffer)(GL_ELEMENT_ARRAY_BUFFER, 0);
-    GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-
-    // Back to no program
-    GLBATCH(glUseProgram)(0);
-
-    // Render!
-    {
-        PROFILE_RANGE("render Batch");
-
-        #if defined(ANDROID)
-        #else
-            glPushMatrix();
-        #endif
-
-        ::gpu::GLBackend::renderBatch(batch, true); // force sync with gl state here
- 
-        #if defined(ANDROID)
-        #else
-            glPopMatrix();
-        #endif
-    }
-
-    // restore all the default material settings
-    _viewState->setupWorldLight();
-    
-    #ifdef WANT_DEBUG_MESHBOXES
-    renderDebugMeshBoxes();
-    #endif
-    
-    return true;
-}
-
 void Model::renderDebugMeshBoxes() {
     int colorNdx = 0;
     _mutex.lock();
@@ -2295,22 +2095,6 @@ void Model::segregateMeshGroups() {
     _meshGroupsKnown = true;
 } 
 
-QVector<int>* Model::pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) {
-    PROFILE_RANGE(__FUNCTION__);
-
-    // depending on which parameters we were called with, pick the correct mesh group to render
-    QVector<int>* whichList = NULL;
-
-    RenderKey key(translucent, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe);
-
-    auto bucket = _renderBuckets.find(key.getRaw());
-    if (bucket != _renderBuckets.end()) {
-        whichList = &(*bucket).second._meshes;
-    }
-
-    return whichList;
-}
-
 void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
                             bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args,
                             Locations*& locations) {
@@ -2339,212 +2123,6 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f
     }
 }
 
-int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
-                            bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args,
-                            bool forceRenderSomeMeshes) {
-
-    PROFILE_RANGE(__FUNCTION__);
-    int meshPartsRendered = 0;
-
-    //Pick the mesh list with the requested render flags
-    QVector<int>* whichList = pickMeshList(translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe);
-    if (!whichList) {
-        return 0;
-    }
-    QVector<int>& list = *whichList;
-
-    // If this list has nothing to render, then don't bother proceeding. This saves us on binding to programs    
-    if (list.empty()) {
-        return 0;
-    }
-
-    Locations* locations = nullptr;
-    pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe,
-                                args, locations);
-    meshPartsRendered = renderMeshesFromList(list, batch, mode, translucent, alphaThreshold,
-                                args, locations, forceRenderSomeMeshes);
-
-    return meshPartsRendered;
-}
-
-
-int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, RenderArgs* args,
-                                        Locations* locations, bool forceRenderMeshes) {
-    PROFILE_RANGE(__FUNCTION__);
-
-    auto textureCache = DependencyManager::get<TextureCache>();
-
-    QString lastMaterialID;
-    int meshPartsRendered = 0;
-    updateVisibleJointStates();
-    const FBXGeometry& geometry = _geometry->getFBXGeometry();
-    const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
-
-    // i is the "index" from the original networkMeshes QVector...
-    foreach (int i, list) {
-    
-        // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
-        // to false to rebuild out mesh groups.
-        
-        if (i < 0 || i >= networkMeshes.size() || i > geometry.meshes.size()) {
-            _meshGroupsKnown = false; // regenerate these lists next time around.
-            _readyWhenAdded = false; // in case any of our users are using scenes
-            invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
-            continue;
-        }
-        
-        // exit early if the translucency doesn't match what we're drawing
-        const NetworkMesh& networkMesh = networkMeshes.at(i);
-        const FBXMesh& mesh = geometry.meshes.at(i);    
-
-        batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0);
-        int vertexCount = mesh.vertices.size();
-        if (vertexCount == 0) {
-            // sanity check
-            continue;
-        }
-        
-        // if we got here, then check to see if this mesh is in view
-        if (args) {
-            bool shouldRender = true;
-            if (args->_viewFrustum) {
-            
-                shouldRender = forceRenderMeshes || 
-                                    args->_viewFrustum->boxInFrustum(_calculatedMeshBoxes.at(i)) != ViewFrustum::OUTSIDE;
-            
-                if (shouldRender && !forceRenderMeshes) {
-                    float distance = args->_viewFrustum->distanceToCamera(_calculatedMeshBoxes.at(i).calcCenter());
-                    shouldRender = !_viewState ? false : _viewState->shouldRenderMesh(_calculatedMeshBoxes.at(i).getLargestDimension(),
-                                                                            distance);
-                }
-            }
-
-            if (!shouldRender) {
-                continue; // skip this mesh
-            }
-        }
-
-        const MeshState& state = _meshStates.at(i);
-        if (state.clusterMatrices.size() > 1) {
-            GLBATCH(glUniformMatrix4fv)(locations->clusterMatrices, state.clusterMatrices.size(), false,
-                (const float*)state.clusterMatrices.constData());
-            batch.setModelTransform(Transform());
-        } else {
-            batch.setModelTransform(Transform(state.clusterMatrices[0]));
-        }
-
-        if (mesh.blendshapes.isEmpty()) {
-            batch.setInputFormat(networkMesh._vertexFormat);
-            batch.setInputStream(0, *networkMesh._vertexStream);
-        } else {
-            batch.setInputFormat(networkMesh._vertexFormat);
-            batch.setInputBuffer(0, _blendedVertexBuffers[i], 0, sizeof(glm::vec3));
-            batch.setInputBuffer(1, _blendedVertexBuffers[i], vertexCount * sizeof(glm::vec3), sizeof(glm::vec3));
-            batch.setInputStream(2, *networkMesh._vertexStream);
-        }
-
-        if (mesh.colors.isEmpty()) {
-            GLBATCH(glColor4f)(1.0f, 1.0f, 1.0f, 1.0f);
-        }
-
-        qint64 offset = 0;
-        for (int j = 0; j < networkMesh.parts.size(); j++) {
-            const NetworkMeshPart& networkPart = networkMesh.parts.at(j);
-            const FBXMeshPart& part = mesh.parts.at(j);
-            model::MaterialPointer material = part._material;
-            if ((networkPart.isTranslucent() || part.opacity != 1.0f) != translucent) {
-                offset += (part.quadIndices.size() + part.triangleIndices.size()) * sizeof(int);
-                continue;
-            }
-
-            // apply material properties
-            if (mode == RenderArgs::SHADOW_RENDER_MODE) {
-             ///   GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
-                
-            } else {
-                if (lastMaterialID != part.materialID) {
-                    const bool wantDebug = false;
-                    if (wantDebug) {
-                        qCDebug(renderutils) << "Material Changed ---------------------------------------------";
-                        qCDebug(renderutils) << "part INDEX:" << j;
-                        qCDebug(renderutils) << "NEW part.materialID:" << part.materialID;
-                    }
-
-                    if (locations->materialBufferUnit >= 0) {
-                        batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer());
-                    }
-
-                    Texture* diffuseMap = networkPart.diffuseTexture.data();
-                    if (mesh.isEye && diffuseMap) {
-                        diffuseMap = (_dilatedTextures[i][j] =
-                            static_cast<DilatableNetworkTexture*>(diffuseMap)->getDilatedTexture(_pupilDilation)).data();
-                    }
-                    static bool showDiffuse = true;
-                    if (showDiffuse && diffuseMap) {
-                        batch.setUniformTexture(0, diffuseMap->getGPUTexture());
-                        
-                    } else {
-                        batch.setUniformTexture(0, textureCache->getWhiteTexture());
-                    }
-
-                    if (locations->texcoordMatrices >= 0) {
-                        glm::mat4 texcoordTransform[2];
-                        if (!part.diffuseTexture.transform.isIdentity()) {
-                            part.diffuseTexture.transform.getMatrix(texcoordTransform[0]);
-                        }
-                        if (!part.emissiveTexture.transform.isIdentity()) {
-                            part.emissiveTexture.transform.getMatrix(texcoordTransform[1]);
-                        }
-                        GLBATCH(glUniformMatrix4fv)(locations->texcoordMatrices, 2, false, (const float*) &texcoordTransform);
-                    }
-
-                    if (!mesh.tangents.isEmpty()) {                 
-                        Texture* normalMap = networkPart.normalTexture.data();
-                        batch.setUniformTexture(1, !normalMap ?
-                            textureCache->getBlueTexture() : normalMap->getGPUTexture());
-
-                    }
-                
-                    if (locations->specularTextureUnit >= 0) {
-                        Texture* specularMap = networkPart.specularTexture.data();
-                        batch.setUniformTexture(locations->specularTextureUnit, !specularMap ?
-                                                    textureCache->getWhiteTexture() : specularMap->getGPUTexture());
-                    }
-                }
-
-                // HACK: For unkwon reason (yet!) this code that should be assigned only if the material changes need to be called for every
-                // drawcall with an emissive, so let's do it for now.
-                if (locations->emissiveTextureUnit >= 0) {
-                    //  assert(locations->emissiveParams >= 0); // we should have the emissiveParams defined in the shader
-                    float emissiveOffset = part.emissiveParams.x;
-                    float emissiveScale = part.emissiveParams.y;
-                    GLBATCH(glUniform2f)(locations->emissiveParams, emissiveOffset, emissiveScale);
-
-                    Texture* emissiveMap = networkPart.emissiveTexture.data();
-                        batch.setUniformTexture(locations->emissiveTextureUnit, !emissiveMap ?
-                                                    textureCache->getWhiteTexture() : emissiveMap->getGPUTexture());
-                }
-
-                lastMaterialID = part.materialID;
-            }
-            
-            meshPartsRendered++;
-            
-            if (part.quadIndices.size() > 0) {
-                batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset);
-                offset += part.quadIndices.size() * sizeof(int);
-            }
-
-            if (part.triangleIndices.size() > 0) {
-                batch.drawIndexed(gpu::TRIANGLES, part.triangleIndices.size(), offset);
-                offset += part.triangleIndices.size() * sizeof(int);
-            }
-
-        }
-    }
-
-    return meshPartsRendered;
-}
 
 ModelBlender::ModelBlender() :
     _pendingBlenders(0) {
diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h
index 043b7e659b..c96e006aa8 100644
--- a/libraries/render-utils/src/Model.h
+++ b/libraries/render-utils/src/Model.h
@@ -399,18 +399,8 @@ private:
     int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;
 
     // helper functions used by render() or renderInScene()
-    bool renderCore(RenderArgs* args, float alpha);
-    int renderMeshes(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold,
-                        bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args = NULL,
-                        bool forceRenderMeshes = false);
-                        
+
     void setupBatchTransform(gpu::Batch& batch, RenderArgs* args);
-    QVector<int>* pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe);
-
-    int renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold,
-                                        RenderArgs* args, Locations* locations, 
-                                        bool forceRenderSomeMeshes = false);
-
     static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold,
                             bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args,
                             Locations*& locations);
@@ -543,13 +533,6 @@ private:
     bool _readyWhenAdded = false;
     bool _needsReload = true;
     
-    
-private:
-    // FIX ME - We want to get rid of this interface for rendering...
-    // right now the only remaining user are Avatar attachments.
-    // that usage has been temporarily disabled... 
-    bool render(RenderArgs* renderArgs, float alpha = 1.0f);
-    
 };
 
 Q_DECLARE_METATYPE(QPointer<Model>)

From 23dab530f911396cea8896f927dbf74d77b5c52b Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Tue, 9 Jun 2015 14:11:01 -0700
Subject: [PATCH 26/88] fix some glitchy behavior during rotation

---
 examples/grab.js                             | 33 +-------------------
 libraries/physics/src/ObjectActionSpring.cpp | 33 +++++++++++++++-----
 2 files changed, 27 insertions(+), 39 deletions(-)

diff --git a/examples/grab.js b/examples/grab.js
index 8f733e9c9d..3355ad65c0 100644
--- a/examples/grab.js
+++ b/examples/grab.js
@@ -21,7 +21,6 @@ var ANGULAR_DAMPING_RATE = 0.40;
 var gIsGrabbing = false;
 var gGrabbedEntity = null;
 var gActionID = null;
-var gPrevMouse = {x: 0, y: 0};
 var gEntityProperties;
 var gStartPosition;
 var gStartRotation;
@@ -259,13 +258,12 @@ function mouseMoveEvent(event) {
         var deltaMouse = { x: 0, y: 0 };
         var dx = event.x - gPreviousMouse.x;
         var dy = event.y - gPreviousMouse.y;
-
         var orientation = Camera.getOrientation();
         var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation));
         dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation)));
         var axis = Vec3.cross(dragOffset, Quat.getFront(orientation));
         axis = Vec3.normalize(axis);
-        var ROTATE_STRENGTH = 8.0; // magic number tuned by hand
+        var ROTATE_STRENGTH = 16.0; // magic number tuned by hand
         var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy));
         var deltaQ = Quat.angleAxis(angle, axis);
         var qZero = entityProperties.rotation;
@@ -327,37 +325,8 @@ function keyPressEvent(event) {
     computeNewGrabPlane();
 }
 
-// function update(deltaTime) {
-//     if (!gIsGrabbing) {
-//         return;
-//     }
-
-//     var entityProperties = Entities.getEntityProperties(gGrabbedEntity);
-//     gCurrentPosition = entityProperties.position;
-//     if (gGrabMode === "rotate") {
-//         gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE));
-//         Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, });
-//     }
-
-//     // always push toward linear grab position, even when rotating
-//     var newVelocity = ZERO_VEC3;
-//     var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition);
-//     var delta = Vec3.length(dPosition);
-//     if (delta > CLOSE_ENOUGH) {
-//         var MAX_POSITION_DELTA = 4.0;
-//         if (delta > MAX_POSITION_DELTA) {
-//             dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta);
-//         }
-//         // desired speed is proportional to displacement by the inverse of timescale
-//         // (for critically damped motion)
-//         newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE);
-//     }
-//     Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, });
-// }
-
 Controller.mouseMoveEvent.connect(mouseMoveEvent);
 Controller.mousePressEvent.connect(mousePressEvent);
 Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
 Controller.keyPressEvent.connect(keyPressEvent);
 Controller.keyReleaseEvent.connect(keyReleaseEvent);
-// Script.update.connect(update);
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
index 5badc91f6a..cf9a226c89 100644
--- a/libraries/physics/src/ObjectActionSpring.cpp
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -60,13 +60,32 @@ void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar
 
             // handle rotation
             if (_rotationalTargetSet) {
-                glm::quat qZeroInverse = glm::inverse(bulletToGLM(rigidBody->getOrientation()));
-                glm::quat deltaQ = _rotationalTarget * qZeroInverse;
-                glm::vec3 axis = glm::axis(deltaQ);
-                float angle = glm::angle(deltaQ);
-                glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis);
-                rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity));
-                rigidBody->activate();
+                glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation());
+                // if qZero and qOne are too close to each other, we can get NaN for angle.
+                auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget);
+                const float almostOne = 0.99999;
+                if (glm::abs(alignmentDot) < almostOne) {
+                    glm::quat target = _rotationalTarget;
+                    if (alignmentDot < 0) {
+                        target = -target;
+                    }
+                    glm::quat qZeroInverse = glm::inverse(bodyRotation);
+                    glm::quat deltaQ = target * qZeroInverse;
+                    glm::vec3 axis = glm::axis(deltaQ);
+                    float angle = glm::angle(deltaQ);
+                    if (isNaN(angle)) {
+                        qDebug() << "ObjectActionSpring::updateAction angle =" << angle
+                                 << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w
+                                 << "target-rotation ="
+                                 << target.x << target.y << target.z<< target.w;
+                    }
+                    assert(!isNaN(angle));
+                    glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis);
+                    rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity));
+                    rigidBody->activate();
+                } else {
+                    rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f)));
+                }
             }
         }
     }

From 6cce8459847567f6508d4cbd0235710418b67ea2 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Tue, 9 Jun 2015 14:26:44 -0700
Subject: [PATCH 27/88] rotate based on initial mouse-position and initial
 entity rotation rather than doing small deltas as the mouse moves

---
 examples/grab.js | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/examples/grab.js b/examples/grab.js
index 3355ad65c0..6a27e1b73b 100644
--- a/examples/grab.js
+++ b/examples/grab.js
@@ -53,6 +53,7 @@ var gTargetRotation;
 var gLiftKey = false; // SHIFT
 var gRotateKey = false; // CONTROL
 
+var gInitialMouse = { x: 0, y: 0 };
 var gPreviousMouse = { x: 0, y: 0 };
 var gMouseCursorLocation = { x: 0, y: 0 };
 var gMouseAtRotateStart = { x: 0, y: 0 };
@@ -173,6 +174,7 @@ function mousePressEvent(event) {
     if (!event.isLeftButton) {
         return;
     }
+    gInitialMouse = {x: event.x, y: event.y };
     gPreviousMouse = {x: event.x, y: event.y };
 
     var pickRay = Camera.computePickRay(event.x, event.y);
@@ -189,12 +191,13 @@ function mousePressEvent(event) {
 
     var clickedEntity = pickResults.entityID;
     var entityProperties = Entities.getEntityProperties(clickedEntity)
-    var objectPosition = entityProperties.position;
+    gStartPosition = entityProperties.position;
+    gStartRotation = entityProperties.rotation;
     var cameraPosition = Camera.getPosition();
 
     gBeaconHeight = Vec3.length(entityProperties.dimensions);
     gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE;
-    if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) {
+    if (Vec3.distance(gStartPosition, cameraPosition) > gMaxGrabDistance) {
         // don't allow grabs of things far away
         return;
     }
@@ -205,20 +208,20 @@ function mousePressEvent(event) {
     gGrabbedEntity = clickedEntity;
     gCurrentPosition = entityProperties.position;
     gOriginalGravity = entityProperties.gravity;
-    gTargetPosition = objectPosition;
+    gTargetPosition = gStartPosition;
 
     // compute the grab point
-    var nearestPoint = Vec3.subtract(objectPosition, cameraPosition);
+    var nearestPoint = Vec3.subtract(gStartPosition, cameraPosition);
     var distanceToGrab = Vec3.dot(nearestPoint, pickRay.direction);
     nearestPoint = Vec3.multiply(distanceToGrab, pickRay.direction);
     gPointOnPlane = Vec3.sum(cameraPosition, nearestPoint);
 
     // compute the grab offset
-    gGrabOffset = Vec3.subtract(objectPosition, gPointOnPlane);
+    gGrabOffset = Vec3.subtract(gStartPosition, gPointOnPlane);
 
     computeNewGrabPlane();
 
-    updateDropLine(objectPosition);
+    updateDropLine(gStartPosition);
 
     // TODO: play sounds again when we aren't leaking AudioInjector threads
     //Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME });
@@ -256,17 +259,18 @@ function mouseMoveEvent(event) {
 
     if (gGrabMode === "rotate") {
         var deltaMouse = { x: 0, y: 0 };
-        var dx = event.x - gPreviousMouse.x;
-        var dy = event.y - gPreviousMouse.y;
+        var dx = event.x - gInitialMouse.x;
+        var dy = event.y - gInitialMouse.y;
         var orientation = Camera.getOrientation();
         var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation));
         dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation)));
         var axis = Vec3.cross(dragOffset, Quat.getFront(orientation));
         axis = Vec3.normalize(axis);
-        var ROTATE_STRENGTH = 16.0; // magic number tuned by hand
+        var ROTATE_STRENGTH = 0.4; // magic number tuned by hand
         var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy));
         var deltaQ = Quat.angleAxis(angle, axis);
-        var qZero = entityProperties.rotation;
+        // var qZero = entityProperties.rotation;
+        var qZero = gStartRotation;
         var qOne = Quat.multiply(deltaQ, qZero);
         actionArgs = {targetRotation: qOne, angularTimeScale: 0.1};
     } else {

From d90e43c5aa36f0e88dcb2d71bba00e00f50d4af4 Mon Sep 17 00:00:00 2001
From: Niraj Venkat <venkatn93@gmail.com>
Date: Tue, 9 Jun 2015 16:15:47 -0700
Subject: [PATCH 28/88] Adding text boxes for hyperlink href and description

---
 examples/html/entityProperties.html | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html
index d9cad0feff..bebbb1d80e 100644
--- a/examples/html/entityProperties.html
+++ b/examples/html/entityProperties.html
@@ -360,6 +360,10 @@
             var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z");
             var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style");
 
+            var elHyperlinkHref = document.getElementById("property-hyperlink-href");
+            var elHyperlinkDescription = document.getElementById("property-hyperlink-description");
+
+
 
             if (window.EventBridge !== undefined) {
                 EventBridge.scriptEventReceived.connect(function(data) {
@@ -850,6 +854,10 @@
             elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction);
             elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle'));
 
+            var hyperlinkChangeFunction = createEmitGroupTextPropertyUpdateFunction('hyperlink','href');
+
+            var hyperlinkChangeFunction = createEmitGroupTextPropertyUpdateFunction('hyperlink','description');
+
 
             elMoveSelectionToGrid.addEventListener("click", function() {
                 EventBridge.emitWebEvent(JSON.stringify({
@@ -937,6 +945,18 @@
                 <input type="text" id="property-name"></input>
             </div>
         </div>
+
+        <div class="property">
+            <div class="label">Hyperlink</div>
+            <div class="input-area">Href<br></div>
+            <div class="value">
+                <input id="property-hyperlink-href" class="url"></input>
+            </div>
+            <div class="input-area">Description<br></div> <div class="value">
+                <input id="property-hyperlink-description" class="url"></input>
+            </div>
+        </div>
+
         <div class="property">
             <span class="label">Locked</span>
             <span class="value">

From b1a209b9db9e6cdad15ef47c37a47a7cc2208ee7 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Tue, 9 Jun 2015 16:17:48 -0700
Subject: [PATCH 29/88] pull some common code out of subclasses and into
 ObjectAction

---
 .../src/EntityTreeRenderer.cpp                |   4 +-
 libraries/physics/src/ObjectAction.cpp        |  22 +++-
 libraries/physics/src/ObjectAction.h          |  11 +-
 .../physics/src/ObjectActionPullToPoint.cpp   |  39 ++-----
 .../physics/src/ObjectActionPullToPoint.h     |   3 +-
 libraries/physics/src/ObjectActionSpring.cpp  | 106 +++++++-----------
 libraries/physics/src/ObjectActionSpring.h    |   3 +-
 7 files changed, 90 insertions(+), 98 deletions(-)

diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index 98cbc1f845..3e83c6f559 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -1012,7 +1012,9 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
 void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
     checkAndCallPreload(entityID);
     auto entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID);
-    addEntityToScene(entity);
+    if (entity) {
+        addEntityToScene(entity);
+    }
 }
 
 void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) {
diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp
index 6ff4098ba8..5f67db4355 100644
--- a/libraries/physics/src/ObjectAction.cpp
+++ b/libraries/physics/src/ObjectAction.cpp
@@ -24,7 +24,27 @@ ObjectAction::~ObjectAction() {
 }
 
 void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
-    qDebug() << "ObjectAction::updateAction called";
+    if (!_active) {
+        return;
+    }
+    if (!_ownerEntity) {
+        qDebug() << "ObjectActionPullToPoint::updateAction no owner entity";
+        return;
+    }
+    if (!tryLockForRead()) {
+        // don't risk hanging the thread running the physics simulation
+        return;
+    }
+    void* physicsInfo = _ownerEntity->getPhysicsInfo();
+
+    if (physicsInfo) {
+        ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
+        btRigidBody* rigidBody = motionState->getRigidBody();
+        if (rigidBody) {
+            updateActionWorker(collisionWorld, deltaTimeStep, motionState, rigidBody);
+        }
+    }
+    unlock();
 }
 
 void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h
index 10b086c07d..7ff11b8ba0 100644
--- a/libraries/physics/src/ObjectAction.h
+++ b/libraries/physics/src/ObjectAction.h
@@ -13,11 +13,14 @@
 #ifndef hifi_ObjectAction_h
 #define hifi_ObjectAction_h
 
-#include <btBulletDynamicsCommon.h>
-
 #include <QUuid>
 
+#include <btBulletDynamicsCommon.h>
+
 #include <EntityItem.h>
+#include "ObjectMotionState.h"
+#include "BulletUtil.h"
+
 
 class ObjectAction : public btActionInterface, public EntityActionInterface {
 public:
@@ -30,6 +33,10 @@ public:
     virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
     virtual bool updateArguments(QVariantMap arguments) { return false; }
 
+    // this is called from updateAction and should be overridden by subclasses
+    virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep,
+                                    ObjectMotionState* motionState, btRigidBody* rigidBody) {}
+
     // these are from btActionInterface
     virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
     virtual void debugDraw(btIDebugDraw* debugDrawer);
diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp
index 28c0e08bd9..2e66b948f5 100644
--- a/libraries/physics/src/ObjectActionPullToPoint.cpp
+++ b/libraries/physics/src/ObjectActionPullToPoint.cpp
@@ -9,9 +9,6 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "ObjectMotionState.h"
-#include "BulletUtil.h"
-
 #include "ObjectActionPullToPoint.h"
 
 ObjectActionPullToPoint::ObjectActionPullToPoint(QUuid id, EntityItemPointer ownerEntity) :
@@ -27,33 +24,17 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() {
     #endif
 }
 
-void ObjectActionPullToPoint::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
-    if (!_ownerEntity) {
-        qDebug() << "ObjectActionPullToPoint::updateAction no owner entity";
-        return;
+void ObjectActionPullToPoint::updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep,
+                                                 ObjectMotionState* motionState, btRigidBody* rigidBody) {
+    glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition());
+    float offsetLength = glm::length(offset);
+    if (offsetLength > IGNORE_POSITION_DELTA) {
+        glm::vec3 newVelocity = glm::normalize(offset) * _speed;
+        rigidBody->setLinearVelocity(glmToBullet(newVelocity));
+        rigidBody->activate();
+    } else {
+        rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
     }
-    if (!tryLockForRead()) {
-        // don't risk hanging the thread running the physics simulation
-        return;
-    }
-    void* physicsInfo = _ownerEntity->getPhysicsInfo();
-
-    if (_active && physicsInfo) {
-        ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
-        btRigidBody* rigidBody = motionState->getRigidBody();
-        if (rigidBody) {
-            glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition());
-            float offsetLength = glm::length(offset);
-            if (offsetLength > IGNORE_POSITION_DELTA) {
-                glm::vec3 newVelocity = glm::normalize(offset) * _speed;
-                rigidBody->setLinearVelocity(glmToBullet(newVelocity));
-                rigidBody->activate();
-            } else {
-                rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
-            }
-        }
-    }
-    unlock();
 }
 
 
diff --git a/libraries/physics/src/ObjectActionPullToPoint.h b/libraries/physics/src/ObjectActionPullToPoint.h
index 3aca70d640..55fd748921 100644
--- a/libraries/physics/src/ObjectActionPullToPoint.h
+++ b/libraries/physics/src/ObjectActionPullToPoint.h
@@ -23,7 +23,8 @@ public:
     virtual ~ObjectActionPullToPoint();
 
     virtual bool updateArguments(QVariantMap arguments);
-    virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
+    virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep,
+                                    ObjectMotionState* motionState, btRigidBody* rigidBody);
 
 private:
 
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
index cf9a226c89..101af665f7 100644
--- a/libraries/physics/src/ObjectActionSpring.cpp
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -9,9 +9,6 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "ObjectMotionState.h"
-#include "BulletUtil.h"
-
 #include "ObjectActionSpring.h"
 
 ObjectActionSpring::ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity) :
@@ -27,69 +24,52 @@ ObjectActionSpring::~ObjectActionSpring() {
     #endif
 }
 
-void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
-    if (!_ownerEntity) {
-        qDebug() << "ObjectActionSpring::updateAction no owner entity";
-        return;
-    }
-    if (!tryLockForRead()) {
-        // don't risk hanging the thread running the physics simulation
-        return;
-    }
-    void* physicsInfo = _ownerEntity->getPhysicsInfo();
+void ObjectActionSpring::updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep,
+                                            ObjectMotionState* motionState, btRigidBody* rigidBody) {
+    // handle the linear part
+    if (_positionalTargetSet) {
+        glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition());
+        float offsetLength = glm::length(offset);
+        float speed = offsetLength / _linearTimeScale;
 
-    if (_active && physicsInfo) {
-        ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
-        btRigidBody* rigidBody = motionState->getRigidBody();
-        if (rigidBody) {
-            // handle the linear part
-            if (_positionalTargetSet) {
-                glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition());
-                float offsetLength = glm::length(offset);
-                float speed = offsetLength / _linearTimeScale;
-
-                if (offsetLength > IGNORE_POSITION_DELTA) {
-                    glm::vec3 newVelocity = glm::normalize(offset) * speed;
-                    rigidBody->setLinearVelocity(glmToBullet(newVelocity));
-                    // void setAngularVelocity (const btVector3 &ang_vel);
-                    rigidBody->activate();
-                } else {
-                    rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
-                }
-            }
-
-            // handle rotation
-            if (_rotationalTargetSet) {
-                glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation());
-                // if qZero and qOne are too close to each other, we can get NaN for angle.
-                auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget);
-                const float almostOne = 0.99999;
-                if (glm::abs(alignmentDot) < almostOne) {
-                    glm::quat target = _rotationalTarget;
-                    if (alignmentDot < 0) {
-                        target = -target;
-                    }
-                    glm::quat qZeroInverse = glm::inverse(bodyRotation);
-                    glm::quat deltaQ = target * qZeroInverse;
-                    glm::vec3 axis = glm::axis(deltaQ);
-                    float angle = glm::angle(deltaQ);
-                    if (isNaN(angle)) {
-                        qDebug() << "ObjectActionSpring::updateAction angle =" << angle
-                                 << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w
-                                 << "target-rotation ="
-                                 << target.x << target.y << target.z<< target.w;
-                    }
-                    assert(!isNaN(angle));
-                    glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis);
-                    rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity));
-                    rigidBody->activate();
-                } else {
-                    rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f)));
-                }
-            }
+        if (offsetLength > IGNORE_POSITION_DELTA) {
+            glm::vec3 newVelocity = glm::normalize(offset) * speed;
+            rigidBody->setLinearVelocity(glmToBullet(newVelocity));
+            rigidBody->activate();
+        } else {
+            rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
+        }
+    }
+
+    // handle rotation
+    if (_rotationalTargetSet) {
+        glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation());
+        // if qZero and qOne are too close to each other, we can get NaN for angle.
+        auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget);
+        const float almostOne = 0.99999;
+        if (glm::abs(alignmentDot) < almostOne) {
+            glm::quat target = _rotationalTarget;
+            if (alignmentDot < 0) {
+                target = -target;
+            }
+            glm::quat qZeroInverse = glm::inverse(bodyRotation);
+            glm::quat deltaQ = target * qZeroInverse;
+            glm::vec3 axis = glm::axis(deltaQ);
+            float angle = glm::angle(deltaQ);
+            if (isNaN(angle)) {
+                qDebug() << "ObjectActionSpring::updateAction angle =" << angle
+                         << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w
+                         << "target-rotation ="
+                         << target.x << target.y << target.z<< target.w;
+            }
+            assert(!isNaN(angle));
+            glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis);
+            rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity));
+            rigidBody->activate();
+        } else {
+            rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f)));
         }
     }
-    unlock();
 }
 
 
diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h
index b211259866..0ce06ab43e 100644
--- a/libraries/physics/src/ObjectActionSpring.h
+++ b/libraries/physics/src/ObjectActionSpring.h
@@ -23,7 +23,8 @@ public:
     virtual ~ObjectActionSpring();
 
     virtual bool updateArguments(QVariantMap arguments);
-    virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
+    virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep,
+                                    ObjectMotionState* motionState, btRigidBody* rigidBody);
 
 private:
 

From 4df87ec4e833a1414d21a15304ab922c51b52b05 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Tue, 9 Jun 2015 17:23:37 -0700
Subject: [PATCH 30/88] fix rendering of simulation ownership debug info

---
 .../src/RenderableBoxEntityItem.cpp           | 51 ++++++++++++++-----
 .../src/RenderableDebugableEntityItem.cpp     | 44 ++++++++--------
 .../src/RenderableDebugableEntityItem.h       |  1 -
 .../src/RenderableModelEntityItem.cpp         | 13 -----
 4 files changed, 59 insertions(+), 50 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
index b2400b797e..2d72897faf 100644
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
@@ -15,6 +15,7 @@
 #include <gpu/Batch.h>
 
 #include <DeferredLightingEffect.h>
+#include <ObjectMotionState.h>
 #include <PerfStat.h>
 
 #include "RenderableBoxEntityItem.h"
@@ -27,23 +28,45 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
     PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
     Q_ASSERT(getType() == EntityTypes::Box);
     glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
-
-    bool debugSimulationOwnership = args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP;
-    bool highlightSimulationOwnership = false;
-    if (debugSimulationOwnership) {
-        auto nodeList = DependencyManager::get<NodeList>();
-        const QUuid& myNodeID = nodeList->getSessionUUID();
-        highlightSimulationOwnership = (getSimulatorID() == myNodeID);
-    }
     
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
     batch.setModelTransform(getTransformToCenter());
-    if (highlightSimulationOwnership) {
-        DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor);
-    } else {
-        DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
-    }
+    DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
 
-    RenderableDebugableEntityItem::render(this, args);
+    // TODO: use RenderableDebugableEntityItem::render (instead of the hack below) 
+    // when we fix the scaling bug that breaks RenderableDebugableEntityItem for boxes.
+    if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) {
+        Q_ASSERT(args->_batch);
+        gpu::Batch& batch = *args->_batch;
+        Transform transform = getTransformToCenter();
+        //transform.postScale(entity->getDimensions()); // HACK: this line breaks for BoxEntityItem
+        batch.setModelTransform(transform);
+
+        auto nodeList = DependencyManager::get<NodeList>();
+        const QUuid& myNodeID = nodeList->getSessionUUID();
+        bool highlightSimulationOwnership = (getSimulatorID() == myNodeID);
+        if (highlightSimulationOwnership) {
+            glm::vec4 greenColor(0.0f, 1.0f, 0.2f, 1.0f);
+            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.08f, greenColor);
+        }
+
+        quint64 now = usecTimestampNow();
+        if (now - getLastEditedFromRemote() < 0.1f * USECS_PER_SECOND) {
+            glm::vec4 redColor(1.0f, 0.0f, 0.0f, 1.0f);
+            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.16f, redColor);
+        }
+
+        if (now - getLastBroadcast() < 0.2f * USECS_PER_SECOND) {
+            glm::vec4 yellowColor(1.0f, 1.0f, 0.2f, 1.0f);
+            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.24f, yellowColor);
+        }
+
+        ObjectMotionState* motionState = static_cast<ObjectMotionState*>(getPhysicsInfo());
+        if (motionState && motionState->isActive()) {
+            glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
+            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.32f, blueColor);
+        }
+    }
+    //RenderableDebugableEntityItem::render(this, args); // TODO: use this instead of the hack above
 };
diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
index ca81ae4f2b..53213eae51 100644
--- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
@@ -15,9 +15,8 @@
 
 #include <gpu/GPUConfig.h>
 #include <gpu/Batch.h>
-
 #include <DeferredLightingEffect.h>
-#include <PhysicsEngine.h>
+#include <ObjectMotionState.h>
 
 #include "RenderableDebugableEntityItem.h"
 
@@ -26,42 +25,43 @@ void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, Render
                                                       float puffedOut, glm::vec4& color) {
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(entity->getTransformToCenter());
+    Transform transform = entity->getTransformToCenter();
+    transform.postScale(entity->getDimensions());
+    batch.setModelTransform(transform);
     DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f + puffedOut, color);
 }
 
-void RenderableDebugableEntityItem::renderHoverDot(EntityItem* entity, RenderArgs* args) {
-    const int SLICES = 8, STACKS = 8;
-    float radius = 0.05f;
-    glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
-    
-    Q_ASSERT(args->_batch);
-    gpu::Batch& batch = *args->_batch;
-    Transform transform = entity->getTransformToCenter();
-    // Cancel true dimensions and set scale to 2 * radius (diameter)
-    transform.postScale(2.0f * glm::vec3(radius, radius, radius) / entity->getDimensions());
-    batch.setModelTransform(transform);
-    DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, blueColor);
-}
-
 void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) {
-    bool debugSimulationOwnership = args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP;
+    if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) {
+        Q_ASSERT(args->_batch);
+        gpu::Batch& batch = *args->_batch;
+        Transform transform = entity->getTransformToCenter();
+        transform.postScale(entity->getDimensions());
+        batch.setModelTransform(transform);
+
+        auto nodeList = DependencyManager::get<NodeList>();
+        const QUuid& myNodeID = nodeList->getSessionUUID();
+        bool highlightSimulationOwnership = (entity->getSimulatorID() == myNodeID);
+        if (highlightSimulationOwnership) {
+            glm::vec4 greenColor(0.0f, 1.0f, 0.2f, 1.0f);
+            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.08f, greenColor);
+        }
 
-    if (debugSimulationOwnership) {
         quint64 now = usecTimestampNow();
         if (now - entity->getLastEditedFromRemote() < 0.1f * USECS_PER_SECOND) {
             glm::vec4 redColor(1.0f, 0.0f, 0.0f, 1.0f);
-            renderBoundingBox(entity, args, 0.2f, redColor);
+            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.16f, redColor);
         }
 
         if (now - entity->getLastBroadcast() < 0.2f * USECS_PER_SECOND) {
             glm::vec4 yellowColor(1.0f, 1.0f, 0.2f, 1.0f);
-            renderBoundingBox(entity, args, 0.3f, yellowColor);
+            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.24f, yellowColor);
         }
 
         ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
         if (motionState && motionState->isActive()) {
-            renderHoverDot(entity, args);
+            glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
+            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.32f, blueColor);
         }
     }
 }
diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.h b/libraries/entities-renderer/src/RenderableDebugableEntityItem.h
index 758bac353b..2680d882f5 100644
--- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.h
@@ -17,7 +17,6 @@
 class RenderableDebugableEntityItem {
 public:
     static void renderBoundingBox(EntityItem* entity, RenderArgs* args, float puffedOut, glm::vec4& color);
-    static void renderHoverDot(EntityItem* entity, RenderArgs* args);
     static void render(EntityItem* entity, RenderArgs* args);
 };
 
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index e0bc493a5c..14a64d289e 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -201,14 +201,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
     glm::vec3 position = getPosition();
     glm::vec3 dimensions = getDimensions();
 
-    bool debugSimulationOwnership = args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP;
-    bool highlightSimulationOwnership = false;
-    if (debugSimulationOwnership) {
-        auto nodeList = DependencyManager::get<NodeList>();
-        const QUuid& myNodeID = nodeList->getSessionUUID();
-        highlightSimulationOwnership = (getSimulatorID() == myNodeID);
-    }
-
     if (hasModel()) {
         if (_model) {
             if (QUrl(getModelURL()) != _model->getURL()) {
@@ -274,11 +266,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
                 }
             }
         }
-
-        if (highlightSimulationOwnership) {
-            glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
-            RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor);
-        }
     } else {
         glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
         RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor);

From 111284158643fd79bd9b85f5b38a4d9ce98772e1 Mon Sep 17 00:00:00 2001
From: Stephen Birarda <commit@birarda.com>
Date: Tue, 9 Jun 2015 19:01:03 -0700
Subject: [PATCH 31/88] fix indentation in AddressManager

---
 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 642ca4748d..51575bfde5 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -218,7 +218,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
                 const QString DOMAIN_NETWORK_PORT_KEY = "network_port";
                 const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address";
 
-               DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress);
+                DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress);
 
                 const QString DOMAIN_ID_KEY = "id";
                 QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();

From 94c414e4e86ed32a5ebc68f673d3d48c41b9d117 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Wed, 10 Jun 2015 08:12:58 -0700
Subject: [PATCH 32/88] Add unit quad, commonly used in compositing the
 overlays

---
 libraries/render-utils/src/GeometryCache.cpp | 15 +++++++++++++++
 libraries/render-utils/src/GeometryCache.h   |  3 +++
 2 files changed, 18 insertions(+)

diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index 303d63bef8..3066fd4890 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -1179,6 +1179,21 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co
     batch.draw(gpu::QUADS, 4, 0);
 }
 
+void GeometryCache::renderUnitQuad(const glm::vec4& color, int id) {
+    gpu::Batch batch;
+    renderUnitQuad(batch, color, id);
+    gpu::GLBackend::renderBatch(batch);
+}
+
+void GeometryCache::renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id) {
+    static const glm::vec2 topLeft(-1, 1);
+    static const glm::vec2 bottomRight(1, -1);
+    static const glm::vec2 texCoordTopLeft(0.0f, 1.0f);
+    static const glm::vec2 texCoordBottomRight(1.0f, 0.0f);
+    renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color, id);
+}
+
+
 void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner,
                     const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, 
                     const glm::vec4& color, int id) {
diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h
index b438eb2d3b..76e03f8669 100644
--- a/libraries/render-utils/src/GeometryCache.h
+++ b/libraries/render-utils/src/GeometryCache.h
@@ -155,6 +155,9 @@ public:
     void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID);
     void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID);
 
+    void renderUnitQuad(const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID);
+    void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID);
+
     void renderQuad(int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID)
             { renderQuad(glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); }
     void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID)

From 7374fb84e896a046361aee080f23459e25bd9921 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Wed, 10 Jun 2015 09:36:14 -0700
Subject: [PATCH 33/88] Working on fixing overlays with team-teaching merge

---
 interface/src/Application.cpp           |   2 +-
 interface/src/devices/OculusManager.cpp |  29 ++-
 interface/src/devices/TV3DManager.cpp   |   2 +-
 interface/src/ui/ApplicationOverlay.cpp | 313 ++++++++++++------------
 interface/src/ui/ApplicationOverlay.h   |  21 +-
 5 files changed, 198 insertions(+), 169 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 1763623fa6..9d5c43b7cf 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -958,7 +958,7 @@ void Application::paintGL() {
                             GL_COLOR_BUFFER_BIT, GL_NEAREST);
         glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
 
-        _applicationOverlay.displayOverlayTexture();
+        _applicationOverlay.displayOverlayTexture(&renderArgs);
     }
 
     if (!OculusManager::isConnected() || OculusManager::allowSwap()) {
diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp
index a7383ae4bb..77263935f6 100644
--- a/interface/src/devices/OculusManager.cpp
+++ b/interface/src/devices/OculusManager.cpp
@@ -615,12 +615,9 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
 
         renderArgs->_renderSide = RenderArgs::MONO;
         qApp->displaySide(renderArgs, *_camera, false);
-        qApp->getApplicationOverlay().displayOverlayTextureHmd(*_camera);
     });
     _activeEye = ovrEye_Count;
 
-    glPopMatrix();
-
     gpu::FramebufferPointer finalFbo;
     //Bind the output texture from the glow shader. If glow effect is disabled, we just grab the texture
     if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) {
@@ -632,9 +629,35 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
         glBindFramebuffer(GL_FRAMEBUFFER, 0);
     }
 
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo));
+    //Render each eye into an fbo
+    for_each_eye(_ovrHmd, [&](ovrEyeType eye) {
+        _activeEye = eye;
+        // Update our camera to what the application camera is doing
+        _camera->setRotation(toGlm(eyeRenderPose[eye].Orientation));
+        _camera->setPosition(toGlm(eyeRenderPose[eye].Position));
+        configureCamera(*_camera);
+        glMatrixMode(GL_PROJECTION);
+        glLoadMatrixf(glm::value_ptr(_camera->getProjection()));
+
+        glMatrixMode(GL_MODELVIEW);
+        glLoadIdentity();
+
+        ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport;
+        vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale;
+        vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale;
+
+        glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h);
+        qApp->getApplicationOverlay().displayOverlayTextureHmd(renderArgs, *_camera);
+    });
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
+    glPopMatrix();
+
     glMatrixMode(GL_PROJECTION);
     glPopMatrix();
 
+
     // restore our normal viewport
     glViewport(0, 0, deviceSize.width(), deviceSize.height());
 
diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp
index 09edb03e5a..c14e589389 100644
--- a/interface/src/devices/TV3DManager.cpp
+++ b/interface/src/devices/TV3DManager.cpp
@@ -120,7 +120,7 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
         glLoadIdentity();
         renderArgs->_renderSide = RenderArgs::MONO;
         qApp->displaySide(renderArgs, eyeCamera, false);
-        qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov);
+        qApp->getApplicationOverlay().displayOverlayTextureStereo(renderArgs, whichCamera, _aspect, fov);
         _activeEye = NULL;
     }, [&]{
         // render right side view
diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index bc8047fc98..79ec39b506 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -14,13 +14,16 @@
 #include <QOpenGLFramebufferObject>
 #include <QOpenGLTexture>
 
+#include <glm/gtc/type_ptr.hpp>
+
 #include <avatar/AvatarManager.h>
+#include <DeferredLightingEffect.h>
 #include <GLMHelpers.h>
-#include <PathUtils.h>
 #include <gpu/GLBackend.h>
 #include <GLMHelpers.h>
-#include <PerfStat.h>
 #include <OffscreenUi.h>
+#include <PathUtils.h>
+#include <PerfStat.h>
 
 #include "AudioClient.h"
 #include "audio/AudioIOStatsRenderer.h"
@@ -149,7 +152,8 @@ ApplicationOverlay::ApplicationOverlay() :
     _previousMagnifierBottomLeft(),
     _previousMagnifierBottomRight(),
     _previousMagnifierTopLeft(),
-    _previousMagnifierTopRight()
+    _previousMagnifierTopRight(),
+    _framebufferObject(nullptr)
 {
     memset(_reticleActive, 0, sizeof(_reticleActive));
     memset(_magActive, 0, sizeof(_reticleActive));
@@ -196,16 +200,17 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
     //Handle fading and deactivation/activation of UI
     
     // Render 2D overlay
-    glMatrixMode(GL_PROJECTION);
     glDisable(GL_DEPTH_TEST);
     glDisable(GL_LIGHTING);
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    _overlays.buildFramebufferObject();
-    _overlays.bind();
+    buildFramebufferObject();
+
+    _framebufferObject->bind();
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glViewport(0, 0, size.x, size.y);
 
+    glMatrixMode(GL_PROJECTION);
     glPushMatrix(); {
         const float NEAR_CLIP = -10000;
         const float FAR_CLIP = 10000;
@@ -227,6 +232,22 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
         renderPointers();
 
         renderDomainConnectionStatusBorder();
+        if (_newUiTexture) {
+            glMatrixMode(GL_PROJECTION);
+            glLoadIdentity();
+            glMatrixMode(GL_MODELVIEW);
+            glLoadIdentity();
+            glEnable(GL_TEXTURE_2D);
+            glEnable(GL_BLEND);
+            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+            glBindTexture(GL_TEXTURE_2D, _newUiTexture);
+            DependencyManager::get<GeometryCache>()->renderUnitQuad();
+            glBindTexture(GL_TEXTURE_2D, 0);
+            glDisable(GL_TEXTURE_2D);
+            glDisable(GL_BLEND);
+        }
+        glLoadIdentity();
+
 
         glMatrixMode(GL_PROJECTION);
     } glPopMatrix();
@@ -236,166 +257,159 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
     glEnable(GL_LIGHTING);
     glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
 
-    _overlays.release();
+    _framebufferObject->release();
 }
 
 // A quick and dirty solution for compositing the old overlay 
 // texture with the new one
-template <typename F>
-void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) {
-    glEnable(GL_TEXTURE_2D);
-    glActiveTexture(GL_TEXTURE0);
-    if (firstPassTexture) {
-        glBindTexture(GL_TEXTURE_2D, firstPassTexture);
-        f();
-    }
-    if (secondPassTexture) {
-        glBindTexture(GL_TEXTURE_2D, secondPassTexture);
-        f();
-    }
-    glBindTexture(GL_TEXTURE_2D, 0);
-    glDisable(GL_TEXTURE_2D);
-}
+//template <typename F>
+//void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) {
+//    glEnable(GL_TEXTURE_2D);
+//    glActiveTexture(GL_TEXTURE0);
+//    if (firstPassTexture) {
+//        glBindTexture(GL_TEXTURE_2D, firstPassTexture);
+//        f();
+//    }
+//    //if (secondPassTexture) {
+//    //    glBindTexture(GL_TEXTURE_2D, secondPassTexture);
+//    //    f();
+//    //}
+//    glBindTexture(GL_TEXTURE_2D, 0);
+//    glDisable(GL_TEXTURE_2D);
+//}
 
 // Draws the FBO texture for the screen
-void ApplicationOverlay::displayOverlayTexture() {
+void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
+
     if (_alpha == 0.0f) {
         return;
     }
+
+    if (!_crosshairTexture) {
+        _crosshairTexture = TextureCache::getImageTexture(
+            PathUtils::resourcesPath() + "images/sixense-reticle.png");
+    }
+
+    /*
+    FIXME - doesn't work
+    renderArgs->_context->syncCache();
+    gpu::Batch batch;
+    DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
+    batch.setModelTransform(Transform());
+    batch.setProjectionTransform(mat4());
+    batch.setViewTransform(Transform());
+    batch.setUniformTexture(0, _crosshairTexture);
+    DependencyManager::get<GeometryCache>()->renderUnitQuad(batch, vec4(vec3(1), _alpha));
+    renderArgs->_context->render(batch);
+    return;
+    */
+
+    
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_LIGHTING);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glEnable(GL_TEXTURE_2D);
+    glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height());
+
     glMatrixMode(GL_PROJECTION);
-    glPushMatrix(); {
-        glLoadIdentity();
-        glDisable(GL_DEPTH_TEST);
-        glDisable(GL_LIGHTING);
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-        glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height());
-
-        static const glm::vec2 topLeft(-1, 1);
-        static const glm::vec2 bottomRight(1, -1);
-        static const glm::vec2 texCoordTopLeft(0.0f, 1.0f);
-        static const glm::vec2 texCoordBottomRight(1.0f, 0.0f);
-        with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
-            DependencyManager::get<GeometryCache>()->renderQuad(
-                topLeft, bottomRight, 
-                texCoordTopLeft, texCoordBottomRight,
-                glm::vec4(1.0f, 1.0f, 1.0f, _alpha));
-        });
-
-        if (!_crosshairTexture) {
-            _crosshairTexture = DependencyManager::get<TextureCache>()->
-                getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
-        }
-
+    glPushMatrix(); 
+    glLoadIdentity();
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadIdentity();
+    {
+        glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
+        DependencyManager::get<GeometryCache>()->renderUnitQuad(vec4(vec3(1), _alpha));
         //draw the mouse pointer
         glm::vec2 canvasSize = qApp->getCanvasSize();
-        glm::vec2 mouseSize = 32.0f / canvasSize;
-        auto mouseTopLeft = topLeft * mouseSize;
-        auto mouseBottomRight = bottomRight * mouseSize;
+
+        // Get the mouse coordinates and convert to NDC [-1, 1]
         vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY());
         mousePosition /= canvasSize;
         mousePosition *= 2.0f;
         mousePosition -= 1.0f;
         mousePosition.y *= -1.0f;
+        mat4 mouseMv = glm::translate(mat4(), vec3(mousePosition, 0));
 
-        glEnable(GL_TEXTURE_2D);
-        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        // Scale the mouse based on the canvasSize (NOT the device size, 
+        // we don't want a smaller mouse on retina displays)
+        glm::vec2 mouseSize = 32.0f / canvasSize;
+        mouseMv = glm::scale(mouseMv, vec3(mouseSize, 1.0f));
+
+        // Push the resulting matrix into modelview
+        glLoadMatrixf(glm::value_ptr(mouseMv));
         glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
         glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
-        DependencyManager::get<GeometryCache>()->renderQuad(
-            mouseTopLeft + mousePosition, mouseBottomRight + mousePosition, 
-            texCoordTopLeft, texCoordBottomRight,
-            reticleColor);
-        glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
-        glDisable(GL_TEXTURE_2D);
-    } glPopMatrix();
+        DependencyManager::get<GeometryCache>()->renderUnitQuad(reticleColor);
+    } 
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    glMatrixMode(GL_MODELVIEW);
+    glPopMatrix();
 }
 
 // Draws the FBO texture for Oculus rift.
-void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) {
+void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) {
     if (_alpha == 0.0f) {
         return;
     }
 
-    glEnable(GL_BLEND);
-    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
-    glEnable(GL_DEPTH_TEST);
-    glDepthMask(GL_TRUE);
+    _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80);
+    glDisable(GL_DEPTH_TEST);
     glDisable(GL_LIGHTING);
-    glEnable(GL_ALPHA_TEST);
-    glAlphaFunc(GL_GREATER, 0.01f);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glEnable(GL_TEXTURE_2D);
+    glActiveTexture(GL_TEXTURE0);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    // The camera here contains only the head pose relative to the avatar position
+    vec3 pos = whichCamera.getPosition();
+    quat rot = whichCamera.getOrientation();
+    mat4 overlayXfm = glm::translate(glm::mat4(), pos) * glm::mat4_cast(rot);
 
+    glLoadMatrixf(glm::value_ptr(glm::inverse(overlayXfm)));
+    glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
+    _overlays.render();
 
     //Update and draw the magnifiers
+    /*
+    // FIXME Mangifiers need to be re-thought
     MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
-    const glm::quat& orientation = myAvatar->getOrientation();
-    // Always display the HMD overlay relative to the camera position but 
-    // remove the HMD pose offset.  This results in an overlay that sticks with you 
-    // even in third person mode, but isn't drawn at a fixed distance.
-    glm::vec3 position = whichCamera.getPosition();
-    position -= qApp->getCamera()->getHmdPosition();
     const float scale = myAvatar->getScale() * _oculusUIRadius;
-    
-//    glm::vec3 eyeOffset = setEyeOffsetPosition;
-    glMatrixMode(GL_MODELVIEW);
-    glPushMatrix(); {
-        glTranslatef(position.x, position.y, position.z);
-        glm::mat4 rotation = glm::toMat4(orientation);
-        glMultMatrixf(&rotation[0][0]);
-        glScalef(scale, scale, scale);
-        for (int i = 0; i < NUMBER_OF_RETICLES; i++) {
-            
-            if (_magActive[i]) {
-                _magSizeMult[i] += MAG_SPEED;
-                if (_magSizeMult[i] > 1.0f) {
-                    _magSizeMult[i] = 1.0f;
-                }
-            } else {
-                _magSizeMult[i] -= MAG_SPEED;
-                if (_magSizeMult[i] < 0.0f) {
-                    _magSizeMult[i] = 0.0f;
-                }
+    overlayXfm = glm::scale(overlayXfm, vec3(scale));
+    for (int i = 0; i < NUMBER_OF_RETICLES; i++) {
+        if (_magActive[i]) {
+            _magSizeMult[i] += MAG_SPEED;
+            if (_magSizeMult[i] > 1.0f) {
+                _magSizeMult[i] = 1.0f;
             }
-            
-            if (_magSizeMult[i] > 0.0f) {
-                //Render magnifier, but dont show border for mouse magnifier
-                glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(),
-                                                                 _reticlePosition[MOUSE].y()));
-                with_each_texture(_overlays.getTexture(), 0, [&] {
-                    renderMagnifier(projection, _magSizeMult[i], i != MOUSE);
-                });
+        } else {
+            _magSizeMult[i] -= MAG_SPEED;
+            if (_magSizeMult[i] < 0.0f) {
+                _magSizeMult[i] = 0.0f;
             }
         }
-        
-        glDepthMask(GL_FALSE);
-        glDisable(GL_ALPHA_TEST);
-        
-        static float textureFOV = 0.0f, textureAspectRatio = 1.0f;
-        if (textureFOV != _textureFov ||
-            textureAspectRatio != _textureAspectRatio) {
-            textureFOV = _textureFov;
-            textureAspectRatio = _textureAspectRatio;
             
-            _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80);
+        if (_magSizeMult[i] > 0.0f) {
+            //Render magnifier, but dont show border for mouse magnifier
+            glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(),
+                                                                _reticlePosition[MOUSE].y()));
+            with_each_texture(_overlays.getTexture(), 0, [&] {
+                renderMagnifier(projection, _magSizeMult[i], i != MOUSE);
+            });
         }
+    }
+    */
 
-        with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
-            _overlays.render();
-        });
-
-        if (!Application::getInstance()->isMouseHidden()) {
-            renderPointersOculus();
-        }
-        glDepthMask(GL_TRUE);
-        glDisable(GL_TEXTURE_2D);
-        
-        glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
-        glEnable(GL_LIGHTING);
-    } glPopMatrix();
+    if (!Application::getInstance()->isMouseHidden()) {
+        renderPointersOculus();
+    }
 }
 
 // Draws the FBO texture for 3DTV.
-void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) {
+void ApplicationOverlay::displayOverlayTextureStereo(RenderArgs* renderArgs, Camera& whichCamera, float aspectRatio, float fov) {
     if (_alpha == 0.0f) {
         return;
     }
@@ -440,15 +454,15 @@ void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float
     GLfloat y = -halfQuadHeight;
     glDisable(GL_DEPTH_TEST);
 
-    with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
-        DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance),
-                                                glm::vec3(x + quadWidth, y + quadHeight, -distance),
-                                                glm::vec3(x + quadWidth, y, -distance),
-                                                glm::vec3(x, y, -distance),
-                                                glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), 
-                                                glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f),
-                                                overlayColor);
-    });
+    //with_each_texture(_framebufferObject->texture(), _newUiTexture, [&] {
+    //    DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance),
+    //                                            glm::vec3(x + quadWidth, y + quadHeight, -distance),
+    //                                            glm::vec3(x + quadWidth, y, -distance),
+    //                                            glm::vec3(x, y, -distance),
+    //                                            glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), 
+    //                                            glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f),
+    //                                            overlayColor);
+    //});
     
     if (!_crosshairTexture) {
         _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() +
@@ -1071,29 +1085,24 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() {
 ApplicationOverlay::TexturedHemisphere::TexturedHemisphere() :
     _vertices(0),
     _indices(0),
-    _framebufferObject(NULL),
     _vbo(0, 0) {
 }
 
 ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() {
     cleanupVBO();
-    if (_framebufferObject != NULL) {
-        delete _framebufferObject;
-    }
-}
-
-void ApplicationOverlay::TexturedHemisphere::bind() {
-    _framebufferObject->bind();
-}
-
-void ApplicationOverlay::TexturedHemisphere::release() {
-    _framebufferObject->release();
 }
 
 void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov,
                                                       const float aspectRatio,
                                                       const int slices,
                                                       const int stacks) {
+    static float textureFOV = 0.0f, textureAspectRatio = 1.0f;
+    if (textureFOV == fov && textureAspectRatio == aspectRatio) {
+        return;
+    }
+    textureFOV = fov;
+    textureAspectRatio = aspectRatio;
+
     if (fov >= PI) {
         qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues";
     }
@@ -1176,7 +1185,11 @@ void ApplicationOverlay::TexturedHemisphere::cleanupVBO() {
     }
 }
 
-void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() {
+GLuint ApplicationOverlay::getOverlayTexture() {
+    return _framebufferObject->texture();
+}
+
+void ApplicationOverlay::buildFramebufferObject() {
     auto canvasSize = qApp->getCanvasSize();
     QSize fboSize = QSize(canvasSize.x, canvasSize.y);
     if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) {
@@ -1189,7 +1202,7 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() {
     }
     
     _framebufferObject = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth);
-    glBindTexture(GL_TEXTURE_2D, getTexture());
+    glBindTexture(GL_TEXTURE_2D, getOverlayTexture());
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
@@ -1201,7 +1214,7 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() {
 
 //Renders a hemisphere with texture coordinates.
 void ApplicationOverlay::TexturedHemisphere::render() {
-    if (_framebufferObject == NULL || _vbo.first == 0 || _vbo.second == 0) {
+    if (_vbo.first == 0 || _vbo.second == 0) {
         qDebug() << "TexturedHemisphere::render(): Incorrect initialisation";
         return;
     }
@@ -1227,10 +1240,6 @@ void ApplicationOverlay::TexturedHemisphere::render() {
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 }
 
-GLuint ApplicationOverlay::TexturedHemisphere::getTexture() {
-    return _framebufferObject->texture();
-}
-
 glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) {
     glm::vec2 result;
     // Compute yaw
diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h
index ee82c9274a..36161dd29d 100644
--- a/interface/src/ui/ApplicationOverlay.h
+++ b/interface/src/ui/ApplicationOverlay.h
@@ -33,9 +33,9 @@ public:
     ~ApplicationOverlay();
 
     void renderOverlay(RenderArgs* renderArgs);
-    void displayOverlayTexture();
-    void displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov);
-    void displayOverlayTextureHmd(Camera& whichCamera);
+    void displayOverlayTexture(RenderArgs* renderArgs);
+    void displayOverlayTextureStereo(RenderArgs* renderArgs, Camera& whichCamera, float aspectRatio, float fov);
+    void displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera);
 
     QPoint getPalmClickLocation(const PalmData *palm) const;
     bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
@@ -59,6 +59,7 @@ public:
     glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const;
     glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const;
     void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const;
+    GLuint getOverlayTexture();
 
     static glm::vec2 directionToSpherical(const glm::vec3 & direction);
     static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos);
@@ -77,12 +78,6 @@ private:
     public:
         TexturedHemisphere();
         ~TexturedHemisphere();
-        
-        void bind();
-        void release();
-        GLuint getTexture();
-        
-        void buildFramebufferObject();
         void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks);
         void render();
         
@@ -91,14 +86,14 @@ private:
         
         GLuint _vertices;
         GLuint _indices;
-        QOpenGLFramebufferObject* _framebufferObject;
         VerticesIndices _vbo;
     };
     
     float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE;
-    
+    QOpenGLFramebufferObject* _framebufferObject;
+
     void renderReticle(glm::quat orientation, float alpha);
-    void renderPointers();;
+    void renderPointers();
     void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder);
     
     void renderControllerPointers();
@@ -109,6 +104,8 @@ private:
     void renderStatsAndLogs();
     void renderDomainConnectionStatusBorder();
 
+    void buildFramebufferObject();
+
     TexturedHemisphere _overlays;
     
     float _textureFov;

From 4617b7331c9614a687c71a4645e1707df0ce571a Mon Sep 17 00:00:00 2001
From: Niraj Venkat <venkatn93@gmail.com>
Date: Wed, 10 Jun 2015 09:50:47 -0700
Subject: [PATCH 34/88] Working on creating functions to serialize and
 deserialize properties

---
 examples/html/entityProperties.html | 1 -
 1 file changed, 1 deletion(-)

diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html
index bebbb1d80e..a2387a51bf 100644
--- a/examples/html/entityProperties.html
+++ b/examples/html/entityProperties.html
@@ -855,7 +855,6 @@
             elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle'));
 
             var hyperlinkChangeFunction = createEmitGroupTextPropertyUpdateFunction('hyperlink','href');
-
             var hyperlinkChangeFunction = createEmitGroupTextPropertyUpdateFunction('hyperlink','description');
 
 

From 3dcc6c9b8ce28722928c891feae1d21009842e21 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 10 Jun 2015 12:04:44 -0700
Subject: [PATCH 35/88] make action-factory interface and subclass it in
 Interface.  this allows an action to use avatar data.  the login the
 AvatarActionHold is still bogus.

---
 interface/src/Application.cpp                 |   5 +-
 interface/src/InterfaceActionFactory.cpp      |  49 ++++++++
 interface/src/InterfaceActionFactory.h        |  28 +++++
 interface/src/avatar/AvatarActionHold.cpp     | 111 ++++++++++++++++++
 interface/src/avatar/AvatarActionHold.h       |  39 ++++++
 .../src/EntityActionFactoryInterface.h        |  33 ++++++
 .../entities/src/EntityActionInterface.cpp    |   2 +
 .../entities/src/EntityActionInterface.h      |  14 ++-
 libraries/entities/src/EntityItem.h           |   5 +-
 .../entities/src/EntityScriptingInterface.cpp |   5 +-
 libraries/entities/src/EntitySimulation.h     |   5 +-
 libraries/physics/src/ObjectAction.cpp        |  94 +++++++++++++--
 libraries/physics/src/ObjectAction.h          |  15 ++-
 .../physics/src/ObjectActionPullToPoint.cpp   |  13 +-
 .../physics/src/ObjectActionPullToPoint.h     |   3 +-
 libraries/physics/src/ObjectActionSpring.cpp  |  13 +-
 libraries/physics/src/ObjectActionSpring.h    |   3 +-
 .../physics/src/PhysicalEntitySimulation.cpp  |  30 +----
 .../physics/src/PhysicalEntitySimulation.h    |   4 -
 19 files changed, 413 insertions(+), 58 deletions(-)
 create mode 100644 interface/src/InterfaceActionFactory.cpp
 create mode 100644 interface/src/InterfaceActionFactory.h
 create mode 100644 interface/src/avatar/AvatarActionHold.cpp
 create mode 100644 interface/src/avatar/AvatarActionHold.h
 create mode 100644 libraries/entities/src/EntityActionFactoryInterface.h

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 188ae6eaf9..0c90fdb9bd 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -100,6 +100,7 @@
 #include "ModelPackager.h"
 #include "Util.h"
 #include "InterfaceLogging.h"
+#include "InterfaceActionFactory.h"
 
 #include "avatar/AvatarManager.h"
 
@@ -257,6 +258,7 @@ bool setupEssentials(int& argc, char** argv) {
 
     DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
     DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
+    DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();
 
     Setting::init();
 
@@ -293,7 +295,8 @@ bool setupEssentials(int& argc, char** argv) {
     auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>();
     auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>();
     auto offscreenUi = DependencyManager::set<OffscreenUi>();
-    auto pathUtils =  DependencyManager::set<PathUtils>();
+    auto pathUtils = DependencyManager::set<PathUtils>();
+    auto actionFactory = DependencyManager::set<InterfaceActionFactory>();
 
     return true;
 }
diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp
new file mode 100644
index 0000000000..08afc3581c
--- /dev/null
+++ b/interface/src/InterfaceActionFactory.cpp
@@ -0,0 +1,49 @@
+//
+//  InterfaceActionFactory.cpp
+//  libraries/entities/src
+//
+//  Created by Seth Alves on 2015-6-2
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+
+
+#include <avatar/AvatarActionHold.h>
+#include <ObjectActionPullToPoint.h>
+#include <ObjectActionSpring.h>
+
+#include "InterfaceActionFactory.h"
+
+
+EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation,
+                                                    EntityActionType type,
+                                                    QUuid id,
+                                                    EntityItemPointer ownerEntity,
+                                                    QVariantMap arguments) {
+    EntityActionPointer action = nullptr;
+    switch (type) {
+        case ACTION_TYPE_NONE:
+            return nullptr;
+        case ACTION_TYPE_PULL_TO_POINT:
+            action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity);
+            break;
+        case ACTION_TYPE_SPRING:
+            action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
+            break;
+        case ACTION_TYPE_HOLD:
+            action = (EntityActionPointer) new AvatarActionHold(id, ownerEntity);
+            break;
+    }
+
+    bool ok = action->updateArguments(arguments);
+    if (ok) {
+        ownerEntity->addAction(simulation, action);
+        return action;
+    }
+
+    action = nullptr;
+    return action;
+}
diff --git a/interface/src/InterfaceActionFactory.h b/interface/src/InterfaceActionFactory.h
new file mode 100644
index 0000000000..5848df4635
--- /dev/null
+++ b/interface/src/InterfaceActionFactory.h
@@ -0,0 +1,28 @@
+//
+//  InterfaceActionFactory.cpp
+//  interface/src/
+//
+//  Created by Seth Alves on 2015-6-10
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_InterfaceActionFactory_h
+#define hifi_InterfaceActionFactory_h
+
+#include "EntityActionFactoryInterface.h"
+
+class InterfaceActionFactory : public EntityActionFactoryInterface {
+public:
+    InterfaceActionFactory() : EntityActionFactoryInterface() { }
+    virtual ~InterfaceActionFactory() { }
+    virtual EntityActionPointer factory(EntitySimulation* simulation,
+                                        EntityActionType type,
+                                        QUuid id,
+                                        EntityItemPointer ownerEntity,
+                                        QVariantMap arguments);
+};
+
+#endif // hifi_InterfaceActionFactory_h
diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp
new file mode 100644
index 0000000000..6d4164367a
--- /dev/null
+++ b/interface/src/avatar/AvatarActionHold.cpp
@@ -0,0 +1,111 @@
+//
+//  AvatarActionHold.cpp
+//  interface/src/avatar/
+//
+//  Created by Seth Alves 2015-6-9
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "avatar/MyAvatar.h"
+#include "avatar/AvatarManager.h"
+
+#include "AvatarActionHold.h"
+
+AvatarActionHold::AvatarActionHold(QUuid id, EntityItemPointer ownerEntity) :
+    ObjectAction(id, ownerEntity) {
+    #if WANT_DEBUG
+    qDebug() << "AvatarActionHold::AvatarActionHold";
+    #endif
+}
+
+AvatarActionHold::~AvatarActionHold() {
+    #if WANT_DEBUG
+    qDebug() << "AvatarActionHold::~AvatarActionHold";
+    #endif
+}
+
+void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
+    // handle the linear part
+    if (_linearOffsetSet) {
+    }
+
+    auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
+
+    glm::vec3 palmPosition = myAvatar->getRightPalmPosition();
+    glm::vec3 position = getPosition();
+    glm::vec3 positionalTarget = palmPosition + _linearOffset;
+    glm::vec3 offset = positionalTarget - position;
+
+    float offsetLength = glm::length(offset);
+    float speed = offsetLength / _timeScale;
+
+    if (offsetLength > IGNORE_POSITION_DELTA) {
+        glm::vec3 newVelocity = glm::normalize(offset) * speed;
+        setLinearVelocity(newVelocity);
+    } else {
+        setLinearVelocity(glm::vec3(0.0f));
+    }
+
+
+    // handle rotation
+    if (_angularOffsetSet) {
+        glm::quat bodyRotation = getRotation();
+        // if qZero and qOne are too close to each other, we can get NaN for angle.
+        auto alignmentDot = glm::dot(bodyRotation, _angularOffset);
+        const float almostOne = 0.99999;
+        if (glm::abs(alignmentDot) < almostOne) {
+            glm::quat target = _angularOffset;
+            if (alignmentDot < 0) {
+                target = -target;
+            }
+            glm::quat qZeroInverse = glm::inverse(bodyRotation);
+            glm::quat deltaQ = target * qZeroInverse;
+            glm::vec3 axis = glm::axis(deltaQ);
+            float angle = glm::angle(deltaQ);
+            if (isNaN(angle)) {
+                qDebug() << "AvatarActionHold::updateAction angle =" << angle
+                         << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w
+                         << "target-rotation ="
+                         << target.x << target.y << target.z<< target.w;
+            }
+            assert(!isNaN(angle));
+            glm::vec3 newAngularVelocity = (angle / _timeScale) * glm::normalize(axis);
+            setAngularVelocity(newAngularVelocity);
+        } else {
+            setAngularVelocity(glm::vec3(0.0f));
+        }
+    }
+}
+
+
+bool AvatarActionHold::updateArguments(QVariantMap arguments) {
+    // targets are required, spring-constants are optional
+    bool ptOk = true;
+    glm::vec3 linearOffset =
+        EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false);
+
+    bool rtOk = true;
+    glm::quat angularOffset =
+        EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false);
+
+    lockForWrite();
+
+    _linearOffsetSet = _angularOffsetSet = false;
+
+    if (ptOk) {
+        _linearOffset = linearOffset;
+        _linearOffsetSet = true;
+    }
+
+    if (rtOk) {
+        _angularOffset = angularOffset;
+        _angularOffsetSet = true;
+    }
+
+    _active = true;
+    unlock();
+    return true;
+}
diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h
new file mode 100644
index 0000000000..19e6e8df55
--- /dev/null
+++ b/interface/src/avatar/AvatarActionHold.h
@@ -0,0 +1,39 @@
+//
+//  AvatarActionHold.h
+//  interface/src/avatar/
+//
+//  Created by Seth Alves 2015-6-9
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_AvatarActionHold_h
+#define hifi_AvatarActionHold_h
+
+#include <QUuid>
+
+#include <EntityItem.h>
+#include <ObjectAction.h>
+
+class AvatarActionHold : public ObjectAction {
+public:
+    AvatarActionHold(QUuid id, EntityItemPointer ownerEntity);
+    virtual ~AvatarActionHold();
+
+    virtual bool updateArguments(QVariantMap arguments);
+    virtual void updateActionWorker(float deltaTimeStep);
+
+private:
+
+    glm::vec3 _linearOffset;
+    bool _linearOffsetSet;
+
+    glm::quat _angularOffset;
+    bool _angularOffsetSet;
+
+    float _timeScale = 0.01;
+};
+
+#endif // hifi_AvatarActionHold_h
diff --git a/libraries/entities/src/EntityActionFactoryInterface.h b/libraries/entities/src/EntityActionFactoryInterface.h
new file mode 100644
index 0000000000..2347eff525
--- /dev/null
+++ b/libraries/entities/src/EntityActionFactoryInterface.h
@@ -0,0 +1,33 @@
+//
+//  EntityActionFactoryInterface.cpp
+//  libraries/entities/src
+//
+//  Created by Seth Alves on 2015-6-2
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_EntityActionFactoryInterface_h
+#define hifi_EntityActionFactoryInterface_h
+
+#include <DependencyManager.h>
+
+#include "EntityActionInterface.h"
+
+class EntityActionFactoryInterface : public QObject, public Dependency {
+    Q_OBJECT
+    SINGLETON_DEPENDENCY
+
+ public:
+    EntityActionFactoryInterface() { }
+    virtual ~EntityActionFactoryInterface() { }
+    virtual EntityActionPointer factory(EntitySimulation* simulation,
+                                        EntityActionType type,
+                                        QUuid id,
+                                        EntityItemPointer ownerEntity,
+                                        QVariantMap arguments) { assert(false); }
+};
+
+#endif // hifi_EntityActionFactoryInterface_h
diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp
index 399c529aa2..09bb933488 100644
--- a/libraries/entities/src/EntityActionInterface.cpp
+++ b/libraries/entities/src/EntityActionInterface.cpp
@@ -38,6 +38,8 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
             return "pullToPoint";
         case ACTION_TYPE_SPRING:
             return "spring";
+        case ACTION_TYPE_HOLD:
+            return "hold";
     }
     assert(false);
     return "none";
diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h
index a0a3db9b68..50f862c535 100644
--- a/libraries/entities/src/EntityActionInterface.h
+++ b/libraries/entities/src/EntityActionInterface.h
@@ -14,13 +14,16 @@
 
 #include <QUuid>
 
+#include "EntityItem.h"
+
 class EntitySimulation;
 
 enum EntityActionType {
     // keep these synchronized with actionTypeFromString and actionTypeToString
     ACTION_TYPE_NONE,
     ACTION_TYPE_PULL_TO_POINT,
-    ACTION_TYPE_SPRING
+    ACTION_TYPE_SPRING,
+    ACTION_TYPE_HOLD
 };
 
 
@@ -40,6 +43,14 @@ public:
     static QString actionTypeToString(EntityActionType actionType);
 
 protected:
+    virtual glm::vec3 getPosition() = 0;
+    virtual void setPosition(glm::vec3 position) = 0;
+    virtual glm::quat getRotation() = 0;
+    virtual void setRotation(glm::quat rotation) = 0;
+    virtual glm::vec3 getLinearVelocity() = 0;
+    virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0;
+    virtual glm::vec3 getAngularVelocity() = 0;
+    virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0;
 
     static glm::vec3 extractVec3Argument (QString objectName, QVariantMap arguments,
                                           QString argumentName, bool& ok, bool required = true);
@@ -49,6 +60,7 @@ protected:
                                       QString argumentName, bool& ok, bool required = true);
 };
 
+
 typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
 
 #endif // hifi_EntityActionInterface_h
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index 77a6627853..7e56fb8ec4 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -28,13 +28,16 @@
 #include "EntityItemID.h"
 #include "EntityItemProperties.h"
 #include "EntityItemPropertiesDefaults.h"
-#include "EntityActionInterface.h"
 #include "EntityTypes.h"
 
 class EntitySimulation;
 class EntityTreeElement;
 class EntityTreeElementExtraEncodeData;
 
+class EntityActionInterface;
+typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
+
+
 namespace render {
     class Scene;
     class PendingChanges;
diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index 836e7f5225..6f6633ce0f 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -17,6 +17,8 @@
 #include "ZoneEntityItem.h"
 #include "EntitiesLogging.h"
 #include "EntitySimulation.h"
+#include "EntityActionInterface.h"
+#include "EntityActionFactoryInterface.h"
 
 #include "EntityScriptingInterface.h"
 
@@ -491,12 +493,13 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
                                           const QUuid& entityID,
                                           const QVariantMap& arguments) {
     QUuid actionID = QUuid::createUuid();
+    auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
     bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
             EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString);
             if (actionType == ACTION_TYPE_NONE) {
                 return false;
             }
-            if (simulation->actionFactory(actionType, actionID, entity, arguments)) {
+            if (actionFactory->factory(simulation, actionType, actionID, entity, arguments)) {
                 return true;
             }
             return false;
diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h
index 0c9b3efee6..7d244086e5 100644
--- a/libraries/entities/src/EntitySimulation.h
+++ b/libraries/entities/src/EntitySimulation.h
@@ -18,6 +18,7 @@
 
 #include <PerfStat.h>
 
+#include "EntityActionInterface.h"
 #include "EntityItem.h"
 #include "EntityTree.h"
 
@@ -56,10 +57,6 @@ public:
 
     friend class EntityTree;
 
-    virtual EntityActionPointer actionFactory(EntityActionType type,
-                                                 QUuid id,
-                                                 EntityItemPointer ownerEntity,
-                                                 QVariantMap arguments) { return nullptr; }
     virtual void addAction(EntityActionPointer action) { _actionsToAdd += action; }
     virtual void removeAction(const QUuid actionID) { _actionsToRemove += actionID; }
     virtual void removeActions(QList<QUuid> actionIDsToRemove) { _actionsToRemove += actionIDsToRemove; }
diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp
index 5f67db4355..8f01a90410 100644
--- a/libraries/physics/src/ObjectAction.cpp
+++ b/libraries/physics/src/ObjectAction.cpp
@@ -35,15 +35,9 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
         // don't risk hanging the thread running the physics simulation
         return;
     }
-    void* physicsInfo = _ownerEntity->getPhysicsInfo();
 
-    if (physicsInfo) {
-        ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
-        btRigidBody* rigidBody = motionState->getRigidBody();
-        if (rigidBody) {
-            updateActionWorker(collisionWorld, deltaTimeStep, motionState, rigidBody);
-        }
-    }
+    updateActionWorker(deltaTimeStep);
+
     unlock();
 }
 
@@ -53,3 +47,87 @@ void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
 void ObjectAction::removeFromSimulation(EntitySimulation* simulation) const {
     simulation->removeAction(_id);
 }
+
+btRigidBody* ObjectAction::getRigidBody() {
+    if (!_ownerEntity) {
+        return nullptr;
+    }
+    void* physicsInfo = _ownerEntity->getPhysicsInfo();
+    if (!physicsInfo) {
+        return nullptr;
+    }
+    ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
+    return motionState->getRigidBody();
+}
+
+glm::vec3 ObjectAction::getPosition() {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return glm::vec3(0.0f);
+    }
+    return bulletToGLM(rigidBody->getCenterOfMassPosition());
+}
+
+void ObjectAction::setPosition(glm::vec3 position) {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return;
+    }
+    // XXX
+    // void setWorldTransform (const btTransform &worldTrans)
+    assert(false);
+    rigidBody->activate();
+}
+
+glm::quat ObjectAction::getRotation() {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
+    }
+    return bulletToGLM(rigidBody->getOrientation());
+}
+
+void ObjectAction::setRotation(glm::quat rotation) {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return;
+    }
+    // XXX
+    // void setWorldTransform (const btTransform &worldTrans)
+    assert(false);
+    rigidBody->activate();
+}
+
+glm::vec3 ObjectAction::getLinearVelocity() {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return glm::vec3(0.0f);
+    }
+    return bulletToGLM(rigidBody->getLinearVelocity());
+}
+
+void ObjectAction::setLinearVelocity(glm::vec3 linearVelocity) {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return;
+    }
+    rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f)));
+    rigidBody->activate();
+}
+
+glm::vec3 ObjectAction::getAngularVelocity() {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return glm::vec3(0.0f);
+    }
+    return bulletToGLM(rigidBody->getAngularVelocity());
+}
+
+void ObjectAction::setAngularVelocity(glm::vec3 angularVelocity) {
+    auto rigidBody = getRigidBody();
+    if (!rigidBody) {
+        return;
+    }
+    rigidBody->setAngularVelocity(glmToBullet(angularVelocity));
+    rigidBody->activate();
+}
diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h
index 7ff11b8ba0..a834a54864 100644
--- a/libraries/physics/src/ObjectAction.h
+++ b/libraries/physics/src/ObjectAction.h
@@ -18,8 +18,10 @@
 #include <btBulletDynamicsCommon.h>
 
 #include <EntityItem.h>
+
 #include "ObjectMotionState.h"
 #include "BulletUtil.h"
+#include "EntityActionInterface.h"
 
 
 class ObjectAction : public btActionInterface, public EntityActionInterface {
@@ -34,8 +36,7 @@ public:
     virtual bool updateArguments(QVariantMap arguments) { return false; }
 
     // this is called from updateAction and should be overridden by subclasses
-    virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep,
-                                    ObjectMotionState* motionState, btRigidBody* rigidBody) {}
+    virtual void updateActionWorker(float deltaTimeStep) {}
 
     // these are from btActionInterface
     virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
@@ -46,6 +47,16 @@ private:
     QReadWriteLock _lock;
 
 protected:
+    btRigidBody* getRigidBody();
+    virtual glm::vec3 getPosition();
+    virtual void setPosition(glm::vec3 position);
+    virtual glm::quat getRotation();
+    virtual void setRotation(glm::quat rotation);
+    virtual glm::vec3 getLinearVelocity();
+    virtual void setLinearVelocity(glm::vec3 linearVelocity);
+    virtual glm::vec3 getAngularVelocity();
+    virtual void setAngularVelocity(glm::vec3 angularVelocity);
+
     bool tryLockForRead() { return _lock.tryLockForRead(); }
     void lockForWrite() { _lock.lockForWrite(); }
     void unlock() { _lock.unlock(); }
diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp
index 2e66b948f5..1069a2b9cf 100644
--- a/libraries/physics/src/ObjectActionPullToPoint.cpp
+++ b/libraries/physics/src/ObjectActionPullToPoint.cpp
@@ -24,8 +24,17 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() {
     #endif
 }
 
-void ObjectActionPullToPoint::updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep,
-                                                 ObjectMotionState* motionState, btRigidBody* rigidBody) {
+void ObjectActionPullToPoint::updateActionWorker(btScalar deltaTimeStep) {
+    void* physicsInfo = _ownerEntity->getPhysicsInfo();
+    if (!physicsInfo) {
+        return;
+    }
+    ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
+    btRigidBody* rigidBody = motionState->getRigidBody();
+    if (!rigidBody) {
+        return;
+    }
+
     glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition());
     float offsetLength = glm::length(offset);
     if (offsetLength > IGNORE_POSITION_DELTA) {
diff --git a/libraries/physics/src/ObjectActionPullToPoint.h b/libraries/physics/src/ObjectActionPullToPoint.h
index 55fd748921..2596f13515 100644
--- a/libraries/physics/src/ObjectActionPullToPoint.h
+++ b/libraries/physics/src/ObjectActionPullToPoint.h
@@ -23,8 +23,7 @@ public:
     virtual ~ObjectActionPullToPoint();
 
     virtual bool updateArguments(QVariantMap arguments);
-    virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep,
-                                    ObjectMotionState* motionState, btRigidBody* rigidBody);
+    virtual void updateActionWorker(float deltaTimeStep);
 
 private:
 
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
index 101af665f7..aea4dafbb8 100644
--- a/libraries/physics/src/ObjectActionSpring.cpp
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -24,8 +24,17 @@ ObjectActionSpring::~ObjectActionSpring() {
     #endif
 }
 
-void ObjectActionSpring::updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep,
-                                            ObjectMotionState* motionState, btRigidBody* rigidBody) {
+void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
+    void* physicsInfo = _ownerEntity->getPhysicsInfo();
+    if (!physicsInfo) {
+        return;
+    }
+    ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
+    btRigidBody* rigidBody = motionState->getRigidBody();
+    if (!rigidBody) {
+        return;
+    }
+
     // handle the linear part
     if (_positionalTargetSet) {
         glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition());
diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h
index 0ce06ab43e..c5cbbe6126 100644
--- a/libraries/physics/src/ObjectActionSpring.h
+++ b/libraries/physics/src/ObjectActionSpring.h
@@ -23,8 +23,7 @@ public:
     virtual ~ObjectActionSpring();
 
     virtual bool updateArguments(QVariantMap arguments);
-    virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep,
-                                    ObjectMotionState* motionState, btRigidBody* rigidBody);
+    virtual void updateActionWorker(float deltaTimeStep);
 
 private:
 
diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index a4d2113be6..c68b993fe2 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+
+
 #include "PhysicsHelpers.h"
 #include "PhysicsLogging.h"
 #include "ShapeManager.h"
-#include "ObjectActionPullToPoint.h"
-#include "ObjectActionSpring.h"
 
 #include "PhysicalEntitySimulation.h"
 
@@ -235,32 +235,6 @@ void PhysicalEntitySimulation::handleCollisionEvents(CollisionEvents& collisionE
     }
 }
 
-EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType type,
-                                                            QUuid id,
-                                                            EntityItemPointer ownerEntity,
-                                                            QVariantMap arguments) {
-    EntityActionPointer action = nullptr;
-    switch (type) {
-        case ACTION_TYPE_NONE:
-            return nullptr;
-        case ACTION_TYPE_PULL_TO_POINT:
-            action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity);
-            break;
-        case ACTION_TYPE_SPRING:
-            action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
-            break;
-    }
-
-    bool ok = action->updateArguments(arguments);
-    if (ok) {
-        ownerEntity->addAction(this, action);
-        return action;
-    }
-
-    action = nullptr;
-    return action;
-}
-
 void PhysicalEntitySimulation::applyActionChanges() {
     if (_physicsEngine) {
         foreach (EntityActionPointer actionToAdd, _actionsToAdd) {
diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h
index 82b0f8ad51..81ab9f5cce 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.h
+++ b/libraries/physics/src/PhysicalEntitySimulation.h
@@ -32,10 +32,6 @@ public:
 
     void init(EntityTree* tree, PhysicsEngine* engine, EntityEditPacketSender* packetSender);
 
-    virtual EntityActionPointer actionFactory(EntityActionType type,
-                                              QUuid id,
-                                              EntityItemPointer ownerEntity,
-                                              QVariantMap arguments);
     virtual void applyActionChanges();
 
 protected: // only called by EntitySimulation

From 8d41960cc395b6e26ba9e8fd94218a1d2609be4e Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 10 Jun 2015 12:40:14 -0700
Subject: [PATCH 36/88] reset simulation bid counters on object activation

---
 libraries/physics/src/EntityMotionState.cpp | 8 ++++----
 libraries/physics/src/ObjectMotionState.h   | 2 --
 libraries/physics/src/PhysicsEngine.cpp     | 5 ++---
 3 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index 23237cab32..186ff40f60 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -180,10 +180,6 @@ btCollisionShape* EntityMotionState::computeNewShape() {
     return nullptr;
 }
 
-// RELIABLE_SEND_HACK: until we have truly reliable resends of non-moving updates
-// we alwasy resend packets for objects that have stopped moving up to some max limit.
-const int MAX_NUM_NON_MOVING_UPDATES = 5;
-
 bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { 
     if (!_body || !_entity) {
         return false;
@@ -495,6 +491,10 @@ void EntityMotionState::measureBodyAcceleration() {
         glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity());
         _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt;
         _lastVelocity = velocity;
+        if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS && !_candidateForOwnership) {
+            _loopsSinceOwnershipBid = 0;
+            _loopsWithoutOwner = 0;
+        }
     }
 }
 glm::vec3 EntityMotionState::getObjectLinearVelocityChange() const {
diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h
index b17dc67cff..561ce02d62 100644
--- a/libraries/physics/src/ObjectMotionState.h
+++ b/libraries/physics/src/ObjectMotionState.h
@@ -53,8 +53,6 @@ const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_TRANSFORM | Enti
 class OctreeEditPacketSender;
 class PhysicsEngine;
 
-extern const int MAX_NUM_NON_MOVING_UPDATES;
-
 class ObjectMotionState : public btMotionState {
 public:
     // These poroperties of the PhysicsEngine are "global" within the context of all ObjectMotionStates
diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp
index e5c974dfbc..55fc5e6295 100644
--- a/libraries/physics/src/PhysicsEngine.cpp
+++ b/libraries/physics/src/PhysicsEngine.cpp
@@ -227,8 +227,7 @@ void PhysicsEngine::stepSimulation() {
     // (3) synchronize outgoing motion states
     // (4) send outgoing packets
 
-    const int MAX_NUM_SUBSTEPS = 4;
-    const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP;
+    const float MAX_TIMESTEP = (float)PHYSICS_ENGINE_MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP;
     float dt = 1.0e-6f * (float)(_clock.getTimeMicroseconds());
     _clock.reset();
     float timeStep = btMin(dt, MAX_TIMESTEP);
@@ -245,7 +244,7 @@ void PhysicsEngine::stepSimulation() {
         _characterController->preSimulation(timeStep);
     }
 
-    int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP);
+    int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP);
     if (numSubsteps > 0) {
         BT_PROFILE("postSimulation");
         _numSubsteps += (uint32_t)numSubsteps;

From 0e12cdc39e1d0932035ef0cc36b0e82de0e8b2ed Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 10 Jun 2015 12:41:15 -0700
Subject: [PATCH 37/88] woops, forgot to include this in last commit

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

diff --git a/libraries/shared/src/PhysicsHelpers.h b/libraries/shared/src/PhysicsHelpers.h
index bef7275067..0e58ae99f0 100644
--- a/libraries/shared/src/PhysicsHelpers.h
+++ b/libraries/shared/src/PhysicsHelpers.h
@@ -15,6 +15,7 @@
 #include <glm/glm.hpp>
 #include <glm/gtc/quaternion.hpp>
 
+const int PHYSICS_ENGINE_MAX_NUM_SUBSTEPS = 4;
 const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 60.0f;
 
 // return incremental rotation (Bullet-style) caused by angularVelocity over timeStep

From 182a3e918cc6680f4eaa9612f26d2e22fd764e37 Mon Sep 17 00:00:00 2001
From: Niraj Venkat <venkatn93@gmail.com>
Date: Wed, 10 Jun 2015 13:12:18 -0700
Subject: [PATCH 38/88] Hyperlink properties now propagate locally and over
 network

---
 examples/html/entityProperties.html           |   5 +
 libraries/entities/src/EntityItem.cpp         |  16 ++-
 libraries/entities/src/EntityItem.h           |  11 +-
 .../entities/src/EntityItemProperties.cpp     |  17 ++-
 libraries/entities/src/EntityItemProperties.h |   8 +-
 .../entities/src/HyperlinkPropertyGroup.cpp   | 136 ------------------
 .../entities/src/HyperlinkPropertyGroup.h     |  88 ------------
 7 files changed, 50 insertions(+), 231 deletions(-)
 delete mode 100644 libraries/entities/src/HyperlinkPropertyGroup.cpp
 delete mode 100644 libraries/entities/src/HyperlinkPropertyGroup.h

diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html
index a2387a51bf..37f2d0085f 100644
--- a/examples/html/entityProperties.html
+++ b/examples/html/entityProperties.html
@@ -471,6 +471,9 @@
                             elScriptURL.value = properties.script;
                             elUserData.value = properties.userData;
 
+                            elHyperlinkHref.value = properties.href;
+                            elHyperlinkDescription.value = properties.description;
+
                             for (var i = 0; i < allSections.length; i++) {
                                 for (var j = 0; j < allSections[i].length; j++) {
                                     allSections[i][j].style.display = 'none';
@@ -616,6 +619,8 @@
 
             elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked'));
             elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name'));
+            elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href'));
+            elHyperlinkDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description'));
             elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible'));
 
             var positionChangeFunction = createEmitVec3PropertyUpdateFunction(
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 4a10aad95d..f64dad0ef4 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -70,7 +70,9 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
     _dirtyFlags(0),
     _element(nullptr),
     _physicsInfo(nullptr),
-    _simulated(false)
+    _simulated(false),
+    _href(""),
+    _description("")
 {
     quint64 now = usecTimestampNow();
     _lastSimulated = now;
@@ -117,6 +119,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
     requestedProperties += PROP_MARKETPLACE_ID;
     requestedProperties += PROP_NAME;
     requestedProperties += PROP_SIMULATOR_ID;
+    requestedProperties += PROP_HREF;
+    requestedProperties += PROP_DESCRIPTION;
     
     return requestedProperties;
 }
@@ -246,6 +250,9 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
         APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID());
         APPEND_ENTITY_PROPERTY(PROP_NAME, getName());
         APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
+        APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
+        APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
+
 
         appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
                                 requestedProperties,
@@ -573,6 +580,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
 
     READ_ENTITY_PROPERTY(PROP_NAME, QString, setName);
     READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
+    READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
+    READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
+
     bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
 
     ////////////////////////////////////
@@ -905,6 +915,8 @@ EntityItemProperties EntityItem::getProperties() const {
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulatorID, getSimulatorID);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription);
 
     properties._defaultSettings = false;
     
@@ -963,6 +975,8 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
 
     if (somethingChanged) {
         uint64_t now = usecTimestampNow();
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index 77a6627853..a145c4c236 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -203,7 +203,14 @@ public:
     
     inline const glm::quat& getRotation() const { return _transform.getRotation(); }
     inline void setRotation(const glm::quat& rotation) { _transform.setRotation(rotation); }
-    
+
+    // Hyperlink related getters and setters
+    QString getHref() const { return _href; }
+    void setHref(QString value) { _href = value; }
+
+    QString getDescription() const { return _description; }
+    void setDescription(QString value) { _description = value; }
+
     /// Dimensions in meters (0.0 - TREE_SCALE)
     inline const glm::vec3& getDimensions() const { return _transform.getScale(); }
     virtual void setDimensions(const glm::vec3& value);
@@ -415,6 +422,8 @@ protected:
     quint64 _simulatorIDChangedTime; // when was _simulatorID last updated?
     QString _marketplaceID;
     QString _name;
+    QString _href; //Hyperlink href
+    QString _description; //Hyperlink description
 
     // NOTE: Damping is applied like this:  v *= pow(1 - damping, dt)
     //
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 90f2b22698..cbb3b1dc31 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -347,6 +347,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
     CHECK_PROPERTY_CHANGE(PROP_VOXEL_SURFACE_STYLE, voxelSurfaceStyle);
     CHECK_PROPERTY_CHANGE(PROP_LINE_WIDTH, lineWidth);
     CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints);
+    CHECK_PROPERTY_CHANGE(PROP_HREF, href);
+    CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description);
+
     changedProperties += _stage.getChangedProperties();
     changedProperties += _atmosphere.getChangedProperties();
     changedProperties += _skybox.getChangedProperties();
@@ -439,7 +442,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
     COPY_PROPERTY_TO_QSCRIPTVALUE(voxelSurfaceStyle);
     COPY_PROPERTY_TO_QSCRIPTVALUE(lineWidth);
     COPY_PROPERTY_TO_QSCRIPTVALUE(linePoints);
-    
+    COPY_PROPERTY_TO_QSCRIPTVALUE(href);
+    COPY_PROPERTY_TO_QSCRIPTVALUE(description);
+
     // Sitting properties support
     if (!skipDefaults) {
         QScriptValue sittingPoints = engine->newObject();
@@ -548,6 +553,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
     COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelSurfaceStyle, uint16_t, setVoxelSurfaceStyle);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(lineWidth, float, setLineWidth);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints);
+    COPY_PROPERTY_FROM_QSCRIPTVALUE(href, QString, setHref);
+    COPY_PROPERTY_FROM_QSCRIPTVALUE(description, QString, setDescription);
+
 
     if (!honorReadOnly) {
         // this is used by the json reader to set things that we don't want javascript to able to affect.
@@ -712,6 +720,8 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
             APPEND_ENTITY_PROPERTY(PROP_LOCKED, properties.getLocked());
             APPEND_ENTITY_PROPERTY(PROP_USER_DATA, properties.getUserData());
             APPEND_ENTITY_PROPERTY(PROP_SIMULATOR_ID, properties.getSimulatorID());
+            APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref());
+            APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription());
             
             if (properties.getType() == EntityTypes::Web) {
                 APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl());
@@ -962,6 +972,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
     READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
     READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData);
     READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATOR_ID, QUuid, setSimulatorID);
+    READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref);
+    READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription);
     
     if (properties.getType() == EntityTypes::Web) {
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl);
@@ -1147,6 +1159,9 @@ void EntityItemProperties::markAllChanged() {
     _lineWidthChanged = true;
     _linePointsChanged = true;
 
+    _hrefChanged = true;
+    _descriptionChanged = true;
+
 }
 
 /// The maximum bounding cube for the entity, independent of it's rotation.
diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index e7d9a7ad18..068bc98f7e 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -36,7 +36,6 @@
 #include "EntityPropertyFlags.h"
 #include "SkyboxPropertyGroup.h"
 #include "StagePropertyGroup.h"
-#include "HyperlinkPropertyGroup.h"
 
 const quint64 UNKNOWN_CREATED_TIME = 0;
 
@@ -56,7 +55,6 @@ class EntityItemProperties {
     friend class WebEntityItem; // TODO: consider removing this friend relationship and use public methods
     friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods
     friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods
-    friend class HyperlinkEntityItem; // TODO: consider removing this friend relationship and use public methods
 public:
     EntityItemProperties();
     virtual ~EntityItemProperties();
@@ -150,8 +148,8 @@ public:
     DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString);
     DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float);
     DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector<glm::vec3>);
-    DEFINE_PROPERTY_GROUP(Hyperlink, hyperlink, HyperlinkPropertyGroup)
-
+    DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString);
+    DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString);
 
     static QString getBackgroundModeString(BackgroundMode mode);
 
@@ -299,6 +297,8 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, "");
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, "");
     DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, "");
+    DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, "");
+    DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, "");
     
     properties.getStage().debugDump();
     properties.getAtmosphere().debugDump();
diff --git a/libraries/entities/src/HyperlinkPropertyGroup.cpp b/libraries/entities/src/HyperlinkPropertyGroup.cpp
deleted file mode 100644
index bc94a8d5d3..0000000000
--- a/libraries/entities/src/HyperlinkPropertyGroup.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-//
-//  HyperlinkPropertyGroup.cpp
-//  libraries/entities/src
-//
-//  Created by Niraj Venkat on 6/9/15.
-//  Copyright 2013 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#include <OctreePacketData.h>
-
-#include "HyperlinkPropertyGroup.h"
-#include "EntityItemProperties.h"
-#include "EntityItemPropertiesMacros.h"
-
-HyperlinkPropertyGroup::HyperlinkPropertyGroup() {
-    const QString DEFAULT_HREF = QString("");
-    const QString DEFAULT_DESCRIPTION = QString("");
-
-    _href = DEFAULT_HREF;
-    _description = DEFAULT_DESCRIPTION;
-}
-
-void HyperlinkPropertyGroup::copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
-    COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Hyperlink, hyperlink, Href, href);
-    COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(Hyperlink, hyperlink, Description, description);
-}
-
-void HyperlinkPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
-    COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(Hyperlink, href, QString, setHref);
-    COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(Hyperlink, description, QString, setDescription);
-}
-
-void HyperlinkPropertyGroup::debugDump() const {
-    qDebug() << "   HyperlinkPropertyGroup: ---------------------------------------------";
-    qDebug() << "       Href:" << getHref() << " has changed:" << hrefChanged();
-    qDebug() << "       Description:" << getDescription() << " has changed:" << descriptionChanged();
-}
-
-bool HyperlinkPropertyGroup::appentToEditPacket(OctreePacketData* packetData,
-    EntityPropertyFlags& requestedProperties,
-    EntityPropertyFlags& propertyFlags,
-    EntityPropertyFlags& propertiesDidntFit,
-    int& propertyCount,
-    OctreeElement::AppendState& appendState) const {
-
-    bool successPropertyFits = true;
-
-    APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
-    APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
-
-    return true;
-}
-
-
-bool HyperlinkPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt, int& processedBytes) {
-
-    int bytesRead = 0;
-    bool overwriteLocalData = true;
-
-    READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
-    READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
-
-    DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_HREF, Href);
-    DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_DESCRIPTION, Description);
-
-    processedBytes += bytesRead;
-
-    return true;
-}
-
-void HyperlinkPropertyGroup::markAllChanged() {
-    _hrefChanged = true;
-    _descriptionChanged = true;
-}
-
-EntityPropertyFlags HyperlinkPropertyGroup::getChangedProperties() const {
-    EntityPropertyFlags changedProperties;
-
-    CHECK_PROPERTY_CHANGE(PROP_HREF, href);
-    CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description);
-
-    return changedProperties;
-}
-
-void HyperlinkPropertyGroup::getProperties(EntityItemProperties& properties) const {
-    COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Hyperlink, Href, getHref);
-    COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Hyperlink, Description, getDescription);
-}
-
-bool HyperlinkPropertyGroup::setProperties(const EntityItemProperties& properties) {
-    bool somethingChanged = false;
-
-    SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Hyperlink, Href, href, setHref);
-    SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Hyperlink, Description, description, setDescription);
-
-    return somethingChanged;
-}
-
-EntityPropertyFlags HyperlinkPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const {
-    EntityPropertyFlags requestedProperties;
-
-    requestedProperties += PROP_HREF;
-    requestedProperties += PROP_DESCRIPTION;
-
-    return requestedProperties;
-}
-
-void HyperlinkPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
-    EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData,
-    EntityPropertyFlags& requestedProperties,
-    EntityPropertyFlags& propertyFlags,
-    EntityPropertyFlags& propertiesDidntFit,
-    int& propertyCount,
-    OctreeElement::AppendState& appendState) const {
-
-    bool successPropertyFits = true;
-
-    APPEND_ENTITY_PROPERTY(PROP_HREF, getHref());
-    APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription());
-}
-
-int HyperlinkPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
-    ReadBitstreamToTreeParams& args,
-    EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
-
-    int bytesRead = 0;
-    const unsigned char* dataAt = data;
-
-    READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setHref);
-    READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
-
-    return bytesRead;
-}
diff --git a/libraries/entities/src/HyperlinkPropertyGroup.h b/libraries/entities/src/HyperlinkPropertyGroup.h
deleted file mode 100644
index b6bd40c91f..0000000000
--- a/libraries/entities/src/HyperlinkPropertyGroup.h
+++ /dev/null
@@ -1,88 +0,0 @@
-//
-//  HyperlinkPropertyGroup.h
-//  libraries/entities/src
-//
-//  Created by Niraj Venkat on 6/9/15.
-//  Copyright 2013 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_HyperlinkPropertyGroup_h
-#define hifi_HyperlinkPropertyGroup_h
-
-#include <QtScript/QScriptEngine>
-
-#include "PropertyGroup.h"
-#include "EntityItemPropertiesMacros.h"
-
-class EntityItemProperties;
-class EncodeBitstreamParams;
-class OctreePacketData;
-class EntityTreeElementExtraEncodeData;
-class ReadBitstreamToTreeParams;
-
-#include <stdint.h>
-#include <glm/glm.hpp>
-
-
-
-class HyperlinkPropertyGroup : public PropertyGroup {
-public:
-    HyperlinkPropertyGroup();
-    virtual ~HyperlinkPropertyGroup() {}
-
-    // EntityItemProperty related helpers
-    virtual void copyToScriptValue(QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const;
-    virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings);
-    virtual void debugDump() const;
-
-    virtual bool appentToEditPacket(OctreePacketData* packetData,
-        EntityPropertyFlags& requestedProperties,
-        EntityPropertyFlags& propertyFlags,
-        EntityPropertyFlags& propertiesDidntFit,
-        int& propertyCount,
-        OctreeElement::AppendState& appendState) const;
-
-    virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt, int& processedBytes);
-    virtual void markAllChanged();
-    virtual EntityPropertyFlags getChangedProperties() const;
-
-    // EntityItem related helpers
-    // methods for getting/setting all properties of an entity
-    virtual void getProperties(EntityItemProperties& propertiesOut) const;
-
-    /// returns true if something changed
-    virtual bool setProperties(const EntityItemProperties& properties);
-
-    virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
-
-    virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
-        EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData,
-        EntityPropertyFlags& requestedProperties,
-        EntityPropertyFlags& propertyFlags,
-        EntityPropertyFlags& propertiesDidntFit,
-        int& propertyCount,
-        OctreeElement::AppendState& appendState) const;
-
-    virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
-        ReadBitstreamToTreeParams& args,
-        EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
-
-    /*
-    DEFINE_PROPERTY_REF(PROP_Hyperlink_CENTER, Center, center, glm::vec3);
-    DEFINE_PROPERTY(PROP_Hyperlink_INNER_RADIUS, InnerRadius, innerRadius, float);
-    DEFINE_PROPERTY(PROP_Hyperlink_OUTER_RADIUS, OuterRadius, outerRadius, float);
-    DEFINE_PROPERTY(PROP_Hyperlink_MIE_SCATTERING, MieScattering, mieScattering, float);
-    DEFINE_PROPERTY(PROP_Hyperlink_RAYLEIGH_SCATTERING, RayleighScattering, rayleighScattering, float);
-    DEFINE_PROPERTY_REF(PROP_Hyperlink_SCATTERING_WAVELENGTHS, ScatteringWavelengths, scatteringWavelengths, glm::vec3);
-    DEFINE_PROPERTY(PROP_Hyperlink_HAS_STARS, HasStars, hasStars, bool);
-    */
-
-    DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString);
-    DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString);
-
-};
-
-#endif // hifi_HyperlinkPropertyGroup_h

From ca1af777637bfaa2d3298822ed1da08acb7f23e0 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Wed, 10 Jun 2015 13:24:47 -0700
Subject: [PATCH 39/88] Working on cursor manager

---
 interface/resources/images/reticleLink.png  | Bin 0 -> 6381 bytes
 interface/resources/images/reticleWhite.png | Bin 0 -> 4749 bytes
 interface/src/Application.cpp               |  39 +++++++--
 interface/src/ui/ApplicationOverlay.cpp     |  42 ++++-----
 interface/src/ui/ApplicationOverlay.h       |   4 +-
 libraries/ui/src/CursorManager.cpp          |  90 ++++++++++++++++----
 libraries/ui/src/CursorManager.h            |  35 +++++++-
 7 files changed, 164 insertions(+), 46 deletions(-)
 create mode 100644 interface/resources/images/reticleLink.png
 create mode 100644 interface/resources/images/reticleWhite.png

diff --git a/interface/resources/images/reticleLink.png b/interface/resources/images/reticleLink.png
new file mode 100644
index 0000000000000000000000000000000000000000..b36ee79d00b261d2c7d422df39302a46eb19daa4
GIT binary patch
literal 6381
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
T{C0ae007unI$FFq>7V#N&}Y{u

literal 0
HcmV?d00001

diff --git a/interface/resources/images/reticleWhite.png b/interface/resources/images/reticleWhite.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c35a186b2e7aa8939937e909586726ef3a10d09
GIT binary patch
literal 4749
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
Har^%O!X>IH

literal 0
HcmV?d00001

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 1763623fa6..5f17fd0510 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -57,6 +57,7 @@
 
 #include <AccountManager.h>
 #include <AddressManager.h>
+#include <CursorManager.h>
 #include <AmbientOcclusionEffect.h>
 #include <AudioInjector.h>
 #include <DeferredLightingEffect.h>
@@ -1229,9 +1230,20 @@ void Application::keyPressEvent(QKeyEvent* event) {
                 }
                 break;
 
-            case Qt::Key_Apostrophe:
-                resetSensors();
+            case Qt::Key_Apostrophe: {
+                if (isMeta) {
+                    auto cursor = Cursor::Manager::instance().getCursor();
+                    auto curIcon = cursor->getIcon();
+                    if (curIcon == Cursor::Icon::DEFAULT) {
+                        cursor->setIcon(Cursor::Icon::LINK);
+                    } else {
+                        cursor->setIcon(Cursor::Icon::DEFAULT);
+                    }
+                } else {
+                    resetSensors();
+                }
                 break;
+            }
 
             case Qt::Key_A:
                 if (isShifted) {
@@ -1355,12 +1367,27 @@ void Application::keyPressEvent(QKeyEvent* event) {
             case Qt::Key_Slash:
                 Menu::getInstance()->triggerOption(MenuOption::Stats);
                 break;
-            case Qt::Key_Plus:
-                _myAvatar->increaseSize();
+
+            case Qt::Key_Plus: {
+                if (isMeta && event->modifiers().testFlag(Qt::KeypadModifier)) {
+                    auto& cursorManager = Cursor::Manager::instance();
+                    cursorManager.setScale(cursorManager.getScale() * 1.1f);
+                } else {
+                    _myAvatar->increaseSize();
+                }
                 break;
-            case Qt::Key_Minus:
-                _myAvatar->decreaseSize();
+            }
+
+            case Qt::Key_Minus: {
+                if (isMeta && event->modifiers().testFlag(Qt::KeypadModifier)) {
+                    auto& cursorManager = Cursor::Manager::instance();
+                    cursorManager.setScale(cursorManager.getScale() / 1.1f);
+                } else {
+                    _myAvatar->decreaseSize();
+                }
                 break;
+            }
+
             case Qt::Key_Equal:
                 _myAvatar->resetSize();
                 break;
diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index bc8047fc98..7895232f8a 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -21,6 +21,7 @@
 #include <GLMHelpers.h>
 #include <PerfStat.h>
 #include <OffscreenUi.h>
+#include <CursorManager.h>
 
 #include "AudioClient.h"
 #include "audio/AudioIOStatsRenderer.h"
@@ -143,7 +144,6 @@ ApplicationOverlay::ApplicationOverlay() :
     _alpha(1.0f),
     _oculusUIRadius(1.0f),
     _trailingAudioLoudness(0.0f),
-    _crosshairTexture(0),
     _previousBorderWidth(-1),
     _previousBorderHeight(-1),
     _previousMagnifierBottomLeft(),
@@ -257,6 +257,18 @@ void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) {
     glDisable(GL_TEXTURE_2D);
 }
 
+void ApplicationOverlay::bindCursorTexture(uint8_t cursorIndex) {
+    auto& cursorManager = Cursor::Manager::instance();
+    auto cursor = cursorManager.getCursor(cursorIndex);
+    auto iconId = cursor->getIcon();
+    if (!_cursors.count(iconId)) {
+        auto iconPath = cursorManager.getIconImage(cursor->getIcon());
+        _cursors[iconId] = DependencyManager::get<TextureCache>()->
+            getImageTexture(iconPath);
+    }
+    glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_cursors[iconId]));
+}
+
 // Draws the FBO texture for the screen
 void ApplicationOverlay::displayOverlayTexture() {
     if (_alpha == 0.0f) {
@@ -282,14 +294,10 @@ void ApplicationOverlay::displayOverlayTexture() {
                 glm::vec4(1.0f, 1.0f, 1.0f, _alpha));
         });
 
-        if (!_crosshairTexture) {
-            _crosshairTexture = DependencyManager::get<TextureCache>()->
-                getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
-        }
-
+        
         //draw the mouse pointer
         glm::vec2 canvasSize = qApp->getCanvasSize();
-        glm::vec2 mouseSize = 32.0f / canvasSize;
+        glm::vec2 mouseSize = 32.0f / canvasSize * Cursor::Manager::instance().getScale();
         auto mouseTopLeft = topLeft * mouseSize;
         auto mouseBottomRight = bottomRight * mouseSize;
         vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY());
@@ -300,7 +308,7 @@ void ApplicationOverlay::displayOverlayTexture() {
 
         glEnable(GL_TEXTURE_2D);
         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-        glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
+        bindCursorTexture();
         glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
         DependencyManager::get<GeometryCache>()->renderQuad(
             mouseTopLeft + mousePosition, mouseBottomRight + mousePosition, 
@@ -450,18 +458,14 @@ void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float
                                                 overlayColor);
     });
     
-    if (!_crosshairTexture) {
-        _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() +
-                                                          "images/sixense-reticle.png");
-    }
-    
     //draw the mouse pointer
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     glEnable(GL_TEXTURE_2D);
-    glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
+    bindCursorTexture();
     glm::vec2 canvasSize = qApp->getCanvasSize();
-    const float reticleSize = 40.0f / canvasSize.x * quadWidth;
+    const float reticleSize = 40.0f / canvasSize.x * quadWidth *
+        Cursor::Manager::instance().getScale();
     x -= reticleSize / 2.0f;
     y += reticleSize / 2.0f;
     const float mouseX = (qApp->getMouseX() / (float)canvasSize.x) * quadWidth;
@@ -583,16 +587,12 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position,
 
 //Renders optional pointers
 void ApplicationOverlay::renderPointers() {
-    //lazily load crosshair texture
-    if (_crosshairTexture == 0) {
-        _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
-    }
     glEnable(GL_TEXTURE_2D);
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
     glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
+    bindCursorTexture();
 
     if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) {
         //If we are in oculus, render reticle later
@@ -757,7 +757,7 @@ void ApplicationOverlay::renderPointersOculus() {
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     glEnable(GL_TEXTURE_2D);
 
-    glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
+    bindCursorTexture();
     glDisable(GL_DEPTH_TEST);
 
     glMatrixMode(GL_MODELVIEW);
diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h
index ee82c9274a..0e8206547a 100644
--- a/interface/src/ui/ApplicationOverlay.h
+++ b/interface/src/ui/ApplicationOverlay.h
@@ -108,6 +108,7 @@ private:
     void renderCameraToggle();
     void renderStatsAndLogs();
     void renderDomainConnectionStatusBorder();
+    void bindCursorTexture(uint8_t cursorId = 0);
 
     TexturedHemisphere _overlays;
     
@@ -127,7 +128,8 @@ private:
     float _trailingAudioLoudness;
 
 
-    gpu::TexturePointer _crosshairTexture;
+    QMap<uint16_t, gpu::TexturePointer> _cursors;
+
     GLuint _newUiTexture{ 0 };
     
     int _reticleQuad;
diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp
index 8b318d549a..efadd09142 100644
--- a/libraries/ui/src/CursorManager.cpp
+++ b/libraries/ui/src/CursorManager.cpp
@@ -8,25 +8,85 @@
 
 #include "CursorManager.h"
 
+#include <QCursor>
+#include <QWidget>
+#include <QUrl>
+
+#include <PathUtils.h>
+
 namespace Cursor {
-    enum class Source {
-        MOUSE,
-        LEFT_HAND,
-        RIGHT_HAND,
-        UNKNOWN,
+
+    void Instance::setIcon(uint16_t icon) {
+        _icon = icon;
+    }
+
+    uint16_t Instance::getIcon() const {
+        return _icon;
+    }
+
+
+    class MouseInstance : public Instance {
+        Source getType() const {
+            return Source::MOUSE;
+        }
+
+        ivec2 getScreenPosition() const {
+            return toGlm(QCursor::pos());
+        }
+
+        ivec2 getWindowPosition(QWidget* widget) const {
+            return toGlm(widget->mapFromGlobal(QCursor::pos()));
+        }
+
+        vec2 getRelativePosition(QWidget* widget) const {
+            vec2 pos = getWindowPosition(widget);
+            pos /= vec2(toGlm(widget->size()));
+            return pos;
+        }
     };
 
-    class Instance {
-        Source type;
-    };
+    static QMap<uint16_t, QString> ICONS;
+    static uint16_t _customIconId = Icon::USER_BASE;
 
-    class Manager {
-    public:
-        static Manager& instance();
+    Manager::Manager() {
+        ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/sixense-reticle.png";
+        ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/reticleLink.png";
+    }
 
-        uint8_t getCount();
-        Instance
-    };
-}
+    Manager& Manager::instance() {
+        static Manager instance;
+        return instance;
+    }
 
+    uint8_t Manager::getCount() {
+        return 1;
+    }
 
+    Instance* Manager::getCursor(uint8_t index) {
+        Q_ASSERT(index < getCount());
+        static MouseInstance mouseInstance;
+        if (index == 0) {
+            return &mouseInstance;
+        }
+        return nullptr;
+    }
+
+    uint16_t Manager::registerIcon(const QString& path) {
+        ICONS[_customIconId] = path;
+        return _customIconId++;
+    }
+
+    const QString& Manager::getIconImage(uint16_t icon) {
+        Q_ASSERT(ICONS.count(icon));
+        return ICONS[icon];
+    }
+
+    float Manager::getScale() {
+        return _scale;
+    }
+
+    void Manager::setScale(float scale) {
+        _scale = scale;
+    }
+
+}
\ No newline at end of file
diff --git a/libraries/ui/src/CursorManager.h b/libraries/ui/src/CursorManager.h
index a2d26efc58..c5810caf58 100644
--- a/libraries/ui/src/CursorManager.h
+++ b/libraries/ui/src/CursorManager.h
@@ -7,6 +7,9 @@
 //
 
 #pragma once
+#include <stdint.h>
+
+#include <GLMHelpers.h>
 
 namespace Cursor {
     enum class Source {
@@ -16,16 +19,42 @@ namespace Cursor {
         UNKNOWN,
     };
 
+    enum Icon {
+        DEFAULT,
+        LINK,
+        GRAB,
+
+        // Add new system cursors here
+
+        // User cursors will have ids over this value
+        USER_BASE = 0xFF,
+    };
+
     class Instance {
-        Source type;
+    public:
+        virtual Source getType() const = 0;
+        virtual ivec2 getWindowPosition(QWidget* widget) const = 0;
+        virtual vec2 getRelativePosition(QWidget* widget) const = 0;
+        virtual ivec2 getScreenPosition() const = 0;
+        virtual void setIcon(uint16_t icon);
+        virtual uint16_t getIcon() const;
+    private:
+        uint16_t _icon;
     };
 
     class Manager {
+        Manager();
+        Manager(const Manager& other) = delete;
     public:
         static Manager& instance();
-
         uint8_t getCount();
-        Instance
+        float getScale();
+        void setScale(float scale);
+        Instance* getCursor(uint8_t index = 0);
+        uint16_t registerIcon(const QString& path);
+        const QString& getIconImage(uint16_t icon);
+    private:
+        float _scale{ 1.0f };
     };
 }
 

From 1dfc9c89eb487906f940411c5434a6a9d1ead68d Mon Sep 17 00:00:00 2001
From: Bradley Austin Davis <bdavis@saintandreas.org>
Date: Wed, 10 Jun 2015 13:57:48 -0700
Subject: [PATCH 40/88] Fix doubled cursor on OSX

---
 interface/src/Application.cpp | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 1763623fa6..3e993450c3 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -524,8 +524,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
     _window->setVisible(true);
     _glWidget->setFocusPolicy(Qt::StrongFocus);
     _glWidget->setFocus();
+#ifdef Q_OS_MAC
+    // OSX doesn't seem to provide for hiding the cursor only on the GL widget
+    _window->setCursor(Qt::BlankCursor);
+#else
+    // On windows and linux, hiding the top level cursor also means it's invisible
+    // when hovering over the window menu, which is a pain, so only hide it for
+    // the GL surface
     _glWidget->setCursor(Qt::BlankCursor);
-
+#endif
+    
     // enable mouse tracking; otherwise, we only get drag events
     _glWidget->setMouseTracking(true);
 

From 6497ac6c8270a7528cd7efa536e4195d1488089b Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 10 Jun 2015 14:24:00 -0700
Subject: [PATCH 41/88] renderBoundingBox() for sim-ownership debug

---
 .../src/RenderableBoxEntityItem.cpp           | 40 ++-----------------
 .../src/RenderableBoxEntityItem.h             |  1 -
 .../src/RenderableDebugableEntityItem.cpp     | 13 +++---
 .../src/RenderableSphereEntityItem.cpp        |  6 ++-
 4 files changed, 15 insertions(+), 45 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
index 2d72897faf..6ddf44b82d 100644
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "RenderableBoxEntityItem.h"
+
 #include <glm/gtx/quaternion.hpp>
 
 #include <gpu/GPUConfig.h>
@@ -18,7 +20,7 @@
 #include <ObjectMotionState.h>
 #include <PerfStat.h>
 
-#include "RenderableBoxEntityItem.h"
+#include "RenderableDebugableEntityItem.h"
 
 EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
     return EntityItemPointer(new RenderableBoxEntityItem(entityID, properties));
@@ -34,39 +36,5 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
     batch.setModelTransform(getTransformToCenter());
     DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
 
-    // TODO: use RenderableDebugableEntityItem::render (instead of the hack below) 
-    // when we fix the scaling bug that breaks RenderableDebugableEntityItem for boxes.
-    if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) {
-        Q_ASSERT(args->_batch);
-        gpu::Batch& batch = *args->_batch;
-        Transform transform = getTransformToCenter();
-        //transform.postScale(entity->getDimensions()); // HACK: this line breaks for BoxEntityItem
-        batch.setModelTransform(transform);
-
-        auto nodeList = DependencyManager::get<NodeList>();
-        const QUuid& myNodeID = nodeList->getSessionUUID();
-        bool highlightSimulationOwnership = (getSimulatorID() == myNodeID);
-        if (highlightSimulationOwnership) {
-            glm::vec4 greenColor(0.0f, 1.0f, 0.2f, 1.0f);
-            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.08f, greenColor);
-        }
-
-        quint64 now = usecTimestampNow();
-        if (now - getLastEditedFromRemote() < 0.1f * USECS_PER_SECOND) {
-            glm::vec4 redColor(1.0f, 0.0f, 0.0f, 1.0f);
-            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.16f, redColor);
-        }
-
-        if (now - getLastBroadcast() < 0.2f * USECS_PER_SECOND) {
-            glm::vec4 yellowColor(1.0f, 1.0f, 0.2f, 1.0f);
-            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.24f, yellowColor);
-        }
-
-        ObjectMotionState* motionState = static_cast<ObjectMotionState*>(getPhysicsInfo());
-        if (motionState && motionState->isActive()) {
-            glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
-            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.32f, blueColor);
-        }
-    }
-    //RenderableDebugableEntityItem::render(this, args); // TODO: use this instead of the hack above
+    RenderableDebugableEntityItem::render(this, args);
 };
diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.h b/libraries/entities-renderer/src/RenderableBoxEntityItem.h
index 06a62706b9..b14da9ee22 100644
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.h
@@ -13,7 +13,6 @@
 #define hifi_RenderableBoxEntityItem_h
 
 #include <BoxEntityItem.h>
-#include "RenderableDebugableEntityItem.h"
 #include "RenderableEntityItem.h"
 
 class RenderableBoxEntityItem : public BoxEntityItem {
diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
index 53213eae51..fecc574af2 100644
--- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
@@ -10,6 +10,7 @@
 //
 
 
+#include "RenderableDebugableEntityItem.h"
 
 #include <glm/gtx/quaternion.hpp>
 
@@ -18,15 +19,13 @@
 #include <DeferredLightingEffect.h>
 #include <ObjectMotionState.h>
 
-#include "RenderableDebugableEntityItem.h"
-
 
 void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, RenderArgs* args,
                                                       float puffedOut, glm::vec4& color) {
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
     Transform transform = entity->getTransformToCenter();
-    transform.postScale(entity->getDimensions());
+    //transform.postScale(entity->getDimensions());
     batch.setModelTransform(transform);
     DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f + puffedOut, color);
 }
@@ -44,24 +43,24 @@ void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args)
         bool highlightSimulationOwnership = (entity->getSimulatorID() == myNodeID);
         if (highlightSimulationOwnership) {
             glm::vec4 greenColor(0.0f, 1.0f, 0.2f, 1.0f);
-            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.08f, greenColor);
+            renderBoundingBox(entity, args, 0.08f, greenColor);
         }
 
         quint64 now = usecTimestampNow();
         if (now - entity->getLastEditedFromRemote() < 0.1f * USECS_PER_SECOND) {
             glm::vec4 redColor(1.0f, 0.0f, 0.0f, 1.0f);
-            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.16f, redColor);
+            renderBoundingBox(entity, args, 0.16f, redColor);
         }
 
         if (now - entity->getLastBroadcast() < 0.2f * USECS_PER_SECOND) {
             glm::vec4 yellowColor(1.0f, 1.0f, 0.2f, 1.0f);
-            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.24f, yellowColor);
+            renderBoundingBox(entity, args, 0.24f, yellowColor);
         }
 
         ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
         if (motionState && motionState->isActive()) {
             glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
-            DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.32f, blueColor);
+            renderBoundingBox(entity, args, 0.32f, blueColor);
         }
     }
 }
diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
index d5cb7d11b8..b0aaebb2c8 100644
--- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "RenderableSphereEntityItem.h"
+
 #include <glm/gtx/quaternion.hpp>
 
 #include <gpu/GPUConfig.h>
@@ -18,7 +20,7 @@
 #include <DeferredLightingEffect.h>
 #include <PerfStat.h>
 
-#include "RenderableSphereEntityItem.h"
+#include "RenderableDebugableEntityItem.h"
 
 EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
     return EntityItemPointer(new RenderableSphereEntityItem(entityID, properties));
@@ -39,4 +41,6 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
     gpu::Batch& batch = *args->_batch;
     batch.setModelTransform(getTransformToCenter());
     DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
+
+    RenderableDebugableEntityItem::render(this, args);
 };

From 1f62fb4b6fbe33f090dc072dd3b0e15efc5b6d2d Mon Sep 17 00:00:00 2001
From: Sam Gateau <sam@highfidelity.io>
Date: Wed, 10 Jun 2015 15:24:29 -0700
Subject: [PATCH 42/88] Adding standard vertex and pixel shaders for drawing
 texture in applicationOverlay

---
 interface/src/ui/ApplicationOverlay.cpp       | 30 ++++++++++++++---
 interface/src/ui/ApplicationOverlay.h         |  4 +++
 .../render-utils/src/standardDrawTexture.slf  | 24 ++++++++++++++
 .../src/standardTransformPNTC.slv             | 33 +++++++++++++++++++
 4 files changed, 87 insertions(+), 4 deletions(-)
 create mode 100644 libraries/render-utils/src/standardDrawTexture.slf
 create mode 100644 libraries/render-utils/src/standardTransformPNTC.slv

diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index 79ec39b506..f57c10b64e 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -36,6 +36,9 @@
 #include "Util.h"
 #include "ui/Stats.h"
 
+#include "../../libraries/render-utils/standardTransformPNTC_vert.h"
+#include "../../libraries/render-utils/standardDrawTexture_frag.h"
+
 // Used to animate the magnification windows
 const float MAG_SPEED = 0.08f;
 
@@ -278,6 +281,24 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
 //    glDisable(GL_TEXTURE_2D);
 //}
 
+gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() {
+    if (!_standardDrawPipeline) {
+        auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)));
+        auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag)));
+        auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
+        gpu::Shader::makeProgram((*program));
+        
+        auto state = gpu::StatePointer(new gpu::State());
+
+        // enable decal blend
+        state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
+        
+        _standardDrawPipeline.reset(gpu::Pipeline::create(program, state));
+    }
+
+    return _standardDrawPipeline;
+}
+
 // Draws the FBO texture for the screen
 void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
 
@@ -290,11 +311,12 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
             PathUtils::resourcesPath() + "images/sixense-reticle.png");
     }
 
-    /*
-    FIXME - doesn't work
+    
+    //FIXME - doesn't work
     renderArgs->_context->syncCache();
     gpu::Batch batch;
-    DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
+    //DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
+    batch.setPipeline(getDrawPipeline());
     batch.setModelTransform(Transform());
     batch.setProjectionTransform(mat4());
     batch.setViewTransform(Transform());
@@ -302,7 +324,7 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
     DependencyManager::get<GeometryCache>()->renderUnitQuad(batch, vec4(vec3(1), _alpha));
     renderArgs->_context->render(batch);
     return;
-    */
+
 
     
     glDisable(GL_DEPTH_TEST);
diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h
index 36161dd29d..63ef48bd92 100644
--- a/interface/src/ui/ApplicationOverlay.h
+++ b/interface/src/ui/ApplicationOverlay.h
@@ -143,6 +143,10 @@ private:
     glm::vec3 _previousMagnifierTopLeft;
     glm::vec3 _previousMagnifierTopRight;
 
+    gpu::PipelinePointer _standardDrawPipeline;
+
+    gpu::PipelinePointer getDrawPipeline();
+
 };
 
 #endif // hifi_ApplicationOverlay_h
diff --git a/libraries/render-utils/src/standardDrawTexture.slf b/libraries/render-utils/src/standardDrawTexture.slf
new file mode 100644
index 0000000000..4fbeb6eb7f
--- /dev/null
+++ b/libraries/render-utils/src/standardDrawTexture.slf
@@ -0,0 +1,24 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+//  Generated on <$_SCRIBE_DATE$>
+//  standardDrawTexture.frag
+//  fragment shader
+//
+//  Created by Sam Gateau on 6/10/15.
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+// the texture
+uniform sampler2D colorMap;
+
+varying vec2 varTexcoord;
+varying vec4 varColor;
+
+
+void main(void) {
+    vec4 color = texture2D(colorMap, varTexcoord);
+    gl_FragColor = color * varColor;
+}
diff --git a/libraries/render-utils/src/standardTransformPNTC.slv b/libraries/render-utils/src/standardTransformPNTC.slv
new file mode 100644
index 0000000000..fd2c28049f
--- /dev/null
+++ b/libraries/render-utils/src/standardTransformPNTC.slv
@@ -0,0 +1,33 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+//  Generated on <$_SCRIBE_DATE$>
+//
+//  standardTransformPNTC.slv
+//  vertex shader
+//
+//  Created by Sam Gateau on 6/10/2015.
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+<@include gpu/Transform.slh@>
+
+<$declareStandardTransform()$>
+
+varying vec3 varNormal;
+varying vec2 varTexcoord;
+varying vec4 varColor;
+
+void main(void) {
+    varTexcoord = gl_MultiTexCoord0.xy;
+    varColor = gl_Color;
+    
+    // standard transform
+    TransformCamera cam = getTransformCamera();
+    TransformObject obj = getTransformObject();
+    <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$>
+    <$transformModelToEyeDir(cam, obj, gl_Normal, varNormal)$>
+    varNormal = normalize(varNormal);
+}
\ No newline at end of file

From 9ed8daa97c4aca572fbe3de7afcb0b861b80da1b Mon Sep 17 00:00:00 2001
From: Niraj Venkat <venkatn93@gmail.com>
Date: Wed, 10 Jun 2015 15:33:07 -0700
Subject: [PATCH 43/88] Removing unnecessary functions

---
 examples/html/entityProperties.html | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html
index 37f2d0085f..f029088b1a 100644
--- a/examples/html/entityProperties.html
+++ b/examples/html/entityProperties.html
@@ -859,10 +859,6 @@
             elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction);
             elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle'));
 
-            var hyperlinkChangeFunction = createEmitGroupTextPropertyUpdateFunction('hyperlink','href');
-            var hyperlinkChangeFunction = createEmitGroupTextPropertyUpdateFunction('hyperlink','description');
-
-
             elMoveSelectionToGrid.addEventListener("click", function() {
                 EventBridge.emitWebEvent(JSON.stringify({
                     type: "action",

From e06422825a54a0ee186b579760261dbf4813a8c9 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Wed, 10 Jun 2015 15:46:54 -0700
Subject: [PATCH 44/88] mostly getting attachments working again

---
 interface/src/avatar/Avatar.cpp      |   4 +-
 interface/src/avatar/MyAvatar.cpp    |  18 +---
 libraries/render-utils/src/Model.cpp | 124 ++++++++-------------------
 libraries/render-utils/src/Model.h   |  29 ++-----
 4 files changed, 43 insertions(+), 132 deletions(-)

diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 627c42ffa8..f644968ff8 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -582,7 +582,9 @@ void Avatar::simulateAttachments(float deltaTime) {
                 _skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) {
             model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale);
             model->setRotation(jointRotation * attachment.rotation);
-            model->setScaleToFit(true, _scale * attachment.scale);
+            model->setScaleToFit(true, _scale * attachment.scale, true); // hack to force rescale
+            model->setSnapModelToCenter(false); // hack to force resnap
+            model->setSnapModelToCenter(true);
             model->simulate(deltaTime);
         }
     }
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index afe0311a29..65b927c2c0 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -1196,23 +1196,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo
     }
     scene->enqueuePendingChanges(pendingChanges);
 
-    Camera *camera = Application::getInstance()->getCamera();
-    const glm::vec3 cameraPos = camera->getPosition();
-
-
-    // HACK: comment this block which possibly change the near and break the rendering 5/6/2015
-    // Only tweak the frustum near far if it's not shadow
- /*   if (renderMode != RenderArgs::SHADOW_RENDER_MODE) {
-        // Set near clip distance according to skeleton model dimensions if first person and there is no separate head model.
-        if (shouldRenderHead(cameraPos, renderMode) || !getHead()->getFaceModel().getURL().isEmpty()) {
-            renderFrustum->setNearClip(DEFAULT_NEAR_CLIP);
-        } else {
-            float clipDistance = _skeletonModel.getHeadClipDistance();
-            clipDistance = glm::length(getEyePosition() 
-                + camera->getOrientation() * glm::vec3(0.0f, 0.0f, -clipDistance) - cameraPos);
-            renderFrustum->setNearClip(clipDistance);
-        }
-    }*/
+    const glm::vec3 cameraPos = Application::getInstance()->getCamera()->getPosition();
 
     //  Render head so long as the camera isn't inside it
     if (shouldRenderHead(renderArgs, cameraPos)) {
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 3cea5c9777..8d234cdef5 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -435,6 +435,7 @@ bool Model::updateGeometry() {
 
     QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis);
     if (_geometry != geometry) {
+
         // NOTE: it is theoretically impossible to reach here after passing through the applyNextGeometry() call above.
         // Which means we don't need to worry about calling deleteGeometry() below immediately after creating new geometry.
 
@@ -811,71 +812,41 @@ void Model::renderSetup(RenderArgs* args) {
 }
 
 
-class TransparentMeshPart {
+class MeshPartPayload {
 public:
-    TransparentMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { }
-    typedef render::Payload<TransparentMeshPart> Payload;
+    MeshPartPayload(bool transparent, Model* model, int meshIndex, int partIndex) :
+        transparent(transparent), model(model), url(model->getURL()), meshIndex(meshIndex), partIndex(partIndex) { }
+    typedef render::Payload<MeshPartPayload> Payload;
     typedef Payload::DataPointer Pointer;
    
-    Model* model;   
+    bool transparent;
+    Model* model;
+    QUrl url;
     int meshIndex;
     int partIndex;
 };
 
 namespace render {
-    template <> const ItemKey payloadGetKey(const TransparentMeshPart::Pointer& payload) { 
+    template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { 
         if (!payload->model->isVisible()) {
             return ItemKey::Builder().withInvisible().build();
         }
-        return ItemKey::Builder::transparentShape();
+        return payload->transparent ? ItemKey::Builder::transparentShape() : ItemKey::Builder::opaqueShape();
     }
     
-    template <> const Item::Bound payloadGetBound(const TransparentMeshPart::Pointer& payload) { 
+    template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { 
         if (payload) {
             return payload->model->getPartBounds(payload->meshIndex, payload->partIndex);
         }
         return render::Item::Bound();
     }
-    template <> void payloadRender(const TransparentMeshPart::Pointer& payload, RenderArgs* args) {
+    template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args) {
         if (args) {
-            return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, true);
+            return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, payload->transparent);
         }
     }
-}
 
-class OpaqueMeshPart {
-public:
-    OpaqueMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { }
-    typedef render::Payload<OpaqueMeshPart> Payload;
-    typedef Payload::DataPointer Pointer;
-
-    Model* model;   
-    int meshIndex;
-    int partIndex;
-};
-
-namespace render {
-    template <> const ItemKey payloadGetKey(const OpaqueMeshPart::Pointer& payload) { 
-        if (!payload->model->isVisible()) {
-            return ItemKey::Builder().withInvisible().build();
-        }
-        return ItemKey::Builder::opaqueShape();
-    }
-    
-    template <> const Item::Bound payloadGetBound(const OpaqueMeshPart::Pointer& payload) { 
-        if (payload) {
-            Item::Bound result = payload->model->getPartBounds(payload->meshIndex, payload->partIndex);
-            //qDebug() << "payloadGetBound(OpaqueMeshPart) " << result;
-            return result;
-        }
-        return render::Item::Bound();
-    }
-    template <> void payloadRender(const OpaqueMeshPart::Pointer& payload, RenderArgs* args) {
-        if (args) {
-            return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, false);
-        }
-    }
-   /* template <> const model::MaterialKey& shapeGetMaterialKey(const OpaqueMeshPart::Pointer& payload) {
+   /* template <> const model::MaterialKey& shapeGetMaterialKey(const MeshPartPayload::Pointer& payload) {
         return payload->model->getPartMaterial(payload->meshIndex, payload->partIndex);
     }*/
 }
@@ -902,16 +873,17 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
 
     foreach (auto renderItem, _transparentRenderItems) {
         auto item = scene->allocateID();
-        auto renderData = TransparentMeshPart::Pointer(renderItem);
-        auto renderPayload = render::PayloadPointer(new TransparentMeshPart::Payload(renderData));
+        auto renderData = MeshPartPayload::Pointer(renderItem);
+        auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData));
         pendingChanges.resetItem(item, renderPayload);
         _renderItems.insert(item, renderPayload);
         somethingAdded = true;
     }
+
     foreach (auto renderItem, _opaqueRenderItems) {
         auto item = scene->allocateID();
-        auto renderData = OpaqueMeshPart::Pointer(renderItem);
-        auto renderPayload = render::PayloadPointer(new OpaqueMeshPart::Payload(renderData));
+        auto renderData = MeshPartPayload::Pointer(renderItem);
+        auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData));
         pendingChanges.resetItem(item, renderPayload);
         _renderItems.insert(item, renderPayload);
         somethingAdded = true;
@@ -1036,12 +1008,12 @@ Extents Model::calculateScaledOffsetExtents(const Extents& extents) const {
 
     Extents translatedExtents = { rotatedExtents.minimum + _translation, 
                                   rotatedExtents.maximum + _translation };
+
     return translatedExtents;
 }
 
 /// Returns the world space equivalent of some box in model space.
 AABox Model::calculateScaledOffsetAABox(const AABox& box) const {
-    
     return AABox(calculateScaledOffsetExtents(Extents(box)));
 }
 
@@ -1110,9 +1082,10 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
     if (_url == url && _geometry && _geometry->getURL() == url) {
         return;
     }
-    
+
     _readyWhenAdded = false; // reset out render items.
     _needsReload = true;
+    invalidCalculatedMeshBoxes();
     
     _url = url;
 
@@ -1301,7 +1274,7 @@ void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) {
     }
 }
 
-void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
+void Model::setScaleToFit(bool scaleToFit, float largestDimension, bool forceRescale) {
     // NOTE: if the model is not active, then it means we don't actually know the true/natural dimensions of the
     // mesh, and so we can't do the needed calculations for scaling to fit to a single largest dimension. In this
     // case we will record that we do want to do this, but we will stick our desired single dimension into the
@@ -1314,7 +1287,7 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
         return;
     }
     
-    if (_scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) {
+    if (forceRescale || _scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) {
         _scaleToFit = scaleToFit;
         
         // we only need to do this work if we're "turning on" scale to fit.
@@ -1324,7 +1297,7 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
             float maxScale = largestDimension / maxDimension;
             glm::vec3 modelMeshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum;
             glm::vec3 dimensions = modelMeshDimensions * maxScale;
-        
+
             _scaleToFitDimensions = dimensions;
             _scaledToFit = false; // force rescaling
         }
@@ -1822,7 +1795,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
         glm::mat4 scale = glm::scale(partBounds.getDimensions());
         glm::mat4 modelToWorldMatrix = translation * scale;
         batch.setModelTransform(modelToWorldMatrix);
-        //qDebug() << "partBounds:" << partBounds;
         DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor);
     }
     #endif //def DEBUG_BOUNDING_PARTS
@@ -1912,16 +1884,18 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
 
     // guard against partially loaded meshes
     if (partIndex >= networkMesh.parts.size() || partIndex >= mesh.parts.size()) {
-        return; 
+        return;
     }
 
     const NetworkMeshPart& networkPart = networkMesh.parts.at(partIndex);
     const FBXMeshPart& part = mesh.parts.at(partIndex);
     model::MaterialPointer material = part._material;
 
+    #ifdef WANT_DEBUG
     if (material == nullptr) {
-    //    qCDebug(renderutils) << "WARNING: material == nullptr!!!";
+        qCDebug(renderutils) << "WARNING: material == nullptr!!!";
     }
+    #endif
     
     if (material != nullptr) {
 
@@ -2023,8 +1997,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
 }
 
 void Model::segregateMeshGroups() {
-    _renderBuckets.clear();
-
     const FBXGeometry& geometry = _geometry->getFBXGeometry();
     const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
 
@@ -2034,6 +2006,9 @@ void Model::segregateMeshGroups() {
         qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet.";
         return;
     }
+    
+    _transparentRenderItems.clear();
+    _opaqueRenderItems.clear();
 
     // Run through all of the meshes, and place them into their segregated, but unsorted buckets
     for (int i = 0; i < networkMeshes.size(); i++) {
@@ -2058,43 +2033,12 @@ void Model::segregateMeshGroups() {
         for (int partIndex = 0; partIndex < totalParts; partIndex++) {
             // this is a good place to create our renderPayloads
             if (translucentMesh) {
-                _transparentRenderItems << std::shared_ptr<TransparentMeshPart>(new TransparentMeshPart(this, i, partIndex));
+                _transparentRenderItems << std::shared_ptr<MeshPartPayload>(new MeshPartPayload(true, this, i, partIndex));
             } else {
-                _opaqueRenderItems << std::shared_ptr<OpaqueMeshPart>(new OpaqueMeshPart(this, i, partIndex));
+                _opaqueRenderItems << std::shared_ptr<MeshPartPayload>(new MeshPartPayload(false, this, i, partIndex));
             }
         }
-
-        
-        QString materialID;
-
-        // create a material name from all the parts. If there's one part, this will be a single material and its
-        // true name. If however the mesh has multiple parts the name will be all the part's materials mashed together
-        // which will result in those parts being sorted away from single material parts.
-        QString lastPartMaterialID;
-        foreach(FBXMeshPart part, mesh.parts) {
-            if (part.materialID != lastPartMaterialID) {
-                materialID += part.materialID;
-            }
-            lastPartMaterialID = part.materialID;
-        }
-        const bool wantDebug = false;
-        if (wantDebug) {
-            qCDebug(renderutils) << "materialID:" << materialID << "parts:" << mesh.parts.size();
-        }
-        
-        RenderKey key(translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe);
-
-        // reuse or create the bucket corresponding to that key and insert the mesh as unsorted
-        _renderBuckets[key.getRaw()]._unsortedMeshes.insertMulti(materialID, i);
     }
-    
-    for(auto& b : _renderBuckets) {
-        foreach(auto i, b.second._unsortedMeshes) {
-            b.second._meshes.append(i);
-        }
-        b.second._unsortedMeshes.clear();
-    }
-
     _meshGroupsKnown = true;
 } 
 
diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h
index c96e006aa8..7c1572418e 100644
--- a/libraries/render-utils/src/Model.h
+++ b/libraries/render-utils/src/Model.h
@@ -51,17 +51,11 @@ namespace render {
     class PendingChanges;
     typedef unsigned int ItemID;
 }
-class OpaqueMeshPart;
-class TransparentMeshPart;
+class MeshPartPayload;
 
-inline uint qHash(const std::shared_ptr<TransparentMeshPart>& a, uint seed) {
+inline uint qHash(const std::shared_ptr<MeshPartPayload>& a, uint seed) {
     return qHash(a.get(), seed);
 }
-inline uint qHash(const std::shared_ptr<OpaqueMeshPart>& a, uint seed) {
-    return qHash(a.get(), seed);
-}
-
-
 
 /// A generic 3D model displaying geometry loaded from a URL.
 class Model : public QObject, public PhysicsEntity {
@@ -77,7 +71,7 @@ public:
     virtual ~Model();
     
     /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension
-    void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f);
+    void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false);
     bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled
     bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit
     const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to
@@ -511,24 +505,11 @@ private:
     };
     static RenderPipelineLib _renderPipelineLib;
 
-   
-    class RenderBucket {
-    public:
-        QVector<int> _meshes;
-        QMap<QString, int> _unsortedMeshes;
-    };
-    typedef std::unordered_map<int, RenderBucket> BaseRenderBucketMap;
-    class RenderBucketMap : public BaseRenderBucketMap {
-    public:
-        typedef RenderKey Key;
-    };
-    RenderBucketMap _renderBuckets;
-
     bool _renderCollisionHull;
     
     
-    QSet<std::shared_ptr<TransparentMeshPart>> _transparentRenderItems;
-    QSet<std::shared_ptr<OpaqueMeshPart>> _opaqueRenderItems;
+    QSet<std::shared_ptr<MeshPartPayload>> _transparentRenderItems;
+    QSet<std::shared_ptr<MeshPartPayload>> _opaqueRenderItems;
     QMap<render::ItemID, render::PayloadPointer> _renderItems;
     bool _readyWhenAdded = false;
     bool _needsReload = true;

From 139855f48792594c5e6b5ea2a51557b0b0a5f515 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Wed, 10 Jun 2015 16:13:17 -0700
Subject: [PATCH 45/88] handle remove attachment case

---
 interface/src/avatar/Avatar.cpp   | 26 ++++++++++++++++++++++----
 interface/src/avatar/Avatar.h     |  3 +++
 interface/src/avatar/MyAvatar.cpp | 18 +-----------------
 3 files changed, 26 insertions(+), 21 deletions(-)

diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index f644968ff8..dbe7d52a3f 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -107,6 +107,9 @@ Avatar::Avatar() :
 }
 
 Avatar::~Avatar() {
+    for(auto attachment : _unusedAttachments) {
+        delete attachment;
+    }
 }
 
 const float BILLBOARD_LOD_DISTANCE = 40.0f;
@@ -525,7 +528,7 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
     return glm::angleAxis(angle * proportion, axis);
 }
 
-void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) {
+void Avatar::fixupModelsInScene() {
     // check to see if when we added our models to the scene they were ready, if they were not ready, then
     // fix them up in the scene
     render::ScenePointer scene = Application::getInstance()->getMain3DScene();
@@ -544,8 +547,18 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool
             attachmentModel->addToScene(scene, pendingChanges);
         }
     }
+    for (auto attachmentModelToRemove : _attachmentsToRemove) {
+        attachmentModelToRemove->removeFromScene(scene, pendingChanges);
+        _unusedAttachments << attachmentModelToRemove;
+    }
+    _attachmentsToRemove.clear();
     scene->enqueuePendingChanges(pendingChanges);
+}
 
+void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel) {
+
+    fixupModelsInScene();
+    
     {
         Glower glower(renderArgs, glowLevel);
 
@@ -947,13 +960,18 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
     }
     // make sure we have as many models as attachments
     while (_attachmentModels.size() < attachmentData.size()) {
-        Model* model = new Model(this);
+        Model* model = nullptr;
+        if (_unusedAttachments.size() > 0) {
+            model = _unusedAttachments.takeFirst();
+        } else {
+            model = new Model(this);
+        }
         model->init();
         _attachmentModels.append(model);
     }
     while (_attachmentModels.size() > attachmentData.size()) {
-        // NOTE: what's really going to happen here? This seems dangerous... has the model been removed from the scene?
-        delete _attachmentModels.takeLast();
+        auto attachmentModel = _attachmentModels.takeLast();
+        _attachmentsToRemove << attachmentModel;
     }
 
     // update the urls
diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index 1113496080..b198d12a6e 100644
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -195,6 +195,8 @@ protected:
     SkeletonModel _skeletonModel;
     glm::vec3 _skeletonOffset;
     QVector<Model*> _attachmentModels;
+    QVector<Model*> _attachmentsToRemove;
+    QVector<Model*> _unusedAttachments;
     float _bodyYawDelta;
 
     // These position histories and derivatives are in the world-frame.
@@ -234,6 +236,7 @@ protected:
     void renderDisplayName(RenderArgs* renderArgs);
     virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f);
     virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const;
+    virtual void fixupModelsInScene();
 
     void simulateAttachments(float deltaTime);
 
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 65b927c2c0..c4e08b5dba 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -1178,23 +1178,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bo
 
     // check to see if when we added our models to the scene they were ready, if they were not ready, then
     // fix them up in the scene
-    render::ScenePointer scene = Application::getInstance()->getMain3DScene();
-    render::PendingChanges pendingChanges;
-    if (_skeletonModel.needsFixupInScene()) {
-        _skeletonModel.removeFromScene(scene, pendingChanges);
-        _skeletonModel.addToScene(scene, pendingChanges);
-    }
-    if (getHead()->getFaceModel().needsFixupInScene()) {
-        getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
-        getHead()->getFaceModel().addToScene(scene, pendingChanges);
-    }
-    for (auto attachmentModel : _attachmentModels) {
-        if (attachmentModel->needsFixupInScene()) {
-            attachmentModel->removeFromScene(scene, pendingChanges);
-            attachmentModel->addToScene(scene, pendingChanges);
-        }
-    }
-    scene->enqueuePendingChanges(pendingChanges);
+    fixupModelsInScene();
 
     const glm::vec3 cameraPos = Application::getInstance()->getCamera()->getPosition();
 

From eccf4eb8a8eb8ab048d205a18f46429f8f2545da Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 10 Jun 2015 17:05:49 -0700
Subject: [PATCH 46/88] hold action works

---
 interface/src/avatar/AvatarActionHold.cpp     | 100 +++++++-----------
 interface/src/avatar/AvatarActionHold.h       |  14 +--
 .../entities/src/EntityActionInterface.cpp    |   3 +
 libraries/physics/src/ObjectAction.cpp        |   7 --
 libraries/physics/src/ObjectAction.h          |   2 +-
 .../physics/src/ObjectActionPullToPoint.cpp   |   9 ++
 libraries/physics/src/ObjectActionSpring.cpp  |  12 +++
 libraries/physics/src/ObjectActionSpring.h    |   2 +-
 8 files changed, 67 insertions(+), 82 deletions(-)

diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp
index 6d4164367a..74a583df58 100644
--- a/interface/src/avatar/AvatarActionHold.cpp
+++ b/interface/src/avatar/AvatarActionHold.cpp
@@ -15,7 +15,7 @@
 #include "AvatarActionHold.h"
 
 AvatarActionHold::AvatarActionHold(QUuid id, EntityItemPointer ownerEntity) :
-    ObjectAction(id, ownerEntity) {
+    ObjectActionSpring(id, ownerEntity) {
     #if WANT_DEBUG
     qDebug() << "AvatarActionHold::AvatarActionHold";
     #endif
@@ -28,83 +28,57 @@ AvatarActionHold::~AvatarActionHold() {
 }
 
 void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
-    // handle the linear part
-    if (_linearOffsetSet) {
-    }
-
     auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
-
     glm::vec3 palmPosition = myAvatar->getRightPalmPosition();
-    glm::vec3 position = getPosition();
-    glm::vec3 positionalTarget = palmPosition + _linearOffset;
-    glm::vec3 offset = positionalTarget - position;
 
-    float offsetLength = glm::length(offset);
-    float speed = offsetLength / _timeScale;
+    auto rotation = myAvatar->getWorldAlignedOrientation();
+    auto offset = rotation * _relativePosition;
+    auto position = palmPosition + offset;
+    rotation *= _relativeRotation;
 
-    if (offsetLength > IGNORE_POSITION_DELTA) {
-        glm::vec3 newVelocity = glm::normalize(offset) * speed;
-        setLinearVelocity(newVelocity);
-    } else {
-        setLinearVelocity(glm::vec3(0.0f));
-    }
+    lockForWrite();
+    _positionalTarget = position;
+    _rotationalTarget = rotation;
+    unlock();
 
-
-    // handle rotation
-    if (_angularOffsetSet) {
-        glm::quat bodyRotation = getRotation();
-        // if qZero and qOne are too close to each other, we can get NaN for angle.
-        auto alignmentDot = glm::dot(bodyRotation, _angularOffset);
-        const float almostOne = 0.99999;
-        if (glm::abs(alignmentDot) < almostOne) {
-            glm::quat target = _angularOffset;
-            if (alignmentDot < 0) {
-                target = -target;
-            }
-            glm::quat qZeroInverse = glm::inverse(bodyRotation);
-            glm::quat deltaQ = target * qZeroInverse;
-            glm::vec3 axis = glm::axis(deltaQ);
-            float angle = glm::angle(deltaQ);
-            if (isNaN(angle)) {
-                qDebug() << "AvatarActionHold::updateAction angle =" << angle
-                         << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w
-                         << "target-rotation ="
-                         << target.x << target.y << target.z<< target.w;
-            }
-            assert(!isNaN(angle));
-            glm::vec3 newAngularVelocity = (angle / _timeScale) * glm::normalize(axis);
-            setAngularVelocity(newAngularVelocity);
-        } else {
-            setAngularVelocity(glm::vec3(0.0f));
-        }
-    }
+    ObjectActionSpring::updateActionWorker(deltaTimeStep);
 }
 
 
 bool AvatarActionHold::updateArguments(QVariantMap arguments) {
-    // targets are required, spring-constants are optional
-    bool ptOk = true;
-    glm::vec3 linearOffset =
-        EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false);
-
-    bool rtOk = true;
-    glm::quat angularOffset =
-        EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false);
+    bool rPOk = true;
+    glm::vec3 relativePosition =
+        EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", rPOk, false);
+    bool rROk = true;
+    glm::quat relativeRotation =
+        EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", rROk, false);
+    bool tSOk = true;
+    float timeScale =
+        EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", tSOk, false);
 
     lockForWrite();
-
-    _linearOffsetSet = _angularOffsetSet = false;
-
-    if (ptOk) {
-        _linearOffset = linearOffset;
-        _linearOffsetSet = true;
+    if (rPOk) {
+        _relativePosition = relativePosition;
+    } else {
+        _relativePosition = glm::vec3(0.0f, 0.0f, 1.0f);
     }
 
-    if (rtOk) {
-        _angularOffset = angularOffset;
-        _angularOffsetSet = true;
+    if (rROk) {
+        _relativeRotation = relativeRotation;
+    } else {
+        _relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
     }
 
+    if (tSOk) {
+        _linearTimeScale = timeScale;
+        _angularTimeScale = timeScale;
+    } else {
+        _linearTimeScale = 0.2;
+        _angularTimeScale = 0.2;
+    }
+
+    _positionalTargetSet = true;
+    _rotationalTargetSet = true;
     _active = true;
     unlock();
     return true;
diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h
index 19e6e8df55..f92ea94aaa 100644
--- a/interface/src/avatar/AvatarActionHold.h
+++ b/interface/src/avatar/AvatarActionHold.h
@@ -15,9 +15,9 @@
 #include <QUuid>
 
 #include <EntityItem.h>
-#include <ObjectAction.h>
+#include <ObjectActionSpring.h>
 
-class AvatarActionHold : public ObjectAction {
+class AvatarActionHold : public ObjectActionSpring {
 public:
     AvatarActionHold(QUuid id, EntityItemPointer ownerEntity);
     virtual ~AvatarActionHold();
@@ -26,14 +26,8 @@ public:
     virtual void updateActionWorker(float deltaTimeStep);
 
 private:
-
-    glm::vec3 _linearOffset;
-    bool _linearOffsetSet;
-
-    glm::quat _angularOffset;
-    bool _angularOffsetSet;
-
-    float _timeScale = 0.01;
+    glm::vec3 _relativePosition;
+    glm::quat _relativeRotation;
 };
 
 #endif // hifi_AvatarActionHold_h
diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp
index 09bb933488..b293d609c2 100644
--- a/libraries/entities/src/EntityActionInterface.cpp
+++ b/libraries/entities/src/EntityActionInterface.cpp
@@ -25,6 +25,9 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS
     if (normalizedActionTypeString == "spring") {
         return ACTION_TYPE_SPRING;
     }
+    if (normalizedActionTypeString == "hold") {
+        return ACTION_TYPE_HOLD;
+    }
 
     qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString;
     return ACTION_TYPE_NONE;
diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp
index 8f01a90410..ee7ba9ce7c 100644
--- a/libraries/physics/src/ObjectAction.cpp
+++ b/libraries/physics/src/ObjectAction.cpp
@@ -31,14 +31,7 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta
         qDebug() << "ObjectActionPullToPoint::updateAction no owner entity";
         return;
     }
-    if (!tryLockForRead()) {
-        // don't risk hanging the thread running the physics simulation
-        return;
-    }
-
     updateActionWorker(deltaTimeStep);
-
-    unlock();
 }
 
 void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h
index a834a54864..0fd7383e6f 100644
--- a/libraries/physics/src/ObjectAction.h
+++ b/libraries/physics/src/ObjectAction.h
@@ -47,7 +47,7 @@ private:
     QReadWriteLock _lock;
 
 protected:
-    btRigidBody* getRigidBody();
+    virtual btRigidBody* getRigidBody();
     virtual glm::vec3 getPosition();
     virtual void setPosition(glm::vec3 position);
     virtual glm::quat getRotation();
diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp
index 1069a2b9cf..053bef6a03 100644
--- a/libraries/physics/src/ObjectActionPullToPoint.cpp
+++ b/libraries/physics/src/ObjectActionPullToPoint.cpp
@@ -25,13 +25,20 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() {
 }
 
 void ObjectActionPullToPoint::updateActionWorker(btScalar deltaTimeStep) {
+    if (!tryLockForRead()) {
+        // don't risk hanging the thread running the physics simulation
+        return;
+    }
+
     void* physicsInfo = _ownerEntity->getPhysicsInfo();
     if (!physicsInfo) {
+        unlock();
         return;
     }
     ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
     btRigidBody* rigidBody = motionState->getRigidBody();
     if (!rigidBody) {
+        unlock();
         return;
     }
 
@@ -44,6 +51,8 @@ void ObjectActionPullToPoint::updateActionWorker(btScalar deltaTimeStep) {
     } else {
         rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
     }
+
+    unlock();
 }
 
 
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
index aea4dafbb8..6883e73766 100644
--- a/libraries/physics/src/ObjectActionSpring.cpp
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -25,13 +25,23 @@ ObjectActionSpring::~ObjectActionSpring() {
 }
 
 void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
+    if (!tryLockForRead()) {
+        // don't risk hanging the thread running the physics simulation
+        qDebug() << "ObjectActionSpring::updateActionWorker lock failed";
+        return;
+    }
+
     void* physicsInfo = _ownerEntity->getPhysicsInfo();
     if (!physicsInfo) {
+        unlock();
+        qDebug() << "ObjectActionSpring::updateActionWorker no physicsInfo";
         return;
     }
     ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
     btRigidBody* rigidBody = motionState->getRigidBody();
     if (!rigidBody) {
+        unlock();
+        qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody";
         return;
     }
 
@@ -79,6 +89,8 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
             rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f)));
         }
     }
+
+    unlock();
 }
 
 
diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h
index c5cbbe6126..9f3df0fdf8 100644
--- a/libraries/physics/src/ObjectActionSpring.h
+++ b/libraries/physics/src/ObjectActionSpring.h
@@ -25,7 +25,7 @@ public:
     virtual bool updateArguments(QVariantMap arguments);
     virtual void updateActionWorker(float deltaTimeStep);
 
-private:
+protected:
 
     glm::vec3 _positionalTarget;
     float _linearTimeScale;

From 45c7cd4929681f09f3209a8a7f5dc074b0ed9b1f Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 10 Jun 2015 18:48:51 -0700
Subject: [PATCH 47/88] respond to code review, add a simple hold-a-stick
 script

---
 examples/stick.js                              | 18 ++++++++++++++++++
 libraries/entities/src/EntityActionInterface.h |  5 +++--
 .../entities/src/EntityScriptingInterface.cpp  |  6 ++++++
 libraries/physics/src/ObjectActionSpring.cpp   | 13 +++----------
 libraries/render/src/render/Scene.h            |  2 +-
 5 files changed, 31 insertions(+), 13 deletions(-)
 create mode 100644 examples/stick.js

diff --git a/examples/stick.js b/examples/stick.js
new file mode 100644
index 0000000000..02101f59e1
--- /dev/null
+++ b/examples/stick.js
@@ -0,0 +1,18 @@
+var stickID = null;
+// sometimes if this is run immediately the stick doesn't get created?  use a timer.
+Script.setTimeout(function() {
+    var stickID = Entities.addEntity({
+        type: "Model",
+        modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
+        compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
+        dimensions: {x: .11, y: .11, z: .59},
+        position: MyAvatar.getRightPalmPosition(),
+        rotation: MyAvatar.orientation,
+        damping: .1,
+        collisionsWillMove: true
+    });
+    Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, timeScale: 0.15});
+}, 3000);
+
+function cleanup() { Entities.deleteEntity(stickID); }
+Script.scriptEnding.connect(cleanup);
diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h
index 50f862c535..457db8f5c3 100644
--- a/libraries/entities/src/EntityActionInterface.h
+++ b/libraries/entities/src/EntityActionInterface.h
@@ -36,8 +36,6 @@ public:
     virtual const EntityItemPointer& getOwnerEntity() const = 0;
     virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0;
     virtual bool updateArguments(QVariantMap arguments) = 0;
-    // virtual QByteArray serialize() = 0;
-    // static EntityActionPointer deserialize(EntityItemPointer ownerEntity, QByteArray data);
 
     static EntityActionType actionTypeFromString(QString actionTypeString);
     static QString actionTypeToString(EntityActionType actionType);
@@ -52,6 +50,9 @@ protected:
     virtual glm::vec3 getAngularVelocity() = 0;
     virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0;
 
+    // these look in the arguments map for a named argument.  if it's not found or isn't well formed,
+    // ok will be set to false (note that it's never set to true -- set it to true before calling these).
+    // if required is true, failure to extract an argument will cause a warning to be printed.
     static glm::vec3 extractVec3Argument (QString objectName, QVariantMap arguments,
                                           QString argumentName, bool& ok, bool required = true);
     static glm::quat extractQuatArgument (QString objectName, QVariantMap arguments,
diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index 6f6633ce0f..a091c786b7 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -495,6 +495,12 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
     QUuid actionID = QUuid::createUuid();
     auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
     bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
+            // create this action even if the entity doesn't have physics info.  it will often be the
+            // case that a script adds an action immediately after an object is created, and the physicsInfo
+            // is computed asynchronously.
+            // if (!entity->getPhysicsInfo()) {
+            //     return false;
+            // }
             EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString);
             if (actionType == ACTION_TYPE_NONE) {
                 return false;
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
index 6883e73766..8eb4f7f652 100644
--- a/libraries/physics/src/ObjectActionSpring.cpp
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -34,7 +34,6 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
     void* physicsInfo = _ownerEntity->getPhysicsInfo();
     if (!physicsInfo) {
         unlock();
-        qDebug() << "ObjectActionSpring::updateActionWorker no physicsInfo";
         return;
     }
     ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
@@ -65,7 +64,7 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
         glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation());
         // if qZero and qOne are too close to each other, we can get NaN for angle.
         auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget);
-        const float almostOne = 0.99999;
+        const float almostOne = 0.99999f;
         if (glm::abs(alignmentDot) < almostOne) {
             glm::quat target = _rotationalTarget;
             if (alignmentDot < 0) {
@@ -75,12 +74,6 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
             glm::quat deltaQ = target * qZeroInverse;
             glm::vec3 axis = glm::axis(deltaQ);
             float angle = glm::angle(deltaQ);
-            if (isNaN(angle)) {
-                qDebug() << "ObjectActionSpring::updateAction angle =" << angle
-                         << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w
-                         << "target-rotation ="
-                         << target.x << target.y << target.z<< target.w;
-            }
             assert(!isNaN(angle));
             glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis);
             rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity));
@@ -130,7 +123,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
         if (pscOk) {
             _linearTimeScale = linearTimeScale;
         } else {
-            _linearTimeScale = 0.1;
+            _linearTimeScale = 0.1f;
         }
     }
 
@@ -141,7 +134,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
         if (rscOk) {
             _angularTimeScale = angularTimeScale;
         } else {
-            _angularTimeScale = 0.1;
+            _angularTimeScale = 0.1f;
         }
     }
 
diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h
index 8cb29609ba..b9481d367e 100644
--- a/libraries/render/src/render/Scene.h
+++ b/libraries/render/src/render/Scene.h
@@ -245,7 +245,7 @@ public:
     void update(const UpdateFunctorPointer& updateFunctor)  { _payload->update(updateFunctor); }
 
     // Shape Type Interface
-    const model::MaterialKey& getMaterialKey() const { return _payload->getMaterialKey(); }
+    const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); }
 
 protected:
     PayloadPointer _payload;

From 93c0bd63e962bc6673371e00bc9a1555570617e6 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Wed, 10 Jun 2015 18:54:07 -0700
Subject: [PATCH 48/88] fix debug rendering transforms

---
 .../entities-renderer/src/RenderableBoxEntityItem.cpp    | 2 +-
 .../src/RenderableDebugableEntityItem.cpp                | 9 +++------
 .../entities-renderer/src/RenderableLightEntityItem.cpp  | 2 +-
 .../entities-renderer/src/RenderableSphereEntityItem.cpp | 2 +-
 4 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
index 6ddf44b82d..066aaa8b31 100644
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
@@ -33,7 +33,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
     
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(getTransformToCenter());
+    batch.setModelTransform(getTransform()); // we want to include the scale as well
     DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
 
     RenderableDebugableEntityItem::render(this, args);
diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
index fecc574af2..32e30c7e96 100644
--- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
@@ -24,9 +24,7 @@ void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, Render
                                                       float puffedOut, glm::vec4& color) {
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    Transform transform = entity->getTransformToCenter();
-    //transform.postScale(entity->getDimensions());
-    batch.setModelTransform(transform);
+    batch.setModelTransform(entity->getTransform()); // we want to include the scale as well
     DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f + puffedOut, color);
 }
 
@@ -34,10 +32,9 @@ void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args)
     if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) {
         Q_ASSERT(args->_batch);
         gpu::Batch& batch = *args->_batch;
-        Transform transform = entity->getTransformToCenter();
-        transform.postScale(entity->getDimensions());
-        batch.setModelTransform(transform);
 
+        batch.setModelTransform(entity->getTransform()); // we want to include the scale as well
+        
         auto nodeList = DependencyManager::get<NodeList>();
         const QUuid& myNodeID = nodeList->getSessionUUID();
         bool highlightSimulationOwnership = (entity->getSimulatorID() == myNodeID);
diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
index 819989d5ec..8a84c167c5 100644
--- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
@@ -49,7 +49,7 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
 #ifdef WANT_DEBUG
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(getTransformToCenter());
+    batch.setModelTransform(getTransform());
     DependencyManager::get<DeferredLightingEffect>()->renderWireSphere(batch, 0.5f, 15, 15, glm::vec4(color, 1.0f));
 #endif
 };
diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
index b0aaebb2c8..5ba9003d43 100644
--- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
@@ -39,7 +39,7 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
     
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(getTransformToCenter());
+    batch.setModelTransform(getTransform()); // use a transform with scale, rotation, registration point and translation
     DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
 
     RenderableDebugableEntityItem::render(this, args);

From ba4fd7adf6b0b3238b2f82e043ddbf7eb49e5d30 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 10 Jun 2015 19:23:01 -0700
Subject: [PATCH 49/88] windows didn't like
 EntityActionFactoryInterface::factory not returning an error

---
 libraries/entities/src/EntityActionFactoryInterface.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/entities/src/EntityActionFactoryInterface.h b/libraries/entities/src/EntityActionFactoryInterface.h
index 2347eff525..9820313aae 100644
--- a/libraries/entities/src/EntityActionFactoryInterface.h
+++ b/libraries/entities/src/EntityActionFactoryInterface.h
@@ -27,7 +27,7 @@ class EntityActionFactoryInterface : public QObject, public Dependency {
                                         EntityActionType type,
                                         QUuid id,
                                         EntityItemPointer ownerEntity,
-                                        QVariantMap arguments) { assert(false); }
+                                        QVariantMap arguments) { assert(false); return nullptr; }
 };
 
 #endif // hifi_EntityActionFactoryInterface_h

From 8bd80c511ee8df21053f7ab47c93105484317dca Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 10 Jun 2015 21:08:00 -0700
Subject: [PATCH 50/88] make stick follow mouse motion

---
 examples/stick.js | 46 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 42 insertions(+), 4 deletions(-)

diff --git a/examples/stick.js b/examples/stick.js
index 02101f59e1..5631f3aa3a 100644
--- a/examples/stick.js
+++ b/examples/stick.js
@@ -1,7 +1,20 @@
+//  stick.js
+//  examples
+//
+//  Created by Seth Alves on 2015-6-10
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Allow avatar to hold a stick
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
 var stickID = null;
+var actionID = "00000000-0000-0000-0000-000000000000";
 // sometimes if this is run immediately the stick doesn't get created?  use a timer.
 Script.setTimeout(function() {
-    var stickID = Entities.addEntity({
+    stickID = Entities.addEntity({
         type: "Model",
         modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
         compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
@@ -11,8 +24,33 @@ Script.setTimeout(function() {
         damping: .1,
         collisionsWillMove: true
     });
-    Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, timeScale: 0.15});
+    actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, timeScale: 0.15});
 }, 3000);
 
-function cleanup() { Entities.deleteEntity(stickID); }
-Script.scriptEnding.connect(cleanup);
+
+function cleanUp() {
+    Entities.deleteEntity(stickID);
+}
+
+function mouseMoveEvent(event) {
+    if (!stickID || actionID == "00000000-0000-0000-0000-000000000000") {
+        return;
+    }
+    var windowCenterX = Window.innerWidth/ 2;
+    var windowCenterY = Window.innerHeight / 2;
+    var mouseXCenterOffset = event.x - windowCenterX;
+    var mouseYCenterOffset = event.y - windowCenterY;
+    var mouseXRatio = mouseXCenterOffset / windowCenterX;
+    var mouseYRatio = mouseYCenterOffset / windowCenterY;
+
+    var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * -90, mouseXRatio * -90, 0);
+    var baseOffset = {x: 0.0, y: 0.0, z: -0.9};
+    var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset);
+
+    Entities.updateAction(stickID, actionID, {relativePosition: offset,
+                                              relativeRotation: stickOrientation,
+                                              timeScale: 0.15});
+}
+
+Script.scriptEnding.connect(cleanUp);
+Controller.mouseMoveEvent.connect(mouseMoveEvent);

From ac0fc5d97405ec304bab0f15e18773dab2556d7c Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Thu, 11 Jun 2015 00:50:24 -0700
Subject: [PATCH 51/88] Working on oculus overlay code

---
 interface/src/ui/ApplicationOverlay.cpp | 235 ++++++++++++------------
 interface/src/ui/ApplicationOverlay.h   |  28 +--
 2 files changed, 117 insertions(+), 146 deletions(-)

diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index f57c10b64e..4be47e342a 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -301,7 +301,6 @@ gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() {
 
 // Draws the FBO texture for the screen
 void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
-
     if (_alpha == 0.0f) {
         return;
     }
@@ -312,80 +311,77 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
     }
 
     
-    //FIXME - doesn't work
     renderArgs->_context->syncCache();
+    glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height());
+
     gpu::Batch batch;
+    Transform model;
     //DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
     batch.setPipeline(getDrawPipeline());
     batch.setModelTransform(Transform());
     batch.setProjectionTransform(mat4());
-    batch.setViewTransform(Transform());
-    batch.setUniformTexture(0, _crosshairTexture);
+    batch.setViewTransform(model);
+    batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
+    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     DependencyManager::get<GeometryCache>()->renderUnitQuad(batch, vec4(vec3(1), _alpha));
+
+    //draw the mouse pointer
+    glm::vec2 canvasSize = qApp->getCanvasSize();
+
+    // Get the mouse coordinates and convert to NDC [-1, 1]
+    vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY());
+    mousePosition /= canvasSize;
+    mousePosition *= 2.0f;
+    mousePosition -= 1.0f;
+    mousePosition.y *= -1.0f;
+    model.setTranslation(vec3(mousePosition, 0));
+    glm::vec2 mouseSize = 32.0f / canvasSize;
+    model.setScale(vec3(mouseSize, 1.0f));
+    batch.setModelTransform(model);
+    batch.setUniformTexture(0, _crosshairTexture);
+    glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
+    DependencyManager::get<GeometryCache>()->renderUnitQuad(batch, vec4(1));
     renderArgs->_context->render(batch);
-    return;
-
-
-    
-    glDisable(GL_DEPTH_TEST);
-    glDisable(GL_LIGHTING);
-    glEnable(GL_BLEND);
-    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glEnable(GL_TEXTURE_2D);
-    glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height());
-
-    glMatrixMode(GL_PROJECTION);
-    glPushMatrix(); 
-    glLoadIdentity();
-    glMatrixMode(GL_MODELVIEW);
-    glPushMatrix();
-    glLoadIdentity();
-    {
-        glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
-        DependencyManager::get<GeometryCache>()->renderUnitQuad(vec4(vec3(1), _alpha));
-        //draw the mouse pointer
-        glm::vec2 canvasSize = qApp->getCanvasSize();
-
-        // Get the mouse coordinates and convert to NDC [-1, 1]
-        vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY());
-        mousePosition /= canvasSize;
-        mousePosition *= 2.0f;
-        mousePosition -= 1.0f;
-        mousePosition.y *= -1.0f;
-        mat4 mouseMv = glm::translate(mat4(), vec3(mousePosition, 0));
-
-        // Scale the mouse based on the canvasSize (NOT the device size, 
-        // we don't want a smaller mouse on retina displays)
-        glm::vec2 mouseSize = 32.0f / canvasSize;
-        mouseMv = glm::scale(mouseMv, vec3(mouseSize, 1.0f));
-
-        // Push the resulting matrix into modelview
-        glLoadMatrixf(glm::value_ptr(mouseMv));
-        glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
-        glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
-        DependencyManager::get<GeometryCache>()->renderUnitQuad(reticleColor);
-    } 
-    glMatrixMode(GL_PROJECTION);
-    glPopMatrix();
-    glMatrixMode(GL_MODELVIEW);
-    glPopMatrix();
 }
 
+
+static gpu::BufferPointer _hemiVertices;
+static gpu::BufferPointer _hemiIndices;
+static int _hemiIndexCount{ 0 };
+
 // Draws the FBO texture for Oculus rift.
 void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) {
     if (_alpha == 0.0f) {
         return;
     }
 
-    _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80);
-    glDisable(GL_DEPTH_TEST);
-    glDisable(GL_LIGHTING);
-    glEnable(GL_BLEND);
-    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glEnable(GL_TEXTURE_2D);
-    glActiveTexture(GL_TEXTURE0);
-    glMatrixMode(GL_MODELVIEW);
-    glLoadIdentity();
+    auto geometryCache = DependencyManager::get<GeometryCache>();
+    gpu::Batch batch;
+    //DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
+    batch.setPipeline(getDrawPipeline());
+    batch._glDisable(GL_DEPTH_TEST);
+    batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
+    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    batch.setProjectionTransform(whichCamera.getProjection());
+    batch.setViewTransform(Transform());
+
+    Transform model;
+    model.setTranslation(vec3(0.0f, 0.0f, -2.0f));
+    batch.setModelTransform(model);
+
+    // FIXME doesn't work
+    drawSphereSection(batch);
+
+    // works...
+    geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha));
+
+    renderArgs->_context->render(batch);
+    // batch.setUniformTexture(0, gpu::TexturePointer());
+    //    geometryCache->renderSolidCube(batch, 0.5f, vec4(1));
+
+    /*
     // The camera here contains only the head pose relative to the avatar position
     vec3 pos = whichCamera.getPosition();
     quat rot = whichCamera.getOrientation();
@@ -394,6 +390,7 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera
     glLoadMatrixf(glm::value_ptr(glm::inverse(overlayXfm)));
     glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
     _overlays.render();
+    */
 
     //Update and draw the magnifiers
     /*
@@ -423,11 +420,11 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera
             });
         }
     }
-    */
 
     if (!Application::getInstance()->isMouseHidden()) {
         renderPointersOculus();
     }
+    */
 }
 
 // Draws the FBO texture for 3DTV.
@@ -1104,109 +1101,102 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() {
     }
 }
 
-ApplicationOverlay::TexturedHemisphere::TexturedHemisphere() :
-    _vertices(0),
-    _indices(0),
-    _vbo(0, 0) {
-}
 
-ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() {
-    cleanupVBO();
-}
-
-void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov,
-                                                      const float aspectRatio,
-                                                      const int slices,
-                                                      const int stacks) {
+void ApplicationOverlay::buildHemiVertices(
+    const float fov, const float aspectRatio, const int slices, const int stacks) {
     static float textureFOV = 0.0f, textureAspectRatio = 1.0f;
-    if (textureFOV == fov && textureAspectRatio == aspectRatio) {
+    if (_hemiVerticesID != GeometryCache::UNKNOWN_ID && textureFOV == fov && textureAspectRatio == aspectRatio) {
         return;
     }
+
     textureFOV = fov;
     textureAspectRatio = aspectRatio;
 
+    auto geometryCache = DependencyManager::get<GeometryCache>();
+    if (_hemiVerticesID == GeometryCache::UNKNOWN_ID) {
+        _hemiVerticesID = geometryCache->allocateID();
+    }
+
+    _hemiVertices = gpu::BufferPointer(new gpu::Buffer());
+    _hemiIndices = gpu::BufferPointer(new gpu::Buffer());
+
+
     if (fov >= PI) {
         qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues";
     }
-    // Cleanup old VBO if necessary
-    cleanupVBO();
-    
+
     //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
     
-    // Compute number of vertices needed
-    _vertices = slices * stacks;
-    
+    vec3 pos; 
     // Compute vertices positions and texture UV coordinate
-    TextureVertex* vertexData = new TextureVertex[_vertices];
-    TextureVertex* vertexPtr = &vertexData[0];
+    // Create and write to buffer
+    //_hemiVertices->(sizeof(vec3) + sizeof(vec2) + sizeof(vec4)) * stacks * slices);
     for (int i = 0; i < stacks; i++) {
         float stacksRatio = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
         // abs(theta) <= fov / 2.0f
         float pitch = -fov * (stacksRatio - 0.5f);
-        
         for (int j = 0; j < slices; j++) {
             float slicesRatio = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
             // abs(phi) <= fov * aspectRatio / 2.0f
             float yaw = -fov * aspectRatio * (slicesRatio - 0.5f);
-            
-            vertexPtr->position = getPoint(yaw, pitch);
-            vertexPtr->uv.x = slicesRatio;
-            vertexPtr->uv.y = stacksRatio;
-            vertexPtr++;
+            pos = getPoint(yaw, pitch);
+            _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos);
+            _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&vec2(slicesRatio, stacksRatio));
+            _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&vec4(1));
         }
     }
-    // Create and write to buffer
-    glGenBuffers(1, &_vbo.first);
-    glBindBuffer(GL_ARRAY_BUFFER, _vbo.first);
-    static const int BYTES_PER_VERTEX = sizeof(TextureVertex);
-    glBufferData(GL_ARRAY_BUFFER, _vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
-    delete[] vertexData;
-    
     
     // Compute number of indices needed
     static const int VERTEX_PER_TRANGLE = 3;
     static const int TRIANGLE_PER_RECTANGLE = 2;
     int numberOfRectangles = (slices - 1) * (stacks - 1);
-    _indices = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
+    _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
     
     // Compute indices order
-    GLushort* indexData = new GLushort[_indices];
-    GLushort* indexPtr = indexData;
+    std::vector<GLushort> indices;
     for (int i = 0; i < stacks - 1; i++) {
         for (int j = 0; j < slices - 1; j++) {
             GLushort bottomLeftIndex = i * slices + j;
             GLushort bottomRightIndex = bottomLeftIndex + 1;
             GLushort topLeftIndex = bottomLeftIndex + slices;
             GLushort topRightIndex = topLeftIndex + 1;
-            
-            *(indexPtr++) = topLeftIndex;
-            *(indexPtr++) = bottomLeftIndex;
-            *(indexPtr++) = topRightIndex;
-            
-            *(indexPtr++) = topRightIndex;
-            *(indexPtr++) = bottomLeftIndex;
-            *(indexPtr++) = bottomRightIndex;
+            // FIXME make a z-order curve for better vertex cache locality
+            indices.push_back(topLeftIndex);
+            indices.push_back(bottomLeftIndex);
+            indices.push_back(topRightIndex);
+
+            indices.push_back(topRightIndex);
+            indices.push_back(bottomLeftIndex);
+            indices.push_back(bottomRightIndex);
         }
     }
-    // Create and write to buffer
-    glGenBuffers(1, &_vbo.second);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second);
-    static const int BYTES_PER_INDEX = sizeof(GLushort);
-    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
-    delete[] indexData;
+    _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]);
 }
 
-void ApplicationOverlay::TexturedHemisphere::cleanupVBO() {
-    if (_vbo.first != 0) {
-        glDeleteBuffers(1, &_vbo.first);
-        _vbo.first = 0;
-    }
-    if (_vbo.second != 0) {
-        glDeleteBuffers(1, &_vbo.second);
-        _vbo.second = 0;
-    }
+
+void ApplicationOverlay::drawSphereSection(gpu::Batch& batch) {
+    buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80);
+    static const int VERTEX_DATA_SLOT = 0;
+    static const int TEXTURE_DATA_SLOT = 1;
+    static const int COLOR_DATA_SLOT = 2;
+    gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone
+    streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
+    streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), sizeof(vec3));
+    streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA), sizeof(vec3) + sizeof(vec2));
+    batch.setInputFormat(streamFormat);
+
+    static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4);
+    gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element);
+    gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element);
+    gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
+    batch.setInputBuffer(VERTEX_DATA_SLOT, posView);
+    batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView);
+    batch.setInputBuffer(COLOR_DATA_SLOT, colView);
+    batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0);
+    batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount);
 }
 
+
 GLuint ApplicationOverlay::getOverlayTexture() {
     return _framebufferObject->texture();
 }
@@ -1234,6 +1224,7 @@ void ApplicationOverlay::buildFramebufferObject() {
     glBindTexture(GL_TEXTURE_2D, 0);
 }
 
+/*
 //Renders a hemisphere with texture coordinates.
 void ApplicationOverlay::TexturedHemisphere::render() {
     if (_vbo.first == 0 || _vbo.second == 0) {
@@ -1261,7 +1252,7 @@ void ApplicationOverlay::TexturedHemisphere::render() {
     glBindBuffer(GL_ARRAY_BUFFER, 0);
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 }
-
+*/
 glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) {
     glm::vec2 result;
     // Compute yaw
diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h
index 63ef48bd92..d78ad4bc9f 100644
--- a/interface/src/ui/ApplicationOverlay.h
+++ b/interface/src/ui/ApplicationOverlay.h
@@ -67,28 +67,8 @@ public:
     static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos);
     
 private:
-    // Interleaved vertex data
-    struct TextureVertex {
-        glm::vec3 position;
-        glm::vec2 uv;
-    };
-
-    typedef QPair<GLuint, GLuint> VerticesIndices;
-    class TexturedHemisphere {
-    public:
-        TexturedHemisphere();
-        ~TexturedHemisphere();
-        void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks);
-        void render();
-        
-    private:
-        void cleanupVBO();
-        
-        GLuint _vertices;
-        GLuint _indices;
-        VerticesIndices _vbo;
-    };
-    
+    void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks);
+    void drawSphereSection(gpu::Batch& batch);
     float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE;
     QOpenGLFramebufferObject* _framebufferObject;
 
@@ -105,11 +85,11 @@ private:
     void renderDomainConnectionStatusBorder();
 
     void buildFramebufferObject();
-
-    TexturedHemisphere _overlays;
     
     float _textureFov;
     float _textureAspectRatio;
+    int _hemiVerticesID{ GeometryCache::UNKNOWN_ID };
+
     
     enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES };
     bool _reticleActive[NUMBER_OF_RETICLES];

From ef520353008fb10205bcd8034b8d14d745966934 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Thu, 11 Jun 2015 02:20:51 -0700
Subject: [PATCH 52/88] Working on functional overlays

---
 interface/src/Application.cpp           |  3 +-
 interface/src/devices/OculusManager.cpp | 38 +++++++++++-----------
 interface/src/devices/TV3DManager.cpp   | 18 +++++------
 interface/src/ui/ApplicationOverlay.cpp | 42 ++++++++++++++-----------
 4 files changed, 53 insertions(+), 48 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 9d5c43b7cf..a93b0186aa 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -941,6 +941,7 @@ void Application::paintGL() {
         glPushMatrix();
         glLoadIdentity();
         displaySide(&renderArgs, _myCamera);
+        _applicationOverlay.displayOverlayTexture(&renderArgs);
         glPopMatrix();
 
         if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
@@ -957,8 +958,6 @@ void Application::paintGL() {
                           0, 0, _glWidget->getDeviceSize().width(), _glWidget->getDeviceSize().height(),
                             GL_COLOR_BUFFER_BIT, GL_NEAREST);
         glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
-
-        _applicationOverlay.displayOverlayTexture(&renderArgs);
     }
 
     if (!OculusManager::isConnected() || OculusManager::allowSwap()) {
diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp
index 77263935f6..2ee62c85f3 100644
--- a/interface/src/devices/OculusManager.cpp
+++ b/interface/src/devices/OculusManager.cpp
@@ -615,6 +615,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
 
         renderArgs->_renderSide = RenderArgs::MONO;
         qApp->displaySide(renderArgs, *_camera, false);
+        qApp->getApplicationOverlay().displayOverlayTextureHmd(renderArgs, *_camera);
     });
     _activeEye = ovrEye_Count;
 
@@ -629,28 +630,27 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
         glBindFramebuffer(GL_FRAMEBUFFER, 0);
     }
 
-    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo));
-    //Render each eye into an fbo
-    for_each_eye(_ovrHmd, [&](ovrEyeType eye) {
-        _activeEye = eye;
-        // Update our camera to what the application camera is doing
-        _camera->setRotation(toGlm(eyeRenderPose[eye].Orientation));
-        _camera->setPosition(toGlm(eyeRenderPose[eye].Position));
-        configureCamera(*_camera);
-        glMatrixMode(GL_PROJECTION);
-        glLoadMatrixf(glm::value_ptr(_camera->getProjection()));
+    //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo));
+    ////Render each eye into an fbo
+    //for_each_eye(_ovrHmd, [&](ovrEyeType eye) {
+    //    _activeEye = eye;
+    //    // Update our camera to what the application camera is doing
+    //    _camera->setRotation(toGlm(eyeRenderPose[eye].Orientation));
+    //    _camera->setPosition(toGlm(eyeRenderPose[eye].Position));
+    //    configureCamera(*_camera);
+    //    glMatrixMode(GL_PROJECTION);
+    //    glLoadMatrixf(glm::value_ptr(_camera->getProjection()));
 
-        glMatrixMode(GL_MODELVIEW);
-        glLoadIdentity();
+    //    glMatrixMode(GL_MODELVIEW);
+    //    glLoadIdentity();
 
-        ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport;
-        vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale;
-        vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale;
+    //    ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport;
+    //    vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale;
+    //    vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale;
 
-        glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h);
-        qApp->getApplicationOverlay().displayOverlayTextureHmd(renderArgs, *_camera);
-    });
-    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+    //    glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h);
+    //});
+    //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
 
     glPopMatrix();
 
diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp
index c14e589389..b0a2ff7e3b 100644
--- a/interface/src/devices/TV3DManager.cpp
+++ b/interface/src/devices/TV3DManager.cpp
@@ -12,6 +12,7 @@
 #include "InterfaceConfig.h"
 
 #include <glm/glm.hpp>
+#include <glm/gtc/type_ptr.hpp>
 
 #include <GlowEffect.h>
 #include "gpu/GLBackend.h"
@@ -106,21 +107,20 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
         _activeEye = &eye;
         glViewport(portalX, portalY, portalW, portalH);
         glScissor(portalX, portalY, portalW, portalH);
+
+        glm::mat4 projection = glm::frustum<float>(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ);
+        float fov = atan(1.0f / projection[1][1]);
+        projection = glm::translate(projection, vec3(eye.modelTranslation, 0, 0));
+        eyeCamera.setProjection(projection);
+
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity(); // reset projection matrix
-        glFrustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); // set left view frustum
-        GLfloat p[4][4];
-        // Really?
-        glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0]));
-        float cotangent = p[1][1];
-        GLfloat fov = atan(1.0f / cotangent);
-        glTranslatef(eye.modelTranslation, 0.0, 0.0); // translate to cancel parallax
-
+        glLoadMatrixf(glm::value_ptr(projection));
         glMatrixMode(GL_MODELVIEW);
         glLoadIdentity();
         renderArgs->_renderSide = RenderArgs::MONO;
         qApp->displaySide(renderArgs, eyeCamera, false);
-        qApp->getApplicationOverlay().displayOverlayTextureStereo(renderArgs, whichCamera, _aspect, fov);
+        qApp->getApplicationOverlay().displayOverlayTexture(renderArgs);
         _activeEye = NULL;
     }, [&]{
         // render right side view
diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index 4be47e342a..b4824e96a3 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -312,7 +312,6 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
 
     
     renderArgs->_context->syncCache();
-    glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height());
 
     gpu::Batch batch;
     Transform model;
@@ -356,9 +355,10 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera
         return;
     }
 
+    renderArgs->_context->syncCache();
+
     auto geometryCache = DependencyManager::get<GeometryCache>();
     gpu::Batch batch;
-    //DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
     batch.setPipeline(getDrawPipeline());
     batch._glDisable(GL_DEPTH_TEST);
     batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
@@ -367,19 +367,23 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera
     batch.setProjectionTransform(whichCamera.getProjection());
     batch.setViewTransform(Transform());
 
-    Transform model;
-    model.setTranslation(vec3(0.0f, 0.0f, -2.0f));
-    batch.setModelTransform(model);
+    Transform mv;
+    mv.setTranslation(vec3(0.0f, 0.0f, -1.0f));
+    mv.preRotate(glm::inverse(qApp->getCamera()->getHmdRotation()));
+    mv.setScale(vec3(1.0f, 1.0f / aspect(qApp->getCanvasSize()), 1.0f));
+    batch.setModelTransform(mv);
+
 
     // FIXME doesn't work
-    drawSphereSection(batch);
+    // drawSphereSection(batch);
 
     // works...
     geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha));
 
+    // sort of works, renders a semi-transparent red quad 
+    // geometryCache->renderSolidCube(batch, 1.0f, vec4(1));
+
     renderArgs->_context->render(batch);
-    // batch.setUniformTexture(0, gpu::TexturePointer());
-    //    geometryCache->renderSolidCube(batch, 0.5f, vec4(1));
 
     /*
     // The camera here contains only the head pose relative to the avatar position
@@ -427,8 +431,9 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera
     */
 }
 
+/*
 // Draws the FBO texture for 3DTV.
-void ApplicationOverlay::displayOverlayTextureStereo(RenderArgs* renderArgs, Camera& whichCamera, float aspectRatio, float fov) {
+void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) {
     if (_alpha == 0.0f) {
         return;
     }
@@ -473,15 +478,15 @@ void ApplicationOverlay::displayOverlayTextureStereo(RenderArgs* renderArgs, Cam
     GLfloat y = -halfQuadHeight;
     glDisable(GL_DEPTH_TEST);
 
-    //with_each_texture(_framebufferObject->texture(), _newUiTexture, [&] {
-    //    DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance),
-    //                                            glm::vec3(x + quadWidth, y + quadHeight, -distance),
-    //                                            glm::vec3(x + quadWidth, y, -distance),
-    //                                            glm::vec3(x, y, -distance),
-    //                                            glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), 
-    //                                            glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f),
-    //                                            overlayColor);
-    //});
+    with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
+        DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance),
+                                                glm::vec3(x + quadWidth, y + quadHeight, -distance),
+                                                glm::vec3(x + quadWidth, y, -distance),
+                                                glm::vec3(x, y, -distance),
+                                                glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), 
+                                                glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f),
+                                                overlayColor);
+    });
     
     if (!_crosshairTexture) {
         _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() +
@@ -521,6 +526,7 @@ void ApplicationOverlay::displayOverlayTextureStereo(RenderArgs* renderArgs, Cam
     glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
     glEnable(GL_LIGHTING);
 }
+*/
 
 void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
     cursorPos *= qApp->getCanvasSize();

From 7685fe2229ff876fa0a3cfe296f45a38f1d1d704 Mon Sep 17 00:00:00 2001
From: Sam Gateau <sam@highfidelity.io>
Date: Thu, 11 Jun 2015 06:40:21 -0700
Subject: [PATCH 53/88] Clean up on the item interface and introduction of the
 Layered concept, fixing the highliting box of the edit tool

---
 examples/libraries/entitySelectionTool.js     |   3 +-
 interface/src/ui/overlays/Overlay.h           |   1 +
 interface/src/ui/overlays/OverlaysPayload.cpp |  13 +-
 .../render-utils/src/RenderDeferredTask.cpp   |   5 +-
 libraries/render/src/render/DrawTask.cpp      |  43 ++++++-
 libraries/render/src/render/DrawTask.h        |   8 ++
 libraries/render/src/render/Scene.cpp         |  35 +-----
 libraries/render/src/render/Scene.h           | 113 +++++++-----------
 8 files changed, 115 insertions(+), 106 deletions(-)

diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js
index d12c9dae3c..d5e2b24f36 100644
--- a/examples/libraries/entitySelectionTool.js
+++ b/examples/libraries/entitySelectionTool.js
@@ -304,7 +304,8 @@ SelectionDisplay = (function () {
                     visible: false,
                     dashed: true,
                     lineWidth: 2.0,
-                    ignoreRayIntersection: true // this never ray intersects
+                    ignoreRayIntersection: true, // this never ray intersects
+                    drawInFront: true
                 });
 
     var selectionBox = Overlays.addOverlay("cube", {
diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h
index 1a808bc15c..0264c6e3c0 100644
--- a/interface/src/ui/overlays/Overlay.h
+++ b/interface/src/ui/overlays/Overlay.h
@@ -123,6 +123,7 @@ protected:
 namespace render {
    template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay); 
    template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay);
+   template <> int payloadGetLayer(const Overlay::Pointer& overlay);
    template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args);
 }
 
diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp
index cf3262d05c..d5e4b34f6b 100644
--- a/interface/src/ui/overlays/OverlaysPayload.cpp
+++ b/interface/src/ui/overlays/OverlaysPayload.cpp
@@ -37,7 +37,7 @@ namespace render {
     template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) {
         if (overlay->is3D() && !static_cast<Base3DOverlay*>(overlay.get())->getDrawOnHUD()) {
             if (static_cast<Base3DOverlay*>(overlay.get())->getDrawInFront()) {
-                return ItemKey::Builder().withTypeShape().withNoDepthSort().build();
+                return ItemKey::Builder().withTypeShape().withLayered().build();
             } else {
                 return ItemKey::Builder::opaqueShape();
             }
@@ -53,6 +53,17 @@ namespace render {
             return AABox(glm::vec3(bounds.x(), bounds.y(), 0.0f), glm::vec3(bounds.width(), bounds.height(), 0.1f));
         }
     }
+    template <> int payloadGetLayer(const Overlay::Pointer& overlay) {
+        // MAgic number while we are defining the layering mechanism:
+        const int LAYER_2D =  2;
+        const int LAYER_3D_FRONT = 1;
+        const int LAYER_3D = 0;
+        if (overlay->is3D()) {
+            return (static_cast<Base3DOverlay*>(overlay.get())->getDrawInFront() ? LAYER_3D_FRONT : LAYER_3D);
+        } else {
+            return LAYER_2D;
+        }
+    }
     template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
         if (args) {
             glPushMatrix();
diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp
index 777d9466a5..3fd7d05666 100755
--- a/libraries/render-utils/src/RenderDeferredTask.cpp
+++ b/libraries/render-utils/src/RenderDeferredTask.cpp
@@ -50,6 +50,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
    _jobs.push_back(Job(RenderDeferred()));
    _jobs.push_back(Job(ResolveDeferred()));
    _jobs.push_back(Job(DrawTransparentDeferred()));
+   _jobs.push_back(Job(DrawPostLayered()));
    _jobs.push_back(Job(ResetGLState()));
 }
 
@@ -85,7 +86,7 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex
 
     // render opaques
     auto& scene = sceneContext->_scene;
-    auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape());
+    auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withoutLayered());
     auto& renderDetails = renderContext->args->_details;
 
     ItemIDsBounds inItems;
@@ -163,7 +164,7 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC
 
     // render transparents
     auto& scene = sceneContext->_scene;
-    auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape());
+    auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape().withoutLayered());
     auto& renderDetails = renderContext->args->_details;
     
     ItemIDsBounds inItems;
diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp
index bfc888ea8a..53964ac1db 100755
--- a/libraries/render/src/render/DrawTask.cpp
+++ b/libraries/render/src/render/DrawTask.cpp
@@ -192,7 +192,6 @@ void render::renderItems(const SceneContextPointer& sceneContext, const RenderCo
     }
 }
 
-
 void addClearStateCommands(gpu::Batch& batch) {
     batch._glDepthMask(true);
     batch._glDepthFunc(GL_LESS);
@@ -446,6 +445,48 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi
     args->_context->syncCache();
 }
 
+template <> void render::jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
+    PerformanceTimer perfTimer("DrawPostLayered");
+    assert(renderContext->args);
+    assert(renderContext->args->_viewFrustum);
+
+    // render backgrounds
+    auto& scene = sceneContext->_scene;
+    auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered());
+
+
+    ItemIDsBounds inItems;
+    inItems.reserve(items.size());
+    for (auto id : items) {
+        auto& item = scene->getItem(id);
+        if (item.getKey().isVisible() && (item.getLayer() > 0)) {
+            inItems.emplace_back(id);
+        }
+    }
+    if (inItems.empty()) {
+        return;
+    }
+
+    RenderArgs* args = renderContext->args;
+    gpu::Batch batch;
+    args->_batch = &batch;
+
+    glm::mat4 projMat;
+    Transform viewMat;
+    args->_viewFrustum->evalProjectionMatrix(projMat);
+    args->_viewFrustum->evalViewTransform(viewMat);
+    batch.setProjectionTransform(projMat);
+    batch.setViewTransform(viewMat);
+
+    batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0);
+
+    renderItems(sceneContext, renderContext, inItems);
+    args->_context->render((*args->_batch));
+    args->_batch = nullptr;
+
+    // Force the context sync
+    args->_context->syncCache();
+}
 
 
 void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) {
diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h
index 1f260583f2..0052c314c0 100755
--- a/libraries/render/src/render/DrawTask.h
+++ b/libraries/render/src/render/DrawTask.h
@@ -87,6 +87,14 @@ public:
 };
 template <> void jobRun(const DrawBackground& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
 
+
+class DrawPostLayered {
+public:
+};
+template <> void jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
+
+
+
 class ResetGLState {
 public:
 };
diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp
index 8615f7cf7a..1d2e54541b 100644
--- a/libraries/render/src/render/Scene.cpp
+++ b/libraries/render/src/render/Scene.cpp
@@ -45,10 +45,12 @@ void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey
 }
 
 void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() {
-    (*this)[ItemFilter::Builder::opaqueShape()];
-    (*this)[ItemFilter::Builder::transparentShape()];
+    (*this)[ItemFilter::Builder::opaqueShape().withoutLayered()];
+    (*this)[ItemFilter::Builder::transparentShape().withoutLayered()];
     (*this)[ItemFilter::Builder::light()];
     (*this)[ItemFilter::Builder::background()];
+    (*this)[ItemFilter::Builder::opaqueShape().withLayered()];
+    (*this)[ItemFilter::Builder::transparentShape().withLayered()];
 }
 
 
@@ -61,11 +63,6 @@ void Item::resetPayload(const PayloadPointer& payload) {
     }
 }
 
-void Item::kill() {
-    _payload.reset();
-    _key._flags.reset();
-}
-
 void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) {
     _resetItems.push_back(id);
     _resetPayloads.push_back(payload);
@@ -164,27 +161,3 @@ void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) {
         _items[(*updateID)].update((*updateFunctor));
     }
 }
-
-void Scene::registerObserver(ObserverPointer& observer) {
-    // make sure it's a valid observer
-    if (observer && (observer->getScene() == nullptr)) {
-        // Then register the observer
-        _observers.push_back(observer);
-
-        // And let it do what it wants to do
-        observer->registerScene(this);
-    }
-}
-
-void Scene::unregisterObserver(ObserverPointer& observer) {
-    // make sure it's a valid observer currently registered
-    if (observer && (observer->getScene() == this)) {
-        // let it do what it wants to do
-        observer->unregisterScene();
-
-        // Then unregister the observer
-        auto it = std::find(_observers.begin(), _observers.end(), observer);
-        _observers.erase(it);
-    }
-}
-
diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h
index 8cb29609ba..5568312dfe 100644
--- a/libraries/render/src/render/Scene.h
+++ b/libraries/render/src/render/Scene.h
@@ -36,7 +36,6 @@ public:
     enum FlagBit {
         TYPE_SHAPE = 0,   // Item is a Shape
         TYPE_LIGHT,       // Item is a Light
-        TYPE_BACKGROUND,  // Item is a Background
         TRANSLUCENT,      // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work...
         VIEW_SPACE,       // Transformed in view space, and not in world space
         DYNAMIC,          // Dynamic and bound will change unlike static item
@@ -44,7 +43,7 @@ public:
         INVISIBLE,        // Visible or not? could be just here to cast shadow
         SHADOW_CASTER,    // Item cast shadows
         PICKABLE,         // Item can be picked/selected
-        NO_DEPTH_SORT,    // Item should not be depth sorted
+        LAYERED,          // Item belongs to one of the layers different from the default layer
 
         NUM_FLAGS,      // Not a valid flag
     };
@@ -57,6 +56,7 @@ public:
     ItemKey(const Flags& flags) : _flags(flags) {}
 
     class Builder {
+        friend class ItemKey;
         Flags _flags{ 0 };
     public:
         Builder() {}
@@ -65,7 +65,6 @@ public:
 
         Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); }
         Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); }
-        Builder& withTypeBackground() { _flags.set(TYPE_BACKGROUND); return (*this); }
         Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
         Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); }
         Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); }
@@ -73,14 +72,15 @@ public:
         Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
         Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
         Builder& withPickable() { _flags.set(PICKABLE); return (*this); }
-        Builder& withNoDepthSort() { _flags.set(NO_DEPTH_SORT); return (*this); }
+        Builder& withLayered() { _flags.set(LAYERED); return (*this); }
 
         // Convenient standard keys that we will keep on using all over the place
-        static ItemKey opaqueShape() { return Builder().withTypeShape().build(); }
-        static ItemKey transparentShape() { return Builder().withTypeShape().withTransparent().build(); }
-        static ItemKey light() { return Builder().withTypeLight().build(); }
-        static ItemKey background() { return Builder().withTypeBackground().build(); }
+        static Builder opaqueShape() { return Builder().withTypeShape(); }
+        static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); }
+        static Builder light() { return Builder().withTypeLight(); }
+        static Builder background() { return Builder().withViewSpace().withLayered(); }
     };
+    ItemKey(const Builder& builder) : ItemKey(builder._flags) {}
 
     bool isOpaque() const { return !_flags[TRANSLUCENT]; }
     bool isTransparent() const { return _flags[TRANSLUCENT]; }
@@ -101,8 +101,7 @@ public:
 
     bool isPickable() const { return _flags[PICKABLE]; }
 
-    bool isDepthSort() const { return !_flags[NO_DEPTH_SORT]; }
-    bool isNoDepthSort() const { return _flags[NO_DEPTH_SORT]; }
+    bool isLayered() const { return _flags[LAYERED]; }
 };
 
 inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) {
@@ -122,6 +121,7 @@ public:
     ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {}
 
     class Builder {
+        friend class ItemFilter;
         ItemKey::Flags _value{ 0 };
         ItemKey::Flags _mask{ 0 };
     public:
@@ -131,7 +131,6 @@ public:
 
         Builder& withTypeShape()        { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); }
         Builder& withTypeLight()        { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); }
-        Builder& withTypeBackground()   { _value.set(ItemKey::TYPE_BACKGROUND); _mask.set(ItemKey::TYPE_BACKGROUND); return (*this); }
         
         Builder& withOpaque()           { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
         Builder& withTransparent()      { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
@@ -153,16 +152,18 @@ public:
 
         Builder& withPickable()         { _value.set(ItemKey::PICKABLE);  _mask.set(ItemKey::PICKABLE); return (*this); }
 
-        Builder& withDepthSort()      { _value.reset(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); }
-        Builder& withNotDepthSort()   { _value.set(ItemKey::NO_DEPTH_SORT);  _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); }
+        Builder& withoutLayered()       { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
+        Builder& withLayered()          { _value.set(ItemKey::LAYERED);  _mask.set(ItemKey::LAYERED); return (*this); }
 
         // Convenient standard keys that we will keep on using all over the place
-        static ItemFilter opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace().build(); }
-        static ItemFilter transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace().build(); }
-        static ItemFilter light() { return Builder().withTypeLight().build(); }
-        static ItemFilter background() { return Builder().withTypeBackground().build(); }
+        static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); }
+        static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); }
+        static Builder light() { return Builder().withTypeLight(); }
+        static Builder background() { return Builder().withViewSpace().withLayered(); }
     };
 
+    ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {}
+
     // Item Filter operator testing if a key pass the filter
     bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); }
 
@@ -179,7 +180,7 @@ public:
 };
 
 inline QDebug operator<<(QDebug debug, const ItemFilter& me) {
-    debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape())
+    debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build())
                  << "]";
     return debug;
 }
@@ -214,35 +215,40 @@ public:
     public:
         virtual const ItemKey getKey() const = 0;
         virtual const Bound getBound() const = 0;
-        virtual void render(RenderArgs* args) = 0;
+        virtual int getLayer() const = 0;
 
-        virtual void update(const UpdateFunctorPointer& functor) = 0;
+        virtual void render(RenderArgs* args) = 0;
 
         virtual const model::MaterialKey getMaterialKey() const = 0;
 
         ~PayloadInterface() {}
     protected:
+        friend class Item;
+        virtual void update(const UpdateFunctorPointer& functor) = 0;
     };
-    
-
-    
     typedef std::shared_ptr<PayloadInterface> PayloadPointer;
 
-    
-
     Item() {}
     ~Item() {}
 
+    // Main scene / item managment interface Reset/Update/Kill
     void resetPayload(const PayloadPointer& payload);
-    void kill();
+    void update(const UpdateFunctorPointer& updateFunctor)  { _payload->update(updateFunctor); } // Communicate update to the payload
+    void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key
 
     // Check heuristic key
     const ItemKey& getKey() const { return _key; }
 
     // Payload Interface
+
+    // Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace())
     const Bound getBound() const { return _payload->getBound(); }
+
+    // Get the layer where the item belongs. 0 by default meaning NOT LAYERED
+    int getLayer() const { return _payload->getLayer(); }
+
+    // Render call for the item
     void render(RenderArgs* args) { _payload->render(args); }
-    void update(const UpdateFunctorPointer& updateFunctor)  { _payload->update(updateFunctor); }
 
     // Shape Type Interface
     const model::MaterialKey& getMaterialKey() const { return _payload->getMaterialKey(); }
@@ -280,6 +286,7 @@ inline QDebug operator<<(QDebug debug, const Item& item) {
 // of the Payload interface
 template <class T> const ItemKey payloadGetKey(const std::shared_ptr<T>& payloadData) { return ItemKey(); }
 template <class T> const Item::Bound payloadGetBound(const std::shared_ptr<T>& payloadData) { return Item::Bound(); }
+template <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) { return 0; }
 template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, RenderArgs* args) { }
     
 // Shape type interface
@@ -290,19 +297,25 @@ public:
     typedef std::shared_ptr<T> DataPointer;
     typedef UpdateFunctor<T> Updater;
 
-    virtual void update(const UpdateFunctorPointer& functor) { static_cast<Updater*>(functor.get())->_func((*_data)); }
+    Payload(const DataPointer& data) : _data(data) {}
 
     // Payload general interface
     virtual const ItemKey getKey() const { return payloadGetKey<T>(_data); }
     virtual const Item::Bound getBound() const { return payloadGetBound<T>(_data); }
+    virtual int getLayer() const { return payloadGetLayer<T>(_data); }
+
+
     virtual void render(RenderArgs* args) { payloadRender<T>(_data, args); } 
 
     // Shape Type interface
     virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey<T>(_data); }
 
-    Payload(const DataPointer& data) : _data(data) {}
 protected:
     DataPointer _data;
+
+    // Update mechanics
+    virtual void update(const UpdateFunctorPointer& functor) { static_cast<Updater*>(functor.get())->_func((*_data)); }
+    friend class Item;
 };
 
 // Let's show how to make a simple FooPayload example:
@@ -358,6 +371,7 @@ public:
 
 typedef std::vector< ItemIDAndBounds > ItemIDsBounds;
 
+
 // A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly
 class ItemBucketMap : public std::map<ItemFilter, ItemIDSet, ItemFilter::Less> {
 public:
@@ -374,8 +388,6 @@ public:
 };
 
 class Engine;
-class Observer;
-
 
 class PendingChanges {
 public:
@@ -411,36 +423,6 @@ typedef std::queue<PendingChanges> PendingChangesQueue;
 // Items are notified accordingly on any update message happening
 class Scene {
 public:
- 
-    class Observer {
-    public:
-        Observer(Scene* scene) {
-            
-        }
-        ~Observer() {}
-
-        const Scene* getScene() const { return _scene; }
-        Scene* editScene() { return _scene; }
-
-    protected:
-        Scene* _scene = nullptr;
-        virtual void onRegisterScene() {}
-        virtual void onUnregisterScene() {}
-
-        friend class Scene;
-        void registerScene(Scene* scene) {
-            _scene = scene;
-            onRegisterScene();
-        }
-
-        void unregisterScene() {
-            onUnregisterScene();
-            _scene = 0;
-        }
-    };
-    typedef std::shared_ptr< Observer > ObserverPointer;
-    typedef std::vector< ObserverPointer > Observers;
-
     Scene();
     ~Scene() {}
 
@@ -450,11 +432,6 @@ public:
     /// Enqueue change batch to the scene
     void enqueuePendingChanges(const PendingChanges& pendingChanges);
 
-    /// Scene Observer listen to any change and get notified
-    void registerObserver(ObserverPointer& observer);
-    void unregisterObserver(ObserverPointer& observer);
-
-
     /// Access the main bucketmap of items
     const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; }
 
@@ -483,10 +460,6 @@ protected:
     void removeItems(const ItemIDs& ids);
     void updateItems(const ItemIDs& ids, UpdateFunctors& functors);
 
-
-    // The scene context listening for any change to the database
-    Observers _observers;
-
     friend class Engine;
 };
 

From 7d0000c5377febd3fc3307b840f5fe020f3d7bfa Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Thu, 11 Jun 2015 08:38:03 -0700
Subject: [PATCH 54/88] fix warning about returning ref to temp variable

---
 libraries/render/src/render/Scene.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h
index 8cb29609ba..b9481d367e 100644
--- a/libraries/render/src/render/Scene.h
+++ b/libraries/render/src/render/Scene.h
@@ -245,7 +245,7 @@ public:
     void update(const UpdateFunctorPointer& updateFunctor)  { _payload->update(updateFunctor); }
 
     // Shape Type Interface
-    const model::MaterialKey& getMaterialKey() const { return _payload->getMaterialKey(); }
+    const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); }
 
 protected:
     PayloadPointer _payload;

From f77f3f1e1e46eea210f9bbefad2befe182177359 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Thu, 11 Jun 2015 08:43:19 -0700
Subject: [PATCH 55/88] fix bug where grab.js moves mouse to screen corner

when CTRL is pressed
---
 examples/grab.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/examples/grab.js b/examples/grab.js
index 306af86c68..c6b1b884b7 100644
--- a/examples/grab.js
+++ b/examples/grab.js
@@ -140,6 +140,10 @@ function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event) {
 }
 
 function computeNewGrabPlane() {
+    if (!gIsGrabbing) {
+        return;
+    }
+
     var maybeResetMousePosition = false;
     if (gGrabMode !== "rotate") {
         gMouseAtRotateStart = gMouseCursorLocation;

From f4f5f167588206987836c3e1b1a2757351a2a9cb Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Thu, 11 Jun 2015 18:34:39 +0200
Subject: [PATCH 56/88] Fix zones wireframe rendering

---
 .../src/RenderableZoneEntityItem.cpp          | 73 ++++++++++++++++++-
 .../src/RenderableZoneEntityItem.h            |  5 ++
 2 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
index 9c4f8ae0bb..8c147cac05 100644
--- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
@@ -14,6 +14,7 @@
 #include <gpu/GPUConfig.h>
 #include <gpu/Batch.h>
 
+#include <AbstractViewStateInterface.h>
 #include <DeferredLightingEffect.h>
 #include <DependencyManager.h>
 #include <GeometryCache.h>
@@ -100,10 +101,17 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
             case SHAPE_TYPE_COMPOUND: {
                 PerformanceTimer perfTimer("zone->renderCompound");
                 updateGeometry();
-                
-                if (_model && _model->isActive()) {
-                    // FIX ME: this is no longer available... we need to switch to payloads
-                    //_model->renderInScene(getLocalRenderAlpha(), args);
+                if (_model && _model->needsFixupInScene()) {
+                    // check to see if when we added our models to the scene they were ready, if they were not ready, then
+                    // fix them up in the scene
+                    render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
+                    render::PendingChanges pendingChanges;
+                    _model->removeFromScene(scene, pendingChanges);
+                    _model->addToScene(scene, pendingChanges);
+                    
+                    scene->enqueuePendingChanges(pendingChanges);
+                    
+                    _model->setVisibleInScene(getVisible(), scene);
                 }
                 break;
             }
@@ -131,6 +139,15 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
                 break;
         }
     }
+    
+    if ((!_drawZoneBoundaries || getShapeType() != SHAPE_TYPE_COMPOUND) &&
+        _model && !_model->needsFixupInScene()) {
+        // If the model is in the scene but doesn't need to be, remove it.
+        render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
+        render::PendingChanges pendingChanges;
+        _model->removeFromScene(scene, pendingChanges);
+        scene->enqueuePendingChanges(pendingChanges);
+    }
 }
 
 bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
@@ -145,3 +162,51 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
     
     return false;
 }
+
+class RenderableZoneEntityItemMeta {
+public:
+    RenderableZoneEntityItemMeta(EntityItemPointer entity) : entity(entity){ }
+    typedef render::Payload<RenderableZoneEntityItemMeta> Payload;
+    typedef Payload::DataPointer Pointer;
+    
+    EntityItemPointer entity;
+};
+
+namespace render {
+    template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) {
+        return ItemKey::Builder::opaqueShape();
+    }
+    
+    template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) {
+        if (payload && payload->entity) {
+            return payload->entity->getAABox();
+        }
+        return render::Item::Bound();
+    }
+    template <> void payloadRender(const RenderableZoneEntityItemMeta::Pointer& payload, RenderArgs* args) {
+        if (args) {
+            if (payload && payload->entity) {
+                payload->entity->render(args);
+            }
+        }
+    }
+}
+
+bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
+                                           render::PendingChanges& pendingChanges) {
+    _myMetaItem = scene->allocateID();
+    
+    auto renderData = RenderableZoneEntityItemMeta::Pointer(new RenderableZoneEntityItemMeta(self));
+    auto renderPayload = render::PayloadPointer(new RenderableZoneEntityItemMeta::Payload(renderData));
+    
+    pendingChanges.resetItem(_myMetaItem, renderPayload);
+    return true;
+}
+
+void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
+                                                render::PendingChanges& pendingChanges) {
+    pendingChanges.removeItem(_myMetaItem);
+    if (_model) {
+        _model->removeFromScene(scene, pendingChanges);
+    }
+}
diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h
index b2a9791d44..f455ea34de 100644
--- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h
@@ -35,6 +35,9 @@ public:
     virtual void render(RenderArgs* args);
     virtual bool contains(const glm::vec3& point) const;
     
+    virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
+    virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
+    
 private:
     Model* getModel();
     void initialSimulation();
@@ -45,6 +48,8 @@ private:
     
     Model* _model;
     bool _needsInitialSimulation;
+    
+    render::ItemID _myMetaItem;
 };
 
 #endif // hifi_RenderableZoneEntityItem_h

From 47888b46716d6808645eeb1ae3a3a96735ff82f7 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Thu, 11 Jun 2015 09:34:56 -0700
Subject: [PATCH 57/88] fix transforms for items with non-default registration
 point

---
 libraries/entities-renderer/src/RenderableBoxEntityItem.cpp   | 2 +-
 .../entities-renderer/src/RenderableDebugableEntityItem.cpp   | 4 ++--
 .../entities-renderer/src/RenderableSphereEntityItem.cpp      | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
index 066aaa8b31..23b93250bc 100644
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
@@ -33,7 +33,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
     
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(getTransform()); // we want to include the scale as well
+    batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
     DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(batch, 1.0f, cubeColor);
 
     RenderableDebugableEntityItem::render(this, args);
diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
index 32e30c7e96..6a511e0d30 100644
--- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp
@@ -24,7 +24,7 @@ void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, Render
                                                       float puffedOut, glm::vec4& color) {
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(entity->getTransform()); // we want to include the scale as well
+    batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well
     DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f + puffedOut, color);
 }
 
@@ -33,7 +33,7 @@ void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args)
         Q_ASSERT(args->_batch);
         gpu::Batch& batch = *args->_batch;
 
-        batch.setModelTransform(entity->getTransform()); // we want to include the scale as well
+        batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well
         
         auto nodeList = DependencyManager::get<NodeList>();
         const QUuid& myNodeID = nodeList->getSessionUUID();
diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
index 5ba9003d43..6d9cb525d6 100644
--- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
@@ -39,7 +39,7 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
     
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(getTransform()); // use a transform with scale, rotation, registration point and translation
+    batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation
     DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
 
     RenderableDebugableEntityItem::render(this, args);

From a878559e0c4a88126c924abc805f236f0f7bb765 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Thu, 11 Jun 2015 09:38:45 -0700
Subject: [PATCH 58/88] fix transforms for items with non-default registration
 point

---
 libraries/entities-renderer/src/RenderableLightEntityItem.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
index 8a84c167c5..819989d5ec 100644
--- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
@@ -49,7 +49,7 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
 #ifdef WANT_DEBUG
     Q_ASSERT(args->_batch);
     gpu::Batch& batch = *args->_batch;
-    batch.setModelTransform(getTransform());
+    batch.setModelTransform(getTransformToCenter());
     DependencyManager::get<DeferredLightingEffect>()->renderWireSphere(batch, 0.5f, 15, 15, glm::vec4(color, 1.0f));
 #endif
 };

From 0e8ec81837f56f4a211fc843c3a51c4aa168308d Mon Sep 17 00:00:00 2001
From: Sam Gondelman <samuel_gondelman@brown.edu>
Date: Thu, 11 Jun 2015 09:41:59 -0700
Subject: [PATCH 59/88] fixed lambda capture

---
 interface/src/devices/KeyboardMouseDevice.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp
index d0f04e5636..8a336064e5 100755
--- a/interface/src/devices/KeyboardMouseDevice.cpp
+++ b/interface/src/devices/KeyboardMouseDevice.cpp
@@ -173,9 +173,9 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
         availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
         return availableInputs;
     };
-    proxy->resetDeviceBindings = [this, &_mapper = mapper, &device = _deviceID] () -> bool {
-        _mapper.removeAllInputChannelsForDevice(device);
-        this->assignDefaultInputMapping(_mapper);
+    proxy->resetDeviceBindings = [this, &mapper] () -> bool {
+        mapper.removeAllInputChannelsForDevice(_deviceID);
+        this->assignDefaultInputMapping(mapper);
         return true;
     };
     mapper.registerDevice(_deviceID, proxy);

From 8ed9a3ca02e94bb064a42cb216b9b5c863384cc2 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Thu, 11 Jun 2015 11:04:19 -0700
Subject: [PATCH 60/88] add handedness setting to avatar-hold action.  update
 stick.js to allow use of hydra

---
 examples/stick.js                             | 58 +++++++++++++++----
 interface/src/avatar/AvatarActionHold.cpp     | 31 ++++++++--
 interface/src/avatar/AvatarActionHold.h       |  2 +
 .../entities/src/EntityActionInterface.cpp    | 18 +++++-
 .../entities/src/EntityActionInterface.h      |  3 +
 5 files changed, 94 insertions(+), 18 deletions(-)

diff --git a/examples/stick.js b/examples/stick.js
index 5631f3aa3a..ea1f3439a9 100644
--- a/examples/stick.js
+++ b/examples/stick.js
@@ -10,8 +10,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+var hand = "left";
+var nullActionID = "00000000-0000-0000-0000-000000000000";
+var controllerID;
+var controllerActive;
 var stickID = null;
-var actionID = "00000000-0000-0000-0000-000000000000";
+var actionID = nullActionID;
 // sometimes if this is run immediately the stick doesn't get created?  use a timer.
 Script.setTimeout(function() {
     stickID = Entities.addEntity({
@@ -19,12 +23,14 @@ Script.setTimeout(function() {
         modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
         compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
         dimensions: {x: .11, y: .11, z: .59},
-        position: MyAvatar.getRightPalmPosition(),
+        position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
         rotation: MyAvatar.orientation,
         damping: .1,
         collisionsWillMove: true
     });
-    actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, timeScale: 0.15});
+    actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9},
+                                                    hand: hand,
+                                                    timeScale: 0.15});
 }, 3000);
 
 
@@ -32,11 +38,20 @@ function cleanUp() {
     Entities.deleteEntity(stickID);
 }
 
+
+function positionStick(stickOrientation) {
+    var baseOffset = {x: 0.0, y: 0.0, z: -0.9};
+    var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset);
+    Entities.updateAction(stickID, actionID, {relativePosition: offset,
+                                              relativeRotation: stickOrientation});
+}
+
+
 function mouseMoveEvent(event) {
-    if (!stickID || actionID == "00000000-0000-0000-0000-000000000000") {
+    if (!stickID || actionID == nullActionID) {
         return;
     }
-    var windowCenterX = Window.innerWidth/ 2;
+    var windowCenterX = Window.innerWidth / 2;
     var windowCenterY = Window.innerHeight / 2;
     var mouseXCenterOffset = event.x - windowCenterX;
     var mouseYCenterOffset = event.y - windowCenterY;
@@ -44,13 +59,34 @@ function mouseMoveEvent(event) {
     var mouseYRatio = mouseYCenterOffset / windowCenterY;
 
     var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * -90, mouseXRatio * -90, 0);
-    var baseOffset = {x: 0.0, y: 0.0, z: -0.9};
-    var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset);
-
-    Entities.updateAction(stickID, actionID, {relativePosition: offset,
-                                              relativeRotation: stickOrientation,
-                                              timeScale: 0.15});
+    positionStick(stickOrientation);
 }
 
+
+function initControls(){
+    if (hand == "right") {
+        controllerID = 3; // right handed
+    } else {
+        controllerID = 4; // left handed
+    }
+}
+
+
+function update(deltaTime){
+    var palmPosition = Controller.getSpatialControlPosition(controllerID);
+    controllerActive = (Vec3.length(palmPosition) > 0);
+    if(!controllerActive){
+        return;
+    }
+
+    stickOrientation = Controller.getSpatialControlRawRotation(controllerID);
+    var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0);
+    stickOrientation = Quat.multiply(stickOrientation, adjustment);
+
+    positionStick(stickOrientation);
+}
+
+
 Script.scriptEnding.connect(cleanUp);
 Controller.mouseMoveEvent.connect(mouseMoveEvent);
+Script.update.connect(update);
diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp
index 74a583df58..1fbb01beb3 100644
--- a/interface/src/avatar/AvatarActionHold.cpp
+++ b/interface/src/avatar/AvatarActionHold.cpp
@@ -29,7 +29,12 @@ AvatarActionHold::~AvatarActionHold() {
 
 void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
     auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
-    glm::vec3 palmPosition = myAvatar->getRightPalmPosition();
+    glm::vec3 palmPosition;
+    if (_hand == "right") {
+        palmPosition = myAvatar->getRightPalmPosition();
+    } else {
+        palmPosition = myAvatar->getLeftPalmPosition();
+    }
 
     auto rotation = myAvatar->getWorldAlignedOrientation();
     auto offset = rotation * _relativePosition;
@@ -55,28 +60,46 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
     bool tSOk = true;
     float timeScale =
         EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", tSOk, false);
+    bool hOk = true;
+    QString hand =
+        EntityActionInterface::extractStringArgument("hold", arguments, "hand", hOk, false);
 
     lockForWrite();
     if (rPOk) {
         _relativePosition = relativePosition;
-    } else {
+    } else if (!_parametersSet) {
         _relativePosition = glm::vec3(0.0f, 0.0f, 1.0f);
     }
 
     if (rROk) {
         _relativeRotation = relativeRotation;
-    } else {
+    } else if (!_parametersSet) {
         _relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f);
     }
 
     if (tSOk) {
         _linearTimeScale = timeScale;
         _angularTimeScale = timeScale;
-    } else {
+    } else if (!_parametersSet) {
         _linearTimeScale = 0.2;
         _angularTimeScale = 0.2;
     }
 
+    if (hOk) {
+        hand = hand.toLower();
+        if (hand == "left") {
+            _hand = "left";
+        } else if (hand == "right") {
+            _hand = "right";
+        } else {
+            qDebug() << "hold action -- invalid hand argument:" << hand;
+            _hand = "right";
+        }
+    } else if (!_parametersSet) {
+        _hand = "right";
+    }
+
+    _parametersSet = true;
     _positionalTargetSet = true;
     _rotationalTargetSet = true;
     _active = true;
diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h
index f92ea94aaa..705c751029 100644
--- a/interface/src/avatar/AvatarActionHold.h
+++ b/interface/src/avatar/AvatarActionHold.h
@@ -28,6 +28,8 @@ public:
 private:
     glm::vec3 _relativePosition;
     glm::quat _relativeRotation;
+    QString _hand;
+    bool _parametersSet = false;
 };
 
 #endif // hifi_AvatarActionHold_h
diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp
index b293d609c2..e97669686c 100644
--- a/libraries/entities/src/EntityActionInterface.cpp
+++ b/libraries/entities/src/EntityActionInterface.cpp
@@ -91,7 +91,6 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian
     return glm::vec3(x, y, z);
 }
 
-
 glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments,
                                                      QString argumentName, bool& ok, bool required) {
     if (!arguments.contains(argumentName)) {
@@ -139,8 +138,6 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian
     return glm::quat(w, x, y, z);
 }
 
-
-
 float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
                                                   QString argumentName, bool& ok, bool required) {
     if (!arguments.contains(argumentName)) {
@@ -162,3 +159,18 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa
 
     return v;
 }
+
+QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments,
+                                                     QString argumentName, bool& ok, bool required) {
+    if (!arguments.contains(argumentName)) {
+        if (required) {
+            qDebug() << objectName << "requires argument:" << argumentName;
+        }
+        ok = false;
+        return "";
+    }
+
+    QVariant vV = arguments[argumentName];
+    QString v = vV.toString();
+    return v;
+}
diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h
index 457db8f5c3..486d3f5948 100644
--- a/libraries/entities/src/EntityActionInterface.h
+++ b/libraries/entities/src/EntityActionInterface.h
@@ -59,6 +59,9 @@ protected:
                                           QString argumentName, bool& ok, bool required = true);
     static float extractFloatArgument(QString objectName, QVariantMap arguments,
                                       QString argumentName, bool& ok, bool required = true);
+    static QString extractStringArgument(QString objectName, QVariantMap arguments,
+                                         QString argumentName, bool& ok, bool required = true);
+
 };
 
 

From bc206633f5b1916a009fad216ee56332065349d3 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Thu, 11 Jun 2015 14:56:21 -0700
Subject: [PATCH 61/88] Fixing overlay rendering in HMD

---
 interface/resources/images/arrow.png    | Bin 0 -> 3562 bytes
 interface/src/ui/ApplicationOverlay.cpp | 342 ++++++------------------
 interface/src/ui/ApplicationOverlay.h   |   2 +-
 3 files changed, 78 insertions(+), 266 deletions(-)
 create mode 100644 interface/resources/images/arrow.png

diff --git a/interface/resources/images/arrow.png b/interface/resources/images/arrow.png
new file mode 100644
index 0000000000000000000000000000000000000000..408881b5856eb47a30875e3e56d5864721161587
GIT binary patch
literal 3562
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
R^nCyTaI$x`E7(oB_+R=!c8UN1

literal 0
HcmV?d00001

diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index b4824e96a3..e5f701e807 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -120,27 +120,6 @@ bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r,
     }
 }
 
-void ApplicationOverlay::renderReticle(glm::quat orientation, float alpha) {
-    glPushMatrix(); {
-        glm::vec3 axis = glm::axis(orientation);
-        glRotatef(glm::degrees(glm::angle(orientation)), axis.x, axis.y, axis.z);
-        glm::vec3 topLeft = getPoint(reticleSize / 2.0f, -reticleSize / 2.0f);
-        glm::vec3 topRight = getPoint(-reticleSize / 2.0f, -reticleSize / 2.0f);
-        glm::vec3 bottomLeft = getPoint(reticleSize / 2.0f, reticleSize / 2.0f);
-        glm::vec3 bottomRight = getPoint(-reticleSize / 2.0f, reticleSize / 2.0f);
-        
-        // TODO: this version of renderQuad() needs to take a color
-        glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], alpha };
-        
-        
-        
-        DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomLeft, bottomRight, topRight,
-                                                            glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f),
-                                                            glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), 
-                                                            reticleColor, _reticleQuad);
-    } glPopMatrix();
-}
-
 ApplicationOverlay::ApplicationOverlay() :
     _textureFov(glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE)),
     _textureAspectRatio(1.0f),
@@ -307,7 +286,7 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
 
     if (!_crosshairTexture) {
         _crosshairTexture = TextureCache::getImageTexture(
-            PathUtils::resourcesPath() + "images/sixense-reticle.png");
+            PathUtils::resourcesPath() + "images/arrow.png");
     }
 
     
@@ -349,6 +328,40 @@ static gpu::BufferPointer _hemiVertices;
 static gpu::BufferPointer _hemiIndices;
 static int _hemiIndexCount{ 0 };
 
+glm::vec2 getPolarCoordinates(const PalmData& palm) {
+    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
+    auto avatarOrientation = myAvatar->getOrientation();
+    auto eyePos = myAvatar->getDefaultEyePosition();
+    glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm);
+    // Direction of the tip relative to the eye
+    glm::vec3 tipDirection = tip - eyePos;
+    // orient into avatar space
+    tipDirection = glm::inverse(avatarOrientation) * tipDirection;
+    // Normalize for trig functions
+    tipDirection = glm::normalize(tipDirection);
+    // Convert to polar coordinates
+    glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y));
+    return polar;
+}
+
+void ApplicationOverlay::renderReticle(gpu::Batch& batch, glm::quat orientation, float alpha) {
+    if (!_crosshairTexture) {
+        _crosshairTexture = TextureCache::getImageTexture(
+            PathUtils::resourcesPath() + "images/arrow.png");
+    }
+    
+    batch.setUniformTexture(0, _crosshairTexture);
+    glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], alpha };
+
+    vec3 dist = getPoint(0, 0);
+    mat4 reticleXfm = glm::translate(mat4(), vec3(0, 0, -1));
+    reticleXfm = glm::mat4_cast(orientation) * reticleXfm;
+    reticleXfm = glm::scale(reticleXfm, vec3(reticleSize, reticleSize, 1.0f));
+    batch.setModelTransform(Transform(reticleXfm));
+    DependencyManager::get<GeometryCache>()->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
+}
+
+
 // Draws the FBO texture for Oculus rift.
 void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) {
     if (_alpha == 0.0f) {
@@ -357,176 +370,60 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera
 
     renderArgs->_context->syncCache();
 
-    auto geometryCache = DependencyManager::get<GeometryCache>();
     gpu::Batch batch;
     batch.setPipeline(getDrawPipeline());
     batch._glDisable(GL_DEPTH_TEST);
+    batch._glDisable(GL_CULL_FACE);
     batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
     batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     batch.setProjectionTransform(whichCamera.getProjection());
     batch.setViewTransform(Transform());
 
-    Transform mv;
-    mv.setTranslation(vec3(0.0f, 0.0f, -1.0f));
-    mv.preRotate(glm::inverse(qApp->getCamera()->getHmdRotation()));
-    mv.setScale(vec3(1.0f, 1.0f / aspect(qApp->getCanvasSize()), 1.0f));
-    batch.setModelTransform(mv);
+    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
+    const quat& avatarOrientation = myAvatar->getOrientation();
+    quat hmdOrientation = qApp->getCamera()->getHmdRotation();
+    vec3 hmdPosition = glm::inverse(avatarOrientation) * qApp->getCamera()->getHmdPosition();
+    mat4 overlayXfm = glm::mat4_cast(glm::inverse(hmdOrientation)) * glm::translate(mat4(), -hmdPosition);
+    batch.setModelTransform(Transform(overlayXfm));
+    drawSphereSection(batch);
 
 
-    // FIXME doesn't work
-    // drawSphereSection(batch);
+    if (!_crosshairTexture) {
+        _crosshairTexture = TextureCache::getImageTexture(
+            PathUtils::resourcesPath() + "images/arrow.png");
+    }
+    batch.setUniformTexture(0, _crosshairTexture);
+    auto geometryCache = DependencyManager::get<GeometryCache>();
+    //Controller Pointers
+    for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
+        PalmData& palm = myAvatar->getHand()->getPalms()[i];
+        if (palm.isActive()) {
+            glm::vec2 polar = getPolarCoordinates(palm);
+            // Convert to quaternion
+            mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
+            mat4 reticleXfm = overlayXfm * pointerXfm;
+            reticleXfm = glm::scale(reticleXfm, vec3(reticleSize, reticleSize, 1.0f));
+            batch.setModelTransform(reticleXfm);
+            // Render reticle at location
+            geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
+        }
+    }
 
-    // works...
-    geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha));
-
-    // sort of works, renders a semi-transparent red quad 
-    // geometryCache->renderSolidCube(batch, 1.0f, vec4(1));
+    //Mouse Pointer
+    if (_reticleActive[MOUSE]) {
+        glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(),
+            _reticlePosition[MOUSE].y()));
+        mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
+        mat4 reticleXfm = overlayXfm * pointerXfm;
+        reticleXfm = glm::scale(reticleXfm, vec3(reticleSize, reticleSize, 1.0f));
+        batch.setModelTransform(reticleXfm);
+        geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
+    }
 
     renderArgs->_context->render(batch);
-
-    /*
-    // The camera here contains only the head pose relative to the avatar position
-    vec3 pos = whichCamera.getPosition();
-    quat rot = whichCamera.getOrientation();
-    mat4 overlayXfm = glm::translate(glm::mat4(), pos) * glm::mat4_cast(rot);
-
-    glLoadMatrixf(glm::value_ptr(glm::inverse(overlayXfm)));
-    glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
-    _overlays.render();
-    */
-
-    //Update and draw the magnifiers
-    /*
-    // FIXME Mangifiers need to be re-thought
-    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
-    const float scale = myAvatar->getScale() * _oculusUIRadius;
-    overlayXfm = glm::scale(overlayXfm, vec3(scale));
-    for (int i = 0; i < NUMBER_OF_RETICLES; i++) {
-        if (_magActive[i]) {
-            _magSizeMult[i] += MAG_SPEED;
-            if (_magSizeMult[i] > 1.0f) {
-                _magSizeMult[i] = 1.0f;
-            }
-        } else {
-            _magSizeMult[i] -= MAG_SPEED;
-            if (_magSizeMult[i] < 0.0f) {
-                _magSizeMult[i] = 0.0f;
-            }
-        }
-            
-        if (_magSizeMult[i] > 0.0f) {
-            //Render magnifier, but dont show border for mouse magnifier
-            glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(),
-                                                                _reticlePosition[MOUSE].y()));
-            with_each_texture(_overlays.getTexture(), 0, [&] {
-                renderMagnifier(projection, _magSizeMult[i], i != MOUSE);
-            });
-        }
-    }
-
-    if (!Application::getInstance()->isMouseHidden()) {
-        renderPointersOculus();
-    }
-    */
 }
 
-/*
-// Draws the FBO texture for 3DTV.
-void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) {
-    if (_alpha == 0.0f) {
-        return;
-    }
-    
-    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
-    const glm::vec3& viewMatrixTranslation = qApp->getViewMatrixTranslation();
-    
-    glEnable(GL_BLEND);
-    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
-    glEnable(GL_DEPTH_TEST);
-    glDisable(GL_LIGHTING);
-    
-    glMatrixMode(GL_MODELVIEW);
-    
-    glPushMatrix();
-    glLoadIdentity();
-    // Transform to world space
-    glm::quat rotation = whichCamera.getRotation();
-    glm::vec3 axis2 = glm::axis(rotation);
-    glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z);
-    glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z);
-    
-    // Translate to the front of the camera
-    glm::vec3 pos = whichCamera.getPosition();
-    glm::quat rot = myAvatar->getOrientation();
-    glm::vec3 axis = glm::axis(rot);
-    
-    glTranslatef(pos.x, pos.y, pos.z);
-    glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z);
-    
-    glm::vec4 overlayColor = {1.0f, 1.0f, 1.0f, _alpha};
-    
-    //Render
-    const GLfloat distance = 1.0f;
-    
-    const GLfloat halfQuadHeight = distance * tan(fov);
-    const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio;
-    const GLfloat quadWidth = halfQuadWidth * 2.0f;
-    const GLfloat quadHeight = halfQuadHeight * 2.0f;
-    
-    GLfloat x = -halfQuadWidth;
-    GLfloat y = -halfQuadHeight;
-    glDisable(GL_DEPTH_TEST);
-
-    with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
-        DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance),
-                                                glm::vec3(x + quadWidth, y + quadHeight, -distance),
-                                                glm::vec3(x + quadWidth, y, -distance),
-                                                glm::vec3(x, y, -distance),
-                                                glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), 
-                                                glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f),
-                                                overlayColor);
-    });
-    
-    if (!_crosshairTexture) {
-        _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() +
-                                                          "images/sixense-reticle.png");
-    }
-    
-    //draw the mouse pointer
-    glEnable(GL_BLEND);
-    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glEnable(GL_TEXTURE_2D);
-    glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
-    glm::vec2 canvasSize = qApp->getCanvasSize();
-    const float reticleSize = 40.0f / canvasSize.x * quadWidth;
-    x -= reticleSize / 2.0f;
-    y += reticleSize / 2.0f;
-    const float mouseX = (qApp->getMouseX() / (float)canvasSize.x) * quadWidth;
-    const float mouseY = (1.0 - (qApp->getMouseY() / (float)canvasSize.y)) * quadHeight;
-    
-    glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
-
-    DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x + mouseX, y + mouseY, -distance), 
-                                                glm::vec3(x + mouseX + reticleSize, y + mouseY, -distance),
-                                                glm::vec3(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance),
-                                                glm::vec3(x + mouseX, y + mouseY - reticleSize, -distance),
-                                                glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), 
-                                                glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f),
-                                                reticleColor, _reticleQuad);
-
-    glEnable(GL_DEPTH_TEST);
-    
-    glPopMatrix();
-    
-    glDepthMask(GL_TRUE);
-    glBindTexture(GL_TEXTURE_2D, 0);
-    glDisable(GL_TEXTURE_2D);
-    
-    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
-    glEnable(GL_LIGHTING);
-}
-*/
 
 void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
     cursorPos *= qApp->getCanvasSize();
@@ -556,22 +453,6 @@ void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origi
     direction = glm::normalize(intersectionWithUi - origin);
 }
 
-glm::vec2 getPolarCoordinates(const PalmData& palm) {
-    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
-    auto avatarOrientation = myAvatar->getOrientation();
-    auto eyePos = myAvatar->getDefaultEyePosition();
-    glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm);
-    // Direction of the tip relative to the eye
-    glm::vec3 tipDirection = tip - eyePos;
-    // orient into avatar space
-    tipDirection = glm::inverse(avatarOrientation) * tipDirection;
-    // Normalize for trig functions
-    tipDirection = glm::normalize(tipDirection);
-    // Convert to polar coordinates
-    glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y));
-    return polar;
-}
-
 //Caculate the click location using one of the sixense controllers. Scale is not applied
 QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const {
     QPoint rv;
@@ -624,7 +505,7 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position,
 void ApplicationOverlay::renderPointers() {
     //lazily load crosshair texture
     if (_crosshairTexture == 0) {
-        _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
+        _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/arrow.png");
     }
     glEnable(GL_TEXTURE_2D);
     glEnable(GL_BLEND);
@@ -790,43 +671,6 @@ void ApplicationOverlay::renderControllerPointers() {
     }
 }
 
-void ApplicationOverlay::renderPointersOculus() {
-
-    glEnable(GL_BLEND);
-    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glEnable(GL_TEXTURE_2D);
-
-    glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
-    glDisable(GL_DEPTH_TEST);
-
-    glMatrixMode(GL_MODELVIEW);
-    
-    //Controller Pointers
-    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
-    for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
-        PalmData& palm = myAvatar->getHand()->getPalms()[i];
-        if (palm.isActive()) {
-            glm::vec2 polar = getPolarCoordinates(palm);
-            // Convert to quaternion
-            glm::quat orientation = glm::quat(glm::vec3(polar.y, -polar.x, 0.0f));
-            // Render reticle at location
-            renderReticle(orientation, _alpha);
-        } 
-    }
-
-    //Mouse Pointer
-    if (_reticleActive[MOUSE]) {
-        glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(),
-                                                           _reticlePosition[MOUSE].y()));
-        glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f));
-        renderReticle(orientation, _alpha);
-    }
-
-    glEnable(GL_DEPTH_TEST);
-    glDisable(GL_TEXTURE_2D);
-    glDisable(GL_BLEND);
-}
-
 //Renders a small magnification of the currently bound texture at the coordinates
 void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) {
     if (!_magnifier) {
@@ -1111,7 +955,7 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() {
 void ApplicationOverlay::buildHemiVertices(
     const float fov, const float aspectRatio, const int slices, const int stacks) {
     static float textureFOV = 0.0f, textureAspectRatio = 1.0f;
-    if (_hemiVerticesID != GeometryCache::UNKNOWN_ID && textureFOV == fov && textureAspectRatio == aspectRatio) {
+    if (textureFOV == fov && textureAspectRatio == aspectRatio) {
         return;
     }
 
@@ -1119,9 +963,6 @@ void ApplicationOverlay::buildHemiVertices(
     textureAspectRatio = aspectRatio;
 
     auto geometryCache = DependencyManager::get<GeometryCache>();
-    if (_hemiVerticesID == GeometryCache::UNKNOWN_ID) {
-        _hemiVerticesID = geometryCache->allocateID();
-    }
 
     _hemiVertices = gpu::BufferPointer(new gpu::Buffer());
     _hemiIndices = gpu::BufferPointer(new gpu::Buffer());
@@ -1187,8 +1028,8 @@ void ApplicationOverlay::drawSphereSection(gpu::Batch& batch) {
     static const int COLOR_DATA_SLOT = 2;
     gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone
     streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
-    streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), sizeof(vec3));
-    streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA), sizeof(vec3) + sizeof(vec2));
+    streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
+    streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA));
     batch.setInputFormat(streamFormat);
 
     static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4);
@@ -1230,35 +1071,6 @@ void ApplicationOverlay::buildFramebufferObject() {
     glBindTexture(GL_TEXTURE_2D, 0);
 }
 
-/*
-//Renders a hemisphere with texture coordinates.
-void ApplicationOverlay::TexturedHemisphere::render() {
-    if (_vbo.first == 0 || _vbo.second == 0) {
-        qDebug() << "TexturedHemisphere::render(): Incorrect initialisation";
-        return;
-    }
-    
-    glBindBuffer(GL_ARRAY_BUFFER, _vbo.first);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second);
-    
-    glEnableClientState(GL_VERTEX_ARRAY);
-    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-    
-    static const int STRIDE = sizeof(TextureVertex);
-    static const void* VERTEX_POINTER = 0;
-    static const void* TEX_COORD_POINTER = (void*)sizeof(glm::vec3);
-    glVertexPointer(3, GL_FLOAT, STRIDE, VERTEX_POINTER);
-    glTexCoordPointer(2, GL_FLOAT, STRIDE, TEX_COORD_POINTER);
-    
-    glDrawRangeElements(GL_TRIANGLES, 0, _vertices - 1, _indices, GL_UNSIGNED_SHORT, 0);
-    
-    glDisableClientState(GL_VERTEX_ARRAY);
-    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-    
-    glBindBuffer(GL_ARRAY_BUFFER, 0);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-}
-*/
 glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) {
     glm::vec2 result;
     // Compute yaw
diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h
index d78ad4bc9f..609bef745d 100644
--- a/interface/src/ui/ApplicationOverlay.h
+++ b/interface/src/ui/ApplicationOverlay.h
@@ -72,7 +72,7 @@ private:
     float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE;
     QOpenGLFramebufferObject* _framebufferObject;
 
-    void renderReticle(glm::quat orientation, float alpha);
+    void renderReticle(gpu::Batch& batch, glm::quat orientation, float alpha);
     void renderPointers();
     void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder);
     

From d3cdbc389a4c7d3fda287ebe4ac440b0ccb4e051 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Thu, 11 Jun 2015 15:05:08 -0700
Subject: [PATCH 62/88] Code cleanup

---
 interface/src/devices/OculusManager.cpp | 24 ------------------------
 interface/src/ui/ApplicationOverlay.cpp | 21 +--------------------
 2 files changed, 1 insertion(+), 44 deletions(-)

diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp
index 2ee62c85f3..414c7f6199 100644
--- a/interface/src/devices/OculusManager.cpp
+++ b/interface/src/devices/OculusManager.cpp
@@ -629,35 +629,11 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
         finalFbo = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer(); 
         glBindFramebuffer(GL_FRAMEBUFFER, 0);
     }
-
-    //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo));
-    ////Render each eye into an fbo
-    //for_each_eye(_ovrHmd, [&](ovrEyeType eye) {
-    //    _activeEye = eye;
-    //    // Update our camera to what the application camera is doing
-    //    _camera->setRotation(toGlm(eyeRenderPose[eye].Orientation));
-    //    _camera->setPosition(toGlm(eyeRenderPose[eye].Position));
-    //    configureCamera(*_camera);
-    //    glMatrixMode(GL_PROJECTION);
-    //    glLoadMatrixf(glm::value_ptr(_camera->getProjection()));
-
-    //    glMatrixMode(GL_MODELVIEW);
-    //    glLoadIdentity();
-
-    //    ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport;
-    //    vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale;
-    //    vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale;
-
-    //    glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h);
-    //});
-    //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
-
     glPopMatrix();
 
     glMatrixMode(GL_PROJECTION);
     glPopMatrix();
 
-
     // restore our normal viewport
     glViewport(0, 0, deviceSize.width(), deviceSize.height());
 
diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index e5f701e807..887af89cd0 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -242,24 +242,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
     _framebufferObject->release();
 }
 
-// A quick and dirty solution for compositing the old overlay 
-// texture with the new one
-//template <typename F>
-//void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) {
-//    glEnable(GL_TEXTURE_2D);
-//    glActiveTexture(GL_TEXTURE0);
-//    if (firstPassTexture) {
-//        glBindTexture(GL_TEXTURE_2D, firstPassTexture);
-//        f();
-//    }
-//    //if (secondPassTexture) {
-//    //    glBindTexture(GL_TEXTURE_2D, secondPassTexture);
-//    //    f();
-//    //}
-//    glBindTexture(GL_TEXTURE_2D, 0);
-//    glDisable(GL_TEXTURE_2D);
-//}
-
 gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() {
     if (!_standardDrawPipeline) {
         auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)));
@@ -977,7 +959,6 @@ void ApplicationOverlay::buildHemiVertices(
     vec3 pos; 
     // Compute vertices positions and texture UV coordinate
     // Create and write to buffer
-    //_hemiVertices->(sizeof(vec3) + sizeof(vec2) + sizeof(vec4)) * stacks * slices);
     for (int i = 0; i < stacks; i++) {
         float stacksRatio = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
         // abs(theta) <= fov / 2.0f
@@ -1052,7 +1033,7 @@ void ApplicationOverlay::buildFramebufferObject() {
     auto canvasSize = qApp->getCanvasSize();
     QSize fboSize = QSize(canvasSize.x, canvasSize.y);
     if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) {
-        // Already build
+        // Already built
         return;
     }
     

From 92f22bc6d52bde359f7b6fc8baa0678e139ba311 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Thu, 11 Jun 2015 15:40:16 -0700
Subject: [PATCH 63/88] Magic number and build errors

---
 interface/src/ui/ApplicationOverlay.cpp | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index 887af89cd0..f6dd4f5773 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -260,6 +260,8 @@ gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() {
     return _standardDrawPipeline;
 }
 
+#define CURSOR_PIXEL_SIZE 32.0f
+
 // Draws the FBO texture for the screen
 void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
     if (_alpha == 0.0f) {
@@ -296,7 +298,7 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
     mousePosition -= 1.0f;
     mousePosition.y *= -1.0f;
     model.setTranslation(vec3(mousePosition, 0));
-    glm::vec2 mouseSize = 32.0f / canvasSize;
+    glm::vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
     model.setScale(vec3(mouseSize, 1.0f));
     batch.setModelTransform(model);
     batch.setUniformTexture(0, _crosshairTexture);
@@ -957,20 +959,22 @@ void ApplicationOverlay::buildHemiVertices(
     //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
     
     vec3 pos; 
+    vec2 uv;
     // Compute vertices positions and texture UV coordinate
     // Create and write to buffer
     for (int i = 0; i < stacks; i++) {
-        float stacksRatio = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
+        uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
         // abs(theta) <= fov / 2.0f
-        float pitch = -fov * (stacksRatio - 0.5f);
+        float pitch = -fov * (uv.y - 0.5f);
         for (int j = 0; j < slices; j++) {
-            float slicesRatio = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
+            uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
             // abs(phi) <= fov * aspectRatio / 2.0f
-            float yaw = -fov * aspectRatio * (slicesRatio - 0.5f);
+            float yaw = -fov * aspectRatio * (uv.x - 0.5f);
             pos = getPoint(yaw, pitch);
+            static const vec4 color(1);
             _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos);
-            _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&vec2(slicesRatio, stacksRatio));
-            _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&vec4(1));
+            _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv);
+            _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color);
         }
     }
     

From e6bafb9bf1a202415540f955fcea739abc108738 Mon Sep 17 00:00:00 2001
From: Howard Stearns <howard@highfidelity.io>
Date: Thu, 11 Jun 2015 16:19:54 -0700
Subject: [PATCH 64/88] Draggable toolBar.js, with persistence, and update some
 scripts for it.

---
 examples/controllers/hydra/gun.js | 23 ++++++----
 examples/dice.js                  | 24 +++++++----
 examples/edit.js                  | 11 +++++
 examples/libraries/toolBars.js    | 70 +++++++++++++++++++++++++++++--
 examples/pointer.js               | 16 +++++--
 5 files changed, 119 insertions(+), 25 deletions(-)

diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js
index 146f9daca3..7d024e2fd3 100644
--- a/examples/controllers/hydra/gun.js
+++ b/examples/controllers/hydra/gun.js
@@ -99,6 +99,13 @@ var NUM_BUTTONS = 3;
 
 var screenSize = Controller.getViewportDimensions();
 var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2;
+Script.include(["../../libraries/toolBars.js"]);
+const persistKey = "highfidelity.gun.toolbar.position";
+var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
+toolBar.save = function () {
+    Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
+};
+var old = JSON.parse(Settings.getValue(persistKey) || '0');
 var reticle = Overlays.addOverlay("image", {
                     x: screenSize.x / 2 - (BUTTON_SIZE / 2),
                     y: screenSize.y / 2 - (BUTTON_SIZE / 2),
@@ -108,9 +115,9 @@ var reticle = Overlays.addOverlay("image", {
                     alpha: 1
                 });
 
-var offButton = Overlays.addOverlay("image", {
-                    x: startX,
-                    y: screenSize.y - (BUTTON_SIZE + PADDING),
+var offButton = toolBar.addOverlay("image", {
+                    x: old ? old[0] : startX,
+                    y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)),
                     width: BUTTON_SIZE,
                     height: BUTTON_SIZE,
                     imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
@@ -118,7 +125,7 @@ var offButton = Overlays.addOverlay("image", {
                 });
 
 startX += BUTTON_SIZE + PADDING;
-var platformButton = Overlays.addOverlay("image", {
+var platformButton = toolBar.addOverlay("image", {
                     x: startX,
                     y: screenSize.y - (BUTTON_SIZE + PADDING),
                     width: BUTTON_SIZE,
@@ -128,7 +135,7 @@ var platformButton = Overlays.addOverlay("image", {
                 });
 
 startX += BUTTON_SIZE + PADDING;
-var gridButton = Overlays.addOverlay("image", {
+var gridButton = toolBar.addOverlay("image", {
                     x: startX,
                     y: screenSize.y - (BUTTON_SIZE + PADDING),
                     width: BUTTON_SIZE,
@@ -493,10 +500,8 @@ function mousePressEvent(event) {
 }
 
 function scriptEnding() {
-    Overlays.deleteOverlay(reticle); 
-    Overlays.deleteOverlay(offButton);
-    Overlays.deleteOverlay(platformButton);
-    Overlays.deleteOverlay(gridButton);
+    Overlays.deleteOverlay(reticle);
+    toolBar.cleanup();
     Overlays.deleteOverlay(pointer[0]);
     Overlays.deleteOverlay(pointer[1]);
     Overlays.deleteOverlay(text);
diff --git a/examples/dice.js b/examples/dice.js
index 515019e740..ac5d1b7426 100644
--- a/examples/dice.js
+++ b/examples/dice.js
@@ -3,6 +3,7 @@
 //  examples
 //
 //  Created by Philip Rosedale on February 2, 2015
+//  Persist toolbar by HRS 6/11/15.
 //  Copyright 2015 High Fidelity, Inc.
 //
 //  Press the dice button to throw some dice from the center of the screen. 
@@ -31,9 +32,16 @@ var screenSize = Controller.getViewportDimensions();
 var BUTTON_SIZE = 32;
 var PADDING = 3;
 
-var offButton = Overlays.addOverlay("image", {
-  x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING,
-  y: screenSize.y - (BUTTON_SIZE + PADDING),
+Script.include(["libraries/toolBars.js"]);
+var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
+const persistKey = "highfidelity.dice.toolbar.position";
+toolBar.save = function () {
+    Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
+};
+var old = JSON.parse(Settings.getValue(persistKey) || '0');
+var offButton = toolBar.addOverlay("image", {
+  x: old ? old[0] : (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING),
+  y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)),
   width: BUTTON_SIZE,
   height: BUTTON_SIZE,
   imageURL: HIFI_PUBLIC_BUCKET + "images/close.png",
@@ -45,7 +53,7 @@ var offButton = Overlays.addOverlay("image", {
   alpha: 1
 });
 
-var deleteButton = Overlays.addOverlay("image", {
+var deleteButton = toolBar.addOverlay("image", {
   x: screenSize.x / 2 - BUTTON_SIZE,
   y: screenSize.y - (BUTTON_SIZE + PADDING),
   width: BUTTON_SIZE,
@@ -59,7 +67,7 @@ var deleteButton = Overlays.addOverlay("image", {
   alpha: 1
 });
 
-var diceButton = Overlays.addOverlay("image", {
+var diceButton = toolBar.addOverlay("image", {
   x: screenSize.x / 2 + PADDING,
   y: screenSize.y - (BUTTON_SIZE + PADDING),
   width: BUTTON_SIZE,
@@ -140,10 +148,8 @@ function mousePressEvent(event) {
 }
 
 function scriptEnding() {
-  Overlays.deleteOverlay(offButton);
-  Overlays.deleteOverlay(diceButton);
-  Overlays.deleteOverlay(deleteButton);
+  toolBar.cleanup();
 }
 
 Controller.mousePressEvent.connect(mousePressEvent);
-Script.scriptEnding.connect(scriptEnding);
\ No newline at end of file
+Script.scriptEnding.connect(scriptEnding);
diff --git a/examples/edit.js b/examples/edit.js
index 6055400289..2974397cde 100644
--- a/examples/edit.js
+++ b/examples/edit.js
@@ -2,6 +2,7 @@
 //  examples
 //
 //  Created by Brad Hefta-Gaub on 10/2/14.
+//  Persist toolbar by HRS 6/11/15.
 //  Copyright 2014 High Fidelity, Inc.
 //
 //  This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing
@@ -320,6 +321,7 @@ var toolBar = (function () {
         }
     }
 
+    const persistKey = "highfidelity.edit.toolbar.position";
     that.move = function () {
         var newViewPort,
             toolsX,
@@ -330,6 +332,15 @@ var toolBar = (function () {
         if (toolBar === undefined) {
             initialize();
 
+            toolBar.save = function () {
+                Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
+            };
+            var old = JSON.parse(Settings.getValue(persistKey) || '0');
+            if (old) {
+                windowDimensions = newViewPort;
+                toolBar.move(old[0], old[1]);
+                return;
+            }
         } else if (windowDimensions.x === newViewPort.x &&
                    windowDimensions.y === newViewPort.y) {
             return;
diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js
index 670a69dec7..94bc1c8af0 100644
--- a/examples/libraries/toolBars.js
+++ b/examples/libraries/toolBars.js
@@ -236,6 +236,7 @@ ToolBar = function(x, y, direction) {
                 y: y - ToolBar.SPACING
             });
         }
+        this.save();
     }
     
     this.setAlpha = function(alpha, tool) {
@@ -313,9 +314,8 @@ ToolBar = function(x, y, direction) {
     this.cleanup = function() {
         for(var tool in this.tools) {
             this.tools[tool].cleanup();
-            delete this.tools[tool];
         }
-        
+
         if (this.back != null) {
             Overlays.deleteOverlay(this.back);
             this.back = null;
@@ -327,7 +327,71 @@ ToolBar = function(x, y, direction) {
         this.width = 0;
         this.height = 0;
     }
+
+    var that = this;
+    this.contains = function (xOrPoint, optionalY) {
+        var x = (optionalY === undefined) ? xOrPoint.x : xOrPoint,
+            y = (optionalY === undefined) ? xOrPoint.y : optionalY;
+        return (that.x <= x) && (x <= (that.x + that.width)) &&
+            (that.y <= y) && (y <= (that.y + that.height));
+    }
+    that.hover = function (enable) {
+        that.isHovering = enable;
+        if (that.back) {
+            if (enable) {
+                that.oldAlpha = Overlays.getProperty(that.back, 'backgroundAlpha');
+            }
+            Overlays.editOverlay(this.back, {
+                visible: enable,
+                backgroundAlpha: enable ? 0.5 : that.oldAlpha
+            });
+        }
+    };
+    // These are currently only doing that which is necessary for toolbar hover and toolbar drag.
+    // They have not yet been extended to tool hover/click/release, etc.
+    this.mousePressEvent = function (event) {
+        if (!that.contains(event)) {
+            that.mightBeDragging = false;
+            return;
+        }
+        that.mightBeDragging = true;
+        that.dragOffsetX = that.x - event.x;
+        that.dragOffsetY = that.y - event.y;
+    };
+    this.mouseMove  = function (event) {
+        if (!that.mightBeDragging || !event.isLeftButton) {
+            that.mightBeDragging = false;
+            if (!that.contains(event)) {
+                if (that.isHovering) {
+                    that.hover(false);
+                }
+                return;
+            }
+            if (!that.isHovering) {
+                that.hover(true);
+            }
+            return;
+        }
+        that.move(that.dragOffsetX + event.x, that.dragOffsetY + event.y);
+    };
+    Controller.mousePressEvent.connect(this.mousePressEvent);
+    Controller.mouseMoveEvent.connect(this.mouseMove);
+    // Called on move. A different approach would be to have all this on the prototype,
+    // and let apps extend where needed. Ex. app defines its toolbar.move() to call this.__proto__.move and then save.
+    this.save = function () { };
+    // This compatability hack breaks the model, but makes converting existing scripts easier:
+    this.addOverlay = function (ignored, oldSchoolProperties) {
+        var properties = JSON.parse(JSON.stringify(oldSchoolProperties)); // a copy
+        if (that.numberOfTools() === 0) {
+            that.move(properties.x, properties.y);
+        }
+        delete properties.x;
+        delete properties.y;
+        var index = that.addTool(properties);
+        var id = that.tools[index].overlay();
+        return id;
+    }
 }
 ToolBar.SPACING = 4;
 ToolBar.VERTICAL = 0;
-ToolBar.HORIZONTAL = 1;
\ No newline at end of file
+ToolBar.HORIZONTAL = 1;
diff --git a/examples/pointer.js b/examples/pointer.js
index cca46709ee..ea6b0c233f 100644
--- a/examples/pointer.js
+++ b/examples/pointer.js
@@ -3,6 +3,7 @@
 //
 //  Created by Seth Alves on May 15th
 //  Modified by Eric Levin on June 4
+//  Persist toolbar by HRS 6/11/15.
 //  Copyright 2015 High Fidelity, Inc.
 //
 //  Provides a pointer with option to draw on surfaces
@@ -31,9 +32,16 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
 var screenSize = Controller.getViewportDimensions();
 
 var userCanPoint = false;
-var pointerButton = Overlays.addOverlay("image", {
-  x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING,
-  y: screenSize.y - (BUTTON_SIZE + PADDING),
+Script.include(["libraries/toolBars.js"]);
+const persistKey = "highfidelity.pointer.toolbar.position";
+var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
+toolBar.save = function () {
+    Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
+};
+var old = JSON.parse(Settings.getValue(persistKey) || '0');
+var pointerButton = toolBar.addOverlay("image", {
+  x: old ? old[0] : screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING,
+  y: old ? old[1] : screenSize.y - (BUTTON_SIZE + PADDING),
   width: BUTTON_SIZE,
   height: BUTTON_SIZE,
   imageURL: HIFI_PUBLIC_BUCKET + "images/laser.png",
@@ -150,4 +158,4 @@ Script.scriptEnding.connect(cleanup);
 
 Controller.mouseMoveEvent.connect(mouseMoveEvent);
 Controller.mousePressEvent.connect(mousePressEvent);
-Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
\ No newline at end of file
+Controller.mouseReleaseEvent.connect(mouseReleaseEvent);

From 3cb4ee183e615a2fcf9192c5a1802cac70cbc100 Mon Sep 17 00:00:00 2001
From: Howard Stearns <howard@highfidelity.io>
Date: Thu, 11 Jun 2015 16:24:22 -0700
Subject: [PATCH 65/88] Update header.

---
 examples/libraries/toolBars.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js
index 94bc1c8af0..6e72795ea5 100644
--- a/examples/libraries/toolBars.js
+++ b/examples/libraries/toolBars.js
@@ -3,6 +3,7 @@
 //  examples
 //
 //  Created by Clément Brisset on 5/7/14.
+//  Persistable drag position by HRS 6/11/15.
 //  Copyright 2014 High Fidelity, Inc.
 //
 //  Distributed under the Apache License, Version 2.0.

From 09cfe004a18ab8c9b5622eb6d33dc22a41b7cf36 Mon Sep 17 00:00:00 2001
From: Brad Davis <bdavis@saintandreas.org>
Date: Thu, 11 Jun 2015 16:55:23 -0700
Subject: [PATCH 66/88] Combining with new cursor render code

---
 interface/src/ui/ApplicationOverlay.cpp | 16 ++++++++--------
 libraries/ui/src/CursorManager.cpp      |  2 +-
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index 971958ae61..ba0d3a60a9 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -210,7 +210,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
 
         overlays.renderHUD(renderArgs);
 
-        //renderPointers();
+        renderPointers();
 
         renderDomainConnectionStatusBorder();
         if (_newUiTexture) {
@@ -268,7 +268,7 @@ void ApplicationOverlay::bindCursorTexture(gpu::Batch& batch, uint8_t cursorInde
         _cursors[iconId] = DependencyManager::get<TextureCache>()->
             getImageTexture(iconPath);
     }
-    glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_cursors[iconId]));
+    batch.setUniformTexture(0, _cursors[iconId]);
 }
 
 #define CURSOR_PIXEL_SIZE 32.0f
@@ -471,11 +471,11 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position,
 
 //Renders optional pointers
 void ApplicationOverlay::renderPointers() {
-    glEnable(GL_TEXTURE_2D);
-    glEnable(GL_BLEND);
-    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    //glEnable(GL_TEXTURE_2D);
+    //glEnable(GL_BLEND);
+    //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
-    glActiveTexture(GL_TEXTURE0);
+    //glActiveTexture(GL_TEXTURE0);
     //bindCursorTexture();
 
     if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) {
@@ -521,8 +521,8 @@ void ApplicationOverlay::renderPointers() {
         _magActive[MOUSE] = false;
         renderControllerPointers();
     }
-    glBindTexture(GL_TEXTURE_2D, 0);
-    glDisable(GL_TEXTURE_2D);
+    //glBindTexture(GL_TEXTURE_2D, 0);
+    //glDisable(GL_TEXTURE_2D);
 }
 
 void ApplicationOverlay::renderControllerPointers() {
diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp
index efadd09142..38c746994d 100644
--- a/libraries/ui/src/CursorManager.cpp
+++ b/libraries/ui/src/CursorManager.cpp
@@ -49,7 +49,7 @@ namespace Cursor {
     static uint16_t _customIconId = Icon::USER_BASE;
 
     Manager::Manager() {
-        ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/sixense-reticle.png";
+        ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/arrow.png";
         ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/reticleLink.png";
     }
 

From 81460e48f46115ff1f3fb3b22c711e2a2126cb6c Mon Sep 17 00:00:00 2001
From: Stephen Birarda <commit@birarda.com>
Date: Thu, 11 Jun 2015 17:25:25 -0700
Subject: [PATCH 67/88] fix for sticking windows audio-mixer

---
 assignment-client/src/AssignmentClient.cpp    |  3 +++
 .../src/AssignmentClientMonitor.cpp           | 24 +++++++++----------
 assignment-client/src/audio/AudioMixer.cpp    |  2 ++
 3 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp
index cfee18941c..e125a44783 100644
--- a/assignment-client/src/AssignmentClient.cpp
+++ b/assignment-client/src/AssignmentClient.cpp
@@ -66,6 +66,9 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
     // set the logging target to the the CHILD_TARGET_NAME
     LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
 
+    // make sure we output process IDs for a child AC otherwise it's insane to parse
+    LogHandler::getInstance().setShouldOutputPID(true);
+
     // setup our _requestAssignment member variable from the passed arguments
     _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool);
 
diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp
index d19eb90df6..8c6478b59f 100644
--- a/assignment-client/src/AssignmentClientMonitor.cpp
+++ b/assignment-client/src/AssignmentClientMonitor.cpp
@@ -39,9 +39,9 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
     _walletUUID(walletUUID),
     _assignmentServerHostname(assignmentServerHostname),
     _assignmentServerPort(assignmentServerPort)
-{    
+{
     qDebug() << "_requestAssignmentType =" << _requestAssignmentType;
-    
+
     // start the Logging class with the parent's target name
     LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME);
 
@@ -77,13 +77,13 @@ void AssignmentClientMonitor::simultaneousWaitOnChildren(int waitMsecs) {
     while(_childProcesses.size() > 0 && !waitTimer.hasExpired(waitMsecs)) {
         // continue processing events so we can handle a process finishing up
         QCoreApplication::processEvents();
-    }     
+    }
 }
 
 void AssignmentClientMonitor::childProcessFinished() {
     QProcess* childProcess = qobject_cast<QProcess*>(sender());
     qint64 processID = _childProcesses.key(childProcess);
-    
+
     if (processID > 0) {
         qDebug() << "Child process" << processID << "has finished. Removing from internal map.";
         _childProcesses.remove(processID);
@@ -98,17 +98,17 @@ void AssignmentClientMonitor::stopChildProcesses() {
         qDebug() << "Attempting to terminate child process" << childProcess->processId();
         childProcess->terminate();
     }
-    
+
     simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS);
-    
+
     if (_childProcesses.size() > 0) {
         // ask even more firmly
         foreach(QProcess* childProcess, _childProcesses) {
             qDebug() << "Attempting to kill child process" << childProcess->processId();
             childProcess->kill();
         }
-        
-        simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); 
+
+        simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS);
     }
 }
 
@@ -122,7 +122,7 @@ void AssignmentClientMonitor::aboutToQuit() {
 void AssignmentClientMonitor::spawnChildClient() {
     QProcess* assignmentClient = new QProcess(this);
 
-    
+
     // unparse the parts of the command-line that the child cares about
     QStringList _childArguments;
     if (_assignmentPool != "") {
@@ -153,7 +153,7 @@ void AssignmentClientMonitor::spawnChildClient() {
 
     // make sure that the output from the child process appears in our output
     assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
-    
+
     assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments);
 
     // make sure we hear that this process has finished when it does
@@ -194,7 +194,7 @@ void AssignmentClientMonitor::checkSpares() {
             qDebug() << "asking child" << aSpareId << "to exit.";
             SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId);
             childNode->activateLocalSocket();
-            
+
             QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode);
             nodeList->writeUnverifiedDatagram(diePacket, childNode);
         }
@@ -239,7 +239,7 @@ void AssignmentClientMonitor::readPendingDatagrams() {
                     // update our records about how to reach this child
                     matchingNode->setLocalSocket(senderSockAddr);
 
-                    QVariantMap packetVariantMap = 
+                    QVariantMap packetVariantMap =
                         JSONBreakableMarshal::fromStringBuffer(receivedPacket.mid(numBytesForPacketHeader(receivedPacket)));
                     QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(packetVariantMap);
 
diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index ea7ac0648b..c3ca6796bc 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -850,7 +850,9 @@ void AudioMixer::run() {
 
         ++_numStatFrames;
 
+        // since we're a while loop we need to help Qt's event processing
         QCoreApplication::processEvents();
+        QCoreApplication::sendPostedEvents(this, 0);
 
         if (_isFinished) {
             break;

From 27f40ea8817e7f274c2b97a56cac71823fc4c711 Mon Sep 17 00:00:00 2001
From: Howard Stearns <howard@highfidelity.io>
Date: Thu, 11 Jun 2015 20:33:30 -0700
Subject: [PATCH 68/88] Signal Audio.disconnected, and use new hello/goodbye
 sounds in resources.

---
 examples/dialTone.js                            |  10 ++++++++--
 interface/resources/sounds/goodbye.wav          | Bin 0 -> 62044 bytes
 interface/resources/sounds/hello.wav            | Bin 0 -> 61324 bytes
 interface/src/Application.cpp                   |   2 ++
 libraries/audio-client/src/AudioClient.cpp      |   1 +
 libraries/audio-client/src/AudioClient.h        |   1 +
 .../script-engine/src/AudioScriptingInterface.h |   1 +
 7 files changed, 13 insertions(+), 2 deletions(-)
 create mode 100644 interface/resources/sounds/goodbye.wav
 create mode 100644 interface/resources/sounds/hello.wav

diff --git a/examples/dialTone.js b/examples/dialTone.js
index 135acb17f4..b55e654f25 100644
--- a/examples/dialTone.js
+++ b/examples/dialTone.js
@@ -3,6 +3,7 @@
 //  examples
 //
 //  Created by Stephen Birarda on 06/08/15.
+//  Added disconnect HRS 6/11/15.
 //  Copyright 2015 High Fidelity, Inc.
 //
 //  Distributed under the Apache License, Version 2.0.
@@ -10,14 +11,19 @@
 //
 
 // setup the local sound we're going to use
-var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/short1.wav");
+var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/hello.wav");
+var disconnectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/goodbye.wav");
 
 // setup the options needed for that sound
 var connectSoundOptions = {
     localOnly: true
-}
+};
 
 // play the sound locally once we get the first audio packet from a mixer
 Audio.receivedFirstPacket.connect(function(){
     Audio.playSound(connectSound, connectSoundOptions);
 });
+
+Audio.disconnected.connect(function(){
+    Audio.playSound(disconnectSound, connectSoundOptions);
+});
diff --git a/interface/resources/sounds/goodbye.wav b/interface/resources/sounds/goodbye.wav
new file mode 100644
index 0000000000000000000000000000000000000000..bd6c51a5c0b6de4d407528d6a24ca8da558c11e6
GIT binary patch
literal 62044
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
ACIA2c

literal 0
HcmV?d00001

diff --git a/interface/resources/sounds/hello.wav b/interface/resources/sounds/hello.wav
new file mode 100644
index 0000000000000000000000000000000000000000..6269dab5db21a869759dfe96cde18449729ebc87
GIT binary patch
literal 61324
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

literal 0
HcmV?d00001

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 79a03164ea..56fe4188e9 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -425,6 +425,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
     connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled);
     connect(audioIO.data(), &AudioClient::receivedFirstPacket,
             &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::receivedFirstPacket);
+    connect(audioIO.data(), &AudioClient::disconnected,
+            &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::disconnected);
 
     audioThread->start();
 
diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp
index 340ca9374d..6450f25208 100644
--- a/libraries/audio-client/src/AudioClient.cpp
+++ b/libraries/audio-client/src/AudioClient.cpp
@@ -156,6 +156,7 @@ void AudioClient::audioMixerKilled() {
     _hasReceivedFirstPacket = false;
     _outgoingAvatarAudioSequenceNumber = 0;
     _stats.reset();
+    emit disconnected();
 }
 
 
diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h
index 3b2c1c1ae6..642edde84a 100644
--- a/libraries/audio-client/src/AudioClient.h
+++ b/libraries/audio-client/src/AudioClient.h
@@ -186,6 +186,7 @@ signals:
     void deviceChanged();
 
     void receivedFirstPacket();
+    void disconnected();
 
 protected:
     AudioClient();
diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h
index d74e1ed1e0..470d038196 100644
--- a/libraries/script-engine/src/AudioScriptingInterface.h
+++ b/libraries/script-engine/src/AudioScriptingInterface.h
@@ -39,6 +39,7 @@ signals:
     void mutedByMixer();
     void environmentMuted();
     void receivedFirstPacket();
+    void disconnected();
 
 private:
     AudioScriptingInterface();

From feb3f68a00bef6b95fc12852dcdbb913f5390401 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Thu, 11 Jun 2015 20:49:19 -0700
Subject: [PATCH 69/88] hack to fix culling bugs for some subMesh parts in the
 windmill scene

---
 libraries/render-utils/src/Model.cpp | 62 +++++++++++++++++++++-------
 1 file changed, 46 insertions(+), 16 deletions(-)

diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 8d234cdef5..94a856073e 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -1019,7 +1019,7 @@ AABox Model::calculateScaledOffsetAABox(const AABox& box) const {
 
 glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const {
     // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix
-    glm::vec3 offsetPoint = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(point, 1.0f));
+    glm::vec3 offsetPoint = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(point, 1.0f)); /// should this be point???
     glm::vec3 scaledPoint = ((offsetPoint + _offset) * _scale);
     glm::vec3 rotatedPoint = _rotation * scaledPoint;
     glm::vec3 translatedPoint = rotatedPoint + _translation;
@@ -1764,13 +1764,33 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
     if (!_calculatedMeshPartBoxesValid) {
         recalculateMeshBoxes(true);
     }
+
+    if (meshIndex < _meshStates.size()) {
+        const MeshState& state = _meshStates.at(meshIndex);
+        bool isSkinned = state.clusterMatrices.size() > 1;
+        if (isSkinned) {
+            // if we're skinned return the entire mesh extents because we can't know for sure our clusters don't move us
+            return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents);
+        }
+    }
+    
     if (_calculatedMeshPartBoxesValid && _calculatedMeshPartBoxes.contains(QPair<int,int>(meshIndex, partIndex))) {
-        return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair<int,int>(meshIndex, partIndex)]);
+        
+        // FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding
+        //           box of the mesh part fails. It seems to create boxes that are not consistent with where the
+        //           geometry actually renders. If instead we make all the parts share the bounds of the entire subMesh
+        //           things will render properly.
+        //
+        //    return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair<int,int>(meshIndex, partIndex)]);
+        //
+        // If we not skinned use the bounds of the subMesh for all it's parts
+        return _calculatedMeshBoxes[meshIndex];
     }
     return AABox();
 }
 
 void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) {
+
     if (!_readyWhenAdded) {
         return; // bail asap
     }
@@ -1785,19 +1805,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
     gpu::Batch& batch = *(args->_batch);
     auto mode = args->_renderMode;
 
-    // render the part bounding box
-    #ifdef DEBUG_BOUNDING_PARTS
-    {
-        glm::vec4 cubeColor(1.0f,0.0f,0.0f,1.0f);
-        AABox partBounds = getPartBounds(meshIndex, partIndex);
-
-        glm::mat4 translation = glm::translate(partBounds.calcCenter());
-        glm::mat4 scale = glm::scale(partBounds.getDimensions());
-        glm::mat4 modelToWorldMatrix = translation * scale;
-        batch.setModelTransform(modelToWorldMatrix);
-        DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor);
-    }
-    #endif //def DEBUG_BOUNDING_PARTS
 
     // Capture the view matrix once for the rendering of this model
     if (_transforms.empty()) {
@@ -1824,6 +1831,29 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
     bool hasLightmap = mesh.hasEmissiveTexture();
     bool isSkinned = state.clusterMatrices.size() > 1;
     bool wireframe = isWireframe();
+
+    AABox partBounds = getPartBounds(meshIndex, partIndex);
+    bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE;
+    
+    // render the part bounding box
+    #ifdef DEBUG_BOUNDING_PARTS
+    {
+        glm::vec4 cubeColor;
+        if (isSkinned) {
+            cubeColor = glm::vec4(0.0f,1.0f,1.0f,1.0f);
+        } else if (inView) {
+            cubeColor = glm::vec4(1.0f,0.0f,1.0f,1.0f);
+        } else {
+            cubeColor = glm::vec4(1.0f,1.0f,0.0f,1.0f);
+        }
+
+        Transform transform;
+        transform.setTranslation(partBounds.calcCenter());
+        transform.setScale(partBounds.getDimensions());
+        batch.setModelTransform(transform);
+        DependencyManager::get<DeferredLightingEffect>()->renderWireCube(batch, 1.0f, cubeColor);
+    }
+    #endif //def DEBUG_BOUNDING_PARTS
     
     if (wireframe) {
         translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
@@ -1977,7 +2007,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
     }
     
     qint64 offset = _calculatedMeshPartOffet[QPair<int,int>(meshIndex, partIndex)];
-    
+
     if (part.quadIndices.size() > 0) {
         batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset);
         offset += part.quadIndices.size() * sizeof(int);

From 2fc0233096d1ff65c301dae50ef8a6c539a2aaea Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Thu, 11 Jun 2015 20:50:31 -0700
Subject: [PATCH 70/88] hack to fix culling bugs for some subMesh parts in the
 windmill scene

---
 libraries/render-utils/src/Model.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 94a856073e..d313ef9b79 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -1832,12 +1832,12 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
     bool isSkinned = state.clusterMatrices.size() > 1;
     bool wireframe = isWireframe();
 
-    AABox partBounds = getPartBounds(meshIndex, partIndex);
-    bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE;
-    
     // render the part bounding box
     #ifdef DEBUG_BOUNDING_PARTS
     {
+        AABox partBounds = getPartBounds(meshIndex, partIndex);
+        bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE;
+
         glm::vec4 cubeColor;
         if (isSkinned) {
             cubeColor = glm::vec4(0.0f,1.0f,1.0f,1.0f);

From 565bf8bcb269f2c17c5694f50da71a2a1734fc35 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 12 Jun 2015 15:41:37 +0200
Subject: [PATCH 71/88] Fix text entities wrapping

---
 .../src/RenderableTextEntityItem.cpp          |  5 +++--
 libraries/render-utils/src/TextRenderer3D.cpp | 21 ++++++++++---------
 2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
index 8a67bb99f2..f61ed6f2c5 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
@@ -51,8 +51,9 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
     transformToTopLeft.setScale(scale); // Scale to have the correct line height
     batch.setModelTransform(transformToTopLeft);
     
-    float leftMargin = 0.5f * _lineHeight, topMargin = 0.5f * _lineHeight;
-    glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, dimensions.y - 2.0f * topMargin);
+    float leftMargin = 0.1f * _lineHeight, topMargin = 0.1f * _lineHeight;
+    glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin,
+                                 dimensions.y - 2.0f * topMargin);
     _textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale);
 }
 
diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp
index d081c0480a..7dbb7ea4fb 100644
--- a/libraries/render-utils/src/TextRenderer3D.cpp
+++ b/libraries/render-utils/src/TextRenderer3D.cpp
@@ -101,7 +101,7 @@ struct QuadBuilder {
                                     texMin);
     }
     QuadBuilder(const Glyph3D& glyph, const glm::vec2& offset) :
-    QuadBuilder(offset + glyph.offset - glm::vec2(0.0f, glyph.size.y), glyph.size,
+    QuadBuilder(offset + glm::vec2(glyph.offset.x, glyph.offset.y - glyph.size.y), glyph.size,
                     glyph.texOffset, glyph.texSize) {}
     
 };
@@ -113,7 +113,7 @@ public:
     void read(QIODevice& path);
 
     glm::vec2 computeExtent(const QString& str) const;
-    float getRowHeight() const { return _rowHeight; }
+    float getRowHeight() const { return _fontSize; }
     
     // Render string to batch
     void drawString(gpu::Batch& batch, float x, float y, const QString& str,
@@ -251,13 +251,14 @@ glm::vec2 Font3D::computeTokenExtent(const QString& token) const {
 glm::vec2 Font3D::computeExtent(const QString& str) const {
     glm::vec2 extent = glm::vec2(0.0f, 0.0f);
     
-    QStringList tokens = splitLines(str);
-    foreach(const QString& token, tokens) {
-        glm::vec2 tokenExtent = computeTokenExtent(token);
-        extent.x = std::max(tokenExtent.x, extent.x);
+    QStringList lines{ splitLines(str) };
+    if (!lines.empty()) {
+        for(const auto& line : lines) {
+            glm::vec2 tokenExtent = computeTokenExtent(line);
+            extent.x = std::max(tokenExtent.x, extent.x);
+        }
+        extent.y = lines.count() * _fontSize;
     }
-    extent.y = tokens.count() * _rowHeight;
-    
     return extent;
 }
 
@@ -393,7 +394,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str,
             }
             if (isNewLine || forceNewLine) {
                 // Character return, move the advance to a new line
-                advance = glm::vec2(x, advance.y - _rowHeight);
+                advance = glm::vec2(x, advance.y - _fontSize);
 
                 if (isNewLine) {
                     // No need to draw anything, go directly to next token
@@ -413,7 +414,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str,
                 for (auto c : token) {
                     auto glyph = _glyphs[c];
                     
-                    QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _fontSize));
+                    QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent));
                     _verticesBuffer->append(sizeof(QuadBuilder), (const gpu::Byte*)&qd);
                     _numVertices += 4;
                     

From 228f8c2e61cd37dc3912b4dd41c62d9be18d6b6b Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 12 Jun 2015 17:34:16 +0200
Subject: [PATCH 72/88] Fix a few items rendering without pipeline

---
 interface/src/avatar/Avatar.cpp                            | 7 ++++++-
 .../entities-renderer/src/RenderableLineEntityItem.cpp     | 1 +
 .../src/RenderableParticleEffectEntityItem.cpp             | 1 +
 3 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index dbe7d52a3f..71f4621205 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -328,6 +328,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
     if (postLighting &&
         glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) {
         auto geometryCache = DependencyManager::get<GeometryCache>();
+        auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
 
         // render pointing lasers
         glm::vec3 laserColor = glm::vec3(1.0f, 0.0f, 1.0f);
@@ -354,6 +355,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
                 pointerTransform.setTranslation(position);
                 pointerTransform.setRotation(rotation);
                 batch->setModelTransform(pointerTransform);
+                deferredLighting->bindSimpleProgram(*batch);
                 geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
             }
         }
@@ -376,6 +378,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
                 pointerTransform.setTranslation(position);
                 pointerTransform.setRotation(rotation);
                 batch->setModelTransform(pointerTransform);
+                deferredLighting->bindSimpleProgram(*batch);
                 geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
             }
         }
@@ -462,7 +465,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
                 Transform transform;
                 transform.setTranslation(position);
                 batch->setModelTransform(transform);
-                DependencyManager::get<GeometryCache>()->renderSphere(*batch, LOOK_AT_INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR);
+                DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(*batch, LOOK_AT_INDICATOR_RADIUS
+                                                                                    , 15, 15, LOOK_AT_INDICATOR_COLOR);
             }
         }
 
@@ -494,6 +498,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo
                     _voiceSphereID = DependencyManager::get<GeometryCache>()->allocateID();
                 }
 
+                DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch);
                 DependencyManager::get<GeometryCache>()->renderSphere(*batch, sphereRadius, 15, 15,
                     glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true,
                     _voiceSphereID);
diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp
index 4e5de331bb..65407c74e7 100644
--- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp
@@ -49,6 +49,7 @@ void RenderableLineEntityItem::render(RenderArgs* args) {
     
     batch._glLineWidth(getLineWidth());
     if (getLinePoints().size() > 1) {
+        DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
         DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID);
     }
     batch._glLineWidth(1.0f);
diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
index 6a50cbf1cb..91c89bb183 100644
--- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
@@ -53,6 +53,7 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
         batch.setUniformTexture(0, _texture->getGPUTexture());
     }
     batch.setModelTransform(getTransformToCenter());
+    DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
     DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::QUADS, _cacheID);
 };
 

From c17ae593f0b8520c274a36b3f77c30d2c41c7f3d Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Fri, 12 Jun 2015 09:04:16 -0700
Subject: [PATCH 73/88] CR feedback

---
 libraries/render-utils/src/Model.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index d313ef9b79..2379058b92 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -1019,7 +1019,7 @@ AABox Model::calculateScaledOffsetAABox(const AABox& box) const {
 
 glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const {
     // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix
-    glm::vec3 offsetPoint = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(point, 1.0f)); /// should this be point???
+    glm::vec3 offsetPoint = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(point, 1.0f));
     glm::vec3 scaledPoint = ((offsetPoint + _offset) * _scale);
     glm::vec3 rotatedPoint = _rotation * scaledPoint;
     glm::vec3 translatedPoint = rotatedPoint + _translation;
@@ -1840,11 +1840,11 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
 
         glm::vec4 cubeColor;
         if (isSkinned) {
-            cubeColor = glm::vec4(0.0f,1.0f,1.0f,1.0f);
+            cubeColor = glm::vec4(0.0f, 1.0f, 1.0f, 1.0f);
         } else if (inView) {
-            cubeColor = glm::vec4(1.0f,0.0f,1.0f,1.0f);
+            cubeColor = glm::vec4(1.0f, 0.0f, 1.0f, 1.0f);
         } else {
-            cubeColor = glm::vec4(1.0f,1.0f,0.0f,1.0f);
+            cubeColor = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f);
         }
 
         Transform transform;

From 128b086e82adbc45875e5d928474643c58a99430 Mon Sep 17 00:00:00 2001
From: Stephen Birarda <commit@birarda.com>
Date: Fri, 12 Jun 2015 10:25:34 -0700
Subject: [PATCH 74/88] don't build the polyvox examples

---
 cmake/externals/polyvox/CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cmake/externals/polyvox/CMakeLists.txt b/cmake/externals/polyvox/CMakeLists.txt
index 28aec6dab7..c28a8e1319 100644
--- a/cmake/externals/polyvox/CMakeLists.txt
+++ b/cmake/externals/polyvox/CMakeLists.txt
@@ -5,7 +5,7 @@ ExternalProject_Add(
   ${EXTERNAL_NAME}
   URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox.zip
   URL_MD5 904b840328278c9b36fa7a14be730c34
-  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
+  CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
   BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
   LOG_DOWNLOAD 1
   LOG_CONFIGURE 1

From 926283956412d1e35fc1ba319f2c513803da2c77 Mon Sep 17 00:00:00 2001
From: Bradley Austin Davis <bdavis@saintandreas.org>
Date: Fri, 12 Jun 2015 10:33:52 -0700
Subject: [PATCH 75/88] Fix broken identity transforms on OSX

---
 libraries/gpu/src/gpu/GLBackendTransform.cpp | 25 +++++++-------------
 1 file changed, 9 insertions(+), 16 deletions(-)

diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp
index 3f760e4cc8..4e524ec24a 100755
--- a/libraries/gpu/src/gpu/GLBackendTransform.cpp
+++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp
@@ -133,11 +133,11 @@ void GLBackend::updateTransform() {
     }
 
     if (_transform._invalidModel || _transform._invalidView) {
+        if (_transform._lastMode != GL_MODELVIEW) {
+            glMatrixMode(GL_MODELVIEW);
+            _transform._lastMode = GL_MODELVIEW;
+        }
         if (!_transform._model.isIdentity()) {
-            if (_transform._lastMode != GL_MODELVIEW) {
-                glMatrixMode(GL_MODELVIEW);
-                _transform._lastMode = GL_MODELVIEW;
-            }
             Transform::Mat4 modelView;
             if (!_transform._view.isIdentity()) {
                 Transform mvx;
@@ -147,19 +147,12 @@ void GLBackend::updateTransform() {
                 _transform._model.getMatrix(modelView);
             }
             glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView));
+        } else if (!_transform._view.isIdentity()) {
+            Transform::Mat4 modelView;
+            _transform._view.getInverseMatrix(modelView);
+            glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView));
         } else {
-            if (!_transform._view.isIdentity()) {
-                if (_transform._lastMode != GL_MODELVIEW) {
-                    glMatrixMode(GL_MODELVIEW);
-                    _transform._lastMode = GL_MODELVIEW;
-                }
-                Transform::Mat4 modelView;
-                _transform._view.getInverseMatrix(modelView);
-                glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView));
-            } else {
-                // TODO: eventually do something about the matrix when neither view nor model is specified?
-                // glLoadIdentity();
-            }
+            glLoadIdentity();
         }
         (void) CHECK_GL_ERROR();
     }

From f3d3bd7bec9ab12c83da8d9013f9a510886f3699 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 12 Jun 2015 19:44:15 +0200
Subject: [PATCH 76/88] Remove _rowHeight

---
 libraries/render-utils/src/TextRenderer3D.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp
index 7dbb7ea4fb..5a6ec89c4f 100644
--- a/libraries/render-utils/src/TextRenderer3D.cpp
+++ b/libraries/render-utils/src/TextRenderer3D.cpp
@@ -139,7 +139,6 @@ private:
     // Font characteristics
     QString _family;
     float _fontSize = 0.0f;
-    float _rowHeight = 0.0f;
     float _leading = 0.0f;
     float _ascent = 0.0f;
     float _descent = 0.0f;
@@ -289,8 +288,7 @@ void Font3D::read(QIODevice& in) {
     readStream(in, _descent);
     readStream(in, _spaceWidth);
     _fontSize = _ascent + _descent;
-    _rowHeight = _fontSize + _leading;
-
+    
     // Read character count
     uint16_t count;
     readStream(in, count);

From 26dd0679827a59bfe8cc25a3a22c692c9fe07ba6 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 12 Jun 2015 19:44:40 +0200
Subject: [PATCH 77/88] Advance with _leading on y

---
 libraries/render-utils/src/TextRenderer3D.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp
index 5a6ec89c4f..4b59877582 100644
--- a/libraries/render-utils/src/TextRenderer3D.cpp
+++ b/libraries/render-utils/src/TextRenderer3D.cpp
@@ -392,7 +392,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str,
             }
             if (isNewLine || forceNewLine) {
                 // Character return, move the advance to a new line
-                advance = glm::vec2(x, advance.y - _fontSize);
+                advance = glm::vec2(x, advance.y - _leading);
 
                 if (isNewLine) {
                     // No need to draw anything, go directly to next token

From 30ae78e3b6a256bf77ad5b6a1cdd240fa2df6e74 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 12 Jun 2015 19:50:32 +0200
Subject: [PATCH 78/88] Rename getRowHeight

---
 .../entities-renderer/src/RenderableTextEntityItem.cpp      | 2 +-
 libraries/render-utils/src/TextRenderer3D.cpp               | 6 +++---
 libraries/render-utils/src/TextRenderer3D.h                 | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
index f61ed6f2c5..d06ffb9400 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
@@ -47,7 +47,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
     glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
     DependencyManager::get<DeferredLightingEffect>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
     
-    float scale = _lineHeight / _textRenderer->getRowHeight();
+    float scale = _lineHeight / _textRenderer->getFontSize();
     transformToTopLeft.setScale(scale); // Scale to have the correct line height
     batch.setModelTransform(transformToTopLeft);
     
diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp
index 4b59877582..0eb560bf72 100644
--- a/libraries/render-utils/src/TextRenderer3D.cpp
+++ b/libraries/render-utils/src/TextRenderer3D.cpp
@@ -113,7 +113,7 @@ public:
     void read(QIODevice& path);
 
     glm::vec2 computeExtent(const QString& str) const;
-    float getRowHeight() const { return _fontSize; }
+    float getFontSize() const { return _fontSize; }
     
     // Render string to batch
     void drawString(gpu::Batch& batch, float x, float y, const QString& str,
@@ -476,9 +476,9 @@ glm::vec2 TextRenderer3D::computeExtent(const QString& str) const {
     return glm::vec2(0.0f, 0.0f);
 }
 
-float TextRenderer3D::getRowHeight() const {
+float TextRenderer3D::getFontSize() const {
     if (_font) {
-        return _font->getRowHeight();
+        return _font->getFontSize();
     }
     return 0.0f;
 }
diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h
index 8f55d0c977..e61203b06f 100644
--- a/libraries/render-utils/src/TextRenderer3D.h
+++ b/libraries/render-utils/src/TextRenderer3D.h
@@ -50,7 +50,7 @@ public:
     ~TextRenderer3D();
 
     glm::vec2 computeExtent(const QString& str) const;
-    float getRowHeight() const;
+    float getFontSize() const;
     
     void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(-1.0f),
                const glm::vec2& bounds = glm::vec2(-1.0f));

From 4ed147418a540c77025dd1abb5fcbedfbc935dab Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Fri, 12 Jun 2015 11:19:30 -0700
Subject: [PATCH 79/88] quick hack to fix stats texture bleed through

---
 interface/src/ui/Stats.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp
index 32df75c46d..8d098b4dc8 100644
--- a/interface/src/ui/Stats.cpp
+++ b/interface/src/ui/Stats.cpp
@@ -161,6 +161,10 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh
                       ((rgba >> 8) & 0xff)  / 255.0f,
                       (rgba & 0xff) / 255.0f);
 
+    // FIX ME: is this correct? It seems to work to fix textures bleeding into us...
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_TEXTURE_2D);
+
     DependencyManager::get<GeometryCache>()->renderQuad(x, y, width, height, color);
 }
 

From 87951ff7b3bfd19f8d824fda7dfa11553aa49b49 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Fri, 12 Jun 2015 11:19:30 -0700
Subject: [PATCH 80/88] quick hack to fix stats texture bleed through

---
 interface/src/ui/Stats.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp
index 32df75c46d..8d098b4dc8 100644
--- a/interface/src/ui/Stats.cpp
+++ b/interface/src/ui/Stats.cpp
@@ -161,6 +161,10 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh
                       ((rgba >> 8) & 0xff)  / 255.0f,
                       (rgba & 0xff) / 255.0f);
 
+    // FIX ME: is this correct? It seems to work to fix textures bleeding into us...
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_TEXTURE_2D);
+
     DependencyManager::get<GeometryCache>()->renderQuad(x, y, width, height, color);
 }
 

From 7d7db65fd1550c0944c962c9a0313a69ea153b49 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Fri, 12 Jun 2015 11:42:38 -0700
Subject: [PATCH 81/88] fix avatar mesh boxes not staying in sync with avatar
 position

---
 libraries/render-utils/src/Model.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 2379058b92..865c225445 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -1783,8 +1783,13 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
         //
         //    return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair<int,int>(meshIndex, partIndex)]);
         //
+        //    NOTE: we also don't want to use the _calculatedMeshBoxes[] because they don't handle avatar moving correctly
+        //          without recalculating them...
+        //    return _calculatedMeshBoxes[meshIndex];
+        //
         // If we not skinned use the bounds of the subMesh for all it's parts
-        return _calculatedMeshBoxes[meshIndex];
+        const FBXMesh& mesh = _geometry->getFBXGeometry().meshes.at(meshIndex);
+        return calculateScaledOffsetExtents(mesh.meshExtents);
     }
     return AABox();
 }

From 7ee609396c5045ae976b8b72dee5fddbacdb974c Mon Sep 17 00:00:00 2001
From: Eric Levin <ericrius1@gmail.com>
Date: Fri, 12 Jun 2015 11:58:43 -0700
Subject: [PATCH 82/88] added lifetime to painted lines to prevent huge
 model.json files when lots of people draw in one domain. Got rid of gaps in
 lines when switching line entities

---
 examples/paint.js | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/examples/paint.js b/examples/paint.js
index 8bba4e2571..c0cc93afc7 100644
--- a/examples/paint.js
+++ b/examples/paint.js
@@ -12,8 +12,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 Script.include('lineRider.js')
-var MAX_POINTS_PER_LINE = 80;
-
+var MAX_POINTS_PER_LINE = 30;
+var LINE_LIFETIME =  60 * 5  //5 minute lifetime
 
 var colorPalette = [{
   red: 236,
@@ -120,10 +120,12 @@ function MousePaint() {
         y: 10,
         z: 10
       },
-      lineWidth: LINE_WIDTH
+      lineWidth: LINE_WIDTH,
+      lifetime: LINE_LIFETIME
     });
     points = [];
     if (point) {
+
       points.push(point);
       path.push(point);
     }
@@ -133,22 +135,22 @@ function MousePaint() {
 
   function mouseMoveEvent(event) {
 
+    if (!isDrawing) {
+      return;
+    }
 
     var pickRay = Camera.computePickRay(event.x, event.y);
     var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE);
     var point = Vec3.sum(Camera.getPosition(), addVector);
+    points.push(point);
+    path.push(point);
     Entities.editEntity(line, {
       linePoints: points
     });
     Entities.editEntity(brush, {
       position: point
     });
-    if (!isDrawing) {
-      return;
-    }
 
-    points.push(point);
-    path.push(point);
 
     if (points.length === MAX_POINTS_PER_LINE) {
       //We need to start a new line!
@@ -253,7 +255,6 @@ function HydraPaint() {
   var maxLineWidth = 10;
   var currentLineWidth = minLineWidth;
   var MIN_PAINT_TRIGGER_THRESHOLD = .01;
-  var LINE_LIFETIME = 20;
   var COLOR_CHANGE_TIME_FACTOR = 0.1;
 
   var RIGHT_BUTTON_1 = 7
@@ -330,7 +331,7 @@ function HydraPaint() {
           z: 10
         },
         lineWidth: 5,
-        // lifetime: LINE_LIFETIME
+        lifetime: LINE_LIFETIME
       });
       this.points = [];
       if (point) {

From 0cdc2b53fe7815bba21a98ecbdfd91bcbb2898f5 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Fri, 12 Jun 2015 12:57:24 -0700
Subject: [PATCH 83/88] Revert "Merge pull request #5106 from
 jherico/render_cursor"

This reverts commit 4d18bd7cec710b5188d1f6ad5c58010117d7a77d, reversing
changes made to 24fda9a7336e4eff1d1310dd3359dd6cbbb6acb5.
---
 interface/resources/images/arrow.png          | Bin 3562 -> 0 bytes
 interface/src/Application.cpp                 |   3 +-
 interface/src/devices/OculusManager.cpp       |   5 +-
 interface/src/devices/TV3DManager.cpp         |  18 +-
 interface/src/ui/ApplicationOverlay.cpp       | 666 +++++++++++-------
 interface/src/ui/ApplicationOverlay.h         |  53 +-
 libraries/render-utils/src/GeometryCache.cpp  |  15 -
 libraries/render-utils/src/GeometryCache.h    |   3 -
 .../render-utils/src/standardDrawTexture.slf  |  24 -
 .../src/standardTransformPNTC.slv             |  33 -
 10 files changed, 481 insertions(+), 339 deletions(-)
 delete mode 100644 interface/resources/images/arrow.png
 delete mode 100644 libraries/render-utils/src/standardDrawTexture.slf
 delete mode 100644 libraries/render-utils/src/standardTransformPNTC.slv

diff --git a/interface/resources/images/arrow.png b/interface/resources/images/arrow.png
deleted file mode 100644
index 408881b5856eb47a30875e3e56d5864721161587..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 3562
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
R^nCyTaI$x`E7(oB_+R=!c8UN1

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 56fe4188e9..0f0abb3996 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -955,7 +955,6 @@ void Application::paintGL() {
         glPushMatrix();
         glLoadIdentity();
         displaySide(&renderArgs, _myCamera);
-        _applicationOverlay.displayOverlayTexture(&renderArgs);
         glPopMatrix();
 
         if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
@@ -972,6 +971,8 @@ void Application::paintGL() {
                           0, 0, _glWidget->getDeviceSize().width(), _glWidget->getDeviceSize().height(),
                             GL_COLOR_BUFFER_BIT, GL_NEAREST);
         glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+
+        _applicationOverlay.displayOverlayTexture();
     }
 
     if (!OculusManager::isConnected() || OculusManager::allowSwap()) {
diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp
index 414c7f6199..a7383ae4bb 100644
--- a/interface/src/devices/OculusManager.cpp
+++ b/interface/src/devices/OculusManager.cpp
@@ -615,10 +615,12 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
 
         renderArgs->_renderSide = RenderArgs::MONO;
         qApp->displaySide(renderArgs, *_camera, false);
-        qApp->getApplicationOverlay().displayOverlayTextureHmd(renderArgs, *_camera);
+        qApp->getApplicationOverlay().displayOverlayTextureHmd(*_camera);
     });
     _activeEye = ovrEye_Count;
 
+    glPopMatrix();
+
     gpu::FramebufferPointer finalFbo;
     //Bind the output texture from the glow shader. If glow effect is disabled, we just grab the texture
     if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) {
@@ -629,7 +631,6 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
         finalFbo = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer(); 
         glBindFramebuffer(GL_FRAMEBUFFER, 0);
     }
-    glPopMatrix();
 
     glMatrixMode(GL_PROJECTION);
     glPopMatrix();
diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp
index b0a2ff7e3b..09edb03e5a 100644
--- a/interface/src/devices/TV3DManager.cpp
+++ b/interface/src/devices/TV3DManager.cpp
@@ -12,7 +12,6 @@
 #include "InterfaceConfig.h"
 
 #include <glm/glm.hpp>
-#include <glm/gtc/type_ptr.hpp>
 
 #include <GlowEffect.h>
 #include "gpu/GLBackend.h"
@@ -107,20 +106,21 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
         _activeEye = &eye;
         glViewport(portalX, portalY, portalW, portalH);
         glScissor(portalX, portalY, portalW, portalH);
-
-        glm::mat4 projection = glm::frustum<float>(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ);
-        float fov = atan(1.0f / projection[1][1]);
-        projection = glm::translate(projection, vec3(eye.modelTranslation, 0, 0));
-        eyeCamera.setProjection(projection);
-
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity(); // reset projection matrix
-        glLoadMatrixf(glm::value_ptr(projection));
+        glFrustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); // set left view frustum
+        GLfloat p[4][4];
+        // Really?
+        glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0]));
+        float cotangent = p[1][1];
+        GLfloat fov = atan(1.0f / cotangent);
+        glTranslatef(eye.modelTranslation, 0.0, 0.0); // translate to cancel parallax
+
         glMatrixMode(GL_MODELVIEW);
         glLoadIdentity();
         renderArgs->_renderSide = RenderArgs::MONO;
         qApp->displaySide(renderArgs, eyeCamera, false);
-        qApp->getApplicationOverlay().displayOverlayTexture(renderArgs);
+        qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov);
         _activeEye = NULL;
     }, [&]{
         // render right side view
diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index ba0d3a60a9..0c1783f8ac 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -14,16 +14,13 @@
 #include <QOpenGLFramebufferObject>
 #include <QOpenGLTexture>
 
-#include <glm/gtc/type_ptr.hpp>
-
 #include <avatar/AvatarManager.h>
-#include <DeferredLightingEffect.h>
 #include <GLMHelpers.h>
+#include <PathUtils.h>
 #include <gpu/GLBackend.h>
 #include <GLMHelpers.h>
-#include <OffscreenUi.h>
-#include <CursorManager.h>
 #include <PerfStat.h>
+#include <OffscreenUi.h>
 
 #include "AudioClient.h"
 #include "audio/AudioIOStatsRenderer.h"
@@ -36,9 +33,6 @@
 #include "Util.h"
 #include "ui/Stats.h"
 
-#include "../../libraries/render-utils/standardTransformPNTC_vert.h"
-#include "../../libraries/render-utils/standardDrawTexture_frag.h"
-
 // Used to animate the magnification windows
 const float MAG_SPEED = 0.08f;
 
@@ -120,6 +114,27 @@ bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r,
     }
 }
 
+void ApplicationOverlay::renderReticle(glm::quat orientation, float alpha) {
+    glPushMatrix(); {
+        glm::vec3 axis = glm::axis(orientation);
+        glRotatef(glm::degrees(glm::angle(orientation)), axis.x, axis.y, axis.z);
+        glm::vec3 topLeft = getPoint(reticleSize / 2.0f, -reticleSize / 2.0f);
+        glm::vec3 topRight = getPoint(-reticleSize / 2.0f, -reticleSize / 2.0f);
+        glm::vec3 bottomLeft = getPoint(reticleSize / 2.0f, reticleSize / 2.0f);
+        glm::vec3 bottomRight = getPoint(-reticleSize / 2.0f, reticleSize / 2.0f);
+        
+        // TODO: this version of renderQuad() needs to take a color
+        glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], alpha };
+        
+        
+        
+        DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomLeft, bottomRight, topRight,
+                                                            glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f),
+                                                            glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), 
+                                                            reticleColor, _reticleQuad);
+    } glPopMatrix();
+}
+
 ApplicationOverlay::ApplicationOverlay() :
     _textureFov(glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE)),
     _textureAspectRatio(1.0f),
@@ -133,8 +148,7 @@ ApplicationOverlay::ApplicationOverlay() :
     _previousMagnifierBottomLeft(),
     _previousMagnifierBottomRight(),
     _previousMagnifierTopLeft(),
-    _previousMagnifierTopRight(),
-    _framebufferObject(nullptr)
+    _previousMagnifierTopRight()
 {
     memset(_reticleActive, 0, sizeof(_reticleActive));
     memset(_magActive, 0, sizeof(_reticleActive));
@@ -181,17 +195,16 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
     //Handle fading and deactivation/activation of UI
     
     // Render 2D overlay
+    glMatrixMode(GL_PROJECTION);
     glDisable(GL_DEPTH_TEST);
     glDisable(GL_LIGHTING);
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    buildFramebufferObject();
-
-    _framebufferObject->bind();
+    _overlays.buildFramebufferObject();
+    _overlays.bind();
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glViewport(0, 0, size.x, size.y);
 
-    glMatrixMode(GL_PROJECTION);
     glPushMatrix(); {
         const float NEAR_CLIP = -10000;
         const float FAR_CLIP = 10000;
@@ -213,22 +226,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
         renderPointers();
 
         renderDomainConnectionStatusBorder();
-        if (_newUiTexture) {
-            glMatrixMode(GL_PROJECTION);
-            glLoadIdentity();
-            glMatrixMode(GL_MODELVIEW);
-            glLoadIdentity();
-            glEnable(GL_TEXTURE_2D);
-            glEnable(GL_BLEND);
-            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-            glBindTexture(GL_TEXTURE_2D, _newUiTexture);
-            DependencyManager::get<GeometryCache>()->renderUnitQuad();
-            glBindTexture(GL_TEXTURE_2D, 0);
-            glDisable(GL_TEXTURE_2D);
-            glDisable(GL_BLEND);
-        }
-        glLoadIdentity();
-
 
         glMatrixMode(GL_PROJECTION);
     } glPopMatrix();
@@ -238,161 +235,259 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
     glEnable(GL_LIGHTING);
     glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
 
-    _framebufferObject->release();
+    _overlays.release();
 }
 
-gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() {
-    if (!_standardDrawPipeline) {
-        auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)));
-        auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag)));
-        auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
-        gpu::Shader::makeProgram((*program));
-        
-        auto state = gpu::StatePointer(new gpu::State());
-
-        // enable decal blend
-        state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
-        
-        _standardDrawPipeline.reset(gpu::Pipeline::create(program, state));
+// A quick and dirty solution for compositing the old overlay 
+// texture with the new one
+template <typename F>
+void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) {
+    glEnable(GL_TEXTURE_2D);
+    glActiveTexture(GL_TEXTURE0);
+    if (firstPassTexture) {
+        glBindTexture(GL_TEXTURE_2D, firstPassTexture);
+        f();
     }
-
-    return _standardDrawPipeline;
-}
-
-void ApplicationOverlay::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) {
-    auto& cursorManager = Cursor::Manager::instance();
-    auto cursor = cursorManager.getCursor(cursorIndex);
-    auto iconId = cursor->getIcon();
-    if (!_cursors.count(iconId)) {
-        auto iconPath = cursorManager.getIconImage(cursor->getIcon());
-        _cursors[iconId] = DependencyManager::get<TextureCache>()->
-            getImageTexture(iconPath);
+    if (secondPassTexture) {
+        glBindTexture(GL_TEXTURE_2D, secondPassTexture);
+        f();
     }
-    batch.setUniformTexture(0, _cursors[iconId]);
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_TEXTURE_2D);
 }
 
-#define CURSOR_PIXEL_SIZE 32.0f
-
 // Draws the FBO texture for the screen
-void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) {
+void ApplicationOverlay::displayOverlayTexture() {
+    if (_alpha == 0.0f) {
+        return;
+    }
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix(); {
+        glLoadIdentity();
+        glDisable(GL_DEPTH_TEST);
+        glDisable(GL_LIGHTING);
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height());
+
+        static const glm::vec2 topLeft(-1, 1);
+        static const glm::vec2 bottomRight(1, -1);
+        static const glm::vec2 texCoordTopLeft(0.0f, 1.0f);
+        static const glm::vec2 texCoordBottomRight(1.0f, 0.0f);
+        with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
+            DependencyManager::get<GeometryCache>()->renderQuad(
+                topLeft, bottomRight, 
+                texCoordTopLeft, texCoordBottomRight,
+                glm::vec4(1.0f, 1.0f, 1.0f, _alpha));
+        });
+
+        if (!_crosshairTexture) {
+            _crosshairTexture = DependencyManager::get<TextureCache>()->
+                getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
+        }
+
+        //draw the mouse pointer
+        glm::vec2 canvasSize = qApp->getCanvasSize();
+        glm::vec2 mouseSize = 32.0f / canvasSize;
+        auto mouseTopLeft = topLeft * mouseSize;
+        auto mouseBottomRight = bottomRight * mouseSize;
+        vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY());
+        mousePosition /= canvasSize;
+        mousePosition *= 2.0f;
+        mousePosition -= 1.0f;
+        mousePosition.y *= -1.0f;
+
+        glEnable(GL_TEXTURE_2D);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
+        glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
+        DependencyManager::get<GeometryCache>()->renderQuad(
+            mouseTopLeft + mousePosition, mouseBottomRight + mousePosition, 
+            texCoordTopLeft, texCoordBottomRight,
+            reticleColor);
+        glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
+        glDisable(GL_TEXTURE_2D);
+    } glPopMatrix();
+}
+
+// Draws the FBO texture for Oculus rift.
+void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) {
+    if (_alpha == 0.0f) {
+        return;
+    }
+
+    glEnable(GL_BLEND);
+    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
+    glEnable(GL_DEPTH_TEST);
+    glDepthMask(GL_TRUE);
+    glDisable(GL_LIGHTING);
+    glEnable(GL_ALPHA_TEST);
+    glAlphaFunc(GL_GREATER, 0.01f);
+
+
+    //Update and draw the magnifiers
+    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
+    const glm::quat& orientation = myAvatar->getOrientation();
+    // Always display the HMD overlay relative to the camera position but 
+    // remove the HMD pose offset.  This results in an overlay that sticks with you 
+    // even in third person mode, but isn't drawn at a fixed distance.
+    glm::vec3 position = whichCamera.getPosition();
+    position -= qApp->getCamera()->getHmdPosition();
+    const float scale = myAvatar->getScale() * _oculusUIRadius;
+    
+//    glm::vec3 eyeOffset = setEyeOffsetPosition;
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix(); {
+        glTranslatef(position.x, position.y, position.z);
+        glm::mat4 rotation = glm::toMat4(orientation);
+        glMultMatrixf(&rotation[0][0]);
+        glScalef(scale, scale, scale);
+        for (int i = 0; i < NUMBER_OF_RETICLES; i++) {
+            
+            if (_magActive[i]) {
+                _magSizeMult[i] += MAG_SPEED;
+                if (_magSizeMult[i] > 1.0f) {
+                    _magSizeMult[i] = 1.0f;
+                }
+            } else {
+                _magSizeMult[i] -= MAG_SPEED;
+                if (_magSizeMult[i] < 0.0f) {
+                    _magSizeMult[i] = 0.0f;
+                }
+            }
+            
+            if (_magSizeMult[i] > 0.0f) {
+                //Render magnifier, but dont show border for mouse magnifier
+                glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(),
+                                                                 _reticlePosition[MOUSE].y()));
+                with_each_texture(_overlays.getTexture(), 0, [&] {
+                    renderMagnifier(projection, _magSizeMult[i], i != MOUSE);
+                });
+            }
+        }
+        
+        glDepthMask(GL_FALSE);
+        glDisable(GL_ALPHA_TEST);
+        
+        static float textureFOV = 0.0f, textureAspectRatio = 1.0f;
+        if (textureFOV != _textureFov ||
+            textureAspectRatio != _textureAspectRatio) {
+            textureFOV = _textureFov;
+            textureAspectRatio = _textureAspectRatio;
+            
+            _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80);
+        }
+
+        with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
+            _overlays.render();
+        });
+
+        if (!Application::getInstance()->isMouseHidden()) {
+            renderPointersOculus();
+        }
+        glDepthMask(GL_TRUE);
+        glDisable(GL_TEXTURE_2D);
+        
+        glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
+        glEnable(GL_LIGHTING);
+    } glPopMatrix();
+}
+
+// Draws the FBO texture for 3DTV.
+void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) {
     if (_alpha == 0.0f) {
         return;
     }
     
-    renderArgs->_context->syncCache();
-
-    gpu::Batch batch;
-    Transform model;
-    //DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);
-    batch.setPipeline(getDrawPipeline());
-    batch.setModelTransform(Transform());
-    batch.setProjectionTransform(mat4());
-    batch.setViewTransform(model);
-    batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
-    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    DependencyManager::get<GeometryCache>()->renderUnitQuad(batch, vec4(vec3(1), _alpha));
+    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
+    const glm::vec3& viewMatrixTranslation = qApp->getViewMatrixTranslation();
+    
+    glEnable(GL_BLEND);
+    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
+    glEnable(GL_DEPTH_TEST);
+    glDisable(GL_LIGHTING);
+    
+    glMatrixMode(GL_MODELVIEW);
+    
+    glPushMatrix();
+    glLoadIdentity();
+    // Transform to world space
+    glm::quat rotation = whichCamera.getRotation();
+    glm::vec3 axis2 = glm::axis(rotation);
+    glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z);
+    glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z);
+    
+    // Translate to the front of the camera
+    glm::vec3 pos = whichCamera.getPosition();
+    glm::quat rot = myAvatar->getOrientation();
+    glm::vec3 axis = glm::axis(rot);
+    
+    glTranslatef(pos.x, pos.y, pos.z);
+    glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z);
+    
+    glm::vec4 overlayColor = {1.0f, 1.0f, 1.0f, _alpha};
+    
+    //Render
+    const GLfloat distance = 1.0f;
+    
+    const GLfloat halfQuadHeight = distance * tan(fov);
+    const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio;
+    const GLfloat quadWidth = halfQuadWidth * 2.0f;
+    const GLfloat quadHeight = halfQuadHeight * 2.0f;
+    
+    GLfloat x = -halfQuadWidth;
+    GLfloat y = -halfQuadHeight;
+    glDisable(GL_DEPTH_TEST);
 
+    with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
+        DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance),
+                                                glm::vec3(x + quadWidth, y + quadHeight, -distance),
+                                                glm::vec3(x + quadWidth, y, -distance),
+                                                glm::vec3(x, y, -distance),
+                                                glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), 
+                                                glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f),
+                                                overlayColor);
+    });
+    
+    if (!_crosshairTexture) {
+        _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() +
+                                                          "images/sixense-reticle.png");
+    }
+    
     //draw the mouse pointer
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
     glm::vec2 canvasSize = qApp->getCanvasSize();
-
-    // Get the mouse coordinates and convert to NDC [-1, 1]
-    vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY());
-    mousePosition /= canvasSize;
-    mousePosition *= 2.0f;
-    mousePosition -= 1.0f;
-    mousePosition.y *= -1.0f;
-    model.setTranslation(vec3(mousePosition, 0));
-    glm::vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
-    model.setScale(vec3(mouseSize, 1.0f));
-    batch.setModelTransform(model);
-    bindCursorTexture(batch);
+    const float reticleSize = 40.0f / canvasSize.x * quadWidth;
+    x -= reticleSize / 2.0f;
+    y += reticleSize / 2.0f;
+    const float mouseX = (qApp->getMouseX() / (float)canvasSize.x) * quadWidth;
+    const float mouseY = (1.0 - (qApp->getMouseY() / (float)canvasSize.y)) * quadHeight;
+    
     glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
-    DependencyManager::get<GeometryCache>()->renderUnitQuad(batch, vec4(1));
-    renderArgs->_context->render(batch);
+
+    DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x + mouseX, y + mouseY, -distance), 
+                                                glm::vec3(x + mouseX + reticleSize, y + mouseY, -distance),
+                                                glm::vec3(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance),
+                                                glm::vec3(x + mouseX, y + mouseY - reticleSize, -distance),
+                                                glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), 
+                                                glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f),
+                                                reticleColor, _reticleQuad);
+
+    glEnable(GL_DEPTH_TEST);
+    
+    glPopMatrix();
+    
+    glDepthMask(GL_TRUE);
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_TEXTURE_2D);
+    
+    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
+    glEnable(GL_LIGHTING);
 }
 
-
-static gpu::BufferPointer _hemiVertices;
-static gpu::BufferPointer _hemiIndices;
-static int _hemiIndexCount{ 0 };
-
-glm::vec2 getPolarCoordinates(const PalmData& palm) {
-    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
-    auto avatarOrientation = myAvatar->getOrientation();
-    auto eyePos = myAvatar->getDefaultEyePosition();
-    glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm);
-    // Direction of the tip relative to the eye
-    glm::vec3 tipDirection = tip - eyePos;
-    // orient into avatar space
-    tipDirection = glm::inverse(avatarOrientation) * tipDirection;
-    // Normalize for trig functions
-    tipDirection = glm::normalize(tipDirection);
-    // Convert to polar coordinates
-    glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y));
-    return polar;
-}
-
-// Draws the FBO texture for Oculus rift.
-void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) {
-    if (_alpha == 0.0f) {
-        return;
-    }
-
-    renderArgs->_context->syncCache();
-
-    gpu::Batch batch;
-    batch.setPipeline(getDrawPipeline());
-    batch._glDisable(GL_DEPTH_TEST);
-    batch._glDisable(GL_CULL_FACE);
-    batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
-    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    batch.setProjectionTransform(whichCamera.getProjection());
-    batch.setViewTransform(Transform());
-
-    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
-    const quat& avatarOrientation = myAvatar->getOrientation();
-    quat hmdOrientation = qApp->getCamera()->getHmdRotation();
-    vec3 hmdPosition = glm::inverse(avatarOrientation) * qApp->getCamera()->getHmdPosition();
-    mat4 overlayXfm = glm::mat4_cast(glm::inverse(hmdOrientation)) * glm::translate(mat4(), -hmdPosition);
-    batch.setModelTransform(Transform(overlayXfm));
-    drawSphereSection(batch);
-
-
-    bindCursorTexture(batch);
-    auto geometryCache = DependencyManager::get<GeometryCache>();
-    vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize);
-    //Controller Pointers
-    for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
-        PalmData& palm = myAvatar->getHand()->getPalms()[i];
-        if (palm.isActive()) {
-            glm::vec2 polar = getPolarCoordinates(palm);
-            // Convert to quaternion
-            mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
-            mat4 reticleXfm = overlayXfm * pointerXfm;
-            reticleXfm = glm::scale(reticleXfm, reticleScale);
-            batch.setModelTransform(reticleXfm);
-            // Render reticle at location
-            geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
-        }
-    }
-
-    //Mouse Pointer
-    if (_reticleActive[MOUSE]) {
-        glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(),
-            _reticlePosition[MOUSE].y()));
-        mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
-        mat4 reticleXfm = overlayXfm * pointerXfm;
-        reticleXfm = glm::scale(reticleXfm, reticleScale);
-        batch.setModelTransform(reticleXfm);
-        geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
-    }
-
-    renderArgs->_context->render(batch);
-}
-
-
 void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
     cursorPos *= qApp->getCanvasSize();
     const glm::vec2 projection = screenToSpherical(cursorPos);
@@ -421,6 +516,22 @@ void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origi
     direction = glm::normalize(intersectionWithUi - origin);
 }
 
+glm::vec2 getPolarCoordinates(const PalmData& palm) {
+    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
+    auto avatarOrientation = myAvatar->getOrientation();
+    auto eyePos = myAvatar->getDefaultEyePosition();
+    glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm);
+    // Direction of the tip relative to the eye
+    glm::vec3 tipDirection = tip - eyePos;
+    // orient into avatar space
+    tipDirection = glm::inverse(avatarOrientation) * tipDirection;
+    // Normalize for trig functions
+    tipDirection = glm::normalize(tipDirection);
+    // Convert to polar coordinates
+    glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y));
+    return polar;
+}
+
 //Caculate the click location using one of the sixense controllers. Scale is not applied
 QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const {
     QPoint rv;
@@ -471,9 +582,13 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position,
 
 //Renders optional pointers
 void ApplicationOverlay::renderPointers() {
-    //glEnable(GL_TEXTURE_2D);
-    //glEnable(GL_BLEND);
-    //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    //lazily load crosshair texture
+    if (_crosshairTexture == 0) {
+        _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
+    }
+    glEnable(GL_TEXTURE_2D);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
     //glActiveTexture(GL_TEXTURE0);
     //bindCursorTexture();
@@ -635,6 +750,43 @@ void ApplicationOverlay::renderControllerPointers() {
     }
 }
 
+void ApplicationOverlay::renderPointersOculus() {
+
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glEnable(GL_TEXTURE_2D);
+
+    glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
+    glDisable(GL_DEPTH_TEST);
+
+    glMatrixMode(GL_MODELVIEW);
+    
+    //Controller Pointers
+    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
+    for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
+        PalmData& palm = myAvatar->getHand()->getPalms()[i];
+        if (palm.isActive()) {
+            glm::vec2 polar = getPolarCoordinates(palm);
+            // Convert to quaternion
+            glm::quat orientation = glm::quat(glm::vec3(polar.y, -polar.x, 0.0f));
+            // Render reticle at location
+            renderReticle(orientation, _alpha);
+        } 
+    }
+
+    //Mouse Pointer
+    if (_reticleActive[MOUSE]) {
+        glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(),
+                                                           _reticlePosition[MOUSE].y()));
+        glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f));
+        renderReticle(orientation, _alpha);
+    }
+
+    glEnable(GL_DEPTH_TEST);
+    glDisable(GL_TEXTURE_2D);
+    glDisable(GL_BLEND);
+}
+
 //Renders a small magnification of the currently bound texture at the coordinates
 void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) {
     if (!_magnifier) {
@@ -915,109 +1067,119 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() {
     }
 }
 
+ApplicationOverlay::TexturedHemisphere::TexturedHemisphere() :
+    _vertices(0),
+    _indices(0),
+    _framebufferObject(NULL),
+    _vbo(0, 0) {
+}
 
-void ApplicationOverlay::buildHemiVertices(
-    const float fov, const float aspectRatio, const int slices, const int stacks) {
-    static float textureFOV = 0.0f, textureAspectRatio = 1.0f;
-    if (textureFOV == fov && textureAspectRatio == aspectRatio) {
-        return;
+ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() {
+    cleanupVBO();
+    if (_framebufferObject != NULL) {
+        delete _framebufferObject;
     }
+}
 
-    textureFOV = fov;
-    textureAspectRatio = aspectRatio;
-
-    auto geometryCache = DependencyManager::get<GeometryCache>();
-
-    _hemiVertices = gpu::BufferPointer(new gpu::Buffer());
-    _hemiIndices = gpu::BufferPointer(new gpu::Buffer());
+void ApplicationOverlay::TexturedHemisphere::bind() {
+    _framebufferObject->bind();
+}
 
+void ApplicationOverlay::TexturedHemisphere::release() {
+    _framebufferObject->release();
+}
 
+void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov,
+                                                      const float aspectRatio,
+                                                      const int slices,
+                                                      const int stacks) {
     if (fov >= PI) {
         qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues";
     }
-
+    // Cleanup old VBO if necessary
+    cleanupVBO();
+    
     //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
     
-    vec3 pos; 
-    vec2 uv;
+    // Compute number of vertices needed
+    _vertices = slices * stacks;
+    
     // Compute vertices positions and texture UV coordinate
-    // Create and write to buffer
+    TextureVertex* vertexData = new TextureVertex[_vertices];
+    TextureVertex* vertexPtr = &vertexData[0];
     for (int i = 0; i < stacks; i++) {
-        uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
+        float stacksRatio = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
         // abs(theta) <= fov / 2.0f
-        float pitch = -fov * (uv.y - 0.5f);
+        float pitch = -fov * (stacksRatio - 0.5f);
+        
         for (int j = 0; j < slices; j++) {
-            uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
+            float slicesRatio = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
             // abs(phi) <= fov * aspectRatio / 2.0f
-            float yaw = -fov * aspectRatio * (uv.x - 0.5f);
-            pos = getPoint(yaw, pitch);
-            static const vec4 color(1);
-            _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos);
-            _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv);
-            _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color);
+            float yaw = -fov * aspectRatio * (slicesRatio - 0.5f);
+            
+            vertexPtr->position = getPoint(yaw, pitch);
+            vertexPtr->uv.x = slicesRatio;
+            vertexPtr->uv.y = stacksRatio;
+            vertexPtr++;
         }
     }
+    // Create and write to buffer
+    glGenBuffers(1, &_vbo.first);
+    glBindBuffer(GL_ARRAY_BUFFER, _vbo.first);
+    static const int BYTES_PER_VERTEX = sizeof(TextureVertex);
+    glBufferData(GL_ARRAY_BUFFER, _vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW);
+    delete[] vertexData;
+    
     
     // Compute number of indices needed
     static const int VERTEX_PER_TRANGLE = 3;
     static const int TRIANGLE_PER_RECTANGLE = 2;
     int numberOfRectangles = (slices - 1) * (stacks - 1);
-    _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
+    _indices = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
     
     // Compute indices order
-    std::vector<GLushort> indices;
+    GLushort* indexData = new GLushort[_indices];
+    GLushort* indexPtr = indexData;
     for (int i = 0; i < stacks - 1; i++) {
         for (int j = 0; j < slices - 1; j++) {
             GLushort bottomLeftIndex = i * slices + j;
             GLushort bottomRightIndex = bottomLeftIndex + 1;
             GLushort topLeftIndex = bottomLeftIndex + slices;
             GLushort topRightIndex = topLeftIndex + 1;
-            // FIXME make a z-order curve for better vertex cache locality
-            indices.push_back(topLeftIndex);
-            indices.push_back(bottomLeftIndex);
-            indices.push_back(topRightIndex);
-
-            indices.push_back(topRightIndex);
-            indices.push_back(bottomLeftIndex);
-            indices.push_back(bottomRightIndex);
+            
+            *(indexPtr++) = topLeftIndex;
+            *(indexPtr++) = bottomLeftIndex;
+            *(indexPtr++) = topRightIndex;
+            
+            *(indexPtr++) = topRightIndex;
+            *(indexPtr++) = bottomLeftIndex;
+            *(indexPtr++) = bottomRightIndex;
         }
     }
-    _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]);
+    // Create and write to buffer
+    glGenBuffers(1, &_vbo.second);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second);
+    static const int BYTES_PER_INDEX = sizeof(GLushort);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW);
+    delete[] indexData;
 }
 
-
-void ApplicationOverlay::drawSphereSection(gpu::Batch& batch) {
-    buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80);
-    static const int VERTEX_DATA_SLOT = 0;
-    static const int TEXTURE_DATA_SLOT = 1;
-    static const int COLOR_DATA_SLOT = 2;
-    gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone
-    streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
-    streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
-    streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA));
-    batch.setInputFormat(streamFormat);
-
-    static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4);
-    gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element);
-    gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element);
-    gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
-    batch.setInputBuffer(VERTEX_DATA_SLOT, posView);
-    batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView);
-    batch.setInputBuffer(COLOR_DATA_SLOT, colView);
-    batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0);
-    batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount);
+void ApplicationOverlay::TexturedHemisphere::cleanupVBO() {
+    if (_vbo.first != 0) {
+        glDeleteBuffers(1, &_vbo.first);
+        _vbo.first = 0;
+    }
+    if (_vbo.second != 0) {
+        glDeleteBuffers(1, &_vbo.second);
+        _vbo.second = 0;
+    }
 }
 
-
-GLuint ApplicationOverlay::getOverlayTexture() {
-    return _framebufferObject->texture();
-}
-
-void ApplicationOverlay::buildFramebufferObject() {
+void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() {
     auto canvasSize = qApp->getCanvasSize();
     QSize fboSize = QSize(canvasSize.x, canvasSize.y);
     if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) {
-        // Already built
+        // Already build
         return;
     }
     
@@ -1026,7 +1188,7 @@ void ApplicationOverlay::buildFramebufferObject() {
     }
     
     _framebufferObject = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth);
-    glBindTexture(GL_TEXTURE_2D, getOverlayTexture());
+    glBindTexture(GL_TEXTURE_2D, getTexture());
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
@@ -1036,6 +1198,38 @@ void ApplicationOverlay::buildFramebufferObject() {
     glBindTexture(GL_TEXTURE_2D, 0);
 }
 
+//Renders a hemisphere with texture coordinates.
+void ApplicationOverlay::TexturedHemisphere::render() {
+    if (_framebufferObject == NULL || _vbo.first == 0 || _vbo.second == 0) {
+        qDebug() << "TexturedHemisphere::render(): Incorrect initialisation";
+        return;
+    }
+    
+    glBindBuffer(GL_ARRAY_BUFFER, _vbo.first);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second);
+    
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+    
+    static const int STRIDE = sizeof(TextureVertex);
+    static const void* VERTEX_POINTER = 0;
+    static const void* TEX_COORD_POINTER = (void*)sizeof(glm::vec3);
+    glVertexPointer(3, GL_FLOAT, STRIDE, VERTEX_POINTER);
+    glTexCoordPointer(2, GL_FLOAT, STRIDE, TEX_COORD_POINTER);
+    
+    glDrawRangeElements(GL_TRIANGLES, 0, _vertices - 1, _indices, GL_UNSIGNED_SHORT, 0);
+    
+    glDisableClientState(GL_VERTEX_ARRAY);
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+GLuint ApplicationOverlay::TexturedHemisphere::getTexture() {
+    return _framebufferObject->texture();
+}
+
 glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) {
     glm::vec2 result;
     // Compute yaw
diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h
index e8b5a77b1f..eb397fe3c6 100644
--- a/interface/src/ui/ApplicationOverlay.h
+++ b/interface/src/ui/ApplicationOverlay.h
@@ -33,9 +33,9 @@ public:
     ~ApplicationOverlay();
 
     void renderOverlay(RenderArgs* renderArgs);
-    void displayOverlayTexture(RenderArgs* renderArgs);
-    void displayOverlayTextureStereo(RenderArgs* renderArgs, Camera& whichCamera, float aspectRatio, float fov);
-    void displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera);
+    void displayOverlayTexture();
+    void displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov);
+    void displayOverlayTextureHmd(Camera& whichCamera);
 
     QPoint getPalmClickLocation(const PalmData *palm) const;
     bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
@@ -59,7 +59,6 @@ public:
     glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const;
     glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const;
     void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const;
-    GLuint getOverlayTexture();
 
     static glm::vec2 directionToSpherical(const glm::vec3 & direction);
     static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos);
@@ -67,12 +66,38 @@ public:
     static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos);
     
 private:
-    void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks);
-    void drawSphereSection(gpu::Batch& batch);
-    float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE;
-    QOpenGLFramebufferObject* _framebufferObject;
+    // Interleaved vertex data
+    struct TextureVertex {
+        glm::vec3 position;
+        glm::vec2 uv;
+    };
 
-    void renderPointers();
+    typedef QPair<GLuint, GLuint> VerticesIndices;
+    class TexturedHemisphere {
+    public:
+        TexturedHemisphere();
+        ~TexturedHemisphere();
+        
+        void bind();
+        void release();
+        GLuint getTexture();
+        
+        void buildFramebufferObject();
+        void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks);
+        void render();
+        
+    private:
+        void cleanupVBO();
+        
+        GLuint _vertices;
+        GLuint _indices;
+        QOpenGLFramebufferObject* _framebufferObject;
+        VerticesIndices _vbo;
+    };
+    
+    float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE;
+    void renderReticle(glm::quat orientation, float alpha);
+    void renderPointers();;
     void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder);
     
     void renderControllerPointers();
@@ -84,12 +109,10 @@ private:
     void renderDomainConnectionStatusBorder();
     void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
 
-    void buildFramebufferObject();
+    TexturedHemisphere _overlays;
     
     float _textureFov;
     float _textureAspectRatio;
-    int _hemiVerticesID{ GeometryCache::UNKNOWN_ID };
-
     
     enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES };
     bool _reticleActive[NUMBER_OF_RETICLES];
@@ -102,6 +125,8 @@ private:
     float _alpha = 1.0f;
     float _oculusUIRadius;
     float _trailingAudioLoudness;
+    
+     gpu::TexturePointer _crosshairTexture;
 
 
     QMap<uint16_t, gpu::TexturePointer> _cursors;
@@ -124,10 +149,6 @@ private:
     glm::vec3 _previousMagnifierTopLeft;
     glm::vec3 _previousMagnifierTopRight;
 
-    gpu::PipelinePointer _standardDrawPipeline;
-
-    gpu::PipelinePointer getDrawPipeline();
-
 };
 
 #endif // hifi_ApplicationOverlay_h
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index 3066fd4890..303d63bef8 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -1179,21 +1179,6 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co
     batch.draw(gpu::QUADS, 4, 0);
 }
 
-void GeometryCache::renderUnitQuad(const glm::vec4& color, int id) {
-    gpu::Batch batch;
-    renderUnitQuad(batch, color, id);
-    gpu::GLBackend::renderBatch(batch);
-}
-
-void GeometryCache::renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id) {
-    static const glm::vec2 topLeft(-1, 1);
-    static const glm::vec2 bottomRight(1, -1);
-    static const glm::vec2 texCoordTopLeft(0.0f, 1.0f);
-    static const glm::vec2 texCoordBottomRight(1.0f, 0.0f);
-    renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color, id);
-}
-
-
 void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner,
                     const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, 
                     const glm::vec4& color, int id) {
diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h
index 76e03f8669..b438eb2d3b 100644
--- a/libraries/render-utils/src/GeometryCache.h
+++ b/libraries/render-utils/src/GeometryCache.h
@@ -155,9 +155,6 @@ public:
     void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID);
     void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID);
 
-    void renderUnitQuad(const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID);
-    void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID);
-
     void renderQuad(int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID)
             { renderQuad(glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); }
     void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID)
diff --git a/libraries/render-utils/src/standardDrawTexture.slf b/libraries/render-utils/src/standardDrawTexture.slf
deleted file mode 100644
index 4fbeb6eb7f..0000000000
--- a/libraries/render-utils/src/standardDrawTexture.slf
+++ /dev/null
@@ -1,24 +0,0 @@
-<@include gpu/Config.slh@>
-<$VERSION_HEADER$>
-//  Generated on <$_SCRIBE_DATE$>
-//  standardDrawTexture.frag
-//  fragment shader
-//
-//  Created by Sam Gateau on 6/10/15.
-//  Copyright 2015 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-// the texture
-uniform sampler2D colorMap;
-
-varying vec2 varTexcoord;
-varying vec4 varColor;
-
-
-void main(void) {
-    vec4 color = texture2D(colorMap, varTexcoord);
-    gl_FragColor = color * varColor;
-}
diff --git a/libraries/render-utils/src/standardTransformPNTC.slv b/libraries/render-utils/src/standardTransformPNTC.slv
deleted file mode 100644
index fd2c28049f..0000000000
--- a/libraries/render-utils/src/standardTransformPNTC.slv
+++ /dev/null
@@ -1,33 +0,0 @@
-<@include gpu/Config.slh@>
-<$VERSION_HEADER$>
-//  Generated on <$_SCRIBE_DATE$>
-//
-//  standardTransformPNTC.slv
-//  vertex shader
-//
-//  Created by Sam Gateau on 6/10/2015.
-//  Copyright 2015 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-<@include gpu/Transform.slh@>
-
-<$declareStandardTransform()$>
-
-varying vec3 varNormal;
-varying vec2 varTexcoord;
-varying vec4 varColor;
-
-void main(void) {
-    varTexcoord = gl_MultiTexCoord0.xy;
-    varColor = gl_Color;
-    
-    // standard transform
-    TransformCamera cam = getTransformCamera();
-    TransformObject obj = getTransformObject();
-    <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$>
-    <$transformModelToEyeDir(cam, obj, gl_Normal, varNormal)$>
-    varNormal = normalize(varNormal);
-}
\ No newline at end of file

From 1feceec0c7b5b3c6d2225c26ddbb56a605bd99a4 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Fri, 12 Jun 2015 12:57:37 -0700
Subject: [PATCH 84/88] Revert "Fix broken identity transforms on OSX"

This reverts commit 926283956412d1e35fc1ba319f2c513803da2c77.
---
 libraries/gpu/src/gpu/GLBackendTransform.cpp | 25 +++++++++++++-------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp
index 4e524ec24a..3f760e4cc8 100755
--- a/libraries/gpu/src/gpu/GLBackendTransform.cpp
+++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp
@@ -133,11 +133,11 @@ void GLBackend::updateTransform() {
     }
 
     if (_transform._invalidModel || _transform._invalidView) {
-        if (_transform._lastMode != GL_MODELVIEW) {
-            glMatrixMode(GL_MODELVIEW);
-            _transform._lastMode = GL_MODELVIEW;
-        }
         if (!_transform._model.isIdentity()) {
+            if (_transform._lastMode != GL_MODELVIEW) {
+                glMatrixMode(GL_MODELVIEW);
+                _transform._lastMode = GL_MODELVIEW;
+            }
             Transform::Mat4 modelView;
             if (!_transform._view.isIdentity()) {
                 Transform mvx;
@@ -147,12 +147,19 @@ void GLBackend::updateTransform() {
                 _transform._model.getMatrix(modelView);
             }
             glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView));
-        } else if (!_transform._view.isIdentity()) {
-            Transform::Mat4 modelView;
-            _transform._view.getInverseMatrix(modelView);
-            glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView));
         } else {
-            glLoadIdentity();
+            if (!_transform._view.isIdentity()) {
+                if (_transform._lastMode != GL_MODELVIEW) {
+                    glMatrixMode(GL_MODELVIEW);
+                    _transform._lastMode = GL_MODELVIEW;
+                }
+                Transform::Mat4 modelView;
+                _transform._view.getInverseMatrix(modelView);
+                glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView));
+            } else {
+                // TODO: eventually do something about the matrix when neither view nor model is specified?
+                // glLoadIdentity();
+            }
         }
         (void) CHECK_GL_ERROR();
     }

From ad773747325e4d80996c6279333211445a6d5f13 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Fri, 12 Jun 2015 13:03:33 -0700
Subject: [PATCH 85/88] Fix setting of _renderMode

---
 interface/src/Application.cpp                     | 8 +++-----
 libraries/render-utils/src/RenderDeferredTask.cpp | 3 ---
 libraries/render/src/render/DrawTask.cpp          | 4 ----
 3 files changed, 3 insertions(+), 12 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 56fe4188e9..0757e6790f 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -958,12 +958,15 @@ void Application::paintGL() {
         _applicationOverlay.displayOverlayTexture(&renderArgs);
         glPopMatrix();
 
+        renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
         if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
             _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos()));
         } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
             renderRearViewMirror(&renderArgs, _mirrorViewRect);       
         }
 
+        renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE;
+
         auto finalFbo = DependencyManager::get<GlowEffect>()->render(&renderArgs);
 
         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
@@ -3439,7 +3442,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
                 "Application::displaySide() ... entities...");
 
             RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE;
-            RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE;
 
             if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
                 renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS);
@@ -3448,10 +3450,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
                 renderDebugFlags =
                     (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
             }
-            if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
-                renderMode = RenderArgs::MIRROR_RENDER_MODE;
-            }
-            renderArgs->_renderMode = renderMode;
             renderArgs->_debugFlags = renderDebugFlags;
             _entities.render(renderArgs);
         }
diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp
index 3fd7d05666..96a197335a 100755
--- a/libraries/render-utils/src/RenderDeferredTask.cpp
+++ b/libraries/render-utils/src/RenderDeferredTask.cpp
@@ -139,7 +139,6 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
-        renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE;
         {
             GLenum buffers[3];
             int bufferCount = 0;
@@ -207,8 +206,6 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
-        args->_renderMode = RenderArgs::NORMAL_RENDER_MODE;
-
         const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f;
 
         {
diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp
index 53964ac1db..96be8d63b0 100755
--- a/libraries/render/src/render/DrawTask.cpp
+++ b/libraries/render/src/render/DrawTask.cpp
@@ -263,7 +263,6 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer
 
     renderContext->_numDrawnOpaqueItems = renderedItems.size();
 
-
     ItemIDsBounds sortedItems;
     sortedItems.reserve(culledItems.size());
     if (renderContext->_sortOpaque) {
@@ -283,7 +282,6 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
-        renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE;
         {
             GLenum buffers[3];
             int bufferCount = 0;
@@ -350,8 +348,6 @@ template <> void render::jobRun(const DrawTransparent& job, const SceneContextPo
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
-        args->_renderMode = RenderArgs::NORMAL_RENDER_MODE;
-
         const float MOSTLY_OPAQUE_THRESHOLD = 0.75f;
         const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f;
 

From 66a65e367511259c699ca6ea382040fccde5c7d1 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Fri, 12 Jun 2015 13:04:02 -0700
Subject: [PATCH 86/88] Update model to select clockwise backface culling
 program when in mirror mode

---
 libraries/render-utils/src/Model.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 8d234cdef5..636bb4bcbe 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -2047,6 +2047,9 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f
                             Locations*& locations) {
 
     RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe);
+    if (mode == RenderArgs::MIRROR_RENDER_MODE) {
+        key = RenderKey(key.getRaw() | RenderKey::IS_MIRROR);
+    }
     auto pipeline = _renderPipelineLib.find(key.getRaw());
     if (pipeline == _renderPipelineLib.end()) {
         qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw();

From 81d003bdb8e34ca5709bb7e326f7169de55c1e8a Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Fri, 12 Jun 2015 13:05:10 -0700
Subject: [PATCH 87/88] Add post-scale to view matrix when in mirror mode

---
 libraries/render-utils/src/RenderDeferredTask.cpp |  6 ++++++
 libraries/render/src/render/DrawTask.cpp          | 12 ++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp
index 96a197335a..d9dda279e0 100755
--- a/libraries/render-utils/src/RenderDeferredTask.cpp
+++ b/libraries/render-utils/src/RenderDeferredTask.cpp
@@ -136,6 +136,9 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex
         Transform viewMat;
         args->_viewFrustum->evalProjectionMatrix(projMat);
         args->_viewFrustum->evalViewTransform(viewMat);
+        if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+            viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+        }
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
@@ -203,6 +206,9 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC
         Transform viewMat;
         args->_viewFrustum->evalProjectionMatrix(projMat);
         args->_viewFrustum->evalViewTransform(viewMat);
+        if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+            viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+        }
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp
index 96be8d63b0..bb4ba00cee 100755
--- a/libraries/render/src/render/DrawTask.cpp
+++ b/libraries/render/src/render/DrawTask.cpp
@@ -279,6 +279,9 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer
         Transform viewMat;
         args->_viewFrustum->evalProjectionMatrix(projMat);
         args->_viewFrustum->evalViewTransform(viewMat);
+        if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+            viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+        }
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
@@ -345,6 +348,9 @@ template <> void render::jobRun(const DrawTransparent& job, const SceneContextPo
         Transform viewMat;
         args->_viewFrustum->evalProjectionMatrix(projMat);
         args->_viewFrustum->evalViewTransform(viewMat);
+        if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+            viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+        }
         batch.setProjectionTransform(projMat);
         batch.setViewTransform(viewMat);
 
@@ -430,6 +436,9 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi
     Transform viewMat;
     args->_viewFrustum->evalProjectionMatrix(projMat);
     args->_viewFrustum->evalViewTransform(viewMat);
+    if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+        viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+    }
     batch.setProjectionTransform(projMat);
     batch.setViewTransform(viewMat);
 
@@ -471,6 +480,9 @@ template <> void render::jobRun(const DrawPostLayered& job, const SceneContextPo
     Transform viewMat;
     args->_viewFrustum->evalProjectionMatrix(projMat);
     args->_viewFrustum->evalViewTransform(viewMat);
+    if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
+        viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
+    }
     batch.setProjectionTransform(projMat);
     batch.setViewTransform(viewMat);
 

From 648f9bebdf8676ea31c5aa2d00ead97958c0eca3 Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Fri, 12 Jun 2015 15:58:26 -0700
Subject: [PATCH 88/88] add LOD stats to the default stats

---
 interface/src/ui/Stats.cpp | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp
index 8d098b4dc8..8359480b03 100644
--- a/interface/src/ui/Stats.cpp
+++ b/interface/src/ui/Stats.cpp
@@ -464,7 +464,7 @@ void Stats::display(
     verticalOffset = STATS_PELS_INITIALOFFSET;
     horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3;
 
-    lines = _expanded ? 10 : 2;
+    lines = _expanded ? 10 : 3;
 
     drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset,
         (lines + 1) * STATS_PELS_PER_LINE);
@@ -612,12 +612,10 @@ void Stats::display(
     }
 
     // LOD Details
-    if (_expanded) {
-        octreeStats.str("");
-        QString displayLODDetails = DependencyManager::get<LODManager>()->getLODFeedbackText();
-        octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed());
-        verticalOffset += STATS_PELS_PER_LINE;
-        drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
-    }
+    octreeStats.str("");
+    QString displayLODDetails = DependencyManager::get<LODManager>()->getLODFeedbackText();
+    octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed());
+    verticalOffset += STATS_PELS_PER_LINE;
+    drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
 }