diff --git a/CMakeLists.txt b/CMakeLists.txt
index 93b784b462..69ea0b7fd8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -180,6 +180,7 @@ add_subdirectory(tools)
 
 if (BUILD_TESTS)
   add_subdirectory(tests)
+  add_subdirectory(tests-manual)
 endif()
 
 if (BUILD_INSTALLER)
diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp
index 1df901dd98..42924a8487 100644
--- a/assignment-client/src/Agent.cpp
+++ b/assignment-client/src/Agent.cpp
@@ -548,16 +548,21 @@ void Agent::setIsAvatar(bool isAvatar) {
     if (_isAvatar && !_avatarIdentityTimer) {
         // set up the avatar timers
         _avatarIdentityTimer = new QTimer(this);
+        _avatarQueryTimer = new QTimer(this);
 
         // connect our slot
         connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket);
+        connect(_avatarQueryTimer, &QTimer::timeout, this, &Agent::queryAvatars);
+
+        static const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
+        static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000;
 
         // start the timers
         _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);  // FIXME - we shouldn't really need to constantly send identity packets
+        _avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS);
 
         // tell the avatarAudioTimer to start ticking
         QMetaObject::invokeMethod(&_avatarAudioTimer, "start");
-
     }
 
     if (!_isAvatar) {
@@ -567,6 +572,10 @@ void Agent::setIsAvatar(bool isAvatar) {
             delete _avatarIdentityTimer;
             _avatarIdentityTimer = nullptr;
 
+            _avatarQueryTimer->stop();
+            delete _avatarQueryTimer;
+            _avatarQueryTimer = nullptr;
+
             // The avatar mixer never times out a connection (e.g., based on identity or data packets)
             // but rather keeps avatars in its list as long as "connected". As a result, clients timeout
             // when we stop sending identity, but then get woken up again by the mixer itself, which sends
@@ -585,6 +594,7 @@ void Agent::setIsAvatar(bool isAvatar) {
                 nodeList->sendPacket(std::move(packet), *node);
             });
         }
+
         QMetaObject::invokeMethod(&_avatarAudioTimer, "stop");
     }
 }
@@ -597,6 +607,31 @@ void Agent::sendAvatarIdentityPacket() {
     }
 }
 
+void Agent::queryAvatars() {
+    auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
+
+    ViewFrustum view;
+    view.setPosition(scriptedAvatar->getWorldPosition());
+    view.setOrientation(scriptedAvatar->getHeadOrientation());
+    view.calculate();
+    ConicalViewFrustum conicalView { view };
+
+    auto avatarPacket = NLPacket::create(PacketType::AvatarQuery);
+    auto destinationBuffer = reinterpret_cast<unsigned char*>(avatarPacket->getPayload());
+    auto bufferStart = destinationBuffer;
+
+    uint8_t numFrustums = 1;
+    memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums));
+    destinationBuffer += sizeof(numFrustums);
+
+    destinationBuffer += conicalView.serialize(destinationBuffer);
+
+    avatarPacket->setPayloadSize(destinationBuffer - bufferStart);
+
+    DependencyManager::get<NodeList>()->broadcastToNodes(std::move(avatarPacket),
+                                                         { NodeType::AvatarMixer });
+}
+
 void Agent::processAgentAvatar() {
     if (!_scriptEngine->isFinished() && _isAvatar) {
         auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h
index 1229f06276..0cdc9e0029 100644
--- a/assignment-client/src/Agent.h
+++ b/assignment-client/src/Agent.h
@@ -97,6 +97,7 @@ private:
     void setAvatarSound(SharedSoundPointer avatarSound) { _avatarSound = avatarSound; }
 
     void sendAvatarIdentityPacket();
+    void queryAvatars();
 
     QString _scriptContents;
     QTimer* _scriptRequestTimeout { nullptr };
@@ -106,6 +107,7 @@ private:
     int _numAvatarSoundSentBytes = 0;
     bool _isAvatar = false;
     QTimer* _avatarIdentityTimer = nullptr;
+    QTimer* _avatarQueryTimer = nullptr;
     QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
 
     AudioGate _audioGate;
diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp
index efced972a0..41e42aa0a1 100644
--- a/assignment-client/src/AssignmentClient.cpp
+++ b/assignment-client/src/AssignmentClient.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AssignmentClient.h"
+
 #include <assert.h>
 
 #include <QProcess>
@@ -32,16 +34,14 @@
 #include <SoundCache.h>
 #include <ResourceScriptingInterface.h>
 #include <UserActivityLoggerScriptingInterface.h>
-
-#include "AssignmentFactory.h"
-#include "AssignmentDynamicFactory.h"
-
-#include "AssignmentClient.h"
-#include "AssignmentClientLogging.h"
-#include "avatars/ScriptableAvatar.h"
 #include <Trace.h>
 #include <StatTracker.h>
 
+#include "AssignmentClientLogging.h"
+#include "AssignmentDynamicFactory.h"
+#include "AssignmentFactory.h"
+#include "avatars/ScriptableAvatar.h"
+
 const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client";
 const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
 
diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp
index 1868ccfafe..2847d4ebf1 100644
--- a/assignment-client/src/AssignmentClientMonitor.cpp
+++ b/assignment-client/src/AssignmentClientMonitor.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AssignmentClientMonitor.h"
+
 #include <memory>
 #include <signal.h>
 
@@ -19,7 +21,6 @@
 #include <LogHandler.h>
 #include <udt/PacketHeaders.h>
 
-#include "AssignmentClientMonitor.h"
 #include "AssignmentClientApp.h"
 #include "AssignmentClientChildData.h"
 #include "SharedUtil.h"
diff --git a/assignment-client/src/AssignmentDynamic.cpp b/assignment-client/src/AssignmentDynamic.cpp
index 7adbd55c39..447097ac74 100644
--- a/assignment-client/src/AssignmentDynamic.cpp
+++ b/assignment-client/src/AssignmentDynamic.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "EntitySimulation.h"
-
 #include "AssignmentDynamic.h"
 
+#include "EntitySimulation.h"
+
 AssignmentDynamic::AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
     EntityDynamicInterface(type, id),
     _data(QByteArray()),
diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp
index 38eb72649f..405039d833 100644
--- a/assignment-client/src/AssignmentFactory.cpp
+++ b/assignment-client/src/AssignmentFactory.cpp
@@ -9,11 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AssignmentFactory.h"
+
 #include <udt/PacketHeaders.h>
 
 #include "Agent.h"
 #include "assets/AssetServer.h"
-#include "AssignmentFactory.h"
 #include "audio/AudioMixer.h"
 #include "avatars/AvatarMixer.h"
 #include "entities/EntityServer.h"
diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h
index f83545c25c..96a220d64d 100644
--- a/assignment-client/src/assets/AssetServer.h
+++ b/assignment-client/src/assets/AssetServer.h
@@ -36,10 +36,11 @@ enum class BakedAssetType : int {
     Undefined
 };
 
-// ATTENTION! If you change the current version for an asset type, you will also
-// need to update the function currentBakeVersionForAssetType() inside of AssetServer.cpp.
+// ATTENTION! Do not remove baking versions, and do not reorder them. If you add
+// a new value, it will immediately become the "current" version.
 enum class ModelBakeVersion : BakeVersion {
     Initial = INITIAL_BAKE_VERSION,
+    MetaTextureJson,
 
     COUNT
 };
@@ -47,6 +48,7 @@ enum class ModelBakeVersion : BakeVersion {
 // ATTENTION! See above.
 enum class TextureBakeVersion : BakeVersion {
     Initial = INITIAL_BAKE_VERSION,
+    MetaTextureJson,
 
     COUNT
 };
diff --git a/assignment-client/src/audio/AudioMixerSlavePool.cpp b/assignment-client/src/audio/AudioMixerSlavePool.cpp
index e28c96e259..dfe7ef56aa 100644
--- a/assignment-client/src/audio/AudioMixerSlavePool.cpp
+++ b/assignment-client/src/audio/AudioMixerSlavePool.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AudioMixerSlavePool.h"
+
 #include <assert.h>
 #include <algorithm>
 
-#include "AudioMixerSlavePool.h"
-
 void AudioMixerSlaveThread::run() {
     while (true) {
         wait();
diff --git a/assignment-client/src/audio/AvatarAudioStream.cpp b/assignment-client/src/audio/AvatarAudioStream.cpp
index 42495b4dd0..22ea8c0617 100644
--- a/assignment-client/src/audio/AvatarAudioStream.cpp
+++ b/assignment-client/src/audio/AvatarAudioStream.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AvatarAudioStream.h"
+
 #include <udt/PacketHeaders.h>
 
 #include "AudioLogging.h"
-#include "AvatarAudioStream.h"
 
 AvatarAudioStream::AvatarAudioStream(bool isStereo, int numStaticJitterFrames) :
     PositionalAudioStream(PositionalAudioStream::Microphone, isStereo, numStaticJitterFrames) {}
diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp
index 29340f6474..9b5c4d4f30 100644
--- a/assignment-client/src/avatars/AvatarMixer.cpp
+++ b/assignment-client/src/avatars/AvatarMixer.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AvatarMixer.h"
+
 #include <cfloat>
 #include <chrono>
 #include <memory>
@@ -31,8 +33,6 @@
 #include <UUID.h>
 #include <TryLocker.h>
 
-#include "AvatarMixer.h"
-
 const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
 
 // FIXME - what we'd actually like to do is send to users at ~50% of their present rate down to 30hz. Assume 90 for now.
@@ -47,7 +47,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
     auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
     packetReceiver.registerListener(PacketType::AvatarData, this, "queueIncomingPacket");
     packetReceiver.registerListener(PacketType::AdjustAvatarSorting, this, "handleAdjustAvatarSorting");
-    packetReceiver.registerListener(PacketType::ViewFrustum, this, "handleViewFrustumPacket");
+    packetReceiver.registerListener(PacketType::AvatarQuery, this, "handleAvatarQueryPacket");
     packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
     packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
     packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
@@ -517,15 +517,13 @@ void AvatarMixer::handleAdjustAvatarSorting(QSharedPointer<ReceivedMessage> mess
 }
 
 
-void AvatarMixer::handleViewFrustumPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
+void AvatarMixer::handleAvatarQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
     auto start = usecTimestampNow();
     getOrCreateClientData(senderNode);
 
-    if (senderNode->getLinkedData()) {
-        AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
-        if (nodeData != nullptr) {
-            nodeData->readViewFrustumPacket(message->getMessage());
-        }
+    AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
+    if (nodeData) {
+        nodeData->readViewFrustumPacket(message->getMessage());
     }
 
     auto end = usecTimestampNow();
@@ -685,7 +683,7 @@ void AvatarMixer::sendStatsPacket() {
     incomingPacketStats["handleNodeIgnoreRequestPacket"] = TIGHT_LOOP_STAT_UINT64(_handleNodeIgnoreRequestPacketElapsedTime);
     incomingPacketStats["handleRadiusIgnoreRequestPacket"] = TIGHT_LOOP_STAT_UINT64(_handleRadiusIgnoreRequestPacketElapsedTime);
     incomingPacketStats["handleRequestsDomainListDataPacket"] = TIGHT_LOOP_STAT_UINT64(_handleRequestsDomainListDataPacketElapsedTime);
-    incomingPacketStats["handleViewFrustumPacket"] = TIGHT_LOOP_STAT_UINT64(_handleViewFrustumPacketElapsedTime);
+    incomingPacketStats["handleAvatarQueryPacket"] = TIGHT_LOOP_STAT_UINT64(_handleViewFrustumPacketElapsedTime);
 
     singleCoreTasks["incoming_packets"] = incomingPacketStats;
     singleCoreTasks["sendStats"] = (float)_sendStatsElapsedTime;
diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h
index 1fbfd7338b..9ef5903eec 100644
--- a/assignment-client/src/avatars/AvatarMixer.h
+++ b/assignment-client/src/avatars/AvatarMixer.h
@@ -46,7 +46,7 @@ public slots:
 private slots:
     void queueIncomingPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);
     void handleAdjustAvatarSorting(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
-    void handleViewFrustumPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
+    void handleAvatarQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
     void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
     void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
     void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp
index 268aba62d6..e185fe9167 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.cpp
+++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp
@@ -9,18 +9,16 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AvatarMixerClientData.h"
+
 #include <udt/PacketHeaders.h>
 
 #include <DependencyManager.h>
 #include <NodeList.h>
 
-#include "AvatarMixerClientData.h"
-
 AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID) :
     NodeData(nodeID)
 {
-    _currentViewFrustum.invalidate();
-
     // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID
     _avatar->setID(nodeID);
 }
@@ -129,11 +127,27 @@ void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self,
 }
 
 void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
-    _currentViewFrustum.fromByteArray(message);
+    _currentViewFrustums.clear();
+
+    auto sourceBuffer = reinterpret_cast<const unsigned char*>(message.constData());
+    
+    uint8_t numFrustums = 0;
+    memcpy(&numFrustums, sourceBuffer, sizeof(numFrustums));
+    sourceBuffer += sizeof(numFrustums);
+
+    for (uint8_t i = 0; i < numFrustums; ++i) {
+        ConicalViewFrustum frustum;
+        sourceBuffer += frustum.deserialize(sourceBuffer);
+
+        _currentViewFrustums.push_back(frustum);
+    }
 }
 
 bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) {
-    return _currentViewFrustum.boxIntersectsKeyhole(otherAvatarBox);
+    return std::any_of(std::begin(_currentViewFrustums), std::end(_currentViewFrustums),
+                       [&](const ConicalViewFrustum& viewFrustum) {
+        return viewFrustum.intersects(otherAvatarBox);
+    });
 }
 
 void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h
index 6963f4df0d..e038e81505 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.h
+++ b/assignment-client/src/avatars/AvatarMixerClientData.h
@@ -28,7 +28,7 @@
 #include <PortableHighResolutionClock.h>
 #include <SimpleMovingAverage.h>
 #include <UUIDHasher.h>
-#include <ViewFrustum.h>
+#include <shared/ConicalViewFrustum.h>
 
 const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps";
 const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
@@ -110,7 +110,7 @@ public:
     bool getRequestsDomainListData() { return _requestsDomainListData; }
     void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
 
-    ViewFrustum getViewFrustum() const { return _currentViewFrustum; }
+    const ConicalViewFrustums& getViewFrustums() const { return _currentViewFrustums; }
 
     uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
     void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time);
@@ -150,7 +150,7 @@ private:
 
     SimpleMovingAverage _avgOtherAvatarDataRate;
     std::unordered_set<QUuid> _radiusIgnoredOthers;
-    ViewFrustum _currentViewFrustum;
+    ConicalViewFrustums _currentViewFrustums;
 
     int _recentOtherAvatarsInView { 0 };
     int _recentOtherAvatarsOutOfView { 0 };
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp
index 6f19b73cc5..984884adb2 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AvatarMixerSlave.h"
+
 #include <algorithm>
 #include <random>
 
@@ -28,10 +30,8 @@
 #include <StDev.h>
 #include <UUID.h>
 
-
 #include "AvatarMixer.h"
 #include "AvatarMixerClientData.h"
-#include "AvatarMixerSlave.h"
 
 void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
     _begin = begin;
@@ -222,8 +222,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
     };
 
     // prepare to sort
-    ViewFrustum cameraView = nodeData->getViewFrustum();
-    PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraView,
+    const auto& cameraViews = nodeData->getViewFrustums();
+    PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraViews,
             AvatarData::_avatarSortCoefficientSize,
             AvatarData::_avatarSortCoefficientCenter,
             AvatarData::_avatarSortCoefficientAge);
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h
index bdddd5ceab..7be119c4b2 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.h
+++ b/assignment-client/src/avatars/AvatarMixerSlave.h
@@ -12,6 +12,8 @@
 #ifndef hifi_AvatarMixerSlave_h
 #define hifi_AvatarMixerSlave_h
 
+#include <NodeList.h>
+
 class AvatarMixerClientData;
 
 class AvatarMixerSlaveStats {
diff --git a/assignment-client/src/avatars/AvatarMixerSlavePool.cpp b/assignment-client/src/avatars/AvatarMixerSlavePool.cpp
index 25b88686b7..962bba21d2 100644
--- a/assignment-client/src/avatars/AvatarMixerSlavePool.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlavePool.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AvatarMixerSlavePool.h"
+
 #include <assert.h>
 #include <algorithm>
 
-#include "AvatarMixerSlavePool.h"
-
 void AvatarMixerSlaveThread::run() {
     while (true) {
         wait();
diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp
index 1f3f770867..e7210db83a 100644
--- a/assignment-client/src/avatars/ScriptableAvatar.cpp
+++ b/assignment-client/src/avatars/ScriptableAvatar.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ScriptableAvatar.h"
+
 #include <QDebug>
 #include <QThread>
 #include <glm/gtx/transform.hpp>
@@ -16,7 +18,6 @@
 #include <shared/QtHelpers.h>
 #include <GLMHelpers.h>
 #include <AnimUtil.h>
-#include "ScriptableAvatar.h"
 
 
 QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) {
diff --git a/assignment-client/src/entities/EntityPriorityQueue.cpp b/assignment-client/src/entities/EntityPriorityQueue.cpp
deleted file mode 100644
index 999a05f2e2..0000000000
--- a/assignment-client/src/entities/EntityPriorityQueue.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//
-//  EntityPriorityQueue.cpp
-//  assignment-client/src/entities
-//
-//  Created by Andrew Meadows 2017.08.08
-//  Copyright 2017 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 "EntityPriorityQueue.h"
-
-const float PrioritizedEntity::DO_NOT_SEND = -1.0e-6f;
-const float PrioritizedEntity::FORCE_REMOVE = -1.0e-5f;
-const float PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY = 1.0f;
-
-void ConicalView::set(const ViewFrustum& viewFrustum) {
-    // The ConicalView has two parts: a central sphere (same as ViewFrustum) and a circular cone that bounds the frustum part.
-    // Why?  Because approximate intersection tests are much faster to compute for a cone than for a frustum.
-    _position = viewFrustum.getPosition();
-    _direction = viewFrustum.getDirection();
-
-    // We cache the sin and cos of the half angle of the cone that bounds the frustum.
-    // (the math here is left as an exercise for the reader)
-    float A = viewFrustum.getAspectRatio();
-    float t = tanf(0.5f * viewFrustum.getFieldOfView());
-    _cosAngle = 1.0f / sqrtf(1.0f + (A * A + 1.0f) * (t * t));
-    _sinAngle = sqrtf(1.0f - _cosAngle * _cosAngle);
-
-    _radius = viewFrustum.getCenterRadius();
-}
-
-float ConicalView::computePriority(const AACube& cube) const {
-    glm::vec3 p = cube.calcCenter() - _position; // position of bounding sphere in view-frame
-    float d = glm::length(p); // distance to center of bounding sphere
-    float r = 0.5f * cube.getScale(); // radius of bounding sphere
-    if (d < _radius + r) {
-        return r;
-    }
-    // We check the angle between the center of the cube and the _direction of the view.
-    // If it is less than the sum of the half-angle from center of cone to outer edge plus
-    // the half apparent angle of the bounding sphere then it is in view.
-    //
-    // The math here is left as an exercise for the reader with the following hints:
-    // (1) We actually check the dot product of the cube's local position rather than the angle and
-    // (2) we take advantage of this trig identity: cos(A+B) = cos(A)*cos(B) - sin(A)*sin(B)
-    if (glm::dot(p, _direction) > sqrtf(d * d - r * r) * _cosAngle - r * _sinAngle) {
-        const float AVOID_DIVIDE_BY_ZERO = 0.001f;
-        return r / (d + AVOID_DIVIDE_BY_ZERO);
-    }
-    return PrioritizedEntity::DO_NOT_SEND;
-}
diff --git a/assignment-client/src/entities/EntityPriorityQueue.h b/assignment-client/src/entities/EntityPriorityQueue.h
deleted file mode 100644
index e308d9b549..0000000000
--- a/assignment-client/src/entities/EntityPriorityQueue.h
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-//  EntityPriorityQueue.h
-//  assignment-client/src/entities
-//
-//  Created by Andrew Meadows 2017.08.08
-//  Copyright 2017 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_EntityPriorityQueue_h
-#define hifi_EntityPriorityQueue_h
-
-#include <queue>
-
-#include <AACube.h>
-#include <EntityTreeElement.h>
-
-const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
-const float DEFAULT_VIEW_RADIUS = 10.0f;
-
-// ConicalView is an approximation of a ViewFrustum for fast calculation of sort priority.
-class ConicalView {
-public:
-    ConicalView() {}
-    ConicalView(const ViewFrustum& viewFrustum) { set(viewFrustum); }
-    void set(const ViewFrustum& viewFrustum);
-    float computePriority(const AACube& cube) const;
-private:
-    glm::vec3 _position { 0.0f, 0.0f, 0.0f };
-    glm::vec3 _direction { 0.0f, 0.0f, 1.0f };
-    float _sinAngle { SQRT_TWO_OVER_TWO };
-    float _cosAngle { SQRT_TWO_OVER_TWO };
-    float _radius { DEFAULT_VIEW_RADIUS };
-};
-
-// PrioritizedEntity is a placeholder in a sorted queue.
-class PrioritizedEntity {
-public:
-    static const float DO_NOT_SEND;
-    static const float FORCE_REMOVE;
-    static const float WHEN_IN_DOUBT_PRIORITY;
-
-    PrioritizedEntity(EntityItemPointer entity, float priority, bool forceRemove = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceRemove(forceRemove) {}
-    EntityItemPointer getEntity() const { return _weakEntity.lock(); }
-    EntityItem* getRawEntityPointer() const { return _rawEntityPointer; }
-    float getPriority() const { return _priority; }
-    bool shouldForceRemove() const { return _forceRemove; }
-
-    class Compare {
-    public:
-        bool operator() (const PrioritizedEntity& A, const PrioritizedEntity& B) { return A._priority < B._priority; }
-    };
-    friend class Compare;
-
-private:
-    EntityItemWeakPointer _weakEntity;
-    EntityItem* _rawEntityPointer;
-    float _priority;
-    bool _forceRemove;
-};
-
-using EntityPriorityQueue = std::priority_queue< PrioritizedEntity, std::vector<PrioritizedEntity>, PrioritizedEntity::Compare >;
-
-#endif // hifi_EntityPriorityQueue_h
diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp
index 70fad03d67..c108dad6cf 100644
--- a/assignment-client/src/entities/EntityServer.cpp
+++ b/assignment-client/src/entities/EntityServer.cpp
@@ -9,21 +9,23 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "EntityServer.h"
+
 #include <QtCore/QEventLoop>
 #include <QTimer>
+#include <QJsonArray>
+#include <QJsonDocument>
+
 #include <EntityTree.h>
 #include <SimpleEntitySimulation.h>
 #include <ResourceCache.h>
 #include <ScriptCache.h>
 #include <EntityEditFilters.h>
 #include <NetworkingConstants.h>
-#include <QJsonArray>
-#include <QJsonDocument>
 #include <AddressManager.h>
 
 #include "AssignmentParentFinder.h"
 #include "EntityNodeData.h"
-#include "EntityServer.h"
 #include "EntityServerConsts.h"
 #include "EntityTreeSendThread.h"
 
diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp
index 4aa52922c0..f008ef9925 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.cpp
+++ b/assignment-client/src/entities/EntityTreeSendThread.cpp
@@ -103,48 +103,41 @@ void EntityTreeSendThread::preDistributionProcessing() {
 void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
             bool viewFrustumChanged, bool isFullScene) {
     if (viewFrustumChanged || _traversal.finished()) {
-        ViewFrustum viewFrustum;
-        nodeData->copyCurrentViewFrustum(viewFrustum);
         EntityTreeElementPointer root = std::dynamic_pointer_cast<EntityTreeElement>(_myServer->getOctree()->getRoot());
+
+
+        DiffTraversal::View newView;
+        newView.viewFrustums = nodeData->getCurrentViews();
+
         int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
-        startNewTraversal(viewFrustum, root, lodLevelOffset, nodeData->getUsesFrustum());
+        newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
+
+        startNewTraversal(newView, root);
 
         // When the viewFrustum changed the sort order may be incorrect, so we re-sort
         // and also use the opportunity to cull anything no longer in view
         if (viewFrustumChanged && !_sendQueue.empty()) {
             EntityPriorityQueue prevSendQueue;
-            _sendQueue.swap(prevSendQueue);
-            _entitiesInQueue.clear();
+            std::swap(_sendQueue, prevSendQueue);
+            assert(_sendQueue.empty());
+
             // Re-add elements from previous traversal if they still need to be sent
-            float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
-            glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
             while (!prevSendQueue.empty()) {
                 EntityItemPointer entity = prevSendQueue.top().getEntity();
                 bool forceRemove = prevSendQueue.top().shouldForceRemove();
                 prevSendQueue.pop();
                 if (entity) {
-                    if (!forceRemove) {
-                        bool success = false;
-                        AACube cube = entity->getQueryAACube(success);
-                        if (success) {
-                            if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
-                                float priority = _conicalView.computePriority(cube);
-                                if (priority != PrioritizedEntity::DO_NOT_SEND) {
-                                    float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
-                                    float angularDiameter = cube.getScale() / distance;
-                                    if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
-                                        _sendQueue.push(PrioritizedEntity(entity, priority));
-                                        _entitiesInQueue.insert(entity.get());
-                                    }
-                                }
-                            }
-                        } else {
-                            _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
-                            _entitiesInQueue.insert(entity.get());
-                        }
+                    float priority = PrioritizedEntity::DO_NOT_SEND;
+
+                    if (forceRemove) {
+                        priority = PrioritizedEntity::FORCE_REMOVE;
                     } else {
-                        _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true));
-                        _entitiesInQueue.insert(entity.get());
+                        const auto& view = _traversal.getCurrentView();
+                        priority = view.computePriority(entity);
+                    }
+
+                    if (priority != PrioritizedEntity::DO_NOT_SEND) {
+                        _sendQueue.emplace(entity, priority, forceRemove);
                     }
                 }
             }
@@ -215,10 +208,9 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
     return hasNewChild || hasNewDescendants;
 }
 
-void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset, 
-        bool usesViewFrustum) {
+void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
 
-    DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, lodLevelOffset, usesViewFrustum);
+    DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root);
     // there are three types of traversal:
     //
     //      (1) FirstTime = at login --> find everything in view
@@ -226,171 +218,80 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
     //      (3) Differential = view has changed --> find what has changed or in new view but not old
     //
     // The "scanCallback" we provide to the traversal depends on the type:
-    //
-    // The _conicalView is updated here as a cached view approximation used by the lambdas for efficient
-    // computation of entity sorting priorities.
-    //
-    _conicalView.set(_traversal.getCurrentView());
 
     switch (type) {
         case DiffTraversal::First:
             // When we get to a First traversal, clear the _knownState
             _knownState.clear();
-            if (usesViewFrustum) {
-                float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
-                glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
-                _traversal.setScanCallback([=](DiffTraversal::VisibleElement& next) {
-                    next.element->forEachEntity([=](EntityItemPointer entity) {
-                        // Bail early if we've already checked this entity this frame
-                        if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
-                            return;
-                        }
-                        bool success = false;
-                        AACube cube = entity->getQueryAACube(success);
-                        if (success) {
-                            if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
-                                // Check the size of the entity, it's possible that a "too small to see" entity is included in a
-                                // larger octree cell because of its position (for example if it crosses the boundary of a cell it
-                                // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen
-                                // before we consider including it.
-                                float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
-                                float angularDiameter = cube.getScale() / distance;
-                                if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
-                                    float priority = _conicalView.computePriority(cube);
-                                    _sendQueue.push(PrioritizedEntity(entity, priority));
-                                    _entitiesInQueue.insert(entity.get());
-                                }
-                            }
-                        } else {
-                            _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
-                            _entitiesInQueue.insert(entity.get());
-                        }
-                    });
-                });
-            } else {
-                _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
-                    next.element->forEachEntity([this](EntityItemPointer entity) {
-                        // Bail early if we've already checked this entity this frame
-                        if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
-                            return;
-                        }
-                        _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
-                        _entitiesInQueue.insert(entity.get());
-                    });
-                });
-            }
-            break;
-        case DiffTraversal::Repeat:
-            if (usesViewFrustum) {
-                float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
-                glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
-                _traversal.setScanCallback([=](DiffTraversal::VisibleElement& next) {
-                    uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
-                    if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
-                        next.element->forEachEntity([=](EntityItemPointer entity) {
-                            // Bail early if we've already checked this entity this frame
-                            if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
-                                return;
-                            }
-                            auto knownTimestamp = _knownState.find(entity.get());
-                            if (knownTimestamp == _knownState.end()) {
-                                bool success = false;
-                                AACube cube = entity->getQueryAACube(success);
-                                if (success) {
-                                    if (next.intersection == ViewFrustum::INSIDE || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
-                                        // See the DiffTraversal::First case for an explanation of the "entity is too small" check
-                                        float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
-                                        float angularDiameter = cube.getScale() / distance;
-                                        if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
-                                            float priority = _conicalView.computePriority(cube);
-                                            _sendQueue.push(PrioritizedEntity(entity, priority));
-                                            _entitiesInQueue.insert(entity.get());
-                                        }
-                                    }
-                                } else {
-                                    _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
-                                    _entitiesInQueue.insert(entity.get());
-                                }
-                            } else if (entity->getLastEdited() > knownTimestamp->second
-                                    || entity->getLastChangedOnServer() > knownTimestamp->second) {
-                                // it is known and it changed --> put it on the queue with any priority
-                                // TODO: sort these correctly
-                                _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
-                                _entitiesInQueue.insert(entity.get());
-                            }
-                        });
-                    }
-                });
-            } else {
-                _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
-                    uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
-                    if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
-                        next.element->forEachEntity([this](EntityItemPointer entity) {
-                            // Bail early if we've already checked this entity this frame
-                            if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
-                                return;
-                            }
-                            auto knownTimestamp = _knownState.find(entity.get());
-                            if (knownTimestamp == _knownState.end()
-                                    || entity->getLastEdited() > knownTimestamp->second
-                                    || entity->getLastChangedOnServer() > knownTimestamp->second) {
-                                _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
-                                _entitiesInQueue.insert(entity.get());
-                            }
-                        });
-                    }
-                });
-            }
-            break;
-        case DiffTraversal::Differential:
-            assert(usesViewFrustum);
-            float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
-            glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
-            float completedLODScaleFactor = _traversal.getCompletedLODScaleFactor();
-            glm::vec3 completedViewPosition = _traversal.getCompletedView().getPosition();
-            _traversal.setScanCallback([=] (DiffTraversal::VisibleElement& next) {
-                next.element->forEachEntity([=](EntityItemPointer entity) {
+            _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
+                next.element->forEachEntity([&](EntityItemPointer entity) {
                     // Bail early if we've already checked this entity this frame
-                    if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
+                    if (_sendQueue.contains(entity.get())) {
                         return;
                     }
+                    const auto& view = _traversal.getCurrentView();
+                    float priority = view.computePriority(entity);
+
+                    if (priority != PrioritizedEntity::DO_NOT_SEND) {
+                        _sendQueue.emplace(entity, priority);
+                    }
+                });
+            });
+            break;
+        case DiffTraversal::Repeat:
+            _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
+                uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
+                if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
+                    next.element->forEachEntity([&](EntityItemPointer entity) {
+                        // Bail early if we've already checked this entity this frame
+                        if (_sendQueue.contains(entity.get())) {
+                            return;
+                        }
+                        float priority = PrioritizedEntity::DO_NOT_SEND;
+
+                        auto knownTimestamp = _knownState.find(entity.get());
+                        if (knownTimestamp == _knownState.end()) {
+                            const auto& view = _traversal.getCurrentView();
+                            priority = view.computePriority(entity);
+
+                        } else if (entity->getLastEdited() > knownTimestamp->second ||
+                                   entity->getLastChangedOnServer() > knownTimestamp->second) {
+                            // it is known and it changed --> put it on the queue with any priority
+                            // TODO: sort these correctly
+                            priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
+                        }
+
+                        if (priority != PrioritizedEntity::DO_NOT_SEND) {
+                            _sendQueue.emplace(entity, priority);
+                        }
+                    });
+                }
+            });
+            break;
+        case DiffTraversal::Differential:
+            assert(view.usesViewFrustums());
+            _traversal.setScanCallback([this] (DiffTraversal::VisibleElement& next) {
+                next.element->forEachEntity([&](EntityItemPointer entity) {
+                    // Bail early if we've already checked this entity this frame
+                    if (_sendQueue.contains(entity.get())) {
+                        return;
+                    }
+                    float priority = PrioritizedEntity::DO_NOT_SEND;
+
                     auto knownTimestamp = _knownState.find(entity.get());
                     if (knownTimestamp == _knownState.end()) {
-                        bool success = false;
-                        AACube cube = entity->getQueryAACube(success);
-                        if (success) {
-                            if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
-                                // See the DiffTraversal::First case for an explanation of the "entity is too small" check
-                                float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
-                                float angularDiameter = cube.getScale() / distance;
-                                if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
-                                    if (!_traversal.getCompletedView().cubeIntersectsKeyhole(cube)) {
-                                        float priority = _conicalView.computePriority(cube);
-                                        _sendQueue.push(PrioritizedEntity(entity, priority));
-                                        _entitiesInQueue.insert(entity.get());
-                                    } else {
-                                        // If this entity was skipped last time because it was too small, we still need to send it
-                                        distance = glm::distance(cube.calcCenter(), completedViewPosition) + MIN_VISIBLE_DISTANCE;
-                                        angularDiameter = cube.getScale() / distance;
-                                        if (angularDiameter <= MIN_ENTITY_ANGULAR_DIAMETER * completedLODScaleFactor) {
-                                            // this object was skipped in last completed traversal
-                                            float priority = _conicalView.computePriority(cube);
-                                            _sendQueue.push(PrioritizedEntity(entity, priority));
-                                            _entitiesInQueue.insert(entity.get());
-                                        }
-                                    }
-                                }
-                            }
-                        } else {
-                            _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
-                            _entitiesInQueue.insert(entity.get());
-                        }
-                    } else if (entity->getLastEdited() > knownTimestamp->second
-                            || entity->getLastChangedOnServer() > knownTimestamp->second) {
+                        const auto& view = _traversal.getCurrentView();
+                        priority = view.computePriority(entity);
+
+                    } else if (entity->getLastEdited() > knownTimestamp->second ||
+                               entity->getLastChangedOnServer() > knownTimestamp->second) {
                         // it is known and it changed --> put it on the queue with any priority
                         // TODO: sort these correctly
-                        _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
-                        _entitiesInQueue.insert(entity.get());
+                        priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
+                    }
+
+                    if (priority != PrioritizedEntity::DO_NOT_SEND) {
+                        _sendQueue.emplace(entity, priority);
                     }
                 });
             });
@@ -479,11 +380,10 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
             }
         }
         _sendQueue.pop();
-        _entitiesInQueue.erase(entity.get());
     }
     nodeData->stats.encodeStopped();
     if (_sendQueue.empty()) {
-        assert(_entitiesInQueue.empty());
+        assert(_sendQueue.empty());
         params.stopReason = EncodeBitstreamParams::FINISHED;
         _extraEncodeData->entities.clear();
     }
@@ -501,18 +401,15 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
 
 void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity) {
     if (entity) {
-        if (_entitiesInQueue.find(entity.get()) == _entitiesInQueue.end() && _knownState.find(entity.get()) != _knownState.end()) {
-            bool success = false;
-            AACube cube = entity->getQueryAACube(success);
-            if (success) {
-                // We can force a removal from _knownState if the current view is used and entity is out of view
-                if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
-                    _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true));
-                    _entitiesInQueue.insert(entity.get());
-                }
-            } else {
-                _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true));
-                _entitiesInQueue.insert(entity.get());
+        if (!_sendQueue.contains(entity.get()) && _knownState.find(entity.get()) != _knownState.end()) {
+            const auto& view = _traversal.getCurrentView();
+            float priority = view.computePriority(entity);
+
+            // We can force a removal from _knownState if the current view is used and entity is out of view
+            if (priority == PrioritizedEntity::DO_NOT_SEND) {
+                _sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true);
+            } else if (priority == PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY) {
+                _sendQueue.emplace(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true);
             }
         }
     }
diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h
index 1e2bd15429..1305d7bfc7 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.h
+++ b/assignment-client/src/entities/EntityTreeSendThread.h
@@ -17,8 +17,9 @@
 #include "../octree/OctreeSendThread.h"
 
 #include <DiffTraversal.h>
+#include <EntityPriorityQueue.h>
+#include <shared/ConicalViewFrustum.h>
 
-#include "EntityPriorityQueue.h"
 
 class EntityNodeData;
 class EntityItem;
@@ -41,8 +42,7 @@ private:
     bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
     bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
 
-    void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, 
-        bool usesViewFrustum);
+    void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root);
     bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
 
     void preDistributionProcessing() override;
@@ -51,9 +51,7 @@ private:
 
     DiffTraversal _traversal;
     EntityPriorityQueue _sendQueue;
-    std::unordered_set<EntityItem*> _entitiesInQueue;
     std::unordered_map<EntityItem*, uint64_t> _knownState;
-    ConicalView _conicalView; // cached optimized view for fast priority calculations
 
     // packet construction stuff
     EntityTreeElementExtraEncodeDataPointer _extraEncodeData { new EntityTreeElementExtraEncodeData() };
diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp
index 4bf708cf34..c11c8f40a0 100644
--- a/assignment-client/src/messages/MessagesMixer.cpp
+++ b/assignment-client/src/messages/MessagesMixer.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "MessagesMixer.h"
+
 #include <QtCore/QCoreApplication>
 #include <QtCore/QJsonObject>
 #include <QBuffer>
@@ -16,7 +18,6 @@
 #include <MessagesClient.h>
 #include <NodeList.h>
 #include <udt/PacketHeaders.h>
-#include "MessagesMixer.h"
 
 const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer";
 
diff --git a/assignment-client/src/octree/OctreeHeadlessViewer.cpp b/assignment-client/src/octree/OctreeHeadlessViewer.cpp
index d3b20fb623..039dbdab78 100644
--- a/assignment-client/src/octree/OctreeHeadlessViewer.cpp
+++ b/assignment-client/src/octree/OctreeHeadlessViewer.cpp
@@ -14,32 +14,21 @@
 #include <NodeList.h>
 #include <OctreeLogging.h>
 
-
-OctreeHeadlessViewer::OctreeHeadlessViewer() {
-    _viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
-}
-
 void OctreeHeadlessViewer::queryOctree() {
     char serverType = getMyNodeType();
     PacketType packetType = getMyQueryMessageType();
 
-    _octreeQuery.setCameraPosition(_viewFrustum.getPosition());
-    _octreeQuery.setCameraOrientation(_viewFrustum.getOrientation());
-    _octreeQuery.setCameraFov(_viewFrustum.getFieldOfView());
-    _octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio());
-    _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());
-    _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());
-    _octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
-    _octreeQuery.setCameraCenterRadius(_viewFrustum.getCenterRadius());
-    _octreeQuery.setOctreeSizeScale(_voxelSizeScale);
-    _octreeQuery.setBoundaryLevelAdjust(_boundaryLevelAdjust);
+    if (_hasViewFrustum) {
+        ConicalViewFrustums views { _viewFrustum };
+        _octreeQuery.setConicalViews(views);
+    } else {
+        _octreeQuery.clearConicalViews();
+    }
 
     auto nodeList = DependencyManager::get<NodeList>();
 
     auto node = nodeList->soloNodeOfType(serverType);
     if (node && node->getActiveSocket()) {
-        _octreeQuery.setMaxQueryPacketsPerSecond(getMaxPacketsPerSecond());
-
         auto queryPacket = NLPacket::create(packetType);
 
         // encode the query data
diff --git a/assignment-client/src/octree/OctreeHeadlessViewer.h b/assignment-client/src/octree/OctreeHeadlessViewer.h
index feb8211c39..dea91ce66f 100644
--- a/assignment-client/src/octree/OctreeHeadlessViewer.h
+++ b/assignment-client/src/octree/OctreeHeadlessViewer.h
@@ -20,9 +20,6 @@
 class OctreeHeadlessViewer : public OctreeProcessor {
     Q_OBJECT
 public:
-    OctreeHeadlessViewer();
-    virtual ~OctreeHeadlessViewer() {};
-    
     OctreeQuery& getOctreeQuery() { return _octreeQuery; }
 
     static int parseOctreeStats(QSharedPointer<ReceivedMessage> message, SharedNodePointer sourceNode);
@@ -32,34 +29,32 @@ public slots:
     void queryOctree();
 
     // setters for camera attributes
-    void setPosition(const glm::vec3& position) { _viewFrustum.setPosition(position); }
-    void setOrientation(const glm::quat& orientation) { _viewFrustum.setOrientation(orientation); }
-    void setCenterRadius(float radius) { _viewFrustum.setCenterRadius(radius); }
-    void setKeyholeRadius(float radius) { _viewFrustum.setCenterRadius(radius); } // TODO: remove this legacy support
+    void setPosition(const glm::vec3& position) { _hasViewFrustum = true; _viewFrustum.setPosition(position); }
+    void setOrientation(const glm::quat& orientation) { _hasViewFrustum = true; _viewFrustum.setOrientation(orientation); }
+    void setCenterRadius(float radius) { _hasViewFrustum = true; _viewFrustum.setCenterRadius(radius); }
+    void setKeyholeRadius(float radius) { _hasViewFrustum = true; _viewFrustum.setCenterRadius(radius); } // TODO: remove this legacy support
 
     // setters for LOD and PPS
-    void setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; }
-    void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
-    void setMaxPacketsPerSecond(int maxPacketsPerSecond) { _maxPacketsPerSecond = maxPacketsPerSecond; }
+    void setVoxelSizeScale(float sizeScale) { _octreeQuery.setOctreeSizeScale(sizeScale) ; }
+    void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _octreeQuery.setBoundaryLevelAdjust(boundaryLevelAdjust); }
+    void setMaxPacketsPerSecond(int maxPacketsPerSecond) { _octreeQuery.setMaxQueryPacketsPerSecond(maxPacketsPerSecond); }
 
     // getters for camera attributes
     const glm::vec3& getPosition() const { return _viewFrustum.getPosition(); }
     const glm::quat& getOrientation() const { return _viewFrustum.getOrientation(); }
 
     // getters for LOD and PPS
-    float getVoxelSizeScale() const { return _voxelSizeScale; }
-    int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
-    int getMaxPacketsPerSecond() const { return _maxPacketsPerSecond; }
+    float getVoxelSizeScale() const { return _octreeQuery.getOctreeSizeScale(); }
+    int getBoundaryLevelAdjust() const { return _octreeQuery.getBoundaryLevelAdjust(); }
+    int getMaxPacketsPerSecond() const { return _octreeQuery.getMaxQueryPacketsPerSecond(); }
 
     unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); }
 
 private:
     OctreeQuery _octreeQuery;
 
+    bool _hasViewFrustum { false };
     ViewFrustum _viewFrustum;
-    float _voxelSizeScale { DEFAULT_OCTREE_SIZE_SCALE };
-    int _boundaryLevelAdjust { 0 };
-    int _maxPacketsPerSecond { DEFAULT_MAX_OCTREE_PPS };
 };
 
 #endif // hifi_OctreeHeadlessViewer_h
diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp
index bce6e7fe44..ef532bb33f 100644
--- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp
+++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctreeInboundPacketProcessor.h"
+
 #include <limits>
 
 #include <NumericalConstants.h>
@@ -17,7 +19,6 @@
 
 #include "OctreeServer.h"
 #include "OctreeServerConsts.h"
-#include "OctreeInboundPacketProcessor.h"
 
 static QUuid DEFAULT_NODE_ID_REF;
 const quint64 TOO_LONG_SINCE_LAST_NACK = 1 * USECS_PER_SECOND;
diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp
index de49bd461c..e9aa44b970 100644
--- a/assignment-client/src/octree/OctreeSendThread.cpp
+++ b/assignment-client/src/octree/OctreeSendThread.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctreeSendThread.h"
+
 #include <chrono>
 #include <thread>
 
@@ -17,7 +19,6 @@
 #include <udt/PacketHeaders.h>
 #include <PerfStat.h>
 
-#include "OctreeSendThread.h"
 #include "OctreeServer.h"
 #include "OctreeServerConsts.h"
 #include "OctreeLogging.h"
@@ -330,8 +331,9 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
     } else {
         // we aren't forcing a full scene, check if something else suggests we should
         isFullScene = nodeData->haveJSONParametersChanged() ||
-            (nodeData->getUsesFrustum()
-             && ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged()));
+                      (nodeData->hasConicalViews() &&
+                       (nodeData->getViewFrustumJustStoppedChanging() ||
+                        nodeData->hasLodChanged()));
     }
 
     if (nodeData->isPacketWaiting()) {
@@ -445,7 +447,6 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
     params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
         _myServer->trackSend(dataID, dataEdited, _nodeUuid);
     };
-    nodeData->copyCurrentViewFrustum(params.viewFrustum);
 
     bool somethingToSend = true; // assume we have something
     bool hadSomething = hasSomethingToSend(nodeData);
diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp
index d242b393bf..eea8e8b470 100644
--- a/assignment-client/src/scripts/EntityScriptServer.cpp
+++ b/assignment-client/src/scripts/EntityScriptServer.cpp
@@ -294,7 +294,6 @@ void EntityScriptServer::run() {
     queryJSONParameters[EntityJSONQueryProperties::FLAGS_PROPERTY] = queryFlags;
     
     // setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter
-    _entityViewer.getOctreeQuery().setUsesFrustum(false);
     _entityViewer.getOctreeQuery().setJSONParameters(queryJSONParameters);
 
     entityScriptingInterface->setEntityTree(_entityViewer.getTree());
diff --git a/cmake/macros/SetupHifiTestCase.cmake b/cmake/macros/SetupHifiTestCase.cmake
index 6c7d38e19c..b0edb41e36 100644
--- a/cmake/macros/SetupHifiTestCase.cmake
+++ b/cmake/macros/SetupHifiTestCase.cmake
@@ -124,15 +124,14 @@ macro(SETUP_HIFI_TESTCASE)
       # This target will also build + run the other test targets using ctest when built.
 
       add_custom_target(${TEST_TARGET}
-        COMMAND ctest .
         SOURCES ${TEST_PROJ_SRC_FILES}    # display source files under the testcase target
         DEPENDS ${${TEST_PROJ_NAME}_TARGETS})
+
       set_target_properties(${TEST_TARGET} PROPERTIES
+        FOLDER "Tests"
         EXCLUDE_FROM_DEFAULT_BUILD TRUE
         EXCLUDE_FROM_ALL TRUE)
 
-      set_target_properties(${TEST_TARGET} PROPERTIES FOLDER "Tests")
-
       list (APPEND ALL_TEST_TARGETS ${TEST_TARGET})
       set(ALL_TEST_TARGETS "${ALL_TEST_TARGETS}" PARENT_SCOPE)
     else ()
diff --git a/domain-server/resources/web/wizard/index.shtml b/domain-server/resources/web/wizard/index.shtml
index 5a3286296d..3bc7503b44 100644
--- a/domain-server/resources/web/wizard/index.shtml
+++ b/domain-server/resources/web/wizard/index.shtml
@@ -26,7 +26,7 @@
       <span class='step-description'>
         <a target='_blank' href='https://docs.highfidelity.com/create-and-explore/start-working-in-your-sandbox/place-names'>Place names</a> are similar to web addresses. Users who want to visit your domain can
         enter its Place Name in High Fidelity's Interface. You can choose a Place Name for your domain.</br>
-        People can also use your <b>domain's IP address (shown below)</b> to visit your High Fidelity domain.
+        Your domain may also be reachable by <b>IP address</b>.
       </span>
     </div>
   </div>
@@ -35,10 +35,10 @@
     <div class="centered-hack-parent">
       <div id="place-name-group" class="centered-hack">
         <p id="place-name-link"></p>
-          <div id="place-name-edit">
-            <span class='glyphicon glyphicon-pencil'></span>
-            <a href="#" id="change-place-name">Choose a custom Place Name instead</a>
-          </div>
+        <div id="place-name-edit">
+          <span class='glyphicon glyphicon-pencil'></span>
+          <a href="#" id="change-place-name">Choose a custom Place Name instead</a>
+        </div>
       </div>
     </div>
   </div>
diff --git a/domain-server/src/DomainServerNodeData.cpp b/domain-server/src/DomainServerNodeData.cpp
index 974d4a59c3..486b51f9eb 100644
--- a/domain-server/src/DomainServerNodeData.cpp
+++ b/domain-server/src/DomainServerNodeData.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "DomainServerNodeData.h"
+
 #include <QtCore/QDataStream>
 #include <QtCore/QJsonArray>
 #include <QtCore/QJsonDocument>
@@ -17,8 +19,6 @@
 
 #include <udt/PacketHeaders.h>
 
-#include "DomainServerNodeData.h"
-
 DomainServerNodeData::StringPairHash DomainServerNodeData::_overrideHash;
 
 DomainServerNodeData::DomainServerNodeData() {
diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h
index db41c77cf2..6b8e9a1718 100644
--- a/domain-server/src/DomainServerNodeData.h
+++ b/domain-server/src/DomainServerNodeData.h
@@ -15,6 +15,7 @@
 #include <QtCore/QElapsedTimer>
 #include <QtCore/QHash>
 #include <QtCore/QUuid>
+#include <QtCore/QJsonObject>
 
 #include <HifiSockAddr.h>
 #include <NLPacket.h>
diff --git a/domain-server/src/DomainServerWebSessionData.cpp b/domain-server/src/DomainServerWebSessionData.cpp
index 3744af77f3..90eea17bec 100644
--- a/domain-server/src/DomainServerWebSessionData.cpp
+++ b/domain-server/src/DomainServerWebSessionData.cpp
@@ -9,13 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "DomainServerWebSessionData.h"
+
 #include <QtCore/QDataStream>
 #include <QtCore/QDebug>
 #include <QtCore/QJsonArray>
 #include <QtCore/QJsonObject>
 
-#include "DomainServerWebSessionData.h"
-
 DomainServerWebSessionData::DomainServerWebSessionData() :
     _username(),
     _roles()
diff --git a/gvr-interface/src/Client.cpp b/gvr-interface/src/Client.cpp
index 65238ad784..8f064c7fd5 100644
--- a/gvr-interface/src/Client.cpp
+++ b/gvr-interface/src/Client.cpp
@@ -9,14 +9,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Client.h"
+
 #include <AccountManager.h>
 #include <AddressManager.h>
 #include <HifiSockAddr.h>
 #include <NodeList.h>
 #include <PacketHeaders.h>
 
-#include "Client.h"
-
 Client::Client(QObject* parent) :
     QObject(parent)
 {   
@@ -70,4 +70,4 @@ void Client::processDatagrams() {
             processVerifiedPacket(senderSockAddr, incomingPacket);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/gvr-interface/src/GVRInterface.cpp b/gvr-interface/src/GVRInterface.cpp
index 3d58396322..f9a29d4ac4 100644
--- a/gvr-interface/src/GVRInterface.cpp
+++ b/gvr-interface/src/GVRInterface.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "GVRInterface.h"
+
 #ifdef ANDROID
 
 #include <jni.h>
@@ -32,8 +34,6 @@
 #include "GVRMainWindow.h"
 #include "RenderingClient.h"
 
-#include "GVRInterface.h"
-
 static QString launchURLString = QString();
 
 #ifdef ANDROID
diff --git a/gvr-interface/src/GVRMainWindow.cpp b/gvr-interface/src/GVRMainWindow.cpp
index 7a36aba66e..5495354233 100644
--- a/gvr-interface/src/GVRMainWindow.cpp
+++ b/gvr-interface/src/GVRMainWindow.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "GVRMainWindow.h"
+
 #include <QtGui/QKeyEvent>
 #include <QtWidgets/QApplication>
 #include <QtWidgets/QInputDialog>
@@ -37,8 +39,6 @@ const float LIBOVR_LONG_PRESS_DURATION = 0.75f;
 #include "LoginDialog.h"
 #include "RenderingClient.h"
 
-#include "GVRMainWindow.h"
-
 
 
 GVRMainWindow::GVRMainWindow(QWidget* parent) :
diff --git a/gvr-interface/src/LoginDialog.cpp b/gvr-interface/src/LoginDialog.cpp
index 95b7451bcb..d4efd425bd 100644
--- a/gvr-interface/src/LoginDialog.cpp
+++ b/gvr-interface/src/LoginDialog.cpp
@@ -9,14 +9,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "LoginDialog.h"
+
 #include <QtWidgets/QDialogButtonBox>
 #include <QtWidgets/QGridLayout>
 #include <QtWidgets/QLabel>
 #include <QtWidgets/QLineEdit>
 #include <QtWidgets/QPushButton>
 
-#include "LoginDialog.h"
-
 LoginDialog::LoginDialog(QWidget* parent) :
     QDialog(parent)
 {
@@ -66,4 +66,4 @@ void LoginDialog::setupGUI() {
 void LoginDialog::loginButtonClicked() {
     emit credentialsEntered(_usernameLineEdit->text(), _passwordLineEdit->text());
     close();
-}
\ No newline at end of file
+}
diff --git a/gvr-interface/src/RenderingClient.cpp b/gvr-interface/src/RenderingClient.cpp
index f04be5cb7f..4c691a48e6 100644
--- a/gvr-interface/src/RenderingClient.cpp
+++ b/gvr-interface/src/RenderingClient.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "RenderingClient.h"
+
 #include <QtCore/QThread>
 #include <QtWidgets/QInputDialog>
 
@@ -17,8 +19,6 @@
 #include <AvatarHashMap.h>
 #include <NodeList.h>
 
-#include "RenderingClient.h"
-
 RenderingClient* RenderingClient::_instance = NULL;
 
 RenderingClient::RenderingClient(QObject *parent, const QString& launchURLString) :
diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml
index c4b6c2aee1..9cb1add704 100644
--- a/interface/resources/qml/LoginDialog/SignInBody.qml
+++ b/interface/resources/qml/LoginDialog/SignInBody.qml
@@ -84,11 +84,9 @@ Item {
             height: undefined // invalidate so that the image's size sets the height
             focus: true
 
-            style: OriginalStyles.ButtonStyle {
-                background: Image {
-                    id: buttonImage
-                    source: "../../images/steam-sign-in.png"
-                }
+            background: Image {
+                id: buttonImage
+                source: "../../images/steam-sign-in.png"
             }
             onClicked: signInBody.login()
         }
diff --git a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
index d49e1e11be..c3e842bc2f 100644
--- a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
+++ b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
@@ -20,15 +20,10 @@ import "../fileDialog"
 Item {
     // Set from OffscreenUi::assetDialog()
     property alias dir: assetTableModel.folder
-    property alias filter: selectionType.filtersString  // FIXME: Currently only supports simple filters, "*.xxx".
-    property int options  // Not used.
+    property alias filter: selectionType.filtersString
+    property int options
 
     property bool selectDirectory: false
-
-    // Not implemented.
-    //property bool saveDialog: false;
-    //property bool multiSelect: false;
-
     property bool singleClickNavigate: false
 
     HifiConstants { id: hifi }
@@ -85,7 +80,6 @@ Item {
                 size: 28
                 width: height
                 enabled: destination !== ""
-                //onClicked: d.navigateHome();
                 onClicked: assetTableModel.folder = destination;
             }
         }
@@ -228,7 +222,9 @@ Item {
 
             function onGetAllMappings(error, map) {
                 var mappings,
-                    fileTypeFilter,
+                    fileTypeFilters = [],
+                    filterListStart,
+                    filterListEnd,
                     index,
                     path,
                     fileName,
@@ -249,7 +245,16 @@ Item {
 
                 if (error === "") {
                     mappings = Object.keys(map);
-                    fileTypeFilter = filter.replace("*", "").toLowerCase();
+                    filter = filter.replace(/\s/g, '');
+                    filterListStart = filter.indexOf("(");
+                    filterListEnd = filter.indexOf(")");
+                    if (filterListStart !== -1 && filterListEnd !== -1) {
+                        var FIRST_EXTENSION_OFFSET = 2;
+                        fileTypeFilters = filter.substring(filterListStart + FIRST_EXTENSION_OFFSET
+                            , filterListEnd).toLowerCase().split("*");
+                    } else if (filter !== "") {
+                        fileTypeFilters[0] = filter.replace("*", "").toLowerCase();
+                    }
 
                     for (i = 0, length = mappings.length; i < length; i++) {
                         index = mappings[i].lastIndexOf("/");
@@ -260,7 +265,24 @@ Item {
                         fileIsDir = false;
                         isValid = false;
 
-                        if (fileType.toLowerCase() === fileTypeFilter) {
+                        if (fileTypeFilters.length > 1) {
+                            if (fileTypeFilters.indexOf(fileType.toLowerCase()) !== -1) {
+                                if (path === folder) {
+                                    isValid = !selectDirectory;
+                                } else if (path.length > folder.length) {
+                                    subDirectory = path.slice(folder.length);
+                                    index = subDirectory.indexOf("/");
+                                    if (index === subDirectory.lastIndexOf("/")) {
+                                        fileName = subDirectory.slice(0, index);
+                                        if (subDirectories.indexOf(fileName) === -1) {
+                                            fileIsDir = true;
+                                            isValid = true;
+                                            subDirectories.push(fileName);
+                                        }
+                                    }
+                                }
+                            }
+                        } else if (fileType.toLowerCase() === fileTypeFilters[0] || fileTypeFilters.length === 0) {
                             if (path === folder) {
                                 isValid = !selectDirectory;
                             } else if (path.length > folder.length) {
diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
index 1cfbcf9075..f25282c738 100644
--- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
+++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
@@ -787,7 +787,7 @@ Rectangle {
                     }
                     lightboxPopup.button2text = "CONFIRM";
                     lightboxPopup.button2method = function() {
-                        Commerce.replaceContentSet(root.itemHref);
+                        Commerce.replaceContentSet(root.itemHref, root.certificateId);
                         lightboxPopup.visible = false;
                         rezzedNotifContainer.visible = true;
                         rezzedNotifContainerTimer.start();
diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
index 4db98091c1..19b57354dc 100644
--- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
@@ -49,6 +49,7 @@ Item {
     property string upgradeTitle;
     property bool updateAvailable: root.upgradeUrl !== "" && !root.isShowingMyItems;
     property bool isShowingMyItems;
+    property bool valid;
 
     property string originalStatusText;
     property string originalStatusColor;
@@ -239,6 +240,7 @@ Item {
                     width: 62;
 
                     onLoaded: {
+                        item.enabled = root.valid;
                         item.buttonGlyphText = hifi.glyphs.gift;
                         item.buttonText = "Gift";
                         item.buttonClicked = function() {
@@ -463,7 +465,7 @@ Item {
 
         Item {
             id: statusContainer;
-            visible: root.purchaseStatus === "pending" || root.purchaseStatus === "invalidated" || root.numberSold > -1;
+            visible: root.purchaseStatus === "pending" || !root.valid || root.numberSold > -1;
             anchors.left: itemName.left;
             anchors.right: itemName.right;
             anchors.top: itemName.bottom;
@@ -480,7 +482,7 @@ Item {
                 text: {
                         if (root.purchaseStatus === "pending") {
                             "PENDING..."
-                        } else if (root.purchaseStatus === "invalidated") {
+                        } else if (!root.valid) {
                             "INVALIDATED"
                         } else if (root.numberSold > -1) {
                             ("Sales: " + root.numberSold + "/" + (root.limitedRun === -1 ? "\u221e" : root.limitedRun))
@@ -492,7 +494,7 @@ Item {
                 color: {
                         if (root.purchaseStatus === "pending") {
                             hifi.colors.blueAccent
-                        } else if (root.purchaseStatus === "invalidated") {
+                        } else if (!root.valid) {
                             hifi.colors.redAccent
                         } else {
                             hifi.colors.baseGray
@@ -506,7 +508,7 @@ Item {
                 text: {
                         if (root.purchaseStatus === "pending") {
                             hifi.glyphs.question
-                        } else if (root.purchaseStatus === "invalidated") {
+                        } else if (!root.valid) {
                             hifi.glyphs.question
                         } else {
                             ""
@@ -523,7 +525,7 @@ Item {
                 color: {
                         if (root.purchaseStatus === "pending") {
                             hifi.colors.blueAccent
-                        } else if (root.purchaseStatus === "invalidated") {
+                        } else if (!root.valid) {
                             hifi.colors.redAccent
                         } else {
                             hifi.colors.baseGray
@@ -538,7 +540,7 @@ Item {
                 onClicked: {
                     if (root.purchaseStatus === "pending") {
                         sendToPurchases({method: 'showPendingLightbox'});
-                    } else if (root.purchaseStatus === "invalidated") {
+                    } else if (!root.valid) {
                         sendToPurchases({method: 'showInvalidatedLightbox'});
                     }
                 }
@@ -546,7 +548,7 @@ Item {
                     if (root.purchaseStatus === "pending") {
                         statusText.color = hifi.colors.blueHighlight;
                         statusIcon.color = hifi.colors.blueHighlight;
-                    } else if (root.purchaseStatus === "invalidated") {
+                    } else if (!root.valid) {
                         statusText.color = hifi.colors.redAccent;
                         statusIcon.color = hifi.colors.redAccent;
                     }
@@ -555,7 +557,7 @@ Item {
                     if (root.purchaseStatus === "pending") {
                         statusText.color = hifi.colors.blueAccent;
                         statusIcon.color = hifi.colors.blueAccent;
-                    } else if (root.purchaseStatus === "invalidated") {
+                    } else if (!root.valid) {
                         statusText.color = hifi.colors.redHighlight;
                         statusIcon.color = hifi.colors.redHighlight;
                     }
@@ -645,8 +647,8 @@ Item {
             width: 160;
             height: 40;
             enabled: root.hasPermissionToRezThis &&
-                root.purchaseStatus !== "invalidated" &&
-                MyAvatar.skeletonModelURL !== root.itemHref;
+                MyAvatar.skeletonModelURL !== root.itemHref &&
+                root.valid;
 
             onHoveredChanged: {
                 if (hovered) {
diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
index 6b48f8d51d..8fe1ebe6c9 100644
--- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
@@ -616,6 +616,7 @@ Rectangle {
                 upgradeTitle: model.upgrade_title;
                 itemType: model.itemType;
                 isShowingMyItems: root.isShowingMyItems;
+                valid: model.valid;
                 anchors.topMargin: 10;
                 anchors.bottomMargin: 10;
 
@@ -729,7 +730,7 @@ Rectangle {
                                 }
                                 lightboxPopup.button2text = "CONFIRM";
                                 lightboxPopup.button2method = function() {
-                                    MyAvatar.skeletonModelURL = '';
+                                    MyAvatar.useFullAvatarURL('');
                                     root.activeView = "giftAsset";
                                     lightboxPopup.visible = false;
                                 };
@@ -995,10 +996,6 @@ Rectangle {
 
         for (var i = 0; i < purchasesModel.count; i++) {
             if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) {
-                if (!purchasesModel.get(i).valid) {
-                    continue;
-                }
-
                 if (purchasesModel.get(i).status !== "confirmed" && !root.isShowingMyItems) {
                     tempPurchasesModel.insert(0, purchasesModel.get(i));
                 } else if ((root.isShowingMyItems && purchasesModel.get(i).edition_number === "0") ||
@@ -1055,10 +1052,6 @@ Rectangle {
             var currentId;
             for (var i = 0; i < tempPurchasesModel.count; i++) {
                 currentId = tempPurchasesModel.get(i).id;
-                
-                if (!purchasesModel.get(i).valid) {
-                    continue;
-                }
                 filteredPurchasesModel.append(tempPurchasesModel.get(i));
                 filteredPurchasesModel.setProperty(i, 'cardBackVisible', false);
                 filteredPurchasesModel.setProperty(i, 'isInstalled', ((root.installedApps).indexOf(currentId) > -1));
diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml
index 1cada9789b..c4abd40d2a 100644
--- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml
@@ -47,6 +47,13 @@ Item {
 
         onWalletAuthenticatedStatusResult: {
             submitPassphraseInputButton.enabled = true;
+
+            // It's not possible to auth with a blank passphrase,
+            // so bail early if we get this signal without anything in the passphrase field
+            if (passphraseField.text === "") {
+                return;
+            }
+
             if (!isAuthenticated) {
                 errorText.text = "Authentication failed - please try again.";
                 passphraseField.error = true;
@@ -211,6 +218,10 @@ Item {
                     error = false;
                     focus = true;
                     forceActiveFocus();
+                } else {
+                    showPassphrase.checked = false;
+                    passphraseField.text = "";
+                    error = false;
                 }
             }
 
diff --git a/interface/resources/shaders/splashSkybox.frag b/interface/resources/shaders/splashSkybox.frag
new file mode 100644
index 0000000000..38c89b4d26
--- /dev/null
+++ b/interface/resources/shaders/splashSkybox.frag
@@ -0,0 +1,47 @@
+const vec3 COLOR = vec3(0x00, 0xD8, 0x02) / vec3(0xFF);
+const float CUTOFF = 0.65;
+const float NOISE_MULT = 8.0;
+const float NOISE_POWER = 1.0;
+
+float noise4D(vec4 p) {
+	return fract(sin(dot(p ,vec4(12.9898,78.233,126.7235, 593.2241))) * 43758.5453);
+}
+
+float worley4D(vec4 p) {
+	float r = 3.0;
+    vec4 f = floor(p);
+    vec4 x = fract(p);
+	for(int i = -1; i<=1; i++)
+	{
+		for(int j = -1; j<=1; j++)
+		{
+			for(int k = -1; k<=1; k++)
+			{
+                for (int l = -1; l <= 1; l++) {
+                	vec4 q = vec4(float(i),float(j),float(k), float(l));
+    				vec4 v = q + vec4(noise4D((q+f)*1.11), noise4D((q+f)*1.14), noise4D((q+f)*1.17), noise4D((q+f)*1.20)) - x;
+    				float d = dot(v, v);
+					r = min(r, d);
+                }
+			}
+		}
+	}
+    return sqrt(r);
+}
+
+
+vec3 mainColor(vec3 direction) {
+    float n = worley4D(vec4(direction * NOISE_MULT, iGlobalTime / 3.0));
+    n = 1.0 - n;
+    n = pow(n, NOISE_POWER);
+    if (n < CUTOFF) {
+        return vec3(0.0);
+    }
+
+    n = (n - CUTOFF) / (1.0 - CUTOFF);
+	return COLOR * (1.0 - n);
+}
+
+vec3 getSkyboxColor() {
+	return mainColor(normalize(_normal));
+}
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 6175baacfe..f995aed2c9 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -144,16 +144,7 @@
 #include <trackers/EyeTracker.h>
 #include <avatars-renderer/ScriptAvatar.h>
 #include <RenderableEntityItem.h>
-
-#include <AnimationLogging.h>
-#include <AvatarLogging.h>
-#include <ScriptEngineLogging.h>
-#include <ModelFormatLogging.h>
-#include <controllers/Logging.h>
-#include <NetworkLogging.h>
-#include <shared/StorageLogging.h>
-#include <ScriptEngineLogging.h>
-#include <ui/Logging.h>
+#include <procedural/ProceduralSkybox.h>
 
 #include "AudioClient.h"
 #include "audio/AudioScope.h"
@@ -385,7 +376,7 @@ Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTRE
 static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com";
 static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds
 static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
-
+static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin";
 static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
 
 const std::vector<std::pair<QString, Application::AcceptURLMethod>> Application::_acceptedExtensions {
@@ -743,6 +734,11 @@ extern DisplayPluginList getDisplayPlugins();
 extern InputPluginList getInputPlugins();
 extern void saveInputPluginSettings(const InputPluginList& plugins);
 
+// Parameters used for running tests from teh command line
+const QString TEST_SCRIPT_COMMAND { "--testScript" };
+const QString TEST_QUIT_WHEN_FINISHED_OPTION { "quitWhenFinished" };
+const QString TEST_SNAPSHOT_LOCATION_COMMAND { "--testSnapshotLocation" };
+
 bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
     const char** constArgv = const_cast<const char**>(argv);
 
@@ -777,7 +773,22 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
 
     static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
     bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
-    bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
+
+    // Ignore any previous crashes if running from command line with a test script.  
+    bool inTestMode { false };
+    for (int i = 0; i < argc; ++i) {
+        QString parameter(argv[i]);
+        if (parameter == TEST_SCRIPT_COMMAND) {
+            inTestMode = true;
+            break;
+        }
+    }
+
+    bool previousSessionCrashed { false };
+    if (!inTestMode) {
+        previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
+    }
+
     // get dir to use for cache
     static const auto CACHE_SWITCH = "--cache";
     QString cacheDir = getCmdOption(argc, const_cast<const char**>(argv), CACHE_SWITCH);
@@ -967,7 +978,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
     _entitySimulation(new PhysicalEntitySimulation()),
     _physicsEngine(new PhysicsEngine(Vectors::ZERO)),
     _entityClipboard(new EntityTree()),
-    _lastQueriedTime(usecTimestampNow()),
     _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
     _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
     _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT),
@@ -997,13 +1007,30 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
     setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
     setProperty(hifi::properties::CRASHED, _previousSessionCrashed);
     {
-        const QString TEST_SCRIPT = "--testScript";
         const QStringList args = arguments();
+
         for (int i = 0; i < args.size() - 1; ++i) {
-            if (args.at(i) == TEST_SCRIPT) {
+            if (args.at(i) == TEST_SCRIPT_COMMAND && (i + 1) < args.size()) {
                 QString testScriptPath = args.at(i + 1);
-                if (QFileInfo(testScriptPath).exists()) {
+
+                // If the URL scheme is http(s) or ftp, then use as is, else - treat it as a local file
+                // This is done so as not break previous command line scripts
+                if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) {
+                    setProperty(hifi::properties::TEST, QUrl::fromUserInput(testScriptPath));
+                } else if (QFileInfo(testScriptPath).exists()) {
                     setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath));
+                }
+
+				// quite when finished parameter must directly follow the test script
+                if ((i + 2) < args.size() && args.at(i + 2) == TEST_QUIT_WHEN_FINISHED_OPTION) {
+                    quitWhenFinished = true;
+                }
+            } else if (args.at(i) == TEST_SNAPSHOT_LOCATION_COMMAND) {
+                // Set test snapshot location only if it is a writeable directory
+                QString pathname(args.at(i + 1));
+                QFileInfo fileInfo(pathname);
+                if (fileInfo.isDir() && fileInfo.isWritable()) {
+                    testSnapshotLocation = pathname;
                 }
             }
         }
@@ -1323,9 +1350,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
     QCoreApplication::processEvents();
     _glWidget->createContext();
 
-    // Create the main thread context, the GPU backend, and the display plugins
+    // Create the main thread context, the GPU backend
     initializeGL();
-    qCDebug(interfaceapp, "Initialized Display.");
+    qCDebug(interfaceapp, "Initialized GL");
+
+    // Initialize the display plugin architecture 
+    initializeDisplayPlugins();
+    qCDebug(interfaceapp, "Initialized Display");
+
     // Create the rendering engine.  This can be slow on some machines due to lots of 
     // GPU pipeline creation.
     initializeRenderEngine();
@@ -1335,8 +1367,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
     // Needs to happen AFTER the render engine initialization to access its configuration
     initializeUi();
 
-    updateVerboseLogging();
-
     init();
     qCDebug(interfaceapp, "init() complete.");
 
@@ -1676,6 +1706,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
 
     loadSettings();
 
+    updateVerboseLogging();
+
     // Now that we've loaded the menu and thus switched to the previous display plugin
     // we can unlock the desktop repositioning code, since all the positions will be
     // relative to the desktop size for this plugin
@@ -2128,14 +2160,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
         return entityServerNode && !isPhysicsEnabled();
     });
 
-    _snapshotSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/snap.wav"));
+    _snapshotSound = DependencyManager::get<SoundCache>()->getSound(PathUtils::resourcesUrl("sounds/snapshot/snap.wav"));
 
     QVariant testProperty = property(hifi::properties::TEST);
     qDebug() << testProperty;
     if (testProperty.isValid()) {
         auto scriptEngines = DependencyManager::get<ScriptEngines>();
         const auto testScript = property(hifi::properties::TEST).toUrl();
-        scriptEngines->loadScript(testScript, false);
+       
+        // Set last parameter to exit interface when the test script finishes, if so requested
+        scriptEngines->loadScript(testScript, false, false, false, false, quitWhenFinished);
+
+        // This is done so we don't get a "connection time-out" message when we haven't passed in a URL.
+        if (arguments().contains("--url")) {
+            auto reply = SandboxUtils::getStatus();
+            connect(reply, &QNetworkReply::finished, this, [=] {
+                handleSandboxStatus(reply);
+            });
+        }
     } else {
         PROFILE_RANGE(render, "GetSandboxStatus");
         auto reply = SandboxUtils::getStatus();
@@ -2210,43 +2252,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
 }
 
 void Application::updateVerboseLogging() {
-    bool enable = Menu::getInstance()->isOptionChecked(MenuOption::VerboseLogging);
+    auto menu = Menu::getInstance();
+    if (!menu) {
+        return;
+    }
+    bool enable = menu->isOptionChecked(MenuOption::VerboseLogging);
 
-    const_cast<QLoggingCategory*>(&animation())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&animation())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&avatars())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&avatars())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&scriptengine())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&scriptengine())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&modelformat())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&modelformat())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&controllers())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&controllers())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&resourceLog())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&resourceLog())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&networking())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&networking())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&asset_client())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&asset_client())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&messages_client())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&messages_client())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&storagelogging())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&storagelogging())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&uiLogging())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&uiLogging())->setEnabled(QtInfoMsg, enable);
-
-    const_cast<QLoggingCategory*>(&glLogging())->setEnabled(QtDebugMsg, enable);
-    const_cast<QLoggingCategory*>(&glLogging())->setEnabled(QtInfoMsg, enable);
+    QString rules =
+        "hifi.*.debug=%1\n"
+        "hifi.*.info=%1\n"
+        "hifi.audio-stream.debug=false\n"
+        "hifi.audio-stream.info=false";
+    rules = rules.arg(enable ? "true" : "false");
+    QLoggingCategory::setFilterRules(rules);
 }
 
 void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
@@ -2359,6 +2377,10 @@ void Application::onAboutToQuit() {
         }
     }
 
+    // The active display plugin needs to be loaded before the menu system is active, 
+    // so its persisted explicitly here
+    Setting::Handle<QString>{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME }.set(getActiveDisplayPlugin()->getName());
+
     getActiveDisplayPlugin()->deactivate();
     if (_autoSwitchDisplayModeSupportedHMDPlugin
         && _autoSwitchDisplayModeSupportedHMDPlugin->isSessionActive()) {
@@ -2600,10 +2622,84 @@ void Application::initializeGL() {
     _glWidget->makeCurrent();
     _gpuContext = std::make_shared<gpu::Context>();
 
+    DependencyManager::get<TextureCache>()->setGPUContext(_gpuContext);
+
     // Restore the default main thread context
     _offscreenContext->makeCurrent();
+}
 
-    updateDisplayMode();
+static const QString SPLASH_SKYBOX{ "{\"ProceduralEntity\":{ \"version\":2, \"shaderUrl\":\"qrc:///shaders/splashSkybox.frag\" } }" };
+
+void Application::initializeDisplayPlugins() {
+    auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
+    Setting::Handle<QString> activeDisplayPluginSetting{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME, displayPlugins.at(0)->getName() };
+    auto lastActiveDisplayPluginName = activeDisplayPluginSetting.get();
+
+    auto defaultDisplayPlugin = displayPlugins.at(0);
+    // Once time initialization code 
+    DisplayPluginPointer targetDisplayPlugin;
+    foreach(auto displayPlugin, displayPlugins) {
+        displayPlugin->setContext(_gpuContext);
+        if (displayPlugin->getName() == lastActiveDisplayPluginName) {
+            targetDisplayPlugin = displayPlugin;
+        }
+        QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged,
+            [this](const QSize& size) { resizeGL(); });
+        QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset);
+    }
+
+    // The default display plugin needs to be activated first, otherwise the display plugin thread
+    // may be launched by an external plugin, which is bad 
+    setDisplayPlugin(defaultDisplayPlugin);
+
+    // Now set the desired plugin if it's not the same as the default plugin
+    if (targetDisplayPlugin != defaultDisplayPlugin) {
+        setDisplayPlugin(targetDisplayPlugin);
+    }
+
+    // Submit a default frame to render until the engine starts up
+    updateRenderArgs(0.0f);
+
+    _offscreenContext->makeCurrent();
+
+#define ENABLE_SPLASH_FRAME 0
+#if ENABLE_SPLASH_FRAME
+    {
+        QMutexLocker viewLocker(&_renderArgsMutex);
+
+        if (_appRenderArgs._isStereo) {
+            _gpuContext->enableStereo(true);
+            _gpuContext->setStereoProjections(_appRenderArgs._eyeProjections);
+            _gpuContext->setStereoViews(_appRenderArgs._eyeOffsets);
+        }
+
+        // Frame resources
+        auto framebufferCache = DependencyManager::get<FramebufferCache>();
+        gpu::FramebufferPointer finalFramebuffer = framebufferCache->getFramebuffer();
+        std::shared_ptr<ProceduralSkybox> procedural = std::make_shared<ProceduralSkybox>();
+        procedural->parse(SPLASH_SKYBOX);
+
+        _gpuContext->beginFrame(_appRenderArgs._view, _appRenderArgs._headPose);
+        gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) {
+            batch.resetStages();
+            batch.enableStereo(false);
+            batch.setFramebuffer(finalFramebuffer);
+            batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, { 0, 0, 0, 1 });
+            batch.enableSkybox(true);
+            batch.enableStereo(_appRenderArgs._isStereo);
+            batch.setViewportTransform({ 0, 0, finalFramebuffer->getSize() });
+            procedural->render(batch, _appRenderArgs._renderArgs.getViewFrustum());
+        });
+        auto frame = _gpuContext->endFrame();
+        frame->frameIndex = 0;
+        frame->framebuffer = finalFramebuffer;
+        frame->pose = _appRenderArgs._headPose;
+        frame->framebufferRecycler = [framebufferCache, procedural](const gpu::FramebufferPointer& framebuffer) {
+            framebufferCache->releaseFramebuffer(framebuffer);
+        };
+        _displayPlugin->submitFrame(frame);
+    }
+#endif
 }
 
 void Application::initializeRenderEngine() {
@@ -2627,6 +2723,7 @@ void Application::initializeRenderEngine() {
 }
 
 extern void setupPreferences();
+static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, bool active);
 
 void Application::initializeUi() {
     // Build a shared canvas / context for the Chromium processes
@@ -2768,10 +2865,25 @@ void Application::initializeUi() {
     offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
     offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
 
-    // Now that the menu is instantiated, ensure the display plugin menu is properly updated
-    updateDisplayMode();
     flushMenuUpdates();
 
+    // Now that the menu is instantiated, ensure the display plugin menu is properly updated
+    {
+        auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
+        // first sort the plugins into groupings: standard, advanced, developer
+        std::stable_sort(displayPlugins.begin(), displayPlugins.end(),
+            [](const DisplayPluginPointer& a, const DisplayPluginPointer& b)->bool { return a->getGrouping() < b->getGrouping(); });
+
+        // concatenate the groupings into a single list in the order: standard, advanced, developer
+        for(const auto& displayPlugin : displayPlugins) {
+            addDisplayPluginToMenu(displayPlugin, _displayPlugin == displayPlugin);
+        }
+
+        // after all plugins have been added to the menu, add a separator to the menu
+        auto parent = getPrimaryMenu()->getMenu(MenuOption::OutputMenu);
+        parent->addSeparator();
+    }
+
     // The display plugins are created before the menu now, so we need to do this here to hide the menu bar
     // now that it exists
     if (_window && _window->isFullScreen()) {
@@ -2923,7 +3035,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
             _thirdPersonHMDCameraBoomValid = false;
 
             _myCamera.setOrientation(myAvatar->getHead()->getOrientation());
-            if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
+            if (isOptionChecked(MenuOption::CenterPlayerInView)) {
                 _myCamera.setPosition(myAvatar->getDefaultEyePosition()
                     + _myCamera.getOrientation() * boomOffset);
             }
@@ -3178,7 +3290,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
         qCDebug(interfaceapp) << "First run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("default location") : addressLookupString);
         DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
 #else
-        showHelp();
         DependencyManager::get<AddressManager>()->goToEntry();
         sentTo = SENT_TO_ENTRY;
 #endif
@@ -3637,7 +3748,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
                     } else {
                         showCursor(Cursor::Icon::DEFAULT);
                     }
-                } else {
+                } else if (!event->isAutoRepeat()){
                     resetSensors(true);
                 }
                 break;
@@ -5096,7 +5207,7 @@ void Application::reloadResourceCaches() {
     resetPhysicsReadyInformation();
 
     // Query the octree to refresh everything in view
-    _lastQueriedTime = 0;
+    _queryExpiry = SteadyClock::now();
     _octreeQuery.incrementConnectionID();
 
     queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
@@ -5232,6 +5343,78 @@ void Application::updateDialogs(float deltaTime) const {
     }
 }
 
+void Application::updateSecondaryCameraViewFrustum() {
+    // TODO: Fix this by modeling the way the secondary camera works on how the main camera works
+    // ie. Use a camera object stored in the game logic and informs the Engine on where the secondary
+    // camera should be.
+
+    // Code based on SecondaryCameraJob
+    auto renderConfig = _renderEngine->getConfiguration();
+    assert(renderConfig);
+    auto camera = dynamic_cast<SecondaryCameraJobConfig*>(renderConfig->getConfig("SecondaryCamera"));
+
+    if (!camera || !camera->isEnabled()) {
+        return;
+    }
+
+    ViewFrustum secondaryViewFrustum;
+    if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) {
+        auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
+        auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
+        glm::vec3 mirrorPropertiesPosition = entityProperties.getPosition();
+        glm::quat mirrorPropertiesRotation = entityProperties.getRotation();
+        glm::vec3 mirrorPropertiesDimensions = entityProperties.getDimensions();
+        glm::vec3 halfMirrorPropertiesDimensions = 0.5f * mirrorPropertiesDimensions;
+
+        // setup mirror from world as inverse of world from mirror transformation using inverted x and z for mirrored image
+        // TODO: we are assuming here that UP is world y-axis
+        glm::mat4 worldFromMirrorRotation = glm::mat4_cast(mirrorPropertiesRotation) * glm::scale(vec3(-1.0f, 1.0f, -1.0f));
+        glm::mat4 worldFromMirrorTranslation = glm::translate(mirrorPropertiesPosition);
+        glm::mat4 worldFromMirror = worldFromMirrorTranslation * worldFromMirrorRotation;
+        glm::mat4 mirrorFromWorld = glm::inverse(worldFromMirror);
+
+        // get mirror camera position by reflecting main camera position's z coordinate in mirror space
+        glm::vec3 mainCameraPositionWorld = getCamera().getPosition();
+        glm::vec3 mainCameraPositionMirror = vec3(mirrorFromWorld * vec4(mainCameraPositionWorld, 1.0f));
+        glm::vec3 mirrorCameraPositionMirror = vec3(mainCameraPositionMirror.x, mainCameraPositionMirror.y,
+                                                    -mainCameraPositionMirror.z);
+        glm::vec3 mirrorCameraPositionWorld = vec3(worldFromMirror * vec4(mirrorCameraPositionMirror, 1.0f));
+
+        // set frustum position to be mirrored camera and set orientation to mirror's adjusted rotation
+        glm::quat mirrorCameraOrientation = glm::quat_cast(worldFromMirrorRotation);
+        secondaryViewFrustum.setPosition(mirrorCameraPositionWorld);
+        secondaryViewFrustum.setOrientation(mirrorCameraOrientation);
+
+        // build frustum using mirror space translation of mirrored camera
+        float nearClip = mirrorCameraPositionMirror.z + mirrorPropertiesDimensions.z * 2.0f;
+        glm::vec3 upperRight = halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
+        glm::vec3 bottomLeft = -halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
+        glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, camera->farClipPlaneDistance);
+        secondaryViewFrustum.setProjection(frustum);
+    } else {
+        if (!camera->attachedEntityId.isNull()) {
+            auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
+            auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
+            secondaryViewFrustum.setPosition(entityProperties.getPosition());
+            secondaryViewFrustum.setOrientation(entityProperties.getRotation());
+        } else {
+            secondaryViewFrustum.setPosition(camera->position);
+            secondaryViewFrustum.setOrientation(camera->orientation);
+        }
+
+        float aspectRatio = (float)camera->textureWidth / (float)camera->textureHeight;
+        secondaryViewFrustum.setProjection(camera->vFoV,
+                                            aspectRatio,
+                                            camera->nearClipPlaneDistance,
+                                            camera->farClipPlaneDistance);
+    }
+    // Without calculating the bound planes, the secondary camera will use the same culling frustum as the main camera,
+    // which is not what we want here.
+    secondaryViewFrustum.calculate();
+
+    _conicalViews.push_back(secondaryViewFrustum);
+}
+
 static bool domainLoadingInProgress = false;
 
 void Application::update(float deltaTime) {
@@ -5596,6 +5779,13 @@ void Application::update(float deltaTime) {
     {
         QMutexLocker viewLocker(&_viewMutex);
         _myCamera.loadViewFrustum(_viewFrustum);
+
+        _conicalViews.clear();
+        _conicalViews.push_back(_viewFrustum);
+        // TODO: Fix this by modeling the way the secondary camera works on how the main camera works
+        // ie. Use a camera object stored in the game logic and informs the Engine on where the secondary
+        // camera should be.
+        updateSecondaryCameraViewFrustum();
     }
 
     quint64 now = usecTimestampNow();
@@ -5605,18 +5795,31 @@ void Application::update(float deltaTime) {
         PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
         PerformanceTimer perfTimer("queryOctree");
         QMutexLocker viewLocker(&_viewMutex);
-        quint64 sinceLastQuery = now - _lastQueriedTime;
-        const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
-        bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
-        bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum);
+
+        bool viewIsDifferentEnough = false;
+        if (_conicalViews.size() == _lastQueriedViews.size()) {
+            for (size_t i = 0; i < _conicalViews.size(); ++i) {
+                if (!_conicalViews[i].isVerySimilar(_lastQueriedViews[i])) {
+                    viewIsDifferentEnough = true;
+                    break;
+                }
+            }
+        } else {
+            viewIsDifferentEnough = true;
+        }
+
+        
         // if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it
-        if (queryIsDue || viewIsDifferentEnough) {
-            _lastQueriedTime = now;
+        static const std::chrono::seconds MIN_PERIOD_BETWEEN_QUERIES { 3 };
+        auto now = SteadyClock::now();
+        if (now > _queryExpiry || viewIsDifferentEnough) {
             if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
                 queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
             }
-            sendAvatarViewFrustum();
-            _lastQueriedViewFrustum = _viewFrustum;
+            queryAvatars();
+
+            _lastQueriedViews = _conicalViews;
+            _queryExpiry = now + MIN_PERIOD_BETWEEN_QUERIES;
         }
     }
 
@@ -5656,6 +5859,32 @@ void Application::update(float deltaTime) {
     }
 
 
+    updateRenderArgs(deltaTime);
+
+    // HACK
+    // load the view frustum
+    // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering.
+    // Then we can move this logic into the Avatar::simulate call.
+    myAvatar->preDisplaySide(&_appRenderArgs._renderArgs);
+
+
+    {
+        PerformanceTimer perfTimer("limitless");
+        AnimDebugDraw::getInstance().update();
+    }
+
+    {
+        PerformanceTimer perfTimer("limitless");
+        DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update();
+    }
+
+    { // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over
+        PerformanceTimer perfTimer("enqueueFrame");
+        getMain3DScene()->enqueueFrame();
+    }
+}
+
+void Application::updateRenderArgs(float deltaTime) {
     editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) {
         PerformanceTimer perfTimer("editRenderArgs");
         appRenderArgs._headPose = getHMDSensorPose();
@@ -5679,9 +5908,9 @@ void Application::update(float deltaTime) {
                 QMutexLocker viewLocker(&_viewMutex);
                 // adjust near clip plane to account for sensor scaling.
                 auto adjustedProjection = glm::perspective(glm::radians(_fieldOfView.get()),
-                                                           getActiveDisplayPlugin()->getRecommendedAspectRatio(),
-                                                           DEFAULT_NEAR_CLIP * sensorToWorldScale,
-                                                           DEFAULT_FAR_CLIP);
+                    getActiveDisplayPlugin()->getRecommendedAspectRatio(),
+                    DEFAULT_NEAR_CLIP * sensorToWorldScale,
+                    DEFAULT_FAR_CLIP);
                 _viewFrustum.setProjection(adjustedProjection);
                 _viewFrustum.calculate();
             }
@@ -5697,8 +5926,14 @@ void Application::update(float deltaTime) {
         }
         {
             PROFILE_RANGE(render, "/resizeGL");
-            PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings));
-            bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
+            bool showWarnings = false;
+            bool suppressShortTimings = false;
+            auto menu = Menu::getInstance();
+            if (menu) {
+                suppressShortTimings = menu->isOptionChecked(MenuOption::SuppressShortTimings);
+                showWarnings = menu->isOptionChecked(MenuOption::PipelineWarnings);
+            }
+            PerformanceWarning::setSuppressShortTimings(suppressShortTimings);
             PerformanceWarning warn(showWarnings, "Application::paintGL()");
             resizeGL();
         }
@@ -5754,12 +5989,6 @@ void Application::update(float deltaTime) {
             }
         }
 
-        // HACK
-        // load the view frustum
-        // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering.
-        // Then we can move this logic into the Avatar::simulate call.
-        myAvatar->preDisplaySide(&appRenderArgs._renderArgs);
-
         {
             QMutexLocker viewLocker(&_viewMutex);
             _myCamera.loadViewFrustum(_displayViewFrustum);
@@ -5771,27 +6000,22 @@ void Application::update(float deltaTime) {
             appRenderArgs._renderArgs.setViewFrustum(_displayViewFrustum);
         }
     });
-
-    {
-        PerformanceTimer perfTimer("limitless");
-        AnimDebugDraw::getInstance().update();
-    }
-
-    {
-        PerformanceTimer perfTimer("limitless");
-        DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update();
-    }
-
-    { // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over
-        PerformanceTimer perfTimer("enqueueFrame");
-        getMain3DScene()->enqueueFrame();
-    }
 }
 
-void Application::sendAvatarViewFrustum() {
-    QByteArray viewFrustumByteArray = _viewFrustum.toByteArray();
-    auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size());
-    avatarPacket->write(viewFrustumByteArray);
+void Application::queryAvatars() {
+    auto avatarPacket = NLPacket::create(PacketType::AvatarQuery);
+    auto destinationBuffer = reinterpret_cast<unsigned char*>(avatarPacket->getPayload());
+    unsigned char* bufferStart = destinationBuffer;
+
+    uint8_t numFrustums = (uint8_t)_conicalViews.size();
+    memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums));
+    destinationBuffer += sizeof(numFrustums);
+
+    for (const auto& view : _conicalViews) {
+        destinationBuffer += view.serialize(destinationBuffer);
+    }
+
+    avatarPacket->setPayloadSize(destinationBuffer - bufferStart);
 
     DependencyManager::get<NodeList>()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
 }
@@ -5853,16 +6077,8 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
         return; // bail early if settings are not loaded
     }
 
-    ViewFrustum viewFrustum;
-    copyViewFrustum(viewFrustum);
-    _octreeQuery.setCameraPosition(viewFrustum.getPosition());
-    _octreeQuery.setCameraOrientation(viewFrustum.getOrientation());
-    _octreeQuery.setCameraFov(viewFrustum.getFieldOfView());
-    _octreeQuery.setCameraAspectRatio(viewFrustum.getAspectRatio());
-    _octreeQuery.setCameraNearClip(viewFrustum.getNearClip());
-    _octreeQuery.setCameraFarClip(viewFrustum.getFarClip());
-    _octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
-    _octreeQuery.setCameraCenterRadius(viewFrustum.getCenterRadius());
+    _octreeQuery.setConicalViews(_conicalViews);
+
     auto lodManager = DependencyManager::get<LODManager>();
     _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
     _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
@@ -6071,7 +6287,7 @@ void Application::nodeActivated(SharedNodePointer node) {
     // If we get a new EntityServer activated, reset lastQueried time
     // so we will do a proper query during update
     if (node->getType() == NodeType::EntityServer) {
-        _lastQueriedTime = 0;
+        _queryExpiry = SteadyClock::now();
         _octreeQuery.incrementConnectionID();
     }
 
@@ -6080,6 +6296,8 @@ void Application::nodeActivated(SharedNodePointer node) {
     }
 
     if (node->getType() == NodeType::AvatarMixer) {
+        _queryExpiry = SteadyClock::now();
+
         // new avatar mixer, send off our identity packet on next update loop
         // Reset skeletonModelUrl if the last server modified our choice.
         // Override the avatar url (but not model name) here too.
@@ -7396,7 +7614,7 @@ void Application::loadAvatarBrowser() const {
 void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
     postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
         // Get a screenshot and save it
-        QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename);
+        QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, testSnapshotLocation);
         // If we're not doing an animated snapshot as well...
         if (!includeAnimated) {
             // Tell the dependency manager that the capture of the still snapshot has taken place.
@@ -7410,7 +7628,7 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
 
 void Application::takeSecondaryCameraSnapshot(const QString& filename) {
     postLambdaEvent([filename, this] {
-        QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename);
+        QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, testSnapshotLocation);
         emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true);
     });
 }
@@ -7423,15 +7641,19 @@ void Application::shareSnapshot(const QString& path, const QUrl& href) {
 }
 
 float Application::getRenderResolutionScale() const {
-    if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) {
+    auto menu = Menu::getInstance();
+    if (!menu) {
         return 1.0f;
-    } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionTwoThird)) {
+    }
+    if (menu->isOptionChecked(MenuOption::RenderResolutionOne)) {
+        return 1.0f;
+    } else if (menu->isOptionChecked(MenuOption::RenderResolutionTwoThird)) {
         return 0.666f;
-    } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionHalf)) {
+    } else if (menu->isOptionChecked(MenuOption::RenderResolutionHalf)) {
         return 0.5f;
-    } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionThird)) {
+    } else if (menu->isOptionChecked(MenuOption::RenderResolutionThird)) {
         return 0.333f;
-    } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionQuarter)) {
+    } else if (menu->isOptionChecked(MenuOption::RenderResolutionQuarter)) {
         return 0.25f;
     } else {
         return 1.0f;
@@ -7655,7 +7877,7 @@ DisplayPluginPointer Application::getActiveDisplayPlugin() const {
 
 static const char* EXCLUSION_GROUP_KEY = "exclusionGroup";
 
-static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
+static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, bool active) {
     auto menu = Menu::getInstance();
     QString name = displayPlugin->getName();
     auto grouping = displayPlugin->getGrouping();
@@ -7700,65 +7922,12 @@ void Application::updateDisplayMode() {
         qFatal("Attempted to switch display plugins from a non-main thread");
     }
 
-    auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
-
-    // Once time initialization code 
-    static std::once_flag once;
-    std::call_once(once, [&] {
-        foreach(auto displayPlugin, displayPlugins) {
-            displayPlugin->setContext(_gpuContext);
-            QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged,
-                [this](const QSize& size) { resizeGL(); });
-            QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset);
-        }
-    });
-
     // Once time initialization code that depends on the UI being available
-    auto menu = Menu::getInstance();
-    if (menu) {
-        static std::once_flag onceUi;
-        std::call_once(onceUi, [&] {
-            bool first = true;
-
-            // first sort the plugins into groupings: standard, advanced, developer
-            DisplayPluginList standard;
-            DisplayPluginList advanced;
-            DisplayPluginList developer;
-            foreach(auto displayPlugin, displayPlugins) {
-                displayPlugin->setContext(_gpuContext);
-                auto grouping = displayPlugin->getGrouping();
-                switch (grouping) {
-                case Plugin::ADVANCED:
-                    advanced.push_back(displayPlugin);
-                    break;
-                case Plugin::DEVELOPER:
-                    developer.push_back(displayPlugin);
-                    break;
-                default:
-                    standard.push_back(displayPlugin);
-                    break;
-                }
-            }
-
-            // concatenate the groupings into a single list in the order: standard, advanced, developer
-            standard.insert(std::end(standard), std::begin(advanced), std::end(advanced));
-            standard.insert(std::end(standard), std::begin(developer), std::end(developer));
-
-            foreach(auto displayPlugin, standard) {
-                addDisplayPluginToMenu(displayPlugin, first);
-                first = false;
-            }
-
-            // after all plugins have been added to the menu, add a separator to the menu
-            auto parent = menu->getMenu(MenuOption::OutputMenu);
-            parent->addSeparator();
-        });
-
-    }
-
+    auto displayPlugins = getDisplayPlugins();
 
     // Default to the first item on the list, in case none of the menu items match
     DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0);
+    auto menu = getPrimaryMenu();
     if (menu) {
         foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
             QString name = displayPlugin->getName();
@@ -7782,6 +7951,14 @@ void Application::updateDisplayMode() {
 }
 
 void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
+    if (newDisplayPlugin == _displayPlugin) {
+        return;
+    }
+
+    // FIXME don't have the application directly set the state of the UI,
+    // instead emit a signal that the display plugin is changing and let 
+    // the desktop lock itself.  Reduces coupling between the UI and display
+    // plugins
     auto offscreenUi = DependencyManager::get<OffscreenUi>();
     auto desktop = offscreenUi->getDesktop();
     auto menu = Menu::getInstance();
@@ -7792,8 +7969,8 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
         bool wasRepositionLocked = false;
         if (desktop) {
             // Tell the desktop to no reposition (which requires plugin info), until we have set the new plugin, below.
-            wasRepositionLocked = offscreenUi->getDesktop()->property("repositionLocked").toBool();
-            offscreenUi->getDesktop()->setProperty("repositionLocked", true);
+            wasRepositionLocked = desktop->property("repositionLocked").toBool();
+            desktop->setProperty("repositionLocked", true);
         }
 
         if (_displayPlugin) {
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 04483fbdd0..ae14c99662 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -47,6 +47,7 @@
 #include <AbstractUriHandler.h>
 #include <shared/RateCounter.h>
 #include <ThreadSafeValueCache.h>
+#include <shared/ConicalViewFrustum.h>
 #include <shared/FileLogger.h>
 
 #include <RunningMarker.h>
@@ -112,7 +113,8 @@ class Application : public QApplication,
                     public AbstractViewStateInterface,
                     public AbstractScriptingServicesInterface,
                     public AbstractUriHandler,
-                    public PluginContainer {
+                    public PluginContainer
+{
     Q_OBJECT
 
     // TODO? Get rid of those
@@ -148,9 +150,12 @@ public:
     Q_INVOKABLE QString getUserAgent();
 
     void initializeGL();
+    void initializeDisplayPlugins();
     void initializeRenderEngine();
     void initializeUi();
 
+    void updateSecondaryCameraViewFrustum();
+
     void updateCamera(RenderArgs& renderArgs, float deltaTime);
     void paintGL();
     void resizeGL();
@@ -180,6 +185,9 @@ public:
     // which might be different from the viewFrustum, i.e. shadowmap
     // passes, mirror window passes, etc
     void copyDisplayViewFrustum(ViewFrustum& viewOut) const;
+
+    const ConicalViewFrustums& getConicalViews() const override { return _conicalViews; }
+
     const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
     QSharedPointer<EntityTreeRenderer> getEntities() const { return DependencyManager::get<EntityTreeRenderer>(); }
     QUndoStack* getUndoStack() { return &_undoStack; }
@@ -490,9 +498,9 @@ private:
     void updateDialogs(float deltaTime) const;
 
     void queryOctree(NodeType_t serverType, PacketType packetType);
+    void queryAvatars();
 
     int sendNackPackets();
-    void sendAvatarViewFrustum();
 
     std::shared_ptr<MyAvatar> getMyAvatar() const;
 
@@ -573,9 +581,14 @@ private:
 
     mutable QMutex _viewMutex { QMutex::Recursive };
     ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
-    ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels)
     ViewFrustum _displayViewFrustum;
-    quint64 _lastQueriedTime;
+
+    ConicalViewFrustums _conicalViews;
+    ConicalViewFrustums _lastQueriedViews; // last views used to query servers
+
+    using SteadyClock = std::chrono::steady_clock;
+    using TimePoint = SteadyClock::time_point;
+    TimePoint _queryExpiry;
 
     OctreeQuery _octreeQuery { true }; // NodeData derived class for querying octee cells from octree servers
 
@@ -665,6 +678,7 @@ private:
 
     using RenderArgsEditor = std::function <void (AppRenderArgs&)>;
     void editRenderArgs(RenderArgsEditor editor);
+    void updateRenderArgs(float deltaTime);
 
 
     Overlays _overlays;
@@ -744,5 +758,8 @@ private:
 
     std::atomic<bool> _pendingIdleEvent { true };
     std::atomic<bool> _pendingRenderEvent { true };
+
+    QString testSnapshotLocation;
+    bool quitWhenFinished { false };
 };
 #endif // hifi_Application_h
diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp
index e1015ca5d1..76babe3682 100644
--- a/interface/src/Application_render.cpp
+++ b/interface/src/Application_render.cpp
@@ -13,8 +13,9 @@
 
 #include <display-plugins/CompositorHelper.h>
 #include <FramebufferCache.h>
-#include "ui/Stats.h"
+#include <plugins/PluginManager.h>
 #include <SceneScriptingInterface.h>
+#include "ui/Stats.h"
 #include "Util.h"
 
 
@@ -233,3 +234,4 @@ void Application::runRenderFrame(RenderArgs* renderArgs) {
         _renderEngine->run();
     }
 }
+
diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp
index 7845158a80..8e15de673f 100644
--- a/interface/src/AvatarBookmarks.cpp
+++ b/interface/src/AvatarBookmarks.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AvatarBookmarks.h"
+
 #include <QAction>
 #include <QInputDialog>
 #include <QMessageBox>
@@ -27,7 +29,6 @@
 
 #include "MainWindow.h"
 #include "Menu.h"
-#include "AvatarBookmarks.h"
 #include "InterfaceLogging.h"
 
 #include "QVariantGLM.h"
diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h
index 177e6e493e..7b47ea8af7 100644
--- a/interface/src/AvatarBookmarks.h
+++ b/interface/src/AvatarBookmarks.h
@@ -18,6 +18,10 @@
 /**jsdoc 
  * This API helps manage adding and deleting avatar bookmarks.
  * @namespace AvatarBookmarks
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  */
 
 class AvatarBookmarks: public Bookmarks, public  Dependency {
diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp
index f48b5e1f5b..6e99b81e50 100644
--- a/interface/src/Bookmarks.cpp
+++ b/interface/src/Bookmarks.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Bookmarks.h"
+
 #include <QAction>
 #include <QDebug>
 #include <QInputDialog>
@@ -22,8 +24,6 @@
 #include "Menu.h"
 #include "InterfaceLogging.h"
 
-#include "Bookmarks.h"
-
 Bookmarks::Bookmarks() :
 _isMenuSorted(false)
 {
diff --git a/interface/src/Crashpad.cpp b/interface/src/Crashpad.cpp
index e39cd42d81..45f1d0778f 100644
--- a/interface/src/Crashpad.cpp
+++ b/interface/src/Crashpad.cpp
@@ -11,6 +11,8 @@
 
 #include "Crashpad.h"
 
+#include <assert.h>
+
 #include <QDebug>
 
 #if HAS_CRASHPAD
@@ -20,7 +22,7 @@
 #include <QStandardPaths>
 #include <QDir>
 
-#include <BuildInfo.h>
+#include <Windows.h>
 
 #include <client/crashpad_client.h>
 #include <client/crash_report_database.h>
@@ -28,28 +30,27 @@
 #include <client/annotation_list.h>
 #include <client/crashpad_info.h>
 
+#include <BuildInfo.h>
+
 using namespace crashpad;
 
 static const std::string BACKTRACE_URL { CMAKE_BACKTRACE_URL };
 static const std::string BACKTRACE_TOKEN { CMAKE_BACKTRACE_TOKEN };
 
-static std::wstring gIPCPipe;
-
 extern QString qAppFileName();
 
+CrashpadClient* client { nullptr };
 std::mutex annotationMutex;
 crashpad::SimpleStringDictionary* crashpadAnnotations { nullptr };
 
-#include <Windows.h>
-
 LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
+    if (!client) {
+        return EXCEPTION_CONTINUE_SEARCH;
+    }
+
     if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_HEAP_CORRUPTION ||
         pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_BUFFER_OVERRUN) {
-        CrashpadClient client;
-        if (gIPCPipe.length()) {
-            client.SetHandlerIPCPipe(gIPCPipe);
-        }
-        client.DumpAndCrash(pExceptionInfo);
+        client->DumpAndCrash(pExceptionInfo);
     }
 
     return EXCEPTION_CONTINUE_SEARCH;
@@ -60,7 +61,8 @@ bool startCrashHandler() {
         return false;
     }
 
-    CrashpadClient client;
+    assert(!client);
+    client = new CrashpadClient();
     std::vector<std::string> arguments;
 
     std::map<std::string, std::string> annotations;
@@ -96,12 +98,9 @@ bool startCrashHandler() {
     // Enable automated uploads.
     database->GetSettings()->SetUploadsEnabled(true);
 
-    bool result = client.StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true);
-    gIPCPipe = client.GetHandlerIPCPipe();
-
     AddVectoredExceptionHandler(0, vectoredExceptionHandler);
 
-    return result;
+    return client->StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true);
 }
 
 void setCrashAnnotation(std::string name, std::string value) {
diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp
index b3c059de7f..684539145e 100644
--- a/interface/src/DiscoverabilityManager.cpp
+++ b/interface/src/DiscoverabilityManager.cpp
@@ -9,7 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "DiscoverabilityManager.h"
+
 #include <QtCore/QJsonDocument>
+#include <QThread>
 
 #include <AccountManager.h>
 #include <AddressManager.h>
@@ -21,11 +24,8 @@
 #include <UUID.h>
 
 #include "Crashpad.h"
-#include "DiscoverabilityManager.h"
 #include "Menu.h"
 
-#include <QThread>
-
 const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::Connections;
 
 DiscoverabilityManager::DiscoverabilityManager() :
diff --git a/interface/src/DiscoverabilityManager.h b/interface/src/DiscoverabilityManager.h
index 96190b25d9..0c62ad5663 100644
--- a/interface/src/DiscoverabilityManager.h
+++ b/interface/src/DiscoverabilityManager.h
@@ -12,9 +12,13 @@
 #ifndef hifi_DiscoverabilityManager_h
 #define hifi_DiscoverabilityManager_h
 
+#include <QJsonObject>
+
 #include <DependencyManager.h>
 #include <SettingHandle.h>
 
+class QNetworkReply;
+
 namespace Discoverability {
     enum Mode {
         None,
diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp
index ec96f7c5d4..0d087c9b48 100644
--- a/interface/src/GLCanvas.cpp
+++ b/interface/src/GLCanvas.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-// FIXME ordering of headers
-#include "Application.h"
 #include "GLCanvas.h"
 
+#include "Application.h"
+
 bool GLCanvas::event(QEvent* event) {
     if (QEvent::Paint == event->type() && qApp->isAboutToQuit()) {
         return true;
diff --git a/interface/src/InterfaceDynamicFactory.cpp b/interface/src/InterfaceDynamicFactory.cpp
index b7861b56c8..e0d912b252 100644
--- a/interface/src/InterfaceDynamicFactory.cpp
+++ b/interface/src/InterfaceDynamicFactory.cpp
@@ -9,7 +9,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-
+#include "InterfaceDynamicFactory.h"
 
 #include <avatar/AvatarActionHold.h>
 #include <avatar/AvatarActionFarGrab.h>
@@ -22,9 +22,6 @@
 #include <ObjectConstraintConeTwist.h>
 #include <LogHandler.h>
 
-#include "InterfaceDynamicFactory.h"
-
-
 EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) {
     switch (type) {
         case DYNAMIC_TYPE_NONE:
diff --git a/interface/src/InterfaceParentFinder.cpp b/interface/src/InterfaceParentFinder.cpp
index 14088bc716..b9be58f04b 100644
--- a/interface/src/InterfaceParentFinder.cpp
+++ b/interface/src/InterfaceParentFinder.cpp
@@ -9,13 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "InterfaceParentFinder.h"
+
 #include <Application.h>
+#include <AvatarData.h>
+#include <avatar/AvatarManager.h>
 #include <EntityTree.h>
 #include <EntityTreeRenderer.h>
-#include <avatar/AvatarManager.h>
-#include <AvatarData.h>
-
-#include "InterfaceParentFinder.h"
 
 SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& success, SpatialParentTree* entityTree) const {
     SpatiallyNestableWeakPointer parent;
diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp
index d7d73e962a..d06ba14bcf 100644
--- a/interface/src/LODManager.cpp
+++ b/interface/src/LODManager.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "LODManager.h"
+
 #include <SettingHandle.h>
 #include <OctreeUtils.h>
 #include <Util.h>
@@ -17,8 +19,6 @@
 #include "ui/DialogsManager.h"
 #include "InterfaceLogging.h"
 
-#include "LODManager.h"
-
 
 Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
 Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h
index 96d92e91e9..8f88da63a8 100644
--- a/interface/src/LODManager.h
+++ b/interface/src/LODManager.h
@@ -37,6 +37,10 @@ class AABox;
 /**jsdoc
  * The LOD class manages your Level of Detail functions within Interface.
  * @namespace LODManager
+  *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  * @property {number} presentTime <em>Read-only.</em>
  * @property {number} engineRunTime <em>Read-only.</em>
  * @property {number} gpuTime <em>Read-only.</em>
diff --git a/interface/src/LocationBookmarks.cpp b/interface/src/LocationBookmarks.cpp
index 285f533a7f..8415c84282 100644
--- a/interface/src/LocationBookmarks.cpp
+++ b/interface/src/LocationBookmarks.cpp
@@ -9,21 +9,16 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "LocationBookmarks.h"
+
 #include <QAction>
-#include <QInputDialog>
-#include <QMessageBox>
 #include <QStandardPaths>
 
 #include <AddressManager.h>
-#include <Application.h>
 #include <OffscreenUi.h>
 
-#include "MainWindow.h"
 #include "Menu.h"
 
-#include "LocationBookmarks.h"
-#include <QtQuick/QQuickWindow>
-
 const QString LocationBookmarks::HOME_BOOKMARK = "Home";
 
 LocationBookmarks::LocationBookmarks() {
diff --git a/interface/src/LocationBookmarks.h b/interface/src/LocationBookmarks.h
index 9a800ba35e..39abea9ba4 100644
--- a/interface/src/LocationBookmarks.h
+++ b/interface/src/LocationBookmarks.h
@@ -13,6 +13,7 @@
 #define hifi_LocationBookmarks_h
 
 #include <DependencyManager.h>
+
 #include "Bookmarks.h"
 
 class LocationBookmarks : public Bookmarks, public  Dependency  {
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 60d5abf260..bf0fc05350 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Menu.h"
+
 #include <QFileDialog>
 #include <QMenuBar>
 #include <QShortcut>
@@ -52,8 +54,6 @@
 #include "SpeechRecognizer.h"
 #endif
 
-#include "Menu.h"
-
 extern bool DEV_DECIMATE_TEXTURES;
 
 Menu* Menu::getInstance() {
diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp
index 0e34eebc80..3a5d92eb8c 100644
--- a/interface/src/ModelPackager.cpp
+++ b/interface/src/ModelPackager.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ModelPackager.h"
+
 #include <QFile>
 #include <QFileDialog>
 #include <QMessageBox>
@@ -21,8 +23,6 @@
 #include "ModelPropertiesDialog.h"
 #include "InterfaceLogging.h"
 
-#include "ModelPackager.h"
-
 static const int MAX_TEXTURE_SIZE = 1024;
 
 void copyDirectoryContent(QDir& from, QDir& to) {
diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h
index 60b3825c4d..76295e5a85 100644
--- a/interface/src/ModelPackager.h
+++ b/interface/src/ModelPackager.h
@@ -12,11 +12,15 @@
 #ifndef hifi_ModelPackager_h
 #define hifi_ModelPackager_h
 
+#include <memory>
+
 #include <QFileInfo>
 #include <QVariantHash>
 
 #include "ui/ModelsBrowser.h"
 
+class FBXGeometry;
+
 class ModelPackager : public QObject {
 public:
     static bool package();
diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp
index 35b07aa2b2..8984f89d07 100644
--- a/interface/src/ModelPropertiesDialog.cpp
+++ b/interface/src/ModelPropertiesDialog.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ModelPropertiesDialog.h"
+
 #include <QCheckBox>
 #include <QComboBox>
 #include <QDialogButtonBox>
@@ -23,8 +25,6 @@
 #include <GLMHelpers.h>
 #include <OffscreenUi.h>
 
-#include "ModelPropertiesDialog.h"
-
 
 ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping,
                                              const QString& basePath, const FBXGeometry& geometry) :
diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp
index 2f85849fbe..7d91359a11 100644
--- a/interface/src/ModelSelector.cpp
+++ b/interface/src/ModelSelector.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ModelSelector.h"
+
 #include <QDialogButtonBox>
 #include <QComboBox>
 #include <QFileDialog>
@@ -16,8 +18,6 @@
 #include <QPushButton>
 #include <QStandardPaths>
 
-#include "ModelSelector.h"
-
 static const QString AVATAR_HEAD_AND_BODY_STRING = "Avatar Body with Head";
 static const QString AVATAR_ATTACHEMENT_STRING = "Avatar Attachment";
 static const QString ENTITY_MODEL_STRING = "Entity Model";
@@ -82,4 +82,4 @@ void ModelSelector::browse() {
         _browseButton->setText(fileInfo.fileName());
         lastModelBrowseLocation.set(fileInfo.path());
     }
-}
\ No newline at end of file
+}
diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp
index a4bf0bcefe..acde535d2b 100644
--- a/interface/src/SecondaryCamera.cpp
+++ b/interface/src/SecondaryCamera.cpp
@@ -9,13 +9,16 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "Application.h"
 #include "SecondaryCamera.h"
-#include <TextureCache.h>
-#include <gpu/Context.h>
-#include <EntityScriptingInterface.h>
+
 #include <glm/gtx/transform.hpp>
 
+#include <EntityScriptingInterface.h>
+#include <gpu/Context.h>
+#include <TextureCache.h>
+
+#include "Application.h"
+
 using RenderArgsPointer = std::shared_ptr<RenderArgs>;
 
 void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) {    
@@ -213,4 +216,4 @@ void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inp
         task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
     }
     task.addJob<EndSecondaryCameraFrame>("EndSecondaryCamera", cachedArg);
-}
\ No newline at end of file
+}
diff --git a/interface/src/SecondaryCamera.h b/interface/src/SecondaryCamera.h
index 026b72d865..3d9e52617c 100644
--- a/interface/src/SecondaryCamera.h
+++ b/interface/src/SecondaryCamera.h
@@ -17,6 +17,8 @@
 #include <render/RenderFetchCullSortTask.h>
 #include <RenderDeferredTask.h>
 #include <RenderForwardTask.h>
+#include <TextureCache.h>
+#include <ViewFrustum.h>
 
 class MainRenderTask {
 public:
diff --git a/interface/src/SpeechRecognizer.cpp b/interface/src/SpeechRecognizer.cpp
index f5d0cb9e24..4815d20a83 100644
--- a/interface/src/SpeechRecognizer.cpp
+++ b/interface/src/SpeechRecognizer.cpp
@@ -9,11 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "SpeechRecognizer.h"
+
 #include <QtGlobal>
 #include <QDebug>
 
 #include "InterfaceLogging.h"
-#include "SpeechRecognizer.h"
 
 #if defined(Q_OS_WIN)
 
diff --git a/interface/src/SpeechRecognizer.h b/interface/src/SpeechRecognizer.h
index d5f9031cfc..b22ab73837 100644
--- a/interface/src/SpeechRecognizer.h
+++ b/interface/src/SpeechRecognizer.h
@@ -24,6 +24,9 @@
 
 /**jsdoc
  * @namespace SpeechRecognizer
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 class SpeechRecognizer : public QObject, public Dependency {
     Q_OBJECT
diff --git a/interface/src/SpeechRecognizer.mm b/interface/src/SpeechRecognizer.mm
index 038bcce3e4..6b9da6f3e8 100644
--- a/interface/src/SpeechRecognizer.mm
+++ b/interface/src/SpeechRecognizer.mm
@@ -16,10 +16,10 @@
 #import <AppKit/NSSpeechRecognizer.h>
 #import <AppKit/NSWorkspace.h>
 
-#include <QDebug>
-
 #include "SpeechRecognizer.h"
 
+#include <QDebug>
+
 @interface SpeechRecognizerDelegate : NSObject <NSSpeechRecognizerDelegate> {
     SpeechRecognizer* _listener;
 }
diff --git a/interface/src/UIUtil.cpp b/interface/src/UIUtil.cpp
index 7b50975c92..a27bd6c5db 100644
--- a/interface/src/UIUtil.cpp
+++ b/interface/src/UIUtil.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "UIUtil.h"
+
 #include <QStyle>
 #include <QStyleOptionTitleBar>
 
-#include "UIUtil.h"
-
 int UIUtil::getWindowTitleBarHeight(const QWidget* window) {
     QStyleOptionTitleBar options;
     options.titleBarState = 1;
diff --git a/interface/src/audio/AudioScope.cpp b/interface/src/audio/AudioScope.cpp
index 1a2e867d51..1750c00d37 100644
--- a/interface/src/audio/AudioScope.cpp
+++ b/interface/src/audio/AudioScope.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AudioScope.h"
+
 #include <qvector2d.h>
 #include <limits>
 
@@ -19,8 +21,6 @@
 #include <gpu/Context.h>
 #include <GLMHelpers.h>
 
-#include "AudioScope.h"
-
 static const unsigned int DEFAULT_FRAMES_PER_SCOPE = 5;
 static const unsigned int MULTIPLIER_SCOPE_HEIGHT = 20;
 static const unsigned int SCOPE_HEIGHT = 2 * 15 * MULTIPLIER_SCOPE_HEIGHT;
diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h
index ff8bfda6dd..41cee8d17d 100644
--- a/interface/src/audio/AudioScope.h
+++ b/interface/src/audio/AudioScope.h
@@ -28,6 +28,10 @@ class AudioScope : public QObject, public Dependency {
     /**jsdoc
      * The AudioScope API helps control the Audio Scope features in Interface
      * @namespace AudioScope
+     *
+     * @hifi-interface
+     * @hifi-client-entity
+     *
      * @property {number} scopeInput <em>Read-only.</em>
      * @property {number} scopeOutputLeft <em>Read-only.</em>
      * @property {number} scopeOutputRight <em>Read-only.</em>
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index b71c060465..094b3bb67b 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AvatarManager.h"
+
 #include <string>
 
 #include <QScriptEngine>
@@ -35,9 +37,9 @@
 #include <UsersScriptingInterface.h>
 #include <UUID.h>
 #include <avatars-renderer/OtherAvatar.h>
+#include <shared/ConicalViewFrustum.h>
 
 #include "Application.h"
-#include "AvatarManager.h"
 #include "InterfaceLogging.h"
 #include "Menu.h"
 #include "MyAvatar.h"
@@ -155,9 +157,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
         AvatarSharedPointer _avatar;
     };
 
-    ViewFrustum cameraView;
-    qApp->copyDisplayViewFrustum(cameraView);
-    PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraView,
+
+    const auto& views = qApp->getConicalViews();
+    PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(views,
             AvatarData::_avatarSortCoefficientSize,
             AvatarData::_avatarSortCoefficientCenter,
             AvatarData::_avatarSortCoefficientAge);
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index d2655914d2..7f5aa00466 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -30,6 +30,9 @@
 /**jsdoc 
  * The AvatarManager API has properties and methods which manage Avatars within the same domain.
  * @namespace AvatarManager
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 
 class AvatarManager : public AvatarHashMap {
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 5ce056e9b7..6c6f6d4d41 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -1486,6 +1486,15 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
     std::shared_ptr<QMetaObject::Connection> skeletonConnection = std::make_shared<QMetaObject::Connection>();
     *skeletonConnection = QObject::connect(_skeletonModel.get(), &SkeletonModel::skeletonLoaded, [this, skeletonModelChangeCount, skeletonConnection]() {
        if (skeletonModelChangeCount == _skeletonModelChangeCount) {
+
+           if (_fullAvatarModelName.isEmpty()) {
+               // Store the FST file name into preferences
+               const auto& mapping = _skeletonModel->getGeometry()->getMapping();
+               if (mapping.value("name").isValid()) {
+                   _fullAvatarModelName = mapping.value("name").toString();
+               }
+           }
+
            initHeadBones();
            _skeletonModel->setCauterizeBoneSet(_headBoneSet);
            _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
@@ -1548,12 +1557,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
 
     if (_fullAvatarURLFromPreferences != fullAvatarURL) {
         _fullAvatarURLFromPreferences = fullAvatarURL;
-        if (modelName.isEmpty()) {
-            QVariantHash fullAvatarFST = FSTReader::downloadMapping(_fullAvatarURLFromPreferences.toString());
-            _fullAvatarModelName = fullAvatarFST["name"].toString();
-        } else {
-            _fullAvatarModelName = modelName;
-        }
+        _fullAvatarModelName = modelName;
     }
 
     const QString& urlString = fullAvatarURL.toString();
@@ -1561,8 +1565,8 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
         setSkeletonModelURL(fullAvatarURL);
         UserActivityLogger::getInstance().changedModel("skeleton", urlString);
     }
+
     markIdentityDataChanged();
-    
 }
 
 void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
@@ -2035,7 +2039,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
     }
 }
 
-void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
+void MyAvatar::preDisplaySide(const RenderArgs* renderArgs) {
 
     // toggle using the cauterizedBones depending on where the camera is and the rendering pass type.
     const bool shouldDrawHead = shouldRenderHead(renderArgs);
@@ -2050,12 +2054,14 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
                 _attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 ||
                 _attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 ||
                 _attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) {
+                uint8_t modelRenderTagBits = shouldDrawHead ? render::ItemKey::TAG_BITS_0 : render::ItemKey::TAG_BITS_NONE;
+                modelRenderTagBits |= render::ItemKey::TAG_BITS_1;
+                _attachmentModels[i]->setVisibleInScene(true, qApp->getMain3DScene(),
+                                                        modelRenderTagBits, false);
 
-                _attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(),
-                                                        render::ItemKey::TAG_BITS_NONE, true);
-
-                _attachmentModels[i]->setCanCastShadow(shouldDrawHead, qApp->getMain3DScene(), 
-                                                       render::ItemKey::TAG_BITS_NONE, true);
+                uint8_t castShadowRenderTagBits = render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1;
+                _attachmentModels[i]->setCanCastShadow(true, qApp->getMain3DScene(),
+                                                       castShadowRenderTagBits, false);
             }
         }
     }
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index ac3d3cd2f4..154e2e4d09 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -272,7 +272,7 @@ public:
 
     void update(float deltaTime);
     virtual void postUpdate(float deltaTime, const render::ScenePointer& scene) override;
-    void preDisplaySide(RenderArgs* renderArgs);
+    void preDisplaySide(const RenderArgs* renderArgs);
 
     const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; }
     const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; }
diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp
index fde8c49933..f791ea25bc 100644
--- a/interface/src/commerce/Ledger.cpp
+++ b/interface/src/commerce/Ledger.cpp
@@ -9,16 +9,19 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Ledger.h"
+
 #include <QJsonObject>
 #include <QJsonArray>
 #include <QTimeZone>
 #include <QJsonDocument>
-#include "Wallet.h"
-#include "Ledger.h"
-#include "CommerceLogging.h"
+
 #include <NetworkingConstants.h>
 #include <AddressManager.h>
 
+#include "Wallet.h"
+#include "CommerceLogging.h"
+
 // inventory answers {status: 'success', data: {assets: [{id: "guid", title: "name", preview: "url"}....]}}
 // balance answers {status: 'success', data: {balance: integer}}
 // buy and receive_at answer {status: 'success'}
diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp
index 568556cb22..722f29ba2f 100644
--- a/interface/src/commerce/QmlCommerce.cpp
+++ b/interface/src/commerce/QmlCommerce.cpp
@@ -301,7 +301,7 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) {
     // Read from the file to know what .js script to stop
     QFile appFile(_appsPath + "/" + appHref.fileName());
     if (!appFile.open(QIODevice::ReadOnly)) {
-        qCDebug(commerce) << "Couldn't open local .app.json file for deletion.";
+        qCDebug(commerce) << "Couldn't open local .app.json file for deletion. Cannot continue with app uninstallation. App filename is:" << appHref.fileName();
         return false;
     }
     QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll());
@@ -309,15 +309,13 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) {
     QString scriptUrl = appFileJsonObject["scriptURL"].toString();
 
     if (!DependencyManager::get<ScriptEngines>()->stopScript(scriptUrl.trimmed(), false)) {
-        qCDebug(commerce) << "Couldn't stop script.";
-        return false;
+        qCWarning(commerce) << "Couldn't stop script during app uninstall. Continuing anyway. ScriptURL is:" << scriptUrl.trimmed();
     }
 
     // Delete the .app.json from the filesystem
     // remove() closes the file first.
     if (!appFile.remove()) {
-        qCDebug(commerce) << "Couldn't delete local .app.json file.";
-        return false;
+        qCWarning(commerce) << "Couldn't delete local .app.json file during app uninstall. Continuing anyway. App filename is:" << appHref.fileName();
     }
 
     emit appUninstalled(itemHref);
diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp
index 35e6ca1c92..982adb4b5e 100644
--- a/interface/src/commerce/Wallet.cpp
+++ b/interface/src/commerce/Wallet.cpp
@@ -9,22 +9,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "CommerceLogging.h"
-#include "Ledger.h"
 #include "Wallet.h"
-#include "Application.h"
-#include "ui/SecurityImageProvider.h"
-#include "scripting/HMDScriptingInterface.h"
-#include <ui/TabletScriptingInterface.h>
-
-#include <PathUtils.h>
-#include <OffscreenUi.h>
-#include <AccountManager.h>
-
-#include <QFile>
-#include <QCryptographicHash>
-#include <QQmlContext>
-#include <QBuffer>
 
 #include <openssl/ssl.h>
 #include <openssl/err.h>
@@ -33,7 +18,6 @@
 #include <openssl/evp.h>
 #include <openssl/aes.h>
 #include <openssl/ecdsa.h>
-
 // I know, right?  But per https://www.openssl.org/docs/faq.html
 // this avoids OPENSSL_Uplink(00007FF847238000,08): no OPENSSL_Applink
 // at runtime.
@@ -41,6 +25,22 @@
 #include <openssl/applink.c>
 #endif
 
+#include <QFile>
+#include <QCryptographicHash>
+#include <QQmlContext>
+#include <QBuffer>
+
+#include <PathUtils.h>
+#include <OffscreenUi.h>
+#include <AccountManager.h>
+#include <ui/TabletScriptingInterface.h>
+
+#include "Application.h"
+#include "CommerceLogging.h"
+#include "Ledger.h"
+#include "ui/SecurityImageProvider.h"
+#include "scripting/HMDScriptingInterface.h"
+
 static const char* KEY_FILE = "hifikey";
 static const char* INSTRUCTIONS_FILE = "backup_instructions.html";
 static const char* IMAGE_HEADER = "-----BEGIN SECURITY IMAGE-----\n";
diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h
index d4af0bbd37..4fe36b582e 100644
--- a/interface/src/devices/DdeFaceTracker.h
+++ b/interface/src/devices/DdeFaceTracker.h
@@ -29,6 +29,9 @@
 /**jsdoc 
  * The FaceTracker API helps manage facial tracking hardware. 
  * @namespace FaceTracker
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 
 class DdeFaceTracker : public FaceTracker, public Dependency {
diff --git a/interface/src/networking/CloseEventSender.cpp b/interface/src/networking/CloseEventSender.cpp
index fe939afe05..16549d5510 100644
--- a/interface/src/networking/CloseEventSender.cpp
+++ b/interface/src/networking/CloseEventSender.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "CloseEventSender.h"
+
 #include <QtCore/QDateTime>
 #include <QtCore/QEventLoop>
 #include <QtCore/QJsonDocument>
@@ -22,8 +24,6 @@
 #include <UserActivityLogger.h>
 #include <UUID.h>
 
-#include "CloseEventSender.h"
-
 QNetworkRequest createNetworkRequest() {
 
     QNetworkRequest request;
diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp
index 0c2883a9a4..7d38e29710 100644
--- a/interface/src/octree/OctreePacketProcessor.cpp
+++ b/interface/src/octree/OctreePacketProcessor.cpp
@@ -9,11 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctreePacketProcessor.h"
+
 #include <PerfStat.h>
 
 #include "Application.h"
 #include "Menu.h"
-#include "OctreePacketProcessor.h"
 #include "SceneScriptingInterface.h"
 
 OctreePacketProcessor::OctreePacketProcessor() {
diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h
index f2cd9287a5..2568dd8457 100644
--- a/interface/src/raypick/PickScriptingInterface.h
+++ b/interface/src/raypick/PickScriptingInterface.h
@@ -18,6 +18,10 @@
  * The Picks API lets you create and manage objects for repeatedly calculating intersections in different ways.
  *
  * @namespace Picks
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  * @property PICK_NOTHING {number} A filter flag.  Don't intersect with anything.
  * @property PICK_ENTITIES {number} A filter flag.  Include entities when intersecting.
  * @property PICK_OVERLAYS {number} A filter flag.  Include overlays when intersecting.
diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h
index 1cc7b56503..e7acfd4037 100644
--- a/interface/src/raypick/PointerScriptingInterface.h
+++ b/interface/src/raypick/PointerScriptingInterface.h
@@ -19,6 +19,9 @@
  *  Pointers can also be configured to automatically generate PointerEvents.
  *
  * @namespace Pointers
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 
 class PointerScriptingInterface : public QObject, public Dependency {
diff --git a/interface/src/scripting/AccountServicesScriptingInterface.cpp b/interface/src/scripting/AccountServicesScriptingInterface.cpp
index fc293098bb..3939fce91d 100644
--- a/interface/src/scripting/AccountServicesScriptingInterface.cpp
+++ b/interface/src/scripting/AccountServicesScriptingInterface.cpp
@@ -9,13 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AccountServicesScriptingInterface.h"
+
 #include "AccountManager.h"
 #include "Application.h"
 #include "DiscoverabilityManager.h"
 #include "ResourceCache.h"
 
-#include "AccountServicesScriptingInterface.h"
-
 AccountServicesScriptingInterface::AccountServicesScriptingInterface() {
     auto accountManager = DependencyManager::get<AccountManager>();
     connect(accountManager.data(), &AccountManager::usernameChanged, this, &AccountServicesScriptingInterface::onUsernameChanged);
diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h
index d38a84d8fa..fb3c329def 100644
--- a/interface/src/scripting/AccountServicesScriptingInterface.h
+++ b/interface/src/scripting/AccountServicesScriptingInterface.h
@@ -18,6 +18,8 @@
 #include <QScriptValue>
 #include <QString>
 #include <QStringList>
+
+#include <AccountManager.h>
 #include <DiscoverabilityManager.h>
 
 class DownloadInfoResult {
@@ -38,6 +40,9 @@ class AccountServicesScriptingInterface : public QObject {
     /**jsdoc
      * The AccountServices API contains helper functions related to user connectivity
      * 
+     * @hifi-interface
+     * @hifi-client-entity
+     *
      * @namespace AccountServices
      * @property {string} username <em>Read-only.</em>
      * @property {boolean} loggedIn <em>Read-only.</em>
diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h
index c77d1522b5..f0a4328c2f 100644
--- a/interface/src/scripting/Audio.h
+++ b/interface/src/scripting/Audio.h
@@ -27,8 +27,14 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
 
     /**jsdoc
      * The Audio API features tools to help control audio contexts and settings.
-     * 
+     *
      * @namespace Audio
+     *
+     * @hifi-interface
+     * @hifi-client-entity
+     * @hifi-server-entity
+     * @hifi-assignment-client
+     *
      * @property {boolean} muted
      * @property {boolean} noiseReduction
      * @property {number} inputVolume
diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp
index a3c80bf1b6..f08a0bf382 100644
--- a/interface/src/scripting/AudioDevices.cpp
+++ b/interface/src/scripting/AudioDevices.cpp
@@ -9,18 +9,17 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AudioDevices.h"
+
 #include <map>
 #include <algorithm>
 
 #include <shared/QtHelpers.h>
 #include <plugins/DisplayPlugin.h>
 
-#include "AudioDevices.h"
-
 #include "Application.h"
 #include "AudioClient.h"
 #include "Audio.h"
-
 #include "UserActivityLogger.h"
 
 using namespace scripting;
diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h
index cce300e831..32b8c64a7d 100644
--- a/interface/src/scripting/ClipboardScriptingInterface.h
+++ b/interface/src/scripting/ClipboardScriptingInterface.h
@@ -21,6 +21,9 @@
  * The Clipboard API enables you to export and import entities to and from JSON files.
  *
  * @namespace Clipboard
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 class ClipboardScriptingInterface : public QObject {
     Q_OBJECT
diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h
index 4fceda3b04..e16383e234 100644
--- a/interface/src/scripting/ControllerScriptingInterface.h
+++ b/interface/src/scripting/ControllerScriptingInterface.h
@@ -200,7 +200,10 @@ class ScriptEngine;
  *
  * @namespace Controller
  *
- * @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end 
+ * @hifi-interface
+ * @hifi-client-entity
+ *
+ * @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
  *     points in a {@link RouteObject} mapping. A synonym for <code>Controller.Hardware.Actions</code>.
  *     <em>Read-only.</em><br />
  *     Default mappings are provided from the <code>Controller.Hardware.Keyboard</code> and <code>Controller.Standard</code> to 
diff --git a/interface/src/scripting/GooglePolyScriptingInterface.cpp b/interface/src/scripting/GooglePolyScriptingInterface.cpp
index 8ed5d59d07..fde2676986 100644
--- a/interface/src/scripting/GooglePolyScriptingInterface.cpp
+++ b/interface/src/scripting/GooglePolyScriptingInterface.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "GooglePolyScriptingInterface.h"
+
 #include <QEventLoop>
 #include <QtGlobal>
 #include <QJsonArray>
@@ -20,9 +22,7 @@
 #include <QString>
 #include <QTime>
 #include <QUrl>
-#include <random>
 
-#include "GooglePolyScriptingInterface.h"
 #include "ScriptEngineLogging.h"
 
 const QString LIST_POLY_URL = "https://poly.googleapis.com/v1/assets?";
diff --git a/interface/src/scripting/GooglePolyScriptingInterface.h b/interface/src/scripting/GooglePolyScriptingInterface.h
index 5c37b394fa..fb5aed9759 100644
--- a/interface/src/scripting/GooglePolyScriptingInterface.h
+++ b/interface/src/scripting/GooglePolyScriptingInterface.h
@@ -18,6 +18,9 @@
 /**jsdoc 
  * The GooglePoly API allows you to interact with Google Poly models direct from inside High Fidelity.
  * @namespace GooglePoly
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 
 class GooglePolyScriptingInterface : public QObject, public Dependency {
diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h
index 9b2482e73a..d2a272851f 100644
--- a/interface/src/scripting/HMDScriptingInterface.h
+++ b/interface/src/scripting/HMDScriptingInterface.h
@@ -28,7 +28,11 @@ class QScriptEngine;
  * The HMD API provides access to the HMD used in VR display mode.
  *
  * @namespace HMD
- * @property {Vec3} position - The position of the HMD if currently in VR display mode, otherwise 
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
+ * @property {Vec3} position - The position of the HMD if currently in VR display mode, otherwise
  *     {@link Vec3(0)|Vec3.ZERO}. <em>Read-only.</em>
  * @property {Quat} orientation - The orientation of the HMD if currently in VR display mode, otherwise 
  *     {@link Quat(0)|Quat.IDENTITY}. <em>Read-only.</em>
diff --git a/interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.cpp b/interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.cpp
index ebb5ca9280..2692608106 100644
--- a/interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.cpp
+++ b/interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.cpp
@@ -9,13 +9,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "LimitlessVoiceRecognitionScriptingInterface.h"
+
 #include <QtConcurrent/QtConcurrentRun>
 
 #include <ThreadHelpers.h>
-#include <src/InterfaceLogging.h>
-#include <src/ui/AvatarInputs.h>
 
-#include "LimitlessVoiceRecognitionScriptingInterface.h"
+#include "InterfaceLogging.h"
+#include "ui/AvatarInputs.h"
 
 const float LimitlessVoiceRecognitionScriptingInterface::_audioLevelThreshold = 0.33f;
 const int LimitlessVoiceRecognitionScriptingInterface::_voiceTimeoutDuration = 2000;
diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h
index 649c444eaf..81cf775de8 100644
--- a/interface/src/scripting/MenuScriptingInterface.h
+++ b/interface/src/scripting/MenuScriptingInterface.h
@@ -32,6 +32,9 @@ class MenuItemProperties;
  * If a menu item doesn't belong to a group it is always displayed.
  *
  * @namespace Menu
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 
 /**
diff --git a/interface/src/scripting/SelectionScriptingInterface.h b/interface/src/scripting/SelectionScriptingInterface.h
index 71ff41248a..8b5c12e3dc 100644
--- a/interface/src/scripting/SelectionScriptingInterface.h
+++ b/interface/src/scripting/SelectionScriptingInterface.h
@@ -86,6 +86,9 @@ protected:
  * The <code>Selection</code> API provides a means of grouping together avatars, entities, and overlays in named lists.
  * @namespace Selection
  *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  * @example <caption>Outline an entity when it is grabbed by a controller.</caption>
  * // Create a box and copy the following text into the entity's "Script URL" field.
  * (function () {
@@ -131,7 +134,7 @@ public:
     /**jsdoc
     * Get the names of all the selection lists.
     * @function Selection.getListNames
-    * @return {list[]} An array of names of all the selection lists.
+    * @returns {list[]} An array of names of all the selection lists.
     */
     Q_INVOKABLE QStringList getListNames() const;
 
@@ -181,7 +184,7 @@ public:
     * Get the list of avatars, entities, and overlays stored in a selection list.
     * @function Selection.getList
     * @param {string} listName - The name of the selection list.
-    * @return {Selection.SelectedItemsList} The content of a selection list. If the list name doesn't exist, the function 
+    * @returns {Selection.SelectedItemsList} The content of a selection list. If the list name doesn't exist, the function 
     *     returns an empty object with no properties.
     */
     Q_INVOKABLE QVariantMap getSelectedItemsList(const QString& listName) const;
@@ -189,7 +192,7 @@ public:
     /**jsdoc
     * Get the names of the highlighted selection lists.
     * @function Selection.getHighlightedListNames
-    * @return {string[]} An array of names of the selection list currently highlight enabled.
+    * @returns {string[]} An array of names of the selection list currently highlight enabled.
     */
     Q_INVOKABLE QStringList getHighlightedListNames() const;
 
diff --git a/interface/src/scripting/SettingsScriptingInterface.h b/interface/src/scripting/SettingsScriptingInterface.h
index 9e0271601b..32d868bb24 100644
--- a/interface/src/scripting/SettingsScriptingInterface.h
+++ b/interface/src/scripting/SettingsScriptingInterface.h
@@ -18,6 +18,9 @@
 /**jsdoc
  * The Settings API provides a facility to store and retrieve values that persist between Interface runs.
  * @namespace Settings
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 
 class SettingsScriptingInterface : public QObject {
diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h
index 0b766d2097..348882e0f8 100644
--- a/interface/src/scripting/WindowScriptingInterface.h
+++ b/interface/src/scripting/WindowScriptingInterface.h
@@ -28,7 +28,11 @@
  * physics.
  *
  * @namespace Window
- * @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other 
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
+ * @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other
  *     chrome), in pixels. <em>Read-only.</em>
  * @property {number} innerHeight - The height of the drawable area of the Interface window (i.e., without borders or other
  *     chrome), in pixels. <em>Read-only.</em>
diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp
index 3053cb8855..0aa352de23 100644
--- a/interface/src/ui/AvatarInputs.cpp
+++ b/interface/src/ui/AvatarInputs.cpp
@@ -18,7 +18,7 @@
 
 static AvatarInputs* INSTANCE{ nullptr };
 
-Setting::Handle<bool> showAudioToolsSetting { QStringList { "AvatarInputs", "showAudioTools" }, false };
+Setting::Handle<bool> showAudioToolsSetting { QStringList { "AvatarInputs", "showAudioTools" }, true };
 
 AvatarInputs* AvatarInputs::getInstance() {
     if (!INSTANCE) {
diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h
index a9d1509770..e67d35e59f 100644
--- a/interface/src/ui/AvatarInputs.h
+++ b/interface/src/ui/AvatarInputs.h
@@ -26,6 +26,10 @@ class AvatarInputs : public QObject {
     /**jsdoc 
      * API to help manage your Avatar's input
      * @namespace AvatarInputs
+     *
+     * @hifi-interface
+     * @hifi-client-entity
+     *
      * @property {boolean} cameraEnabled <em>Read-only.</em>
      * @property {boolean} cameraMuted <em>Read-only.</em>
      * @property {boolean} isHMD <em>Read-only.</em>
diff --git a/interface/src/ui/DomainConnectionDialog.cpp b/interface/src/ui/DomainConnectionDialog.cpp
index c0471dc5e1..57a8d41257 100644
--- a/interface/src/ui/DomainConnectionDialog.cpp
+++ b/interface/src/ui/DomainConnectionDialog.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "DomainConnectionDialog.h"
+
 #include <QtCore/QMetaEnum>
 #include <QtWidgets/QHBoxLayout>
 #include <QtWidgets/QHeaderView>
@@ -17,8 +19,6 @@
 #include <NodeList.h>
 #include <NumericalConstants.h>
 
-#include "DomainConnectionDialog.h"
-
 DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) :
     QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint)
 {
diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp
index 55c321723e..63794da60f 100644
--- a/interface/src/ui/HMDToolsDialog.cpp
+++ b/interface/src/ui/HMDToolsDialog.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "HMDToolsDialog.h"
+
 #include <QDesktopWidget>
 #include <QDialogButtonBox>
 #include <QFormLayout>
@@ -25,8 +27,7 @@
 #include "Application.h"
 #include "MainWindow.h"
 #include "Menu.h"
-#include "ui/DialogsManager.h"
-#include "ui/HMDToolsDialog.h"
+#include "DialogsManager.h"
 
 static const int WIDTH = 350;
 static const int HEIGHT = 100;
diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp
index 34b68a123e..71e5293f30 100644
--- a/interface/src/ui/LodToolsDialog.cpp
+++ b/interface/src/ui/LodToolsDialog.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "LodToolsDialog.h"
+
 #include <QCheckBox>
 #include <QColor>
 #include <QDialogButtonBox>
@@ -23,7 +25,6 @@
 #include <LODManager.h>
 
 #include "Menu.h"
-#include "ui/LodToolsDialog.h"
 
 
 LodToolsDialog::LodToolsDialog(QWidget* parent) :
diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp
index 44089119c6..4709cc0a9c 100644
--- a/interface/src/ui/ModelsBrowser.cpp
+++ b/interface/src/ui/ModelsBrowser.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ModelsBrowser.h"
+
 #include <QDialog>
 #include <QDialogButtonBox>
 #include <QGridLayout>
@@ -27,8 +29,6 @@
 #include <NetworkAccessManager.h>
 #include <SharedUtil.h>
 
-#include "ModelsBrowser.h"
-
 const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons", "attachments" };
 
 static const QString S3_URL = "http://s3.amazonaws.com/hifi-public";
diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp
index ec5d800042..ea0f05f47f 100644
--- a/interface/src/ui/OctreeStatsDialog.cpp
+++ b/interface/src/ui/OctreeStatsDialog.cpp
@@ -9,20 +9,19 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctreeStatsDialog.h"
+
 #include <sstream>
 
 #include <QFormLayout>
 #include <QDialogButtonBox>
-
 #include <QPalette>
 #include <QColor>
 
 #include <OctreeSceneStats.h>
 
 #include "Application.h"
-
 #include "../octree/OctreePacketProcessor.h"
-#include "ui/OctreeStatsDialog.h"
 
 OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* model) :
     QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint),
diff --git a/interface/src/ui/OctreeStatsDialog.h b/interface/src/ui/OctreeStatsDialog.h
index 81bf5f251f..c05c8eadba 100644
--- a/interface/src/ui/OctreeStatsDialog.h
+++ b/interface/src/ui/OctreeStatsDialog.h
@@ -19,7 +19,6 @@
 #include <OctreeSceneStats.h>
 
 #define MAX_STATS 100
-#define DEFAULT_COLOR 0
 
 class OctreeStatsDialog : public QDialog {
     Q_OBJECT
@@ -42,7 +41,7 @@ protected:
     // Emits a 'closed' signal when this dialog is closed.
     void closeEvent(QCloseEvent*) override;
 
-    int AddStatItem(const char* caption, unsigned colorRGBA = DEFAULT_COLOR);
+    int AddStatItem(const char* caption, unsigned colorRGBA = 0);
     void RemoveStatItem(int item);
     void showAllOctreeServers();
 
diff --git a/interface/src/ui/OctreeStatsProvider.cpp b/interface/src/ui/OctreeStatsProvider.cpp
index a393660c17..8917056be4 100644
--- a/interface/src/ui/OctreeStatsProvider.cpp
+++ b/interface/src/ui/OctreeStatsProvider.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "Application.h"
+#include "OctreeStatsProvider.h"
 
-#include "../octree/OctreePacketProcessor.h"
-#include "ui/OctreeStatsProvider.h"
+#include "Application.h"
+#include "octree/OctreePacketProcessor.h"
 
 OctreeStatsProvider::OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model) :
     QObject(parent),
diff --git a/interface/src/ui/OverlayConductor.cpp b/interface/src/ui/OverlayConductor.cpp
index ed8fa53fe2..e7e3c91d13 100644
--- a/interface/src/ui/OverlayConductor.cpp
+++ b/interface/src/ui/OverlayConductor.cpp
@@ -8,13 +8,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OverlayConductor.h"
+
 #include <OffscreenUi.h>
 #include <display-plugins/CompositorHelper.h>
 
 #include "Application.h"
 #include "avatar/AvatarManager.h"
 #include "InterfaceLogging.h"
-#include "OverlayConductor.h"
 
 OverlayConductor::OverlayConductor() {
 
diff --git a/interface/src/ui/OverlayConductor.h b/interface/src/ui/OverlayConductor.h
index 1bdfe2ed79..cdd596a7bc 100644
--- a/interface/src/ui/OverlayConductor.h
+++ b/interface/src/ui/OverlayConductor.h
@@ -11,6 +11,8 @@
 #ifndef hifi_OverlayConductor_h
 #define hifi_OverlayConductor_h
 
+#include <cstdint>
+
 class OverlayConductor {
 public:
     OverlayConductor();
@@ -34,12 +36,12 @@ private:
     bool _hmdMode { false };
 
     // used by updateAvatarHasDriveInput
-    quint64 _desiredDrivingTimer { 0 };
+    uint64_t _desiredDrivingTimer { 0 };
     bool _desiredDriving { false };
     bool _currentDriving { false };
 
     // used by updateAvatarIsAtRest
-    quint64 _desiredAtRestTimer { 0 };
+    uint64_t _desiredAtRestTimer { 0 };
     bool _desiredAtRest { true };
     bool _currentAtRest { true };
 };
diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp
index 69103a40b5..39fef1d742 100644
--- a/interface/src/ui/Snapshot.cpp
+++ b/interface/src/ui/Snapshot.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Snapshot.h"
+
 #include <QtCore/QDateTime>
 #include <QtCore/QDir>
 #include <QtCore/QFile>
@@ -31,7 +33,6 @@
 #include <SharedUtil.h>
 
 #include "Application.h"
-#include "Snapshot.h"
 #include "SnapshotUploader.h"
 
 // filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg
@@ -73,9 +74,9 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
     return data;
 }
 
-QString Snapshot::saveSnapshot(QImage image, const QString& filename) {
+QString Snapshot::saveSnapshot(QImage image, const QString& filename, const QString& pathname) {
 
-    QFile* snapshotFile = savedFileForSnapshot(image, false, filename);
+    QFile* snapshotFile = savedFileForSnapshot(image, false, filename, pathname);
 
     // we don't need the snapshot file, so close it, grab its filename and delete it
     snapshotFile->close();
@@ -92,7 +93,7 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
     return static_cast<QTemporaryFile*>(savedFileForSnapshot(image, true));
 }
 
-QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename) {
+QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename, const QString& userSelectedPathname) {
 
     // adding URL to snapshot
     QUrl currentURL = DependencyManager::get<AddressManager>()->currentPublicAddress();
@@ -117,7 +118,13 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
     const int IMAGE_QUALITY = 100;
 
     if (!isTemporary) {
-        QString snapshotFullPath = snapshotsLocation.get();
+        // If user has requested specific path then use it, else use the application value
+        QString snapshotFullPath;
+        if (!userSelectedPathname.isNull()) {
+            snapshotFullPath = userSelectedPathname;
+        } else {
+            snapshotFullPath = snapshotsLocation.get();
+        }
 
         if (snapshotFullPath.isEmpty()) {
             snapshotFullPath = OffscreenUi::getExistingDirectory(nullptr, "Choose Snapshots Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h
index 62d3ed3db8..606313f3c3 100644
--- a/interface/src/ui/Snapshot.h
+++ b/interface/src/ui/Snapshot.h
@@ -16,6 +16,7 @@
 
 #include <QString>
 #include <QStandardPaths>
+#include <QUrl>
 
 #include <SettingHandle.h>
 #include <DependencyManager.h>
@@ -37,7 +38,7 @@ class Snapshot : public QObject, public Dependency {
     Q_OBJECT
     SINGLETON_DEPENDENCY
 public:
-    static QString saveSnapshot(QImage image, const QString& filename);
+    static QString saveSnapshot(QImage image, const QString& filename, const QString& pathname = QString());
     static QTemporaryFile* saveTempSnapshot(QImage image);
     static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
 
@@ -51,7 +52,10 @@ public slots:
     Q_INVOKABLE QString getSnapshotsLocation();
     Q_INVOKABLE void setSnapshotsLocation(const QString& location);
 private:
-    static QFile* savedFileForSnapshot(QImage & image, bool isTemporary, const QString& userSelectedFilename = QString());
+    static QFile* savedFileForSnapshot(QImage& image,
+                                       bool isTemporary,
+                                       const QString& userSelectedFilename = QString(),
+                                       const QString& userSelectedPathname = QString());
 };
 
 #endif // hifi_Snapshot_h
diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp
index 3c00be8358..7866e742d9 100644
--- a/interface/src/ui/SnapshotAnimated.cpp
+++ b/interface/src/ui/SnapshotAnimated.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "SnapshotAnimated.h"
+
 #include <QtCore/QDateTime>
 #include <QtCore/QObject>
 #include <QtCore/QString>
@@ -16,7 +18,6 @@
 #include <QtConcurrent/QtConcurrentRun>
 
 #include <plugins/DisplayPlugin.h>
-#include "SnapshotAnimated.h"
 
 QTimer* SnapshotAnimated::snapshotAnimatedTimer = NULL;
 qint64 SnapshotAnimated::snapshotAnimatedTimestamp = 0;
diff --git a/interface/src/ui/SnapshotUploader.cpp b/interface/src/ui/SnapshotUploader.cpp
index 37505db629..67902c1a35 100644
--- a/interface/src/ui/SnapshotUploader.cpp
+++ b/interface/src/ui/SnapshotUploader.cpp
@@ -9,11 +9,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "SnapshotUploader.h"
+
 #include <QtCore/QJsonDocument>
 #include <QtCore/QJsonArray>
+
 #include <AddressManager.h>
+
 #include "scripting/WindowScriptingInterface.h"
-#include "SnapshotUploader.h"
 
 SnapshotUploader::SnapshotUploader(QUrl inWorldLocation, QString pathname) :
     _inWorldLocation(inWorldLocation),
diff --git a/interface/src/ui/StandAloneJSConsole.cpp b/interface/src/ui/StandAloneJSConsole.cpp
index 72b6ecc547..49cf22a9eb 100644
--- a/interface/src/ui/StandAloneJSConsole.cpp
+++ b/interface/src/ui/StandAloneJSConsole.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "StandAloneJSConsole.h"
+
 #include <QMainWindow>
 #include <QDialog>
 #include <QVBoxLayout>
@@ -16,8 +18,6 @@
 #include <Application.h>
 #include <MainWindow.h>
 
-#include "StandAloneJSConsole.h"
-
 void StandAloneJSConsole::toggleConsole()  {
     QMainWindow* mainWindow = qApp->getWindow();
     if (!_jsConsole) {
diff --git a/interface/src/ui/TestingDialog.cpp b/interface/src/ui/TestingDialog.cpp
index 6f499f2a8d..6143f20ee6 100644
--- a/interface/src/ui/TestingDialog.cpp
+++ b/interface/src/ui/TestingDialog.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "ScriptEngines.h"
+#include "TestingDialog.h"
 
-#include "ui/TestingDialog.h"
 #include "Application.h"
+#include "ScriptEngines.h"
 
 TestingDialog::TestingDialog(QWidget* parent) :
     QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint),
diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h
index b80a3a70fb..808c3a4ee3 100644
--- a/interface/src/ui/overlays/ContextOverlayInterface.h
+++ b/interface/src/ui/overlays/ContextOverlayInterface.h
@@ -31,9 +31,6 @@
 #include "EntityTree.h"
 #include "ContextOverlayLogging.h"
 
-/**jsdoc
-* @namespace ContextOverlay
-*/
 class ContextOverlayInterface : public QObject, public Dependency {
     Q_OBJECT
 
diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp
index 7edc03490c..27e3bd0e2d 100644
--- a/interface/src/ui/overlays/ModelOverlay.cpp
+++ b/interface/src/ui/overlays/ModelOverlay.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ModelOverlay.h"
+
 #include <glm/gtx/quaternion.hpp>
 #include <glm/gtx/transform.hpp>
 
-#include "ModelOverlay.h"
 #include <Rig.h>
 
 #include "Application.h"
diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h
index c2f6e3e693..cf1151b46a 100644
--- a/interface/src/ui/overlays/Overlays.h
+++ b/interface/src/ui/overlays/Overlays.h
@@ -76,6 +76,10 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
  * The Overlays API provides facilities to create and interact with overlays. Overlays are 2D and 3D objects visible only to
  * yourself and that aren't persisted to the domain. They are used for UI.
  * @namespace Overlays
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  * @property {Uuid} keyboardFocusOverlay - Get or set the {@link Overlays.OverlayType|web3d} overlay that has keyboard focus.
  *     If no overlay has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid|Uuid.NULL} to 
  *     clear keyboard focus.
diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp
index 2ead4558fe..7d358e85cc 100644
--- a/libraries/animation/src/AnimClip.cpp
+++ b/libraries/animation/src/AnimClip.cpp
@@ -8,8 +8,9 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "GLMHelpers.h"
 #include "AnimClip.h"
+
+#include "GLMHelpers.h"
 #include "AnimationLogging.h"
 #include "AnimUtil.h"
 
diff --git a/libraries/animation/src/AnimExpression.cpp b/libraries/animation/src/AnimExpression.cpp
index 9777e9c6af..ddcbd01220 100644
--- a/libraries/animation/src/AnimExpression.cpp
+++ b/libraries/animation/src/AnimExpression.cpp
@@ -8,10 +8,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <StreamUtils.h>
+#include "AnimExpression.h"
+
 #include <QRegExp>
 
-#include "AnimExpression.h"
+#include <StreamUtils.h>
+
 #include "AnimationLogging.h"
 
 AnimExpression::AnimExpression(const QString& str) :
diff --git a/libraries/animation/src/AnimNode.cpp b/libraries/animation/src/AnimNode.cpp
index 80093e43db..ba8e095109 100644
--- a/libraries/animation/src/AnimNode.cpp
+++ b/libraries/animation/src/AnimNode.cpp
@@ -8,9 +8,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QtGlobal>
 #include "AnimNode.h"
 
+#include <QtGlobal>
+
 AnimNode::Pointer AnimNode::getParent() {
     return _parent.lock();
 }
diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp
index 8173845205..4169ff61a7 100644
--- a/libraries/animation/src/AnimNodeLoader.cpp
+++ b/libraries/animation/src/AnimNodeLoader.cpp
@@ -8,6 +8,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AnimNodeLoader.h"
+
 #include <QJsonDocument>
 #include <QJsonObject>
 #include <QJsonArray>
@@ -19,7 +21,6 @@
 #include "AnimBlendLinearMove.h"
 #include "AnimationLogging.h"
 #include "AnimOverlay.h"
-#include "AnimNodeLoader.h"
 #include "AnimStateMachine.h"
 #include "AnimManipulator.h"
 #include "AnimInverseKinematics.h"
diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp
index 832ab8678c..483a7999c9 100644
--- a/libraries/animation/src/AnimVariant.cpp
+++ b/libraries/animation/src/AnimVariant.cpp
@@ -1,5 +1,5 @@
 //
-//  AnimVariantMap.cpp
+//  AnimVariant.cpp
 //  library/animation
 //
 //  Created by Howard Stearns on 10/15/15.
@@ -9,11 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AnimVariant.h" // which has AnimVariant/AnimVariantMap
+
 #include <QScriptEngine>
 #include <QScriptValueIterator>
 #include <QThread>
 #include <RegisteredMetaTypes.h>
-#include "AnimVariant.h" // which has AnimVariant/AnimVariantMap
 
 const AnimVariant AnimVariant::False = AnimVariant();
 
diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h
index 03b37aef2f..d8f8a13cde 100644
--- a/libraries/animation/src/AnimationCache.h
+++ b/libraries/animation/src/AnimationCache.h
@@ -37,51 +37,49 @@ public:
      * API to manage animation cache resources.
      * @namespace AnimationCache
      *
+     * @hifi-interface
+     * @hifi-client-entity
+     * @hifi-assignment-client
+     *
      * @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
      * @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
      * @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
      * @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
      */
 
-     // Functions are copied over from ResourceCache (see ResourceCache.h for reason).
+    // Functions are copied over from ResourceCache (see ResourceCache.h for reason).
 
-     /**jsdoc
+    /**jsdoc
      * Get the list of all resource URLs.
      * @function AnimationCache.getResourceList
-     * @return {string[]}
+     * @returns {string[]}
      */
 
-     /**jsdoc
+    /**jsdoc
      * @function AnimationCache.dirty
      * @returns {Signal}
      */
 
-     /**jsdoc
+    /**jsdoc
      * @function AnimationCache.updateTotalSize
      * @param {number} deltaSize
      */
 
-     /**jsdoc
+    /**jsdoc
+     * Prefetches a resource.
      * @function AnimationCache.prefetch
-     * @param {string} url
-     * @param {object} extra
-     * @returns {object}
+     * @param {string} url - URL of the resource to prefetch.
+     * @param {object} [extra=null]
+     * @returns {Resource}
      */
 
-     /**jsdoc
+    /**jsdoc
      * Asynchronously loads a resource from the specified URL and returns it.
      * @function AnimationCache.getResource
      * @param {string} url - URL of the resource to load.
      * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
      * @param {} [extra=null]
-     * @return {Resource}
-     */
-
-     /**jsdoc
-     * Prefetches a resource.
-     * @function AnimationCache.prefetch
-     * @param {string} url - URL of the resource to prefetch.
-     * @return {Resource}
+     * @returns {Resource}
      */
 
 
diff --git a/libraries/animation/src/AnimationObject.cpp b/libraries/animation/src/AnimationObject.cpp
index 25a5743121..7f0f35b104 100644
--- a/libraries/animation/src/AnimationObject.cpp
+++ b/libraries/animation/src/AnimationObject.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AnimationObject.h"
+
 #include <QScriptEngine>
 
 #include "AnimationCache.h"
-#include "AnimationObject.h"
 
 QStringList AnimationObject::getJointNames() const {
     return qscriptvalue_cast<AnimationPointer>(thisObject())->getJointNames();
diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp
index f643719a2e..a5f79290cd 100644
--- a/libraries/audio-client/src/AudioClient.cpp
+++ b/libraries/audio-client/src/AudioClient.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AudioClient.h"
+
 #include <cstring>
 #include <math.h>
 #include <sys/stat.h>
@@ -50,8 +52,6 @@
 #include "AudioLogging.h"
 #include "AudioHelpers.h"
 
-#include "AudioClient.h"
-
 const int AudioClient::MIN_BUFFER_FRAMES = 1;
 
 const int AudioClient::MAX_BUFFER_FRAMES = 20;
diff --git a/libraries/audio-client/src/AudioIOStats.cpp b/libraries/audio-client/src/AudioIOStats.cpp
index 3bd3f4a47d..1717ad1f9c 100644
--- a/libraries/audio-client/src/AudioIOStats.cpp
+++ b/libraries/audio-client/src/AudioIOStats.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AudioIOStats.h"
+
 #include <AudioConstants.h>
 #include <MixedProcessedAudioStream.h>
 #include <NodeList.h>
@@ -16,8 +18,6 @@
 
 #include "AudioClient.h"
 
-#include "AudioIOStats.h"
-
 // This is called 1x/sec (see AudioClient) and we want it to log the last 5s
 static const int INPUT_READS_WINDOW = 5;
 static const int INPUT_UNPLAYED_WINDOW = 5;
diff --git a/libraries/audio-client/src/AudioIOStats.h b/libraries/audio-client/src/AudioIOStats.h
index 89db4942ec..45fcf365da 100644
--- a/libraries/audio-client/src/AudioIOStats.h
+++ b/libraries/audio-client/src/AudioIOStats.h
@@ -41,6 +41,10 @@ class AudioStreamStatsInterface : public QObject {
 
     /**jsdoc
      * @class AudioStats.AudioStreamStats
+     *
+     * @hifi-interface
+     * @hifi-client-entity
+     *
      * @property {number} lossRate <em>Read-only.</em>
      * @property {number} lossCount <em>Read-only.</em>
      * @property {number} lossRateWindow <em>Read-only.</em>
@@ -185,6 +189,10 @@ class AudioStatsInterface : public QObject {
     /**jsdoc
      * Audio stats from the client.
      * @namespace AudioStats
+     *
+     * @hifi-interface
+     * @hifi-client-entity
+     *
      * @property {number} pingMs <em>Read-only.</em>
      * @property {number} inputReadMsMax <em>Read-only.</em>
      * @property {number} inputUnplayedMsMax <em>Read-only.</em>
diff --git a/libraries/audio/src/AudioDynamics.h b/libraries/audio/src/AudioDynamics.h
index a43833610a..8dbc7a75cc 100644
--- a/libraries/audio/src/AudioDynamics.h
+++ b/libraries/audio/src/AudioDynamics.h
@@ -10,9 +10,9 @@
 // Inline functions to implement audio dynamics processing
 //
 
-#include <stddef.h>
 #include <math.h>
 #include <stdint.h>
+#include <stddef.h>
 
 #ifndef MAX
 #define MAX(a,b) ((a) > (b) ? (a) : (b))
@@ -21,7 +21,15 @@
 #define MIN(a,b) ((a) < (b) ? (a) : (b))
 #endif
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER)
+#define FORCEINLINE __forceinline
+#elif defined(__GNUC__)
+#define FORCEINLINE inline __attribute__((always_inline))
+#else
+#define FORCEINLINE inline
+#endif
+
+#if defined(_MSC_VER)
 #include <intrin.h>
 #define MUL64(a,b)  __emul((a), (b))
 #else
@@ -42,14 +50,14 @@
 
 #include <xmmintrin.h>
 // convert float to int using round-to-nearest
-static inline int32_t floatToInt(float x) {
+FORCEINLINE static int32_t floatToInt(float x) {
     return _mm_cvt_ss2si(_mm_load_ss(&x));
 }
 
 #else 
 
 // convert float to int using round-to-nearest
-static inline int32_t floatToInt(float x) {
+FORCEINLINE static int32_t floatToInt(float x) {
     x += (x < 0.0f ? -0.5f : 0.5f); // round
     return (int32_t)x;
 }
@@ -60,12 +68,12 @@ static const double FIXQ31 = 2147483648.0;              // convert float to Q31
 static const double DB_TO_LOG2 = 0.16609640474436813;   // convert dB to log2
 
 // convert dB to amplitude
-static inline double dBToGain(double dB) {
+FORCEINLINE static double dBToGain(double dB) {
     return pow(10.0, dB / 20.0);
 }
 
 // convert milliseconds to first-order time constant
-static inline int32_t msToTc(double ms, double sampleRate) {
+FORCEINLINE static int32_t msToTc(double ms, double sampleRate) {
     double tc = exp(-1000.0 / (ms * sampleRate));
     return (int32_t)(FIXQ31 * tc);  // Q31
 }
@@ -144,16 +152,16 @@ static const int IEEE754_EXPN_BIAS = 127;
 // x < 2^(31-LOG2_HEADROOM) returns 0x7fffffff
 // x > 2^LOG2_HEADROOM undefined
 //
-static inline int32_t peaklog2(float* input) {
+FORCEINLINE static int32_t peaklog2(float* input) {
 
     // float as integer bits
-    int32_t u = *(int32_t*)input;
+    uint32_t u = *(uint32_t*)input;
 
     // absolute value
-    int32_t peak = u & IEEE754_FABS_MASK;
+    uint32_t peak = u & IEEE754_FABS_MASK;
 
     // split into e and x - 1.0
-    int32_t e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM;
+    int e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM;
     int32_t x = (peak << IEEE754_EXPN_BITS) & 0x7fffffff;
 
     // saturate
@@ -180,19 +188,19 @@ static inline int32_t peaklog2(float* input) {
 // x < 2^(31-LOG2_HEADROOM) returns 0x7fffffff
 // x > 2^LOG2_HEADROOM undefined
 //
-static inline int32_t peaklog2(float* input0, float* input1) {
+FORCEINLINE static int32_t peaklog2(float* input0, float* input1) {
 
     // float as integer bits
-    int32_t u0 = *(int32_t*)input0;
-    int32_t u1 = *(int32_t*)input1;
+    uint32_t u0 = *(uint32_t*)input0;
+    uint32_t u1 = *(uint32_t*)input1;
 
     // max absolute value
     u0 &= IEEE754_FABS_MASK;
     u1 &= IEEE754_FABS_MASK;
-    int32_t peak = MAX(u0, u1);
+    uint32_t peak = MAX(u0, u1);
 
     // split into e and x - 1.0
-    int32_t e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM;
+    int e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM;
     int32_t x = (peak << IEEE754_EXPN_BITS) & 0x7fffffff;
 
     // saturate
@@ -219,23 +227,23 @@ static inline int32_t peaklog2(float* input0, float* input1) {
 // x < 2^(31-LOG2_HEADROOM) returns 0x7fffffff
 // x > 2^LOG2_HEADROOM undefined
 //
-static inline int32_t peaklog2(float* input0, float* input1, float* input2, float* input3) {
+FORCEINLINE static int32_t peaklog2(float* input0, float* input1, float* input2, float* input3) {
 
     // float as integer bits
-    int32_t u0 = *(int32_t*)input0;
-    int32_t u1 = *(int32_t*)input1;
-    int32_t u2 = *(int32_t*)input2;
-    int32_t u3 = *(int32_t*)input3;
+    uint32_t u0 = *(uint32_t*)input0;
+    uint32_t u1 = *(uint32_t*)input1;
+    uint32_t u2 = *(uint32_t*)input2;
+    uint32_t u3 = *(uint32_t*)input3;
 
     // max absolute value
     u0 &= IEEE754_FABS_MASK;
     u1 &= IEEE754_FABS_MASK;
     u2 &= IEEE754_FABS_MASK;
     u3 &= IEEE754_FABS_MASK;
-    int32_t peak = MAX(MAX(u0, u1), MAX(u2, u3));
+    uint32_t peak = MAX(MAX(u0, u1), MAX(u2, u3));
 
     // split into e and x - 1.0
-    int32_t e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM;
+    int e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM;
     int32_t x = (peak << IEEE754_EXPN_BITS) & 0x7fffffff;
 
     // saturate
@@ -261,7 +269,7 @@ static inline int32_t peaklog2(float* input0, float* input1, float* input2, floa
 // Count Leading Zeros
 // Emulates the CLZ (ARM) and LZCNT (x86) instruction
 //
-static inline int CLZ(uint32_t u) {
+FORCEINLINE static int CLZ(uint32_t u) {
 
     if (u == 0) {
         return 32;
@@ -294,7 +302,7 @@ static inline int CLZ(uint32_t u) {
 // Compute -log2(x) for x=[0,1] in Q31, result in Q26
 // x <= 0 returns 0x7fffffff
 //
-static inline int32_t fixlog2(int32_t x) {
+FORCEINLINE static int32_t fixlog2(int32_t x) {
 
     if (x <= 0) {
         return 0x7fffffff;
@@ -303,8 +311,7 @@ static inline int32_t fixlog2(int32_t x) {
     // split into e and x - 1.0
     uint32_t u = (uint32_t)x;
     int e = CLZ(u);
-    u <<= e;            // normalize to [0x80000000, 0xffffffff]
-    x = u & 0x7fffffff; // x - 1.0
+    x = (u << e) & 0x7fffffff;
 
     int k = x >> (31 - LOG2_TABBITS);
 
@@ -324,7 +331,7 @@ static inline int32_t fixlog2(int32_t x) {
 // Compute exp2(-x) for x=[0,32] in Q26, result in Q31
 // x <= 0 returns 0x7fffffff
 //
-static inline int32_t fixexp2(int32_t x) {
+FORCEINLINE static int32_t fixexp2(int32_t x) {
 
     if (x <= 0) {
         return 0x7fffffff;
@@ -350,12 +357,12 @@ static inline int32_t fixexp2(int32_t x) {
 }
 
 // fast TPDF dither in [-1.0f, 1.0f]
-static inline float dither() {
+FORCEINLINE static float dither() {
     static uint32_t rz = 0;
     rz = rz * 69069 + 1;
     int32_t r0 = rz & 0xffff;
     int32_t r1 = rz >> 16;
-    return (int32_t)(r0 - r1) * (1/65536.0f);
+    return (r0 - r1) * (1/65536.0f);
 }
 
 //
diff --git a/libraries/audio/src/AudioFOA.cpp b/libraries/audio/src/AudioFOA.cpp
index 718b29d1b2..16c0721047 100644
--- a/libraries/audio/src/AudioFOA.cpp
+++ b/libraries/audio/src/AudioFOA.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AudioFOA.h"
+
 #include <string.h>
 #include <assert.h>
 
-#include "AudioFOA.h"
 #include "AudioFOAData.h"
 
 #if defined(_MSC_VER)
diff --git a/libraries/audio/src/AudioGate.cpp b/libraries/audio/src/AudioGate.cpp
index 5ef5ee25b5..e9cdf832d2 100644
--- a/libraries/audio/src/AudioGate.cpp
+++ b/libraries/audio/src/AudioGate.cpp
@@ -6,12 +6,13 @@
 //  Copyright 2017 High Fidelity, Inc.
 //
 
+#include "AudioGate.h"
+
 #include <string.h>
 #include <stdlib.h>
 #include <assert.h>
 
 #include "AudioDynamics.h"
-#include "AudioGate.h"
 
 // log2 domain headroom bits above 0dB (int32_t)
 static const int LOG2_HEADROOM_Q30 = 1;
diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp
index aa951210a6..c0751c6a20 100644
--- a/libraries/audio/src/AudioHRTF.cpp
+++ b/libraries/audio/src/AudioHRTF.cpp
@@ -9,11 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AudioHRTF.h"
+
 #include <math.h>
 #include <string.h>
 #include <assert.h>
 
-#include "AudioHRTF.h"
 #include "AudioHRTFData.h"
 
 #if defined(_MSC_VER)
diff --git a/libraries/audio/src/AudioLimiter.cpp b/libraries/audio/src/AudioLimiter.cpp
index e20a070bd6..79bb84f7b1 100644
--- a/libraries/audio/src/AudioLimiter.cpp
+++ b/libraries/audio/src/AudioLimiter.cpp
@@ -6,10 +6,11 @@
 //  Copyright 2016 High Fidelity, Inc.
 //
 
+#include "AudioLimiter.h"
+
 #include <assert.h>
 
 #include "AudioDynamics.h"
-#include "AudioLimiter.h"
 
 //
 // Limiter (common)
diff --git a/libraries/audio/src/AudioLogging.cpp b/libraries/audio/src/AudioLogging.cpp
index 8a668a4bcb..73091885cb 100644
--- a/libraries/audio/src/AudioLogging.cpp
+++ b/libraries/audio/src/AudioLogging.cpp
@@ -12,5 +12,4 @@
 #include "AudioLogging.h"
 
 Q_LOGGING_CATEGORY(audio, "hifi.audio")
-Q_LOGGING_CATEGORY(audiostream, "hifi.audio-stream", QtWarningMsg)
-
+Q_LOGGING_CATEGORY(audiostream, "hifi.audio-stream")
diff --git a/libraries/audio/src/AudioReverb.cpp b/libraries/audio/src/AudioReverb.cpp
index c561231376..a7c6fefd39 100644
--- a/libraries/audio/src/AudioReverb.cpp
+++ b/libraries/audio/src/AudioReverb.cpp
@@ -6,25 +6,18 @@
 //  Copyright 2015 High Fidelity, Inc.
 //
 
+#include "AudioReverb.h"
+
 #include <stdint.h>
 #include <string.h>
 #include <math.h>
 
-#include "AudioReverb.h"
-
 #ifdef _MSC_VER
-
 #include <intrin.h>
-inline static int MULHI(int a, int b) {
-    long long c = __emul(a, b);
-    return ((int*)&c)[1];
-}
-
+#define MULHI(a,b)  ((int32_t)(__emul(a, b) >> 32))
 #else
-
-#define MULHI(a,b)  (int)(((long long)(a) * (b)) >> 32)
-
-#endif  // _MSC_VER
+#define MULHI(a,b)  ((int32_t)(((int64_t)(a) * (int64_t)(b)) >> 32))
+#endif
 
 #ifndef MAX
 #define MAX(a,b)    (((a) > (b)) ? (a) : (b))
@@ -1954,7 +1947,7 @@ static inline float dither() {
     rz = rz * 69069 + 1;
     int32_t r0 = rz & 0xffff;
     int32_t r1 = rz >> 16;
-    return (int32_t)(r0 - r1) * (1/65536.0f);
+    return (r0 - r1) * (1/65536.0f);
 }
 
 // convert float to int16_t with dither, interleave stereo
diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp
index 518fdd3c17..d8531ec216 100644
--- a/libraries/audio/src/AudioRingBuffer.cpp
+++ b/libraries/audio/src/AudioRingBuffer.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AudioRingBuffer.h"
+
 #include <cstdlib>
 #include <cstring>
 #include <functional>
@@ -21,8 +23,6 @@
 
 #include "AudioLogging.h"
 
-#include "AudioRingBuffer.h"
-
 static const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };
 static const QString DROPPED_SILENT_DEBUG { "AudioRingBuffer::addSilentSamples dropping silent samples to prevent overflow." };
 
diff --git a/libraries/audio/src/AudioSRC.cpp b/libraries/audio/src/AudioSRC.cpp
index 80cb756d04..d488eccb6a 100644
--- a/libraries/audio/src/AudioSRC.cpp
+++ b/libraries/audio/src/AudioSRC.cpp
@@ -9,11 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AudioSRC.h"
+
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include "AudioSRC.h"
 #include "AudioSRCData.h"
 
 #ifndef MAX
@@ -1200,7 +1201,7 @@ static inline float dither() {
     rz = rz * 69069 + 1;
     int32_t r0 = rz & 0xffff;
     int32_t r1 = rz >> 16;
-    return (int32_t)(r0 - r1) * (1/65536.0f);
+    return (r0 - r1) * (1/65536.0f);
 }
 
 // convert float to int16_t with dither, interleave stereo
diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp
index d60c5ba4ab..7645a674e4 100644
--- a/libraries/audio/src/InboundAudioStream.cpp
+++ b/libraries/audio/src/InboundAudioStream.cpp
@@ -9,13 +9,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "InboundAudioStream.h"
+
 #include <glm/glm.hpp>
 
 #include <NLPacket.h>
 #include <Node.h>
 #include <NodeList.h>
 
-#include "InboundAudioStream.h"
 #include "AudioLogging.h"
 
 const bool InboundAudioStream::DEFAULT_DYNAMIC_JITTER_BUFFER_ENABLED = true;
diff --git a/libraries/audio/src/InjectedAudioStream.cpp b/libraries/audio/src/InjectedAudioStream.cpp
index a06dba5389..2f357416f2 100644
--- a/libraries/audio/src/InjectedAudioStream.cpp
+++ b/libraries/audio/src/InjectedAudioStream.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "InjectedAudioStream.h"
+
 #include <cstring>
 
 #include <QtCore/QDataStream>
@@ -17,7 +19,6 @@
 #include <udt/PacketHeaders.h>
 #include <UUID.h>
 
-#include "InjectedAudioStream.h"
 #include "AudioHelpers.h"
 
 InjectedAudioStream::InjectedAudioStream(const QUuid& streamIdentifier, bool isStereo, int numStaticJitterFrames) :
diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp
index 672c0b69b3..cd93f7b52e 100644
--- a/libraries/audio/src/Sound.cpp
+++ b/libraries/audio/src/Sound.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Sound.h"
+
 #include <stdint.h>
 
 #include <glm/glm.hpp>
@@ -29,8 +31,6 @@
 #include "AudioLogging.h"
 #include "AudioSRC.h"
 
-#include "Sound.h"
-
 QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in) {
     return engine->newQObject(new SoundScriptingInterface(in), QScriptEngine::ScriptOwnership);
 }
diff --git a/libraries/audio/src/SoundCache.h b/libraries/audio/src/SoundCache.h
index d8c52635e0..347f324353 100644
--- a/libraries/audio/src/SoundCache.h
+++ b/libraries/audio/src/SoundCache.h
@@ -29,6 +29,11 @@ public:
      * API to manage sound cache resources.
      * @namespace SoundCache
      *
+     * @hifi-interface
+     * @hifi-client-entity
+     * @hifi-server-entity
+     * @hifi-assignment-client
+     *
      * @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
      * @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
      * @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
@@ -36,12 +41,12 @@ public:
      */
 
 
-     // Functions are copied over from ResourceCache (see ResourceCache.h for reason).
+    // Functions are copied over from ResourceCache (see ResourceCache.h for reason).
 
     /**jsdoc
      * Get the list of all resource URLs.
      * @function SoundCache.getResourceList
-     * @return {string[]}
+     * @returns {string[]}
      */
 
     /**jsdoc
@@ -55,10 +60,11 @@ public:
      */
 
     /**jsdoc
+     * Prefetches a resource.
      * @function SoundCache.prefetch
-     * @param {string} url
-     * @param {object} extra
-     * @returns {object}
+     * @param {string} url - URL of the resource to prefetch.
+     * @param {object} [extra=null]
+     * @returns {Resource}
      */
 
     /**jsdoc
@@ -67,14 +73,7 @@ public:
      * @param {string} url - URL of the resource to load.
      * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
      * @param {} [extra=null]
-     * @return {Resource}
-     */
-
-    /**jsdoc
-     * Prefetches a resource.
-     * @function SoundCache.prefetch
-     * @param {string} url - URL of the resource to prefetch.
-     * @return {Resource}
+     * @returns {Resource}
      */
 
 
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index 888c3bfb24..bbcdd3693d 100644
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -277,8 +277,6 @@ namespace AvatarDataPacket {
 
 const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation
 
-const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
-
 // See also static AvatarData::defaultFullAvatarModelUrl().
 const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default");
 
diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp
index b564ad6a3b..829c98a418 100644
--- a/libraries/avatars/src/AvatarHashMap.cpp
+++ b/libraries/avatars/src/AvatarHashMap.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AvatarHashMap.h"
+
 #include <QtCore/QDataStream>
 
 #include <NodeList.h>
@@ -17,7 +19,6 @@
 #include <SharedUtil.h>
 
 #include "AvatarLogging.h"
-#include "AvatarHashMap.h"
 
 AvatarHashMap::AvatarHashMap() {
     auto nodeList = DependencyManager::get<NodeList>();
diff --git a/libraries/baking/src/Baker.cpp b/libraries/baking/src/Baker.cpp
index 2adedf08a1..78a34ac5e8 100644
--- a/libraries/baking/src/Baker.cpp
+++ b/libraries/baking/src/Baker.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "ModelBakingLoggingCategory.h"
-
 #include "Baker.h"
 
+#include "ModelBakingLoggingCategory.h"
+
 bool Baker::shouldStop() {
     if (_shouldAbort) {
         setWasAborted(true);
diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp
index 0407c8508c..e3839bb95a 100644
--- a/libraries/baking/src/FBXBaker.cpp
+++ b/libraries/baking/src/FBXBaker.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "FBXBaker.h"
+
 #include <cmath> // need this include so we don't get an error looking for std::isnan
 
 #include <QtConcurrent>
@@ -31,8 +33,6 @@
 #include "ModelBakingLoggingCategory.h"
 #include "TextureBaker.h"
 
-#include "FBXBaker.h"
-
 void FBXBaker::bake() {    
     qDebug() << "FBXBaker" << _modelURL << "bake starting";
 
@@ -70,13 +70,6 @@ void FBXBaker::bakeSourceCopy() {
         return;
     }
 
-    // export the FBX with re-written texture references
-    exportScene();
-
-    if (shouldStop()) {
-        return;
-    }
-
     // check if we're already done with textures (in case we had none to re-write)
     checkIfTexturesFinished();
 }
@@ -352,27 +345,3 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
         }
     }
 }
-
-void FBXBaker::exportScene() {
-    // save the relative path to this FBX inside our passed output folder
-    auto fileName = _modelURL.fileName();
-    auto baseName = fileName.left(fileName.lastIndexOf('.'));
-    auto bakedFilename = baseName + BAKED_FBX_EXTENSION;
-
-    _bakedModelFilePath = _bakedOutputDir + "/" + bakedFilename;
-
-    auto fbxData = FBXWriter::encodeFBX(_rootNode);
-
-    QFile bakedFile(_bakedModelFilePath);
-
-    if (!bakedFile.open(QIODevice::WriteOnly)) {
-        handleError("Error opening " + _bakedModelFilePath + " for writing");
-        return;
-    }
-
-    bakedFile.write(fbxData);
-
-    _outputFiles.push_back(_bakedModelFilePath);
-
-    qCDebug(model_baking) << "Exported" << _modelURL << "with re-written paths to" << _bakedModelFilePath;
-}
diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h
index 2888a60f73..58a7bffa18 100644
--- a/libraries/baking/src/FBXBaker.h
+++ b/libraries/baking/src/FBXBaker.h
@@ -26,8 +26,6 @@
 
 #include <FBX.h>
 
-static const QString BAKED_FBX_EXTENSION = ".baked.fbx";
-
 using TextureBakerThreadGetter = std::function<QThread*()>;
 
 class FBXBaker : public ModelBaker {
@@ -51,11 +49,11 @@ private:
     void loadSourceFBX();
 
     void importScene();
+    void embedTextureMetaData();
     void rewriteAndBakeSceneModels();
     void rewriteAndBakeSceneTextures();
     void exportScene();
 
-    FBXNode _rootNode;
     FBXGeometry* _geometry;
     QHash<QString, int> _textureNameMatchCount;
     QHash<QUrl, QString> _remappedTexturePaths;
diff --git a/libraries/baking/src/JSBaker.cpp b/libraries/baking/src/JSBaker.cpp
index 9932ad633e..b19336f4ca 100644
--- a/libraries/baking/src/JSBaker.cpp
+++ b/libraries/baking/src/JSBaker.cpp
@@ -9,9 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "JSBaker.h"
+
 #include <PathUtils.h>
 
-#include "JSBaker.h"
 #include "Baker.h"
 
 const int ASCII_CHARACTERS_UPPER_LIMIT = 126;
diff --git a/libraries/baking/src/JSBaker.h b/libraries/baking/src/JSBaker.h
index b5889440cb..a7c3e62174 100644
--- a/libraries/baking/src/JSBaker.h
+++ b/libraries/baking/src/JSBaker.h
@@ -12,6 +12,8 @@
 #ifndef hifi_JSBaker_h
 #define hifi_JSBaker_h
 
+#include <QUrl>
+
 #include "Baker.h"
 #include "JSBakingLoggingCategory.h"
 
diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp
index 16a0c89c7f..ac332f4ceb 100644
--- a/libraries/baking/src/ModelBaker.cpp
+++ b/libraries/baking/src/ModelBaker.cpp
@@ -246,9 +246,9 @@ bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMe
 
 QString ModelBaker::compressTexture(QString modelTextureFileName, image::TextureUsage::Type textureType) {
 
-    QFileInfo modelTextureFileInfo{ modelTextureFileName.replace("\\", "/") };
+    QFileInfo modelTextureFileInfo { modelTextureFileName.replace("\\", "/") };
     
-    if (modelTextureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) {
+    if (modelTextureFileInfo.suffix().toLower() == BAKED_TEXTURE_KTX_EXT.mid(1)) {
         // re-baking a model that already references baked textures
         // this is an error - return from here
         handleError("Cannot re-bake a file that already references compressed textures");
@@ -273,31 +273,31 @@ QString ModelBaker::compressTexture(QString modelTextureFileName, image::Texture
         }
         auto urlToTexture = getTextureURL(modelTextureFileInfo, modelTextureFileName, !textureContent.isNull());
 
-        QString bakedTextureFileName;
+        QString baseTextureFileName;
         if (_remappedTexturePaths.contains(urlToTexture)) {
-            bakedTextureFileName = _remappedTexturePaths[urlToTexture];
+            baseTextureFileName = _remappedTexturePaths[urlToTexture];
         } else {
             // construct the new baked texture file name and file path
             // ensuring that the baked texture will have a unique name
             // even if there was another texture with the same name at a different path
-            bakedTextureFileName = createBakedTextureFileName(modelTextureFileInfo);
-            _remappedTexturePaths[urlToTexture] = bakedTextureFileName;
+            baseTextureFileName = createBaseTextureFileName(modelTextureFileInfo);
+            _remappedTexturePaths[urlToTexture] = baseTextureFileName;
         }
 
         qCDebug(model_baking).noquote() << "Re-mapping" << modelTextureFileName
-            << "to" << bakedTextureFileName;
+            << "to" << baseTextureFileName;
 
-        QString bakedTextureFilePath{
-            _bakedOutputDir + "/" + bakedTextureFileName
+        QString bakedTextureFilePath {
+            _bakedOutputDir + "/" + baseTextureFileName + BAKED_META_TEXTURE_SUFFIX
         };
 
-        textureChild = bakedTextureFileName;
+        textureChild = baseTextureFileName + BAKED_META_TEXTURE_SUFFIX;
 
         if (!_bakingTextures.contains(urlToTexture)) {
             _outputFiles.push_back(bakedTextureFilePath);
 
             // bake this texture asynchronously
-            bakeTexture(urlToTexture, textureType, _bakedOutputDir, bakedTextureFileName, textureContent);
+            bakeTexture(urlToTexture, textureType, _bakedOutputDir, baseTextureFileName, textureContent);
         }
     }
    
@@ -309,7 +309,7 @@ void ModelBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type t
     
     // start a bake for this texture and add it to our list to keep track of
     QSharedPointer<TextureBaker> bakingTexture{
-        new TextureBaker(textureURL, textureType, outputDir, bakedFilename, textureContent),
+        new TextureBaker(textureURL, textureType, outputDir, "../", bakedFilename, textureContent),
         &TextureBaker::deleteLater
     };
     
@@ -484,30 +484,30 @@ void ModelBaker::checkIfTexturesFinished() {
         } else {
             qCDebug(model_baking) << "Finished baking, emitting finished" << _modelURL;
 
+            texturesFinished();
+
             setIsFinished(true);
         }
     }
 }
 
-QString ModelBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) {
+QString ModelBaker::createBaseTextureFileName(const QFileInfo& textureFileInfo) {
     // first make sure we have a unique base name for this texture
     // in case another texture referenced by this model has the same base name
     auto& nameMatches = _textureNameMatchCount[textureFileInfo.baseName()];
 
-    QString bakedTextureFileName{ textureFileInfo.completeBaseName() };
+    QString baseTextureFileName{ textureFileInfo.completeBaseName() };
 
     if (nameMatches > 0) {
         // there are already nameMatches texture with this name
         // append - and that number to our baked texture file name so that it is unique
-        bakedTextureFileName += "-" + QString::number(nameMatches);
+        baseTextureFileName += "-" + QString::number(nameMatches);
     }
 
-    bakedTextureFileName += BAKED_TEXTURE_EXT;
-
     // increment the number of name matches
     ++nameMatches;
 
-    return bakedTextureFileName;
+    return baseTextureFileName;
 }
 
 void ModelBaker::setWasAborted(bool wasAborted) {
@@ -519,3 +519,91 @@ void ModelBaker::setWasAborted(bool wasAborted) {
         }
     }
 }
+
+void ModelBaker::texturesFinished() {
+    embedTextureMetaData();
+    exportScene();
+}
+
+void ModelBaker::embedTextureMetaData() {
+    std::vector<FBXNode> embeddedTextureNodes;
+
+    for (FBXNode& rootChild : _rootNode.children) {
+        if (rootChild.name == "Objects") {
+            qlonglong maxId = 0;
+            for (auto &child : rootChild.children) {
+                if (child.properties.length() == 3) {
+                    maxId = std::max(maxId, child.properties[0].toLongLong());
+                }
+            }
+
+            for (auto& object : rootChild.children) {
+                if (object.name == "Texture") {
+                    QVariant relativeFilename;
+                    for (auto& child : object.children) {
+                        if (child.name == "RelativeFilename") {
+                            relativeFilename = child.properties[0];
+                            break;
+                        }
+                    }
+
+                    if (relativeFilename.isNull()
+                        || !relativeFilename.toString().endsWith(BAKED_META_TEXTURE_SUFFIX)) {
+                        continue;
+                    }
+                    if (object.properties.length() < 2) {
+                        qWarning() << "Found texture with unexpected number of properties: " << object.name;
+                        continue;
+                    }
+
+                    FBXNode videoNode;
+                    videoNode.name = "Video";
+                    videoNode.properties.append(++maxId);
+                    videoNode.properties.append(object.properties[1]);
+                    videoNode.properties.append("Clip");
+
+                    QString bakedTextureFilePath {
+                        _bakedOutputDir + "/" + relativeFilename.toString()
+                    };
+
+                    QFile textureFile { bakedTextureFilePath };
+                    if (!textureFile.open(QIODevice::ReadOnly)) {
+                        qWarning() << "Failed to open: " << bakedTextureFilePath;
+                        continue;
+                    }
+
+                    videoNode.children.append({ "RelativeFilename", { relativeFilename }, { } });
+                    videoNode.children.append({ "Content", { textureFile.readAll() }, { } });
+
+                    rootChild.children.append(videoNode);
+
+                    textureFile.close();
+                }
+            }
+        }
+    }
+}
+
+void ModelBaker::exportScene() {
+    // save the relative path to this FBX inside our passed output folder
+    auto fileName = _modelURL.fileName();
+    auto baseName = fileName.left(fileName.lastIndexOf('.'));
+    auto bakedFilename = baseName + BAKED_FBX_EXTENSION;
+
+    _bakedModelFilePath = _bakedOutputDir + "/" + bakedFilename;
+
+    auto fbxData = FBXWriter::encodeFBX(_rootNode);
+
+    QFile bakedFile(_bakedModelFilePath);
+
+    if (!bakedFile.open(QIODevice::WriteOnly)) {
+        handleError("Error opening " + _bakedModelFilePath + " for writing");
+        return;
+    }
+
+    bakedFile.write(fbxData);
+
+    _outputFiles.push_back(_bakedModelFilePath);
+
+    qCDebug(model_baking) << "Exported" << _modelURL << "with re-written paths to" << _bakedModelFilePath;
+}
diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h
index 6fd529af92..1fd77ab761 100644
--- a/libraries/baking/src/ModelBaker.h
+++ b/libraries/baking/src/ModelBaker.h
@@ -29,6 +29,8 @@
 using TextureBakerThreadGetter = std::function<QThread*()>;
 using GetMaterialIDCallback = std::function <int(int)>;
 
+static const QString BAKED_FBX_EXTENSION = ".baked.fbx";
+
 class ModelBaker : public Baker {
     Q_OBJECT
 
@@ -49,7 +51,11 @@ public slots:
 
 protected:
     void checkIfTexturesFinished();
+    void texturesFinished();
+    void embedTextureMetaData();
+    void exportScene();
     
+    FBXNode _rootNode;
     QHash<QByteArray, QByteArray> _textureContentMap;
     QUrl _modelURL;
     QString _bakedOutputDir;
@@ -63,7 +69,7 @@ private slots:
     void handleAbortedTexture();
 
 private:
-    QString createBakedTextureFileName(const QFileInfo & textureFileInfo);
+    QString createBaseTextureFileName(const QFileInfo & textureFileInfo);
     QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded = false);
     void bakeTexture(const QUrl & textureURL, image::TextureUsage::Type textureType, const QDir & outputDir, 
                      const QString & bakedFilename, const QByteArray & textureContent);
diff --git a/libraries/baking/src/OBJBaker.cpp b/libraries/baking/src/OBJBaker.cpp
index 85771ff2e3..cf62bc4fa8 100644
--- a/libraries/baking/src/OBJBaker.cpp
+++ b/libraries/baking/src/OBJBaker.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OBJBaker.h"
+
 #include <PathUtils.h>
 #include <NetworkAccessManager.h>
 
-#include "OBJBaker.h"
 #include "OBJReader.h"
 #include "FBXWriter.h"
 
@@ -147,31 +148,7 @@ void OBJBaker::bakeOBJ() {
     auto geometry = reader.readOBJ(objData, QVariantHash(), combineParts, _modelURL);
 
     // Write OBJ Data as FBX tree nodes
-    FBXNode rootNode;
-    createFBXNodeTree(rootNode, *geometry);
-
-    // Serialize the resultant FBX tree
-    auto encodedFBX = FBXWriter::encodeFBX(rootNode);
-
-    // Export as baked FBX
-    auto fileName = _modelURL.fileName();
-    auto baseName = fileName.left(fileName.lastIndexOf('.'));
-    auto bakedFilename = baseName + ".baked.fbx";
-
-    _bakedModelFilePath = _bakedOutputDir + "/" + bakedFilename;
-
-    QFile bakedFile;
-    bakedFile.setFileName(_bakedModelFilePath);
-    if (!bakedFile.open(QIODevice::WriteOnly)) {
-        handleError("Error opening " + _bakedModelFilePath + " for writing");
-        return;
-    }
-
-    bakedFile.write(encodedFBX);
-
-    // Export successful
-    _outputFiles.push_back(_bakedModelFilePath);
-    qCDebug(model_baking) << "Exported" << _modelURL << "to" << _bakedModelFilePath;
+    createFBXNodeTree(_rootNode, *geometry);
 
     checkIfTexturesFinished();
 }
@@ -203,15 +180,17 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
     globalSettingsNode.children = { properties70Node };
 
     // Generating Object node
-    _objectNode.name = OBJECTS_NODE_NAME;
+    FBXNode objectNode;
+    objectNode.name = OBJECTS_NODE_NAME;
 
     // Generating Object node's child - Geometry node 
     FBXNode geometryNode;
     geometryNode.name = GEOMETRY_NODE_NAME;
+    NodeID geometryID;
     {
-        _geometryID = nextNodeID();
+        geometryID = nextNodeID();
         geometryNode.properties = {
-            _geometryID,
+            geometryID,
             GEOMETRY_NODE_NAME,
             MESH
         };
@@ -226,12 +205,13 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
     // Generating Object node's child - Model node
     FBXNode modelNode;
     modelNode.name = MODEL_NODE_NAME;
+    NodeID modelID;
     {
-        _modelID = nextNodeID();
-        modelNode.properties = { _modelID, MODEL_NODE_NAME, MESH };
+        modelID = nextNodeID();
+        modelNode.properties = { modelID, MODEL_NODE_NAME, MESH };
     }
 
-    _objectNode.children = { geometryNode, modelNode };
+    objectNode.children = { geometryNode, modelNode };
 
     // Generating Objects node's child - Material node
     auto& meshParts = geometry.meshes[0].parts;
@@ -247,7 +227,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
             setMaterialNodeProperties(materialNode, meshPart.materialID, geometry);
         }
 
-        _objectNode.children.append(materialNode);
+        objectNode.children.append(materialNode);
     }
 
     // Generating Texture Node
@@ -257,13 +237,13 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
         QString material = meshParts[i].materialID;
         FBXMaterial currentMaterial = geometry.materials[material];
         if (!currentMaterial.albedoTexture.filename.isEmpty() || !currentMaterial.specularTexture.filename.isEmpty()) {
-            _textureID = nextNodeID();
-            _mapTextureMaterial.emplace_back(_textureID, i);
+            auto textureID = nextNodeID();
+            _mapTextureMaterial.emplace_back(textureID, i);
 
             FBXNode textureNode;
             {
                 textureNode.name = TEXTURE_NODE_NAME;
-                textureNode.properties = { _textureID };
+                textureNode.properties = { textureID, "texture" + QString::number(textureID) };
             }
 
             // Texture node child - TextureName node
@@ -295,7 +275,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
 
             textureNode.children = { textureNameNode, relativeFilenameNode };
 
-            _objectNode.children.append(textureNode);
+            objectNode.children.append(textureNode);
         }
     }
 
@@ -306,14 +286,14 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
     // connect Geometry to Model 
     FBXNode cNode;
     cNode.name = C_NODE_NAME;
-    cNode.properties = { CONNECTIONS_NODE_PROPERTY, _geometryID, _modelID };
+    cNode.properties = { CONNECTIONS_NODE_PROPERTY, geometryID, modelID };
     connectionsNode.children = { cNode };
 
     // connect all materials to model
     for (auto& materialID : _materialIDs) {
         FBXNode cNode;
         cNode.name = C_NODE_NAME;
-        cNode.properties = { CONNECTIONS_NODE_PROPERTY, materialID, _modelID };
+        cNode.properties = { CONNECTIONS_NODE_PROPERTY, materialID, modelID };
         connectionsNode.children.append(cNode);
     }
 
@@ -341,7 +321,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
     }
 
     // Make all generated nodes children of rootNode
-    rootNode.children = { globalSettingsNode, _objectNode, connectionsNode };
+    rootNode.children = { globalSettingsNode, objectNode, connectionsNode };
 }
 
 // Set properties for material nodes
diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h
index e888c7b1d8..8e49692d35 100644
--- a/libraries/baking/src/OBJBaker.h
+++ b/libraries/baking/src/OBJBaker.h
@@ -43,12 +43,9 @@ private:
     void setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry);
     NodeID nextNodeID() { return _nodeID++; }
 
+
     NodeID _nodeID { 0 };
-    NodeID _geometryID;
-    NodeID _modelID;
     std::vector<NodeID> _materialIDs;
-    NodeID _textureID;
     std::vector<std::pair<NodeID, int>> _mapTextureMaterial;
-    FBXNode _objectNode;
 };
 #endif // hifi_OBJBaker_h
diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp
index b6edd07965..b6957a2712 100644
--- a/libraries/baking/src/TextureBaker.cpp
+++ b/libraries/baking/src/TextureBaker.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "TextureBaker.h"
+
 #include <QtCore/QDir>
 #include <QtCore/QEventLoop>
 #include <QtCore/QFile>
@@ -18,26 +20,28 @@
 #include <ktx/KTX.h>
 #include <NetworkAccessManager.h>
 #include <SharedUtil.h>
+#include <TextureMeta.h>
 
 #include "ModelBakingLoggingCategory.h"
 
-#include "TextureBaker.h"
-
-const QString BAKED_TEXTURE_EXT = ".ktx";
+const QString BAKED_TEXTURE_KTX_EXT = ".ktx";
+const QString BAKED_TEXTURE_BCN_SUFFIX = "_bcn.ktx";
+const QString BAKED_META_TEXTURE_SUFFIX = ".texmeta.json";
 
 TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType,
-                           const QDir& outputDirectory, const QString& bakedFilename,
-                           const QByteArray& textureContent) :
+                           const QDir& outputDirectory, const QString& metaTexturePathPrefix,
+                           const QString& baseFilename, const QByteArray& textureContent) :
     _textureURL(textureURL),
     _originalTexture(textureContent),
     _textureType(textureType),
+    _baseFilename(baseFilename),
     _outputDirectory(outputDirectory),
-    _bakedTextureFileName(bakedFilename)
+    _metaTexturePathPrefix(metaTexturePathPrefix)
 {
-    if (bakedFilename.isEmpty()) {
+    if (baseFilename.isEmpty()) {
         // figure out the baked texture filename
         auto originalFilename = textureURL.fileName();
-        _bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT;
+        _baseFilename = originalFilename.left(originalFilename.lastIndexOf('.'));
     }
 }
 
@@ -118,6 +122,19 @@ void TextureBaker::processTexture() {
     auto hashData = QCryptographicHash::hash(_originalTexture, QCryptographicHash::Md5);
     std::string hash = hashData.toHex().toStdString();
 
+    TextureMeta meta;
+
+    {
+        auto filePath = _outputDirectory.absoluteFilePath(_textureURL.fileName());
+        QFile file { filePath };
+        if (!file.open(QIODevice::WriteOnly) || file.write(_originalTexture) == -1) {
+            handleError("Could not write original texture for " + _textureURL.toString());
+            return;
+        }
+        _outputFiles.push_back(filePath);
+        meta.original = _metaTexturePathPrefix +_textureURL.fileName();
+    }
+
     // IMPORTANT: _originalTexture is empty past this point
     auto processedTexture = image::processImage(std::move(_originalTexture), _textureURL.toString().toStdString(),
                                                 ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, _abortProcessing);
@@ -140,17 +157,38 @@ void TextureBaker::processTexture() {
         return;
     }
 
-    const char* data = reinterpret_cast<const char*>(memKTX->_storage->data());
-    const size_t length = memKTX->_storage->size();
+    const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat());
+    if (name == nullptr) {
+        handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString());
+        return;
+    }
 
     // attempt to write the baked texture to the destination file path
-    auto filePath = _outputDirectory.absoluteFilePath(_bakedTextureFileName);
-    QFile bakedTextureFile { filePath };
+    {
+        const char* data = reinterpret_cast<const char*>(memKTX->_storage->data());
+        const size_t length = memKTX->_storage->size();
 
-    if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) {
-        handleError("Could not write baked texture for " + _textureURL.toString());
-    } else {
+        auto fileName = _baseFilename + BAKED_TEXTURE_BCN_SUFFIX;
+        auto filePath = _outputDirectory.absoluteFilePath(fileName);
+        QFile bakedTextureFile { filePath };
+        if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) {
+            handleError("Could not write baked texture for " + _textureURL.toString());
+            return;
+        }
         _outputFiles.push_back(filePath);
+        meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName;
+    }
+
+
+    {
+        auto data = meta.serialize();
+        _metaTextureFileName = _outputDirectory.absoluteFilePath(_baseFilename + BAKED_META_TEXTURE_SUFFIX);
+        QFile file { _metaTextureFileName };
+        if (!file.open(QIODevice::WriteOnly) || file.write(data) == -1) {
+            handleError("Could not write meta texture for " + _textureURL.toString());
+        } else {
+            _outputFiles.push_back(_metaTextureFileName);
+        }
     }
 
     qCDebug(model_baking) << "Baked texture" << _textureURL;
diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h
index 90ecfe52f7..54839c001a 100644
--- a/libraries/baking/src/TextureBaker.h
+++ b/libraries/baking/src/TextureBaker.h
@@ -15,28 +15,29 @@
 #include <QtCore/QObject>
 #include <QtCore/QUrl>
 #include <QtCore/QRunnable>
+#include <QDir>
 #include <QImageReader>
 
 #include <image/Image.h>
 
 #include "Baker.h"
 
-extern const QString BAKED_TEXTURE_EXT;
+extern const QString BAKED_TEXTURE_KTX_EXT;
+extern const QString BAKED_META_TEXTURE_SUFFIX;
 
 class TextureBaker : public Baker {
     Q_OBJECT
 
 public:
     TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType,
-                 const QDir& outputDirectory, const QString& bakedFilename = QString(),
-                 const QByteArray& textureContent = QByteArray());
+                 const QDir& outputDirectory, const QString& metaTexturePathPrefix = "",
+                 const QString& baseFilename = QString(), const QByteArray& textureContent = QByteArray());
 
     const QByteArray& getOriginalTexture() const { return _originalTexture; }
 
     QUrl getTextureURL() const { return _textureURL; }
 
-    QString getDestinationFilePath() const { return _outputDirectory.absoluteFilePath(_bakedTextureFileName); }
-    QString getBakedTextureFileName() const { return _bakedTextureFileName; }
+    QString getMetaTextureFileName() const { return _metaTextureFileName; }
 
     virtual void setWasAborted(bool wasAborted) override;
 
@@ -58,8 +59,10 @@ private:
     QByteArray _originalTexture;
     image::TextureUsage::Type _textureType;
 
+    QString _baseFilename;
     QDir _outputDirectory;
-    QString _bakedTextureFileName;
+    QString _metaTextureFileName;
+    QString _metaTexturePathPrefix;
 
     std::atomic<bool> _abortProcessing { false };
 };
diff --git a/libraries/controllers/src/controllers/DeviceProxy.cpp b/libraries/controllers/src/controllers/DeviceProxy.cpp
deleted file mode 100644
index f03354c52d..0000000000
--- a/libraries/controllers/src/controllers/DeviceProxy.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-//  Created by Bradley Austin Davis on 2015/10/18
-//  (based on UserInputMapper inner class created by Sam Gateau on 4/27/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
-//
-
-// NOTE: we don't need to include this header unless/until we add additional symbols.
-//       By removing this header we prevent these warnings on windows:
-//
-//           warning LNK4221: This object file does not define any previously undefined public symbols, 
-//           so it will not be used by any link operation that consumes this library
-//
-//#include "DeviceProxy.h"
-
diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp
index 6f0296c09d..967838ef84 100644
--- a/libraries/controllers/src/controllers/Pose.cpp
+++ b/libraries/controllers/src/controllers/Pose.cpp
@@ -6,13 +6,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Pose.h"
+
 #include <QtScript/QScriptEngine>
 #include <QtScript/QScriptValue>
 
 #include <RegisteredMetaTypes.h>
 
-#include "Pose.h"
-
 namespace controller {
 
     Pose::Pose(const vec3& translation, const quat& rotation,
diff --git a/libraries/controllers/src/controllers/impl/Mapping.cpp b/libraries/controllers/src/controllers/impl/Mapping.cpp
deleted file mode 100644
index dd8e1c1d48..0000000000
--- a/libraries/controllers/src/controllers/impl/Mapping.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-//  Created by Bradley Austin Davis 2015/10/09
-//  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
-//
-
-// NOTE: we don't need to include this header unless/until we add additional symbols.
-//       By removing this header we prevent these warnings on windows:
-//
-//           warning LNK4221: This object file does not define any previously undefined public symbols, 
-//           so it will not be used by any link operation that consumes this library
-//
-//#include "Mapping.h"
diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
index 3c3858e2ba..5bc7357dd7 100644
--- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
+++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
@@ -54,6 +54,9 @@ class UserInputMapper;
  * </ul>
  *
  * @class MappingObject
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 
 /**jsdoc
diff --git a/libraries/controllers/src/controllers/impl/Route.cpp b/libraries/controllers/src/controllers/impl/Route.cpp
deleted file mode 100644
index c74f809195..0000000000
--- a/libraries/controllers/src/controllers/impl/Route.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-//  Created by Bradley Austin Davis 2015/10/09
-//  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
-//
-
-// NOTE: we don't need to include this header unless/until we add additional symbols.
-//       By removing this header we prevent these warnings on windows:
-//
-//           warning LNK4221: This object file does not define any previously undefined public symbols, 
-//           so it will not be used by any link operation that consumes this library
-//
-//#include "Route.h"
diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
index d33f3e3383..804709ebfa 100644
--- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
+++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
@@ -36,6 +36,9 @@ class ScriptingInterface;
  * types.<p>
  *
  * @class RouteObject
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 
 // TODO migrate functionality to a RouteBuilder class and make the proxy defer to that 
diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp
deleted file mode 100644
index f833eedb60..0000000000
--- a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-//  Created by Bradley Austin Davis 2015/10/23
-//  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
-//
-
-// NOTE: we don't need to include this header unless/until we add additional symbols.
-//       By removing this header we prevent these warnings on windows:
-//
-//           warning LNK4221: This object file does not define any previously undefined public symbols, 
-//           so it will not be used by any link operation that consumes this library
-//
-//#include "EndpointConditional.h"
\ No newline at end of file
diff --git a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp
deleted file mode 100644
index 5dea1de34e..0000000000
--- a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-//  Created by Bradley Austin Davis 2015/10/23
-//  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
-//
-
-// NOTE: we don't need to include this header unless/until we add additional symbols.
-//       By removing this header we prevent these warnings on windows:
-//
-//           warning LNK4221: This object file does not define any previously undefined public symbols, 
-//           so it will not be used by any link operation that consumes this library
-//
-//#include "ArrayEndpoint.h"
\ No newline at end of file
diff --git a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp
deleted file mode 100644
index 89bbe5d777..0000000000
--- a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-//  Created by Bradley Austin Davis 2015/10/23
-//  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
-//
-
-// NOTE: we don't need to include this header unless/until we add additional symbols.
-//       By removing this header we prevent these warnings on windows:
-//
-//           warning LNK4221: This object file does not define any previously undefined public symbols, 
-//           so it will not be used by any link operation that consumes this library
-//
-//#include "StandardEndpoint.h"
\ No newline at end of file
diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp
deleted file mode 100644
index 8bd3d2db89..0000000000
--- a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-//  Created by Bradley Austin Davis 2015/10/25
-//  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
-//
-
-// NOTE: we don't need to include this header unless/until we add additional symbols.
-//       By removing this header we prevent these warnings on windows:
-//
-//           warning LNK4221: This object file does not define any previously undefined public symbols, 
-//           so it will not be used by any link operation that consumes this library
-//
-//#include "ConstrainToIntegerFilter.h"
diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp
deleted file mode 100644
index f1abc8cecd..0000000000
--- a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-//  Created by Bradley Austin Davis 2015/10/25
-//  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
-//
-
-// NOTE: we don't need to include this header unless/until we add additional symbols.
-//       By removing this header we prevent these warnings on windows:
-//
-//           warning LNK4221: This object file does not define any previously undefined public symbols, 
-//           so it will not be used by any link operation that consumes this library
-//
-//#include "ConstrainToPositiveIntegerFilter.h"
diff --git a/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp b/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp
deleted file mode 100644
index 5407c6dd1d..0000000000
--- a/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-//  Created by Bradley Austin Davis 2015/10/25
-//  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
-//
-
-// NOTE: we don't need to include this header unless/until we add additional symbols.
-//       By removing this header we prevent these warnings on windows:
-//
-//           warning LNK4221: This object file does not define any previously undefined public symbols, 
-//           so it will not be used by any link operation that consumes this library
-//
-//#include "InvertFilter.h"
diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h
index bc6ed63363..fb712c26fa 100644
--- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h
+++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h
@@ -174,6 +174,10 @@ private:
 
 /**jsdoc
  * @namespace Reticle
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  * @property {boolean} allowMouseCapture
  * @property {number} depth
  * @property {Vec2} maximumPosition
diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
index 4b33565bfd..3cfc0651d8 100644
--- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
+++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp
@@ -336,9 +336,8 @@ void OpenGLDisplayPlugin::deactivate() {
 
     _container->showDisplayPluginsTools(false);
     if (!_container->currentDisplayActions().isEmpty()) {
-        auto menu = _container->getPrimaryMenu();
         foreach(auto itemInfo, _container->currentDisplayActions()) {
-            menu->removeMenuItem(itemInfo.first, itemInfo.second);
+            _container->removeMenuItem(itemInfo.first, itemInfo.second);
         }
         _container->currentDisplayActions().clear();
     }
diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp
index 280b44cec0..12da599575 100644
--- a/libraries/embedded-webserver/src/HTTPConnection.cpp
+++ b/libraries/embedded-webserver/src/HTTPConnection.cpp
@@ -9,15 +9,15 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "HTTPConnection.h"
 
 #include <QBuffer>
 #include <QCryptographicHash>
 #include <QTcpSocket>
+#include <QUrlQuery>
 
-#include "HTTPConnection.h"
 #include "EmbeddedWebserverLogging.h"
 #include "HTTPManager.h"
-#include <QUrlQuery>
 
 const char* HTTPConnection::StatusCode200 = "200 OK";
 const char* HTTPConnection::StatusCode301 = "301 Moved Permanently";
diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp
index bd1b545412..db6fc8e084 100644
--- a/libraries/embedded-webserver/src/HTTPManager.cpp
+++ b/libraries/embedded-webserver/src/HTTPManager.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "HTTPManager.h"
+
 #include <QtCore/QCoreApplication>
 #include <QtCore/QDebug>
 #include <QtCore/QFile>
@@ -18,7 +20,6 @@
 
 #include "HTTPConnection.h"
 #include "EmbeddedWebserverLogging.h"
-#include "HTTPManager.h"
 
 const int SOCKET_ERROR_EXIT_CODE = 2;
 const int SOCKET_CHECK_INTERVAL_IN_MS = 30000;
diff --git a/libraries/embedded-webserver/src/HTTPSConnection.cpp b/libraries/embedded-webserver/src/HTTPSConnection.cpp
index 7af14ce0a7..f5473d577f 100644
--- a/libraries/embedded-webserver/src/HTTPSConnection.cpp
+++ b/libraries/embedded-webserver/src/HTTPSConnection.cpp
@@ -9,9 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "EmbeddedWebserverLogging.h"
 #include "HTTPSConnection.h"
 
+#include "EmbeddedWebserverLogging.h"
+
 HTTPSConnection::HTTPSConnection(QSslSocket* sslSocket, HTTPSManager* parentManager) :
     HTTPConnection(sslSocket, parentManager)
 {
diff --git a/libraries/embedded-webserver/src/HTTPSManager.cpp b/libraries/embedded-webserver/src/HTTPSManager.cpp
index ee61f15457..8ba44f98ac 100644
--- a/libraries/embedded-webserver/src/HTTPSManager.cpp
+++ b/libraries/embedded-webserver/src/HTTPSManager.cpp
@@ -9,12 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "HTTPSManager.h"
+
 #include <QtNetwork/QSslSocket>
 
 #include "HTTPSConnection.h"
 
-#include "HTTPSManager.h"
-
 HTTPSManager::HTTPSManager(QHostAddress listenAddress, quint16 port, const QSslCertificate& certificate, const QSslKey& privateKey,
                            const QString& documentRoot, HTTPSRequestHandler* requestHandler, QObject* parent) :
     HTTPManager(listenAddress, port, documentRoot, requestHandler, parent),
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index 3ffb1f8b92..91f6f02897 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -306,7 +306,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
     }
 }
 
-void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction) {
+void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, render::Transaction& transaction) {
     PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size());
     PerformanceTimer pt("change");
     std::unordered_set<EntityItemID> changedEntities;
@@ -367,7 +367,9 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
 
         // prioritize and sort the renderables
         uint64_t sortStart = usecTimestampNow();
-        PrioritySortUtil::PriorityQueue<SortableRenderer> sortedRenderables(view);
+
+        const auto& views = _viewState->getConicalViews();
+        PrioritySortUtil::PriorityQueue<SortableRenderer> sortedRenderables(views);
         {
             PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
             std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr = _renderablesToUpdate.begin();
@@ -427,9 +429,8 @@ void EntityTreeRenderer::update(bool simulate) {
             if (scene) {
                 render::Transaction transaction;
                 addPendingEntities(scene, transaction);
-                ViewFrustum view;
-                _viewState->copyCurrentViewFrustum(view);
-                updateChangedEntities(scene, view, transaction);
+
+                updateChangedEntities(scene, transaction);
                 scene->enqueueTransaction(transaction);
             }
         }
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h
index a425ad4676..2736fcc4d1 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.h
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.h
@@ -154,7 +154,7 @@ protected:
 
 private:
     void addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction);
-    void updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction);
+    void updateChangedEntities(const render::ScenePointer& scene, render::Transaction& transaction);
     EntityRendererPointer renderableForEntity(const EntityItemPointer& entity) const { return renderableForEntityId(entity->getID()); }
     render::ItemID renderableIdForEntity(const EntityItemPointer& entity) const { return renderableIdForEntityId(entity->getID()); }
 
diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
index b3a4a1a1ab..ee77646920 100644
--- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp
@@ -23,8 +23,8 @@ using namespace render::entities;
 static uint8_t CUSTOM_PIPELINE_NUMBER = 0;
 static gpu::Stream::FormatPointer _vertexFormat;
 static std::weak_ptr<gpu::Pipeline> _texturedPipeline;
-// FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 11 to avoid collisions
-static int32_t PARTICLE_UNIFORM_SLOT { 11 };
+// FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 12 to avoid collisions
+static int32_t PARTICLE_UNIFORM_SLOT { 12 };
 
 static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch) {
     auto texturedPipeline = _texturedPipeline.lock();
diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
index 394ab08dfb..d571eac35c 100644
--- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
@@ -34,8 +34,8 @@ using namespace render::entities;
 
 static uint8_t CUSTOM_PIPELINE_NUMBER { 0 };
 static const int32_t PAINTSTROKE_TEXTURE_SLOT { 0 };
-// FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 11 to avoid collisions
-static const int32_t PAINTSTROKE_UNIFORM_SLOT { 11 };
+// FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 12 to avoid collisions
+static const int32_t PAINTSTROKE_UNIFORM_SLOT { 12 };
 static gpu::Stream::FormatPointer polylineFormat;
 static gpu::PipelinePointer polylinePipeline;
 #ifdef POLYLINE_ENTITY_USE_FADE_EFFECT
diff --git a/libraries/entities/src/DeleteEntityOperator.cpp b/libraries/entities/src/DeleteEntityOperator.cpp
index 8e6e545401..1dca171ae3 100644
--- a/libraries/entities/src/DeleteEntityOperator.cpp
+++ b/libraries/entities/src/DeleteEntityOperator.cpp
@@ -9,12 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "DeleteEntityOperator.h"
+
 #include "EntityItem.h"
 #include "EntityTree.h"
 #include "EntityTreeElement.h"
-
 #include "EntitiesLogging.h"
-#include "DeleteEntityOperator.h"
 
 DeleteEntityOperator::DeleteEntityOperator(EntityTreePointer tree, const EntityItemID& searchEntityID) :
     _tree(tree),
diff --git a/libraries/entities/src/DeleteEntityOperator.h b/libraries/entities/src/DeleteEntityOperator.h
index 135949a53d..3b3ee2a868 100644
--- a/libraries/entities/src/DeleteEntityOperator.h
+++ b/libraries/entities/src/DeleteEntityOperator.h
@@ -12,6 +12,12 @@
 #ifndef hifi_DeleteEntityOperator_h
 #define hifi_DeleteEntityOperator_h
 
+#include <QSet>
+
+#include <AACube.h>
+
+#include "EntityItem.h"
+
 class EntityToDeleteDetails {
 public:
     EntityItemPointer entity;
diff --git a/libraries/entities/src/DiffTraversal.cpp b/libraries/entities/src/DiffTraversal.cpp
index 764c420197..36d1f41267 100644
--- a/libraries/entities/src/DiffTraversal.cpp
+++ b/libraries/entities/src/DiffTraversal.cpp
@@ -13,6 +13,7 @@
 
 #include <OctreeUtils.h>
 
+#include "EntityPriorityQueue.h"
 
 DiffTraversal::Waypoint::Waypoint(EntityTreeElementPointer& element) : _nextIndex(0) {
     assert(element);
@@ -36,20 +37,9 @@ void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::Visi
             while (_nextIndex < NUMBER_OF_CHILDREN) {
                 EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
                 ++_nextIndex;
-                if (nextElement) {
-                    if (!view.usesViewFrustum) {
-                        // No LOD truncation if we aren't using the view frustum
-                        next.element = nextElement;
-                        return;
-                    } else if (view.viewFrustum.cubeIntersectsKeyhole(nextElement->getAACube())) {
-                        // check for LOD truncation
-                        float distance = glm::distance(view.viewFrustum.getPosition(), nextElement->getAACube().calcCenter()) + MIN_VISIBLE_DISTANCE;
-                        float angularDiameter = nextElement->getAACube().getScale() / distance;
-                        if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
-                            next.element = nextElement;
-                            return;
-                        }
-                    }
+                if (nextElement && view.shouldTraverseElement(*nextElement)) {
+                    next.element = nextElement;
+                    return;
                 }
             }
         }
@@ -65,7 +55,6 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
         EntityTreeElementPointer element = _weakElement.lock();
         if (element->getLastChangedContent() > lastTime) {
             next.element = element;
-            next.intersection = ViewFrustum::INTERSECT;
             return;
         }
     }
@@ -75,31 +64,17 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
             while (_nextIndex < NUMBER_OF_CHILDREN) {
                 EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
                 ++_nextIndex;
-                if (nextElement && nextElement->getLastChanged() > lastTime) {
-                    if (!view.usesViewFrustum) {
-                        // No LOD truncation if we aren't using the view frustum
-                        next.element = nextElement;
-                        next.intersection = ViewFrustum::INSIDE;
-                        return;
-                    } else {
-                        // check for LOD truncation
-                        float distance = glm::distance(view.viewFrustum.getPosition(), nextElement->getAACube().calcCenter()) + MIN_VISIBLE_DISTANCE;
-                        float angularDiameter = nextElement->getAACube().getScale() / distance;
-                        if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
-                            ViewFrustum::intersection intersection = view.viewFrustum.calculateCubeKeyholeIntersection(nextElement->getAACube());
-                            if (intersection != ViewFrustum::OUTSIDE) {
-                                next.element = nextElement;
-                                next.intersection = intersection;
-                                return;
-                            }
-                        }
-                    }
+                if (nextElement &&
+                    nextElement->getLastChanged() > lastTime &&
+                    view.shouldTraverseElement(*nextElement)) {
+
+                    next.element = nextElement;
+                    return;
                 }
             }
         }
     }
     next.element.reset();
-    next.intersection = ViewFrustum::OUTSIDE;
 }
 
 void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
@@ -109,7 +84,6 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
         ++_nextIndex;
         EntityTreeElementPointer element = _weakElement.lock();
         next.element = element;
-        next.intersection = ViewFrustum::INTERSECT;
         return;
     } else if (_nextIndex < NUMBER_OF_CHILDREN) {
         EntityTreeElementPointer element = _weakElement.lock();
@@ -117,24 +91,101 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
             while (_nextIndex < NUMBER_OF_CHILDREN) {
                 EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
                 ++_nextIndex;
-                if (nextElement) {
-                    AACube cube = nextElement->getAACube();
-                    // check for LOD truncation
-                    float distance = glm::distance(view.viewFrustum.getPosition(), cube.calcCenter()) + MIN_VISIBLE_DISTANCE;
-                    float angularDiameter = cube.getScale() / distance;
-                    if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
-                        if (view.viewFrustum.calculateCubeKeyholeIntersection(cube) != ViewFrustum::OUTSIDE) {
-                            next.element = nextElement;
-                            next.intersection = ViewFrustum::OUTSIDE;
-                            return;
-                        }
-                    }
+                if (nextElement && view.shouldTraverseElement(*nextElement)) {
+                    next.element = nextElement;
+                    return;
                 }
             }
         }
     }
     next.element.reset();
-    next.intersection = ViewFrustum::OUTSIDE;
+}
+
+bool DiffTraversal::View::usesViewFrustums() const {
+    return !viewFrustums.empty();
+}
+
+bool DiffTraversal::View::isVerySimilar(const View& view) const {
+    auto size = view.viewFrustums.size();
+
+    if (view.lodScaleFactor != lodScaleFactor ||
+        viewFrustums.size() != size) {
+        return false;
+    }
+
+    for (size_t i = 0; i < size; ++i) {
+        if (!viewFrustums[i].isVerySimilar(view.viewFrustums[i])) {
+            return false;
+        }
+    }
+    return true;
+}
+
+float DiffTraversal::View::computePriority(const EntityItemPointer& entity) const {
+    if (!entity) {
+        return PrioritizedEntity::DO_NOT_SEND;
+    }
+
+    if (!usesViewFrustums()) {
+        return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
+    }
+
+    bool success = false;
+    auto cube = entity->getQueryAACube(success);
+    if (!success) {
+        return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
+    }
+
+    auto center = cube.calcCenter(); // center of bounding sphere
+    auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
+
+    auto priority = PrioritizedEntity::DO_NOT_SEND;
+
+    for (const auto& frustum : viewFrustums) {
+        auto position = center - frustum.getPosition(); // position of bounding sphere in view-frame
+        float distance = glm::length(position); // distance to center of bounding sphere
+
+        // Check the size of the entity, it's possible that a "too small to see" entity is included in a
+        // larger octree cell because of its position (for example if it crosses the boundary of a cell it
+        // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen
+        // before we consider including it.
+        float angularSize = frustum.getAngularSize(distance, radius);
+        if (angularSize > lodScaleFactor * MIN_ENTITY_ANGULAR_DIAMETER &&
+            frustum.intersects(position, distance, radius)) {
+
+            // use the angular size as priority
+            // we compute the max priority for all frustums
+            priority = std::max(priority, angularSize);
+        }
+    }
+
+    return priority;
+}
+
+bool DiffTraversal::View::shouldTraverseElement(const EntityTreeElement& element) const {
+    if (!usesViewFrustums()) {
+        return true;
+    }
+
+    const auto& cube = element.getAACube();
+
+    auto center = cube.calcCenter(); // center of bounding sphere
+    auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
+
+
+    return any_of(begin(viewFrustums), end(viewFrustums), [&](const ConicalViewFrustum& frustum) {
+        auto position = center - frustum.getPosition(); // position of bounding sphere in view-frame
+        float distance = glm::length(position); // distance to center of bounding sphere
+
+        // Check the size of the entity, it's possible that a "too small to see" entity is included in a
+        // larger octree cell because of its position (for example if it crosses the boundary of a cell it
+        // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen
+        // before we consider including it.
+        float angularSize = frustum.getAngularSize(distance, radius);
+
+        return angularSize > lodScaleFactor * MIN_ELEMENT_ANGULAR_DIAMETER &&
+               frustum.intersects(position, distance, radius);
+    });
 }
 
 DiffTraversal::DiffTraversal() {
@@ -142,8 +193,7 @@ DiffTraversal::DiffTraversal() {
     _path.reserve(MIN_PATH_DEPTH);
 }
 
-DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, 
-        int32_t lodLevelOffset, bool usesViewFrustum) {
+DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
     assert(root);
     // there are three types of traversal:
     //
@@ -159,29 +209,25 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFr
     //
     // external code should update the _scanElementCallback after calling prepareNewTraversal
     //
-    _currentView.usesViewFrustum = usesViewFrustum;
-    float lodScaleFactor = powf(2.0f, lodLevelOffset);
 
     Type type;
     // If usesViewFrustum changes, treat it as a First traversal
-    if (_completedView.startTime == 0 || _currentView.usesViewFrustum != _completedView.usesViewFrustum) {
+    if (_completedView.startTime == 0 || _currentView.usesViewFrustums() != _completedView.usesViewFrustums()) {
         type = Type::First;
-        _currentView.viewFrustum = viewFrustum;
-        _currentView.lodScaleFactor = lodScaleFactor;
+        _currentView.viewFrustums = view.viewFrustums;
+        _currentView.lodScaleFactor = view.lodScaleFactor;
         _getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
             _path.back().getNextVisibleElementFirstTime(next, _currentView);
         };
-    } else if (!_currentView.usesViewFrustum ||
-               (_completedView.viewFrustum.isVerySimilar(viewFrustum) &&
-                lodScaleFactor == _completedView.lodScaleFactor)) {
+    } else if (!_currentView.usesViewFrustums() || _completedView.isVerySimilar(view)) {
         type = Type::Repeat;
         _getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
             _path.back().getNextVisibleElementRepeat(next, _completedView, _completedView.startTime);
         };
     } else {
         type = Type::Differential;
-        _currentView.viewFrustum = viewFrustum;
-        _currentView.lodScaleFactor = lodScaleFactor;
+        _currentView.viewFrustums = view.viewFrustums;
+        _currentView.lodScaleFactor = view.lodScaleFactor;
         _getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
             _path.back().getNextVisibleElementDifferential(next, _currentView, _completedView);
         };
@@ -200,7 +246,6 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFr
 void DiffTraversal::getNextVisibleElement(DiffTraversal::VisibleElement& next) {
     if (_path.empty()) {
         next.element.reset();
-        next.intersection = ViewFrustum::OUTSIDE;
         return;
     }
     _getNextVisibleElementCallback(next);
diff --git a/libraries/entities/src/DiffTraversal.h b/libraries/entities/src/DiffTraversal.h
index 69431d8db5..d62c7b8ee1 100644
--- a/libraries/entities/src/DiffTraversal.h
+++ b/libraries/entities/src/DiffTraversal.h
@@ -12,7 +12,7 @@
 #ifndef hifi_DiffTraversal_h
 #define hifi_DiffTraversal_h
 
-#include <ViewFrustum.h>
+#include <shared/ConicalViewFrustum.h>
 
 #include "EntityTreeElement.h"
 
@@ -24,16 +24,20 @@ public:
     class VisibleElement {
     public:
         EntityTreeElementPointer element;
-        ViewFrustum::intersection intersection { ViewFrustum::OUTSIDE };
     };
 
     // View is a struct with a ViewFrustum and LOD parameters
     class View {
     public:
-        ViewFrustum viewFrustum;
+        bool usesViewFrustums() const;
+        bool isVerySimilar(const View& view) const;
+
+        bool shouldTraverseElement(const EntityTreeElement& element) const;
+        float computePriority(const EntityItemPointer& entity) const;
+
+        ConicalViewFrustums viewFrustums;
         uint64_t startTime { 0 };
         float lodScaleFactor { 1.0f };
-        bool usesViewFrustum { true };
     };
 
     // Waypoint is an bookmark in a "path" of waypoints during a traversal.
@@ -57,15 +61,9 @@ public:
 
     DiffTraversal();
 
-    Type prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, 
-        bool usesViewFrustum);
+    Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root);
 
-    const ViewFrustum& getCurrentView() const { return _currentView.viewFrustum; }
-    const ViewFrustum& getCompletedView() const { return _completedView.viewFrustum; }
-
-    bool doesCurrentUseViewFrustum() const { return _currentView.usesViewFrustum; }
-    float getCurrentLODScaleFactor() const { return _currentView.lodScaleFactor; }
-    float getCompletedLODScaleFactor() const { return _completedView.lodScaleFactor; }
+    const View& getCurrentView() const { return _currentView; }
 
     uint64_t getStartOfCompletedTraversal() const { return _completedView.startTime; }
     bool finished() const { return _path.empty(); }
diff --git a/libraries/entities/src/EntityDynamicInterface.cpp b/libraries/entities/src/EntityDynamicInterface.cpp
index d43bdd7b51..1115559342 100644
--- a/libraries/entities/src/EntityDynamicInterface.cpp
+++ b/libraries/entities/src/EntityDynamicInterface.cpp
@@ -89,10 +89,9 @@ variables.  These argument variables are used by the code which is run when bull
 
 */
 
-#include "EntityItem.h"
-
 #include "EntityDynamicInterface.h"
 
+#include "EntityItem.h"
 
 /**jsdoc
 * <p>An entity action may be one of the following types:</p>
diff --git a/libraries/entities/src/EntityDynamicInterface.h b/libraries/entities/src/EntityDynamicInterface.h
index 40e39eecf1..6b82e7df73 100644
--- a/libraries/entities/src/EntityDynamicInterface.h
+++ b/libraries/entities/src/EntityDynamicInterface.h
@@ -13,9 +13,12 @@
 #define hifi_EntityDynamicInterface_h
 
 #include <memory>
-#include <QUuid>
+
 #include <glm/glm.hpp>
 
+#include <QUuid>
+#include <QVariantMap>
+
 class EntityItem;
 class EntityItemID;
 class EntitySimulation;
diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp
index 676b1ce518..94df7eb465 100644
--- a/libraries/entities/src/EntityEditFilters.cpp
+++ b/libraries/entities/src/EntityEditFilters.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "EntityEditFilters.h"
 
 #include <QUrl>
 
 #include <ResourceManager.h>
-#include "EntityEditFilters.h"
 
 QList<EntityItemID> EntityEditFilters::getZonesByPosition(glm::vec3& position) {
     QList<EntityItemID> zones;
diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp
index 5d7bd61854..6c6c4b37d0 100644
--- a/libraries/entities/src/EntityEditPacketSender.cpp
+++ b/libraries/entities/src/EntityEditPacketSender.cpp
@@ -9,16 +9,20 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "EntityEditPacketSender.h"
+
 #include <assert.h>
+
 #include <QJsonDocument>
+
+#include <AddressManager.h>
 #include <PerfStat.h>
 #include <OctalCode.h>
 #include <udt/PacketHeaders.h>
-#include "EntityEditPacketSender.h"
+
 #include "EntitiesLogging.h"
 #include "EntityItem.h"
 #include "EntityItemProperties.h"
-#include <AddressManager.h>
 
 EntityEditPacketSender::EntityEditPacketSender() {
     auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
@@ -73,7 +77,6 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type,
     _myAvatar->updateAvatarEntity(entityItemID, binaryProperties);
 
     entity->setLastBroadcast(usecTimestampNow());
-    return;
 }
 
 
@@ -81,10 +84,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
                                                     EntityTreePointer entityTree,
                                                     EntityItemID entityItemID,
                                                     const EntityItemProperties& properties) {
-    if (!_shouldSend) {
-        return; // bail early
-    }
-
     if (properties.getClientOnly() && properties.getOwningAvatarID() == _myAvatar->getID()) {
         // this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server
         queueEditAvatarEntityMessage(type, entityTree, entityItemID, properties);
@@ -143,9 +142,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
 }
 
 void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityItemID) {
-    if (!_shouldSend) {
-        return; // bail early
-    }
 
     // in case this was a clientOnly entity:
     if(_myAvatar) {
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 295126c2e7..0447284b98 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -1069,9 +1069,9 @@ bool EntityItem::stepKinematicMotion(float timeElapsed) {
     }
 
     const float MAX_TIME_ELAPSED = 1.0f; // seconds
-//    if (timeElapsed > MAX_TIME_ELAPSED) {
-//        qCWarning(entities) << "kinematic timestep = " << timeElapsed << " truncated to " << MAX_TIME_ELAPSED;
-//    }
+    if (timeElapsed > MAX_TIME_ELAPSED) {
+        qCDebug(entities) << "kinematic timestep = " << timeElapsed << " truncated to " << MAX_TIME_ELAPSED;
+    }
     timeElapsed = glm::min(timeElapsed, MAX_TIME_ELAPSED);
 
     if (isSpinning) {
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 4638b46437..4d7c114176 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -9,24 +9,28 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QDebug>
-#include <QHash>
-#include <QObject>
-#include <QtCore/QJsonDocument>
+#include "EntityItemProperties.h"
+
 #include <openssl/err.h>
 #include <openssl/pem.h>
 #include <openssl/x509.h>
 #include <openssl/ecdsa.h>
-#include <NetworkAccessManager.h>
+
+#include <QDebug>
+#include <QHash>
+#include <QObject>
+#include <QtCore/QJsonDocument>
 #include <QtNetwork/QNetworkReply>
 #include <QtNetwork/QNetworkRequest>
+
+#include <NetworkAccessManager.h>
 #include <ByteCountCoding.h>
 #include <GLMHelpers.h>
 #include <RegisteredMetaTypes.h>
 #include <Extents.h>
+
 #include "EntitiesLogging.h"
 #include "EntityItem.h"
-#include "EntityItemProperties.h"
 #include "ModelEntityItem.h"
 #include "PolyLineEntityItem.h"
 
diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index 38e4f0c8c0..39ea2e0bdd 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -27,6 +27,7 @@
 #include <PropertyFlags.h>
 #include <OctreeConstants.h>
 #include <ShapeInfo.h>
+#include <ColorUtils.h>
 
 #include "AnimationPropertyGroup.h"
 #include "EntityItemID.h"
diff --git a/libraries/entities/src/EntityPriorityQueue.h b/libraries/entities/src/EntityPriorityQueue.h
new file mode 100644
index 0000000000..339676d237
--- /dev/null
+++ b/libraries/entities/src/EntityPriorityQueue.h
@@ -0,0 +1,92 @@
+//
+//  EntityPriorityQueue.h
+//  assignment-client/src/entities
+//
+//  Created by Andrew Meadows 2017.08.08
+//  Copyright 2017 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_EntityPriorityQueue_h
+#define hifi_EntityPriorityQueue_h
+
+#include <queue>
+#include <unordered_set>
+
+#include "EntityItem.h"
+
+// PrioritizedEntity is a placeholder in a sorted queue.
+class PrioritizedEntity {
+public:
+    static constexpr float DO_NOT_SEND { -1.0e6f };
+    static constexpr float FORCE_REMOVE { -1.0e5f };
+    static constexpr float WHEN_IN_DOUBT_PRIORITY { 1.0f };
+
+    PrioritizedEntity(EntityItemPointer entity, float priority, bool forceRemove = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceRemove(forceRemove) {}
+    EntityItemPointer getEntity() const { return _weakEntity.lock(); }
+    EntityItem* getRawEntityPointer() const { return _rawEntityPointer; }
+    float getPriority() const { return _priority; }
+    bool shouldForceRemove() const { return _forceRemove; }
+
+    class Compare {
+    public:
+        bool operator() (const PrioritizedEntity& A, const PrioritizedEntity& B) { return A._priority < B._priority; }
+    };
+    friend class Compare;
+
+private:
+    EntityItemWeakPointer _weakEntity;
+    EntityItem* _rawEntityPointer;
+    float _priority;
+    bool _forceRemove;
+};
+
+class EntityPriorityQueue {
+public:
+    inline bool empty() const {
+        assert(_queue.empty() == _entities.empty());
+        return _queue.empty();
+    }
+
+    inline const PrioritizedEntity& top() const {
+        assert(!_queue.empty());
+        return _queue.top();
+    }
+
+    inline bool contains(const EntityItem* entity) const {
+        return _entities.find(entity) != std::end(_entities);
+    }
+
+    inline void emplace(const EntityItemPointer& entity, float priority, bool forceRemove = false) {
+        assert(entity && !contains(entity.get()));
+        _queue.emplace(entity, priority, forceRemove);
+        _entities.insert(entity.get());
+        assert(_queue.size() == _entities.size());
+    }
+
+    inline void pop() {
+        assert(!empty());
+        _entities.erase(_queue.top().getRawEntityPointer());
+        _queue.pop();
+        assert(_queue.size() == _entities.size());
+    }
+
+    inline void swap(EntityPriorityQueue& other) {
+        std::swap(_queue, other._queue);
+        std::swap(_entities, other._entities);
+    }
+
+private:
+    using PriorityQueue = std::priority_queue<PrioritizedEntity,
+                                              std::vector<PrioritizedEntity>,
+                                              PrioritizedEntity::Compare>;
+
+    PriorityQueue _queue;
+    // Keep dictionary of all the entities in the queue for fast contain checks.
+    std::unordered_set<const EntityItem*> _entities;
+
+};
+
+#endif // hifi_EntityPriorityQueue_h
diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h
index 5e76d18515..7e47d9e2d4 100644
--- a/libraries/entities/src/EntityScriptingInterface.h
+++ b/libraries/entities/src/EntityScriptingInterface.h
@@ -94,6 +94,12 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
  * Interface has displayed and so knows about.
  *
  * @namespace Entities
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
  * @property {Uuid} keyboardFocusEntity - Get or set the {@link Entities.EntityType|Web} entity that has keyboard focus.
  *     If no entity has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid|Uuid.NULL} to 
  *     clear keyboard focus.
@@ -475,8 +481,8 @@ public slots:
     /**jsdoc
      * Gets the status of server entity script attached to an entity
      * @function Entities.getServerScriptStatus
-     * @property {Uuid} entityID - The ID of the entity to get the server entity script status for.
-     * @property {Entities~getServerScriptStatusCallback} callback - The function to call upon completion.
+     * @param {Uuid} entityID - The ID of the entity to get the server entity script status for.
+     * @param {Entities~getServerScriptStatusCallback} callback - The function to call upon completion.
      * @returns {boolean} <code>true</code> always.
      */
     /**jsdoc
diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h
index 10919c6eac..e3ce7b2923 100644
--- a/libraries/entities/src/EntityTree.h
+++ b/libraries/entities/src/EntityTree.h
@@ -18,18 +18,13 @@
 #include <Octree.h>
 #include <SpatialParentFinder.h>
 
-class EntityTree;
-using EntityTreePointer = std::shared_ptr<EntityTree>;
-
 #include "AddEntityOperator.h"
 #include "EntityTreeElement.h"
 #include "DeleteEntityOperator.h"
 #include "MovingEntitiesOperator.h"
 
-class EntityEditFilters;
-class Model;
-using ModelPointer = std::shared_ptr<Model>;
-using ModelWeakPointer = std::weak_ptr<Model>;
+class EntityTree;
+using EntityTreePointer = std::shared_ptr<EntityTree>;
 
 class EntitySimulation;
 
diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp
index 694542b04e..9611063f8b 100644
--- a/libraries/entities/src/EntityTypes.cpp
+++ b/libraries/entities/src/EntityTypes.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "EntityTypes.h"
+
 #include <QtCore/QObject>
 
 #include <ByteCountCoding.h>
@@ -16,7 +18,6 @@
 
 #include "EntityItem.h"
 #include "EntityItemProperties.h"
-#include "EntityTypes.h"
 #include "EntitiesLogging.h"
 
 #include "LightEntityItem.h"
diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h
index 0e2fca8180..1f3434d254 100644
--- a/libraries/entities/src/EntityTypes.h
+++ b/libraries/entities/src/EntityTypes.h
@@ -12,6 +12,7 @@
 #ifndef hifi_EntityTypes_h
 #define hifi_EntityTypes_h
 
+#include <memory>
 #include <stdint.h>
 
 #include <QHash>
diff --git a/libraries/entities/src/HazePropertyGroup.cpp b/libraries/entities/src/HazePropertyGroup.cpp
index f137fca5ce..c15b28707c 100644
--- a/libraries/entities/src/HazePropertyGroup.cpp
+++ b/libraries/entities/src/HazePropertyGroup.cpp
@@ -9,9 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "HazePropertyGroup.h"
+
 #include <OctreePacketData.h>
 
-#include "HazePropertyGroup.h"
 #include "EntityItemProperties.h"
 #include "EntityItemPropertiesMacros.h"
 
diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp
index f0fbb20f98..e95af7ebf9 100644
--- a/libraries/entities/src/LightEntityItem.cpp
+++ b/libraries/entities/src/LightEntityItem.cpp
@@ -9,6 +9,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "LightEntityItem.h"
 
 #include <QDebug>
 
@@ -19,7 +20,6 @@
 #include "EntityItemProperties.h"
 #include "EntityTree.h"
 #include "EntityTreeElement.h"
-#include "LightEntityItem.h"
 
 const bool LightEntityItem::DEFAULT_IS_SPOTLIGHT = false;
 const float LightEntityItem::DEFAULT_INTENSITY = 1.0f;
diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp
index 0f59bc673d..cf89a73214 100644
--- a/libraries/entities/src/ModelEntityItem.cpp
+++ b/libraries/entities/src/ModelEntityItem.cpp
@@ -9,18 +9,20 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ModelEntityItem.h"
+
 #include <QtCore/QJsonDocument>
 
+#include <glm/gtx/transform.hpp>
+
 #include <ByteCountCoding.h>
 #include <GLMHelpers.h>
-#include <glm/gtx/transform.hpp>
 
 #include "EntitiesLogging.h"
 #include "EntityItemProperties.h"
 #include "EntityTree.h"
 #include "EntityTreeElement.h"
 #include "ResourceCache.h"
-#include "ModelEntityItem.h"
 
 const QString ModelEntityItem::DEFAULT_MODEL_URL = QString("");
 const QString ModelEntityItem::DEFAULT_COMPOUND_SHAPE_URL = QString("");
diff --git a/libraries/entities/src/MovingEntitiesOperator.cpp b/libraries/entities/src/MovingEntitiesOperator.cpp
index cf043dd93e..4b908745e0 100644
--- a/libraries/entities/src/MovingEntitiesOperator.cpp
+++ b/libraries/entities/src/MovingEntitiesOperator.cpp
@@ -9,13 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "MovingEntitiesOperator.h"
+
 #include "EntityItem.h"
 #include "EntityTree.h"
 #include "EntityTreeElement.h"
 #include "EntitiesLogging.h"
 
-#include "MovingEntitiesOperator.h"
-
 MovingEntitiesOperator::MovingEntitiesOperator() { }
 
 MovingEntitiesOperator::~MovingEntitiesOperator() {
diff --git a/libraries/entities/src/MovingEntitiesOperator.h b/libraries/entities/src/MovingEntitiesOperator.h
index d93efa60f2..9e98374fc3 100644
--- a/libraries/entities/src/MovingEntitiesOperator.h
+++ b/libraries/entities/src/MovingEntitiesOperator.h
@@ -14,8 +14,7 @@
 
 #include <QSet>
 
-#include "EntityTypes.h"
-#include "EntityTreeElement.h"
+#include "EntityItem.h"
 
 class EntityToMoveDetails {
 public:
diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp
index d9ef5e2178..d1fc3d2775 100644
--- a/libraries/entities/src/ParticleEffectEntityItem.cpp
+++ b/libraries/entities/src/ParticleEffectEntityItem.cpp
@@ -26,6 +26,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ParticleEffectEntityItem.h"
 
 #include <glm/gtx/transform.hpp>
 #include <QtCore/QJsonDocument>
@@ -38,8 +39,6 @@
 #include "EntityTreeElement.h"
 #include "EntitiesLogging.h"
 #include "EntityScriptingInterface.h"
-#include "ParticleEffectEntityItem.h"
-
 
 using namespace particle;
 
diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp
index 420c570e8d..5b3167b9ba 100644
--- a/libraries/entities/src/PolyLineEntityItem.cpp
+++ b/libraries/entities/src/PolyLineEntityItem.cpp
@@ -9,6 +9,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "PolyLineEntityItem.h"
 
 #include <QDebug>
 
@@ -19,7 +20,6 @@
 #include "EntityTree.h"
 #include "EntityTreeElement.h"
 #include "OctreeConstants.h"
-#include "PolyLineEntityItem.h"
 
 const float PolyLineEntityItem::DEFAULT_LINE_WIDTH = 0.1f;
 const int PolyLineEntityItem::MAX_POINTS_PER_LINE = 60;
diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp
index 520d892682..943ae2e462 100644
--- a/libraries/entities/src/ShapeEntityItem.cpp
+++ b/libraries/entities/src/ShapeEntityItem.cpp
@@ -6,6 +6,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ShapeEntityItem.h"
 
 #include <glm/gtx/transform.hpp>
 
@@ -17,7 +18,6 @@
 #include "EntityItemProperties.h"
 #include "EntityTree.h"
 #include "EntityTreeElement.h"
-#include "ShapeEntityItem.h"
 
 namespace entity {
 
diff --git a/libraries/entities/src/SkyboxPropertyGroup.cpp b/libraries/entities/src/SkyboxPropertyGroup.cpp
index f8baf57856..ba40c3fa6f 100644
--- a/libraries/entities/src/SkyboxPropertyGroup.cpp
+++ b/libraries/entities/src/SkyboxPropertyGroup.cpp
@@ -9,9 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "SkyboxPropertyGroup.h"
+
 #include <OctreePacketData.h>
 
-#include "SkyboxPropertyGroup.h"
 #include "EntityItemProperties.h"
 #include "EntityItemPropertiesMacros.h"
 
diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h
index d7b422bf11..a94365d24d 100644
--- a/libraries/entities/src/SkyboxPropertyGroup.h
+++ b/libraries/entities/src/SkyboxPropertyGroup.h
@@ -18,6 +18,8 @@
 
 #include <QtScript/QScriptEngine>
 
+#include <ColorUtils.h>
+
 #include "PropertyGroup.h"
 #include "EntityItemPropertiesMacros.h"
 
diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp
index 97080d3ca2..56e12e66d9 100644
--- a/libraries/entities/src/TextEntityItem.cpp
+++ b/libraries/entities/src/TextEntityItem.cpp
@@ -9,6 +9,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "TextEntityItem.h"
 
 #include <glm/gtx/transform.hpp>
 
@@ -21,7 +22,6 @@
 #include "EntitiesLogging.h"
 #include "EntityTree.h"
 #include "EntityTreeElement.h"
-#include "TextEntityItem.h"
 
 const QString TextEntityItem::DEFAULT_TEXT("");
 const float TextEntityItem::DEFAULT_LINE_HEIGHT = 0.1f;
diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp
index b07d0597bc..3a6095b89f 100644
--- a/libraries/entities/src/ZoneEntityItem.cpp
+++ b/libraries/entities/src/ZoneEntityItem.cpp
@@ -9,6 +9,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ZoneEntityItem.h"
 
 #include <QDebug>
 
@@ -18,7 +19,6 @@
 #include "EntityItemProperties.h"
 #include "EntityTree.h"
 #include "EntityTreeElement.h"
-#include "ZoneEntityItem.h"
 #include "EntityEditFilters.h"
 
 bool ZoneEntityItem::_zonesArePickable = false;
diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp
index 86422ef70c..81637e82a8 100644
--- a/libraries/fbx/src/FBXReader.cpp
+++ b/libraries/fbx/src/FBXReader.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "FBXReader.h"
+
 #include <iostream>
 #include <QBuffer>
 #include <QDataStream>
@@ -31,7 +33,6 @@
 #include <gpu/Format.h>
 #include <LogHandler.h>
 
-#include "FBXReader.h"
 #include "ModelFormatLogging.h"
 
 // TOOL: Uncomment the following line to enable the filtering of all the unkwnon fields of a node so we can break point easily while loading a model with problems...
diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp
index d63a5b3cc4..75596862d2 100644
--- a/libraries/fbx/src/FSTReader.cpp
+++ b/libraries/fbx/src/FSTReader.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "FSTReader.h"
+
 #include <QBuffer>
 #include <QEventLoop>
 #include <QNetworkReply>
@@ -17,8 +19,6 @@
 #include <NetworkAccessManager.h>
 #include <SharedUtil.h>
 
-#include "FSTReader.h"
-
 QVariantHash FSTReader::parseMapping(QIODevice* device) {
     QVariantHash properties;
     
diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp
index 0c04b3d733..f322c2319e 100644
--- a/libraries/fbx/src/GLTFReader.cpp
+++ b/libraries/fbx/src/GLTFReader.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "GLTFReader.h"
+
 #include <QtCore/QBuffer>
 #include <QtCore/QIODevice>
 #include <QtCore/QEventLoop>
@@ -28,7 +30,6 @@
 #include <NetworkAccessManager.h>
 #include <ResourceManager.h>
 
-#include "GLTFReader.h"
 #include "FBXReader.h"
 
 
@@ -1377,4 +1378,4 @@ void GLTFReader::fbxDebugDump(const FBXGeometry& fbxgeo) {
     }
 
     qCDebug(modelformat) << "\n";
-}
\ No newline at end of file
+}
diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp
index 4a2c5fd7f7..6598a26c99 100644
--- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp
+++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp
@@ -17,15 +17,18 @@
 #include <QtCore/QProcessEnvironment>
 #include <QtCore/QDebug>
 #include <QtCore/QThread>
+#include <QtCore/QThreadStorage>
+#include <QtCore/QPointer>
 #include <QtGui/QOffscreenSurface>
 #include <QtGui/QOpenGLContext>
 #include <QtGui/QOpenGLDebugLogger>
 
+#include <DependencyManager.h>
+
 #include "Context.h"
 #include "GLHelpers.h"
 #include "GLLogging.h"
 
-
 OffscreenGLCanvas::OffscreenGLCanvas() :
     _context(new QOpenGLContext),
     _offscreenSurface(new QOffscreenSurface)
@@ -33,6 +36,8 @@ OffscreenGLCanvas::OffscreenGLCanvas() :
 }
 
 OffscreenGLCanvas::~OffscreenGLCanvas() {
+    clearThreadContext();
+
     // A context with logging enabled needs to be current when it's destroyed
     _context->makeCurrent(_offscreenSurface);
     delete _context;
@@ -98,12 +103,15 @@ void OffscreenGLCanvas::onMessageLogged(const QOpenGLDebugMessage& debugMessage)
 
 bool OffscreenGLCanvas::makeCurrent() {
     bool result = _context->makeCurrent(_offscreenSurface);
-    std::call_once(_reportOnce, []{
-        qCDebug(glLogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
-        qCDebug(glLogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
-        qCDebug(glLogging) << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR));
-        qCDebug(glLogging) << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
-    });
+    if (glGetString) {
+        std::call_once(_reportOnce, [] {
+            qCDebug(glLogging) << "GL Version: " << QString((const char*)glGetString(GL_VERSION));
+            qCDebug(glLogging) << "GL Shader Language Version: "
+                               << QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
+            qCDebug(glLogging) << "GL Vendor: " << QString((const char*)glGetString(GL_VENDOR));
+            qCDebug(glLogging) << "GL Renderer: " << QString((const char*)glGetString(GL_RENDERER));
+        });
+    }
 
     return result;
 }
@@ -117,25 +125,51 @@ QObject* OffscreenGLCanvas::getContextObject() {
 }
 
 void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) {
+    clearThreadContext();
     moveToThread(thread);
     _context->moveToThread(thread);
 }
 
-static const char* THREAD_CONTEXT_PROPERTY = "offscreenGlCanvas";
+struct ThreadContextStorage : public Dependency {
+    QThreadStorage<QPointer<OffscreenGLCanvas>> threadContext;
+};
 
 void OffscreenGLCanvas::setThreadContext() {
-    QThread::currentThread()->setProperty(THREAD_CONTEXT_PROPERTY, QVariant::fromValue<QObject*>(this));
+    if (!DependencyManager::isSet<ThreadContextStorage>()) {
+        DependencyManager::set<ThreadContextStorage>();
+    }
+    auto threadContextStorage = DependencyManager::get<ThreadContextStorage>();
+    QPointer<OffscreenGLCanvas> p(this);
+    threadContextStorage->threadContext.setLocalData(p);
+}
+
+void OffscreenGLCanvas::clearThreadContext() {
+    if (!DependencyManager::isSet<ThreadContextStorage>()) {
+        return;
+    }
+    auto threadContextStorage = DependencyManager::get<ThreadContextStorage>();
+    if (!threadContextStorage->threadContext.hasLocalData()) {
+        return;
+    }
+    auto& threadContext = threadContextStorage->threadContext.localData();
+    if (this != threadContext.operator OffscreenGLCanvas *()) {
+        return;
+    }
+    threadContextStorage->threadContext.setLocalData(nullptr);
 }
 
 bool OffscreenGLCanvas::restoreThreadContext() {
     // Restore the rendering context for this thread
-    auto threadCanvasVariant = QThread::currentThread()->property(THREAD_CONTEXT_PROPERTY);
-    if (!threadCanvasVariant.isValid()) {
+    if (!DependencyManager::isSet<ThreadContextStorage>()) {
         return false;
     }
 
-    auto threadCanvasObject = qvariant_cast<QObject*>(threadCanvasVariant);
-    auto threadCanvas = static_cast<OffscreenGLCanvas*>(threadCanvasObject);
+    auto threadContextStorage = DependencyManager::get<ThreadContextStorage>();
+    if (!threadContextStorage->threadContext.hasLocalData()) {
+        return false;
+    }
+
+    auto threadCanvas = threadContextStorage->threadContext.localData();
     if (!threadCanvas) {
         return false;
     }
diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h
index ed644b98fb..a4960ae234 100644
--- a/libraries/gl/src/gl/OffscreenGLCanvas.h
+++ b/libraries/gl/src/gl/OffscreenGLCanvas.h
@@ -39,6 +39,8 @@ private slots:
     void onMessageLogged(const QOpenGLDebugMessage &debugMessage);
 
 protected:
+    void clearThreadContext();
+
     std::once_flag _reportOnce;
     QOpenGLContext* _context{ nullptr };
     QOffscreenSurface* _offscreenSurface{ nullptr };
diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h
index 5bbb44f9e1..314bbee387 100644
--- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h
+++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h
@@ -91,7 +91,7 @@ public:
 
     // this is the maximum per shader stage on the low end apple
     // TODO make it platform dependant at init time
-    static const int MAX_NUM_UNIFORM_BUFFERS = 12;
+    static const int MAX_NUM_UNIFORM_BUFFERS = 14;
     size_t getMaxNumUniformBuffers() const { return MAX_NUM_UNIFORM_BUFFERS; }
 
     // this is the maximum per shader stage on the low end apple
diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h
index 9479321747..f3b452b1f9 100644
--- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h
+++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h
@@ -52,6 +52,8 @@ public:
     static const std::string GL41_VERSION;
     const std::string& getVersion() const override { return GL41_VERSION; }
 
+    bool supportedTextureFormat(const gpu::Element& format) override;
+
     class GL41Texture : public GLTexture {
         using Parent = GLTexture;
         friend class GL41Backend;
@@ -173,8 +175,6 @@ protected:
     void makeProgramBindings(ShaderObject& shaderObject) override;
     int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
 
-    static bool supportedTextureFormat(const gpu::Element& format);
-
 };
 
 } }
diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp
index 151b5bd1f9..64c9033cf7 100644
--- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp
+++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendShader.cpp
@@ -17,7 +17,6 @@ std::string GL41Backend::getBackendShaderHeader() const {
     static const std::string header(
         R"SHADER(#version 410 core
         #define GPU_GL410
-        #define PRECISIONQ
         #define BITFIELD int
         )SHADER");
     return header;
diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h
index c23a83eaf9..616b6d1075 100644
--- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h
+++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h
@@ -54,6 +54,8 @@ public:
     static const std::string GL45_VERSION;
     const std::string& getVersion() const override { return GL45_VERSION; }
 
+    bool supportedTextureFormat(const gpu::Element& format) override;
+
     class GL45Texture : public GLTexture {
         using Parent = GLTexture;
         friend class GL45Backend;
diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp
index 0bb707a11d..c2b1c8e1af 100644
--- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp
+++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendShader.cpp
@@ -18,7 +18,6 @@ std::string GL45Backend::getBackendShaderHeader() const {
     static const std::string header(
         R"SHADER(#version 450 core
         #define GPU_GL450
-        #define PRECISIONQ
         #define BITFIELD int
         )SHADER"
 #ifdef GPU_SSBO_TRANSFORM_OBJECT
diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp
index 4d5ffefa67..6b3c99ccc3 100644
--- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp
+++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp
@@ -32,6 +32,24 @@ using namespace gpu::gl45;
 #define FORCE_STRICT_TEXTURE 0
 #define ENABLE_SPARSE_TEXTURE 0
 
+bool GL45Backend::supportedTextureFormat(const gpu::Element& format) {
+    switch (format.getSemantic()) {
+        case gpu::Semantic::COMPRESSED_ETC2_RGB:
+        case gpu::Semantic::COMPRESSED_ETC2_SRGB:
+        case gpu::Semantic::COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA:
+        case gpu::Semantic::COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA:
+        case gpu::Semantic::COMPRESSED_ETC2_RGBA:
+        case gpu::Semantic::COMPRESSED_ETC2_SRGBA:
+        case gpu::Semantic::COMPRESSED_EAC_RED:
+        case gpu::Semantic::COMPRESSED_EAC_RED_SIGNED:
+        case gpu::Semantic::COMPRESSED_EAC_XY:
+        case gpu::Semantic::COMPRESSED_EAC_XY_SIGNED:
+            return false;
+        default:
+            return true;
+    }
+}
+
 GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) {
     if (!texturePointer) {
         return nullptr;
diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h
index 38e28e630a..47a123718a 100644
--- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h
+++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h
@@ -32,7 +32,6 @@ public:
     static const GLint RESOURCE_TRANSFER_EXTRA_TEX_UNIT { 33 };
     static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 34 };
     static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 35 };
-    static bool supportedTextureFormat(const gpu::Element& format);
     explicit GLESBackend(bool syncCache) : Parent(syncCache) {}
     GLESBackend() : Parent() {}
     virtual ~GLESBackend() {
@@ -40,6 +39,8 @@ public:
         // which is pure virtual from GLBackend's dtor.
         resetStages();
     }
+
+    bool supportedTextureFormat(const gpu::Element& format) override;
     
     static const std::string GLES_VERSION;
     const std::string& getVersion() const override { return GLES_VERSION; }
diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp
index 4278d732c8..7e8056ba79 100644
--- a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp
+++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp
@@ -17,10 +17,9 @@ std::string GLESBackend::getBackendShaderHeader() const {
     static const std::string header(
         R"SHADER(#version 310 es
         #extension GL_EXT_texture_buffer : enable
-        precision lowp float; // check precision 2
-        precision lowp samplerBuffer;
-        precision lowp sampler2DShadow;
-        #define PRECISIONQ highp
+        precision highp float;
+        precision highp samplerBuffer;
+        precision highp sampler2DShadow;
         #define BITFIELD highp int
         )SHADER");
     return header;
diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp
index 84a7c275f0..90115806b4 100644
--- a/libraries/gpu/src/gpu/Batch.cpp
+++ b/libraries/gpu/src/gpu/Batch.cpp
@@ -34,7 +34,7 @@ ProfileRangeBatch::~ProfileRangeBatch() {
 using namespace gpu;
 
 // FIXME make these backend / pipeline dependent.
-static const int MAX_NUM_UNIFORM_BUFFERS = 12;
+static const int MAX_NUM_UNIFORM_BUFFERS = 14;
 static const int MAX_NUM_RESOURCE_BUFFERS = 16;
 static const int MAX_NUM_RESOURCE_TEXTURES = 16;
 
diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h
index eda8fee596..8c5a4d493e 100644
--- a/libraries/gpu/src/gpu/Context.h
+++ b/libraries/gpu/src/gpu/Context.h
@@ -64,6 +64,8 @@ public:
     virtual void recycle() const = 0;
     virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
 
+    virtual bool supportedTextureFormat(const gpu::Element& format) = 0;
+
     // Shared header between C++ and GLSL
 #include "TransformCamera_shared.slh"
 
diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h
index d4e4a89636..7a3e0a2f82 100644
--- a/libraries/gpu/src/gpu/Format.h
+++ b/libraries/gpu/src/gpu/Format.h
@@ -50,47 +50,50 @@ enum Type : uint8_t {
 };
 // Array providing the size in bytes for a given scalar type
 static const int TYPE_SIZE[NUM_TYPES] = {
-    4,
-    4,
-    4,
-    2,
-    2,
-    2,
-    1,
-    1,
+    4, // FLOAT
+    4, // INT32
+    4, // UINT32
+    2, // HALF
+    2, // INT16
+    2, // UINT16
+    1, // INT8
+    1, // UINT8
 
     // normalized values
-    4,
-    4,
-    2,
-    2,
-    1,
-    1,
-    4,
+    4, // NINT32
+    4, // NUINT32
+    2, // NINT16 
+    2, // NUINT16
+    1, // NINT8
+    1, // NUINT8
+    1, // NUINT2
+    1, // NINT2_10_10_10
 
-    1
+    1, // COMPRESSED
 };
+
 // Array answering the question Does this type is integer or not 
 static const bool TYPE_IS_INTEGER[NUM_TYPES] = {
-    false,
-    true,
-    true,
-    false,
-    true,
-    true,
-    true,
-    true,
+    false, // FLOAT
+    true, // INT32
+    true, // UINT32
+    false, // HALF
+    true, // INT16
+    true, // UINT16
+    true, // INT8
+    true, // UINT8
 
     // Normalized values
-    false,
-    false,
-    false,
-    false,
-    false,
-    false,
-    false,
+    false, // NINT32
+    false, // NUINT32
+    false, // NINT16 
+    false, // NUINT16
+    false, // NINT8
+    false, // NUINT8
+    false, // NUINT2
+    false, // NINT2_10_10_10
 
-    false,
+    false, // COMPRESSED
 };
 
 // Dimension of an Element
@@ -367,9 +370,9 @@ public:
     static const Element PART_DRAWCALL;
     
  protected:
-    uint8 _semantic;
-    uint8 _dimension : 4;
-    uint8 _type : 4;
+    uint16 _semantic : 7;
+    uint16 _dimension : 4;
+    uint16 _type : 5;
 };
 
   
diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h
index 4d82aba595..09b2bc9475 100755
--- a/libraries/gpu/src/gpu/Texture.h
+++ b/libraries/gpu/src/gpu/Texture.h
@@ -37,6 +37,10 @@ namespace ktx {
     using KeyValues = std::list<KeyValue>;
 }
 
+namespace khronos { namespace gl { namespace texture {
+    enum class InternalFormat: uint32_t;
+}}}
+
 namespace gpu {
 
 
@@ -565,6 +569,7 @@ public:
 
     static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header);
     static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat);
+    static bool getCompressedFormat(khronos::gl::texture::InternalFormat format, Element& elFormat);
 
 protected:
     const TextureUsageType _usageType;
diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp
index 5e354c0a5c..129c125411 100644
--- a/libraries/gpu/src/gpu/Texture_ktx.cpp
+++ b/libraries/gpu/src/gpu/Texture_ktx.cpp
@@ -46,13 +46,14 @@ struct GPUKTXPayload {
 
         memcpy(data, &_samplerDesc, sizeof(Sampler::Desc));
         data += sizeof(Sampler::Desc);
-        
+
         // We can't copy the bitset in Texture::Usage in a crossplateform manner
         // So serialize it manually
-        *(uint32*)data = _usage._flags.to_ulong();
+        uint32 usageData = _usage._flags.to_ulong();
+        memcpy(data, &usageData, sizeof(uint32));
         data += sizeof(uint32);
 
-        *(TextureUsageType*)data = _usageType;
+        memcpy(data, &_usageType, sizeof(TextureUsageType));
         data += sizeof(TextureUsageType);
 
         return data + PADDING;
@@ -77,13 +78,15 @@ struct GPUKTXPayload {
 
         memcpy(&_samplerDesc, data, sizeof(Sampler::Desc));
         data += sizeof(Sampler::Desc);
-        
+
         // We can't copy the bitset in Texture::Usage in a crossplateform manner
         // So unserialize it manually
-        _usage = Texture::Usage(*(const uint32*)data);
+        uint32 usageData;
+        memcpy(&usageData, data, sizeof(uint32));
+        _usage = Texture::Usage(usageData);
         data += sizeof(uint32);
 
-        _usageType = *(const TextureUsageType*)data;
+        memcpy(&_usageType, data, sizeof(TextureUsageType));
         return true;
     }
 
@@ -619,6 +622,47 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat
     return true;
 }
 
+bool Texture::getCompressedFormat(ktx::GLInternalFormat format, Element& elFormat) {
+    if (format == ktx::GLInternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT) {
+        elFormat = Format::COLOR_COMPRESSED_BCX_SRGB;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) {
+        elFormat = Format::COLOR_COMPRESSED_BCX_SRGBA_MASK;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) {
+        elFormat = Format::COLOR_COMPRESSED_BCX_SRGBA;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_RED_RGTC1) {
+        elFormat = Format::COLOR_COMPRESSED_BCX_RED;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_RG_RGTC2) {
+        elFormat = Format::COLOR_COMPRESSED_BCX_XY;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM) {
+        elFormat = Format::COLOR_COMPRESSED_BCX_SRGBA_HIGH;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT) {
+        elFormat = Format::COLOR_COMPRESSED_BCX_HDR_RGB;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_RGB8_ETC2) {
+        elFormat = Format::COLOR_COMPRESSED_ETC2_RGB;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB8_ETC2) {
+        elFormat = Format::COLOR_COMPRESSED_ETC2_SRGB;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2) {
+        elFormat = Format::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) {
+        elFormat = Format::COLOR_COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_RGBA8_ETC2_EAC) {
+        elFormat = Format::COLOR_COMPRESSED_ETC2_RGBA;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) {
+        elFormat = Format::COLOR_COMPRESSED_ETC2_SRGBA;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_R11_EAC) {
+        elFormat = Format::COLOR_COMPRESSED_EAC_RED;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_SIGNED_R11_EAC) {
+        elFormat = Format::COLOR_COMPRESSED_EAC_RED_SIGNED;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_RG11_EAC) {
+        elFormat = Format::COLOR_COMPRESSED_EAC_XY;
+    } else if (format == ktx::GLInternalFormat::COMPRESSED_SIGNED_RG11_EAC) {
+        elFormat = Format::COLOR_COMPRESSED_EAC_XY_SIGNED;
+    } else {
+        return false;
+    }
+    return true;
+}
+
 bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat) {
     if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) {
         if (header.getGLInternaFormat() == ktx::GLInternalFormat::RGBA8) {
@@ -661,41 +705,7 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E
         mipFormat = Format::COLOR_RGB9E5;
         texelFormat = Format::COLOR_RGB9E5;
     } else if (header.isCompressed()) {
-        if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT) {
-            texelFormat = Format::COLOR_COMPRESSED_BCX_SRGB;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) {
-            texelFormat = Format::COLOR_COMPRESSED_BCX_SRGBA_MASK;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) {
-            texelFormat = Format::COLOR_COMPRESSED_BCX_SRGBA;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RED_RGTC1) {
-            texelFormat = Format::COLOR_COMPRESSED_BCX_RED;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RG_RGTC2) {
-            texelFormat = Format::COLOR_COMPRESSED_BCX_XY;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM) {
-            texelFormat = Format::COLOR_COMPRESSED_BCX_SRGBA_HIGH;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT) {
-            texelFormat = Format::COLOR_COMPRESSED_BCX_HDR_RGB;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RGB8_ETC2) {
-            texelFormat = Format::COLOR_COMPRESSED_ETC2_RGB;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB8_ETC2) {
-            texelFormat = Format::COLOR_COMPRESSED_ETC2_SRGB;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2) {
-            texelFormat = Format::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) {
-            texelFormat = Format::COLOR_COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RGBA8_ETC2_EAC) {
-            texelFormat = Format::COLOR_COMPRESSED_ETC2_RGBA;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) {
-            texelFormat = Format::COLOR_COMPRESSED_ETC2_SRGBA;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_R11_EAC) {
-            texelFormat = Format::COLOR_COMPRESSED_EAC_RED;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SIGNED_R11_EAC) {
-            texelFormat = Format::COLOR_COMPRESSED_EAC_RED_SIGNED;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RG11_EAC) {
-            texelFormat = Format::COLOR_COMPRESSED_EAC_XY;
-        } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SIGNED_RG11_EAC) {
-            texelFormat = Format::COLOR_COMPRESSED_EAC_XY_SIGNED;
-        } else {
+        if (!getCompressedFormat(header.getGLInternaFormat(), texelFormat)) {
             return false;
         }
         mipFormat = texelFormat;
@@ -703,4 +713,4 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E
         return false;
     }
     return true;
-}
\ No newline at end of file
+}
diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h
index 526352804b..b88c6345cf 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h
+++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h
@@ -24,6 +24,9 @@
 /**jsdoc
  * The experimental Graphics API <em>(experimental)</em> lets you query and manage certain graphics-related structures (like underlying meshes and textures) from scripting.
  * @namespace Graphics
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 
 class GraphicsScriptingInterface : public QObject, public QScriptable, public Dependency {
@@ -39,7 +42,7 @@ public slots:
      *
      * @function Graphics.getModel
      * @param {UUID} entityID - The objectID of the model whose meshes are to be retrieved.
-     * @return {Graphics.Model} the resulting Model object
+     * @returns {Graphics.Model} the resulting Model object
      */
     scriptable::ScriptableModelPointer getModel(QUuid uuid);
 
@@ -54,7 +57,7 @@ public slots:
      *
      * @function Graphics.newMesh
      * @param {Graphics.IFSData} ifsMeshData Index-Faced Set (IFS) arrays used to create the new mesh.
-     * @return {Graphics.Mesh} the resulting Mesh / Mesh Part object
+     * @returns {Graphics.Mesh} the resulting Mesh / Mesh Part object
      */
     /**jsdoc
     * @typedef {object} Graphics.IFSData
diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp
index 8e6d4bec9b..585a719638 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp
+++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp
@@ -5,22 +5,24 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "Forward.h"
-
 #include "ScriptableMesh.h"
-#include "ScriptableMeshPart.h"
 
-#include "GraphicsScriptingUtil.h"
-#include "OBJWriter.h"
-#include <BaseScriptEngine.h>
 #include <QtScript/QScriptValue>
-#include <RegisteredMetaTypes.h>
+
 #include <glm/glm.hpp>
 #include <glm/gtx/norm.hpp>
 #include <glm/gtx/transform.hpp>
+
+#include <BaseScriptEngine.h>
 #include <graphics/BufferViewHelpers.h>
 #include <graphics/GpuHelpers.h>
 #include <graphics/Geometry.h>
+#include <RegisteredMetaTypes.h>
+
+#include "Forward.h"
+#include "ScriptableMeshPart.h"
+#include "GraphicsScriptingUtil.h"
+#include "OBJWriter.h"
 
 // #define SCRIPTABLE_MESH_DEBUG 1
 
diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp
index 4414b0ad7e..192071d3af 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp
+++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp
@@ -5,22 +5,23 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "Forward.h"
-
 #include "ScriptableMeshPart.h"
 
-#include "GraphicsScriptingUtil.h"
-#include "OBJWriter.h"
-#include <BaseScriptEngine.h>
-#include <QtScript/QScriptValue>
-#include <RegisteredMetaTypes.h>
 #include <glm/glm.hpp>
 #include <glm/gtx/norm.hpp>
 #include <glm/gtx/transform.hpp>
+
+#include <BaseScriptEngine.h>
+#include <QtScript/QScriptValue>
+#include <RegisteredMetaTypes.h>
 #include <graphics/BufferViewHelpers.h>
 #include <graphics/GpuHelpers.h>
 #include <graphics/Geometry.h>
 
+#include "Forward.h"
+#include "GraphicsScriptingUtil.h"
+#include "OBJWriter.h"
+
 
 QString scriptable::ScriptableMeshPart::toOBJ() {
     if (!getMeshPointer()) {
diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp
index c65764a225..7aaa182163 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp
+++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp
@@ -8,14 +8,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "GraphicsScriptingUtil.h"
 #include "ScriptableModel.h"
-#include "ScriptableMesh.h"
 
 #include <QtScript/QScriptEngine>
 
+#include "GraphicsScriptingUtil.h"
+#include "ScriptableMesh.h"
 #include "graphics/Material.h"
-
 #include "image/Image.h"
 
 // #define SCRIPTABLE_MESH_DEBUG 1
diff --git a/libraries/graphics/src/graphics/Haze.cpp b/libraries/graphics/src/graphics/Haze.cpp
index dfe70175f4..d5a060b90b 100644
--- a/libraries/graphics/src/graphics/Haze.cpp
+++ b/libraries/graphics/src/graphics/Haze.cpp
@@ -9,9 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <memory>
 #include "Haze.h"
 
+#include <memory>
+
 using namespace graphics;
 
 const float Haze::INITIAL_HAZE_RANGE{ 1000.0f };
diff --git a/libraries/graphics/src/graphics/Light.slh b/libraries/graphics/src/graphics/Light.slh
index c8992730f0..53b840f5fb 100644
--- a/libraries/graphics/src/graphics/Light.slh
+++ b/libraries/graphics/src/graphics/Light.slh
@@ -34,7 +34,7 @@ vec3 getLightIrradiance(Light l) { return lightIrradiance_getIrradiance(l.irradi
 // Light Ambient
 
 struct LightAmbient {
-    PRECISIONQ vec4 _ambient;
+    vec4 _ambient;
     SphericalHarmonics _ambientSphere;
     mat4 transform;
 };
diff --git a/libraries/graphics/src/graphics/LightIrradiance.shared.slh b/libraries/graphics/src/graphics/LightIrradiance.shared.slh
index 4ae7967bf5..13d6cf4b93 100644
--- a/libraries/graphics/src/graphics/LightIrradiance.shared.slh
+++ b/libraries/graphics/src/graphics/LightIrradiance.shared.slh
@@ -14,9 +14,9 @@
 #define LightIrradianceConstRef LightIrradiance
 
 struct LightIrradiance {
-    PRECISIONQ vec4 colorIntensity;
+    vec4 colorIntensity;
     // falloffRadius, cutoffRadius, falloffSpot, spare
-    PRECISIONQ vec4 attenuation;
+    vec4 attenuation;
 };
 
 
diff --git a/libraries/graphics/src/graphics/LightVolume.shared.slh b/libraries/graphics/src/graphics/LightVolume.shared.slh
index 4e4359eac0..3f0cb135f5 100644
--- a/libraries/graphics/src/graphics/LightVolume.shared.slh
+++ b/libraries/graphics/src/graphics/LightVolume.shared.slh
@@ -16,8 +16,8 @@
 #define LightVolumeConstRef LightVolume
 
 struct LightVolume {
-    PRECISIONQ vec4 positionRadius;
-    PRECISIONQ vec4 directionSpotCos;
+    vec4 positionRadius;
+    vec4 directionSpotCos;
 };
 
 bool lightVolume_isPoint(LightVolume lv) { return bool(lv.directionSpotCos.w < 0.f); }
diff --git a/libraries/graphics/src/graphics/Material.slh b/libraries/graphics/src/graphics/Material.slh
index ecf3c18a0e..dd2985b4da 100644
--- a/libraries/graphics/src/graphics/Material.slh
+++ b/libraries/graphics/src/graphics/Material.slh
@@ -15,10 +15,10 @@
 // to what is provided by the uniform buffer, or the material key has the wrong bits
 
 struct Material {
-    PRECISIONQ vec4 _emissiveOpacity;
-    PRECISIONQ vec4 _albedoRoughness;
-    PRECISIONQ vec4 _fresnelMetallic;
-    PRECISIONQ vec4 _scatteringSpare2Key;
+    vec4 _emissiveOpacity;
+    vec4 _albedoRoughness;
+    vec4 _fresnelMetallic;
+    vec4 _scatteringSpare2Key;
 };
 
 uniform materialBuffer {
@@ -64,7 +64,4 @@ const BITFIELD OCCLUSION_MAP_BIT             = 0x00004000;
 const BITFIELD LIGHTMAP_MAP_BIT              = 0x00008000;
 const BITFIELD SCATTERING_MAP_BIT            = 0x00010000;
 
-#ifdef GL_ES
-precision lowp float;
-#endif
 <@endif@>
diff --git a/libraries/graphics/src/graphics/SphericalHarmonics.shared.slh b/libraries/graphics/src/graphics/SphericalHarmonics.shared.slh
index 6e1763dcba..312824c5a3 100644
--- a/libraries/graphics/src/graphics/SphericalHarmonics.shared.slh
+++ b/libraries/graphics/src/graphics/SphericalHarmonics.shared.slh
@@ -16,15 +16,15 @@
 #define SphericalHarmonicsConstRef SphericalHarmonics
 
 struct SphericalHarmonics {
-    PRECISIONQ vec4 L00;
-    PRECISIONQ vec4 L1m1;
-    PRECISIONQ vec4 L10;
-    PRECISIONQ vec4 L11;
-    PRECISIONQ vec4 L2m2;
-    PRECISIONQ vec4 L2m1;
-    PRECISIONQ vec4 L20;
-    PRECISIONQ vec4 L21;
-    PRECISIONQ vec4 L22;
+    vec4 L00;
+    vec4 L1m1;
+    vec4 L10;
+    vec4 L11;
+    vec4 L2m2;
+    vec4 L2m1;
+    vec4 L20;
+    vec4 L21;
+    vec4 L22;
 };
 
 vec4 sphericalHarmonics_evalSphericalLight(SphericalHarmonicsConstRef sh, vec3 direction) {
diff --git a/libraries/ktx/src/TextureMeta.cpp b/libraries/ktx/src/TextureMeta.cpp
new file mode 100644
index 0000000000..3a2abf24c4
--- /dev/null
+++ b/libraries/ktx/src/TextureMeta.cpp
@@ -0,0 +1,64 @@
+//
+//  TextureMeta.cpp
+//  libraries/shared/src
+//
+//  Created by Ryan Huffman on 04/10/18.
+//  Copyright 2018 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 "TextureMeta.h"
+
+#include <QDebug>
+#include <QJsonDocument>
+#include <QJsonObject>
+
+const QString TEXTURE_META_EXTENSION = ".texmeta.json";
+
+bool TextureMeta::deserialize(const QByteArray& data, TextureMeta* meta) {
+    QJsonParseError error;
+    auto doc = QJsonDocument::fromJson(data, &error);
+    if (error.error != QJsonParseError::NoError) {
+        qDebug() << "Failed to parse TextureMeta:" << error.errorString();
+        return false;
+    }
+    if (!doc.isObject()) {
+        qDebug() << "Unable to process TextureMeta: top-level value is not an Object";
+        return false;
+    }
+
+    auto root = doc.object();
+    if (root.contains("original")) {
+        meta->original = root["original"].toString();
+    }
+    if (root.contains("compressed")) {
+        auto compressed = root["compressed"].toObject();
+        for (auto it = compressed.constBegin(); it != compressed.constEnd(); it++) {
+            khronos::gl::texture::InternalFormat format;
+            auto formatName = it.key().toLatin1();
+            if (khronos::gl::texture::fromString(formatName.constData(), &format)) {
+                meta->availableTextureTypes[format] = it.value().toString();
+            }
+        }
+    }
+
+    return true;
+}
+
+QByteArray TextureMeta::serialize() {
+    QJsonDocument doc;
+    QJsonObject root;
+    QJsonObject compressed;
+
+    for (auto kv : availableTextureTypes) {
+        const char* name = khronos::gl::texture::toString(kv.first);
+        compressed[name] = kv.second.toString();
+    }
+    root["original"] = original.toString();
+    root["compressed"] = compressed;
+    doc.setObject(root);
+
+    return doc.toJson();
+}
diff --git a/libraries/ktx/src/TextureMeta.h b/libraries/ktx/src/TextureMeta.h
new file mode 100644
index 0000000000..6582c29e70
--- /dev/null
+++ b/libraries/ktx/src/TextureMeta.h
@@ -0,0 +1,42 @@
+//
+//  TextureMeta.h
+//  libraries/shared/src
+//
+//  Created by Ryan Huffman on 04/10/18.
+//  Copyright 2018 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_TextureMeta_h
+#define hifi_TextureMeta_h
+
+#include <type_traits>
+#include <unordered_map>
+#include <QUrl>
+
+#include "khronos/KHR.h"
+
+extern const QString TEXTURE_META_EXTENSION;
+
+namespace std {
+    template<> struct hash<khronos::gl::texture::InternalFormat> {
+        using enum_type = std::underlying_type<khronos::gl::texture::InternalFormat>::type;
+        typedef std::size_t result_type;
+        result_type operator()(khronos::gl::texture::InternalFormat const& v) const noexcept {
+            return std::hash<enum_type>()(static_cast<enum_type>(v));
+        }
+    };
+}
+
+struct TextureMeta {
+    static bool deserialize(const QByteArray& data, TextureMeta* meta);
+    QByteArray serialize();
+
+    QUrl original;
+    std::unordered_map<khronos::gl::texture::InternalFormat, QUrl> availableTextureTypes;
+};
+
+
+#endif // hifi_TextureMeta_h
diff --git a/libraries/ktx/src/khronos/KHR.h b/libraries/ktx/src/khronos/KHR.h
index 4ee893e4fc..617e40ce06 100644
--- a/libraries/ktx/src/khronos/KHR.h
+++ b/libraries/ktx/src/khronos/KHR.h
@@ -10,6 +10,8 @@
 #ifndef khronos_khr_hpp
 #define khronos_khr_hpp
 
+#include <unordered_map>
+
 namespace khronos {
 
     namespace gl {
@@ -209,6 +211,63 @@ namespace khronos {
                 COMPRESSED_SIGNED_RG11_EAC = 0x9273,
             };
 
+            static std::unordered_map<std::string, InternalFormat> nameToFormat {
+                { "COMPRESSED_RED", InternalFormat::COMPRESSED_RED },
+                { "COMPRESSED_RG", InternalFormat::COMPRESSED_RG },
+                { "COMPRESSED_RGB", InternalFormat::COMPRESSED_RGB },
+                { "COMPRESSED_RGBA", InternalFormat::COMPRESSED_RGBA },
+
+                { "COMPRESSED_SRGB", InternalFormat::COMPRESSED_SRGB },
+                { "COMPRESSED_SRGB_ALPHA", InternalFormat::COMPRESSED_SRGB_ALPHA },
+
+                { "COMPRESSED_ETC1_RGB8_OES", InternalFormat::COMPRESSED_ETC1_RGB8_OES },
+
+                { "COMPRESSED_SRGB_S3TC_DXT1_EXT", InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT },
+                { "COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT", InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT },
+                { "COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT", InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT },
+                { "COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT", InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT },
+
+                { "COMPRESSED_RED_RGTC1", InternalFormat::COMPRESSED_RED_RGTC1 },
+                { "COMPRESSED_SIGNED_RED_RGTC1", InternalFormat::COMPRESSED_SIGNED_RED_RGTC1 },
+                { "COMPRESSED_RG_RGTC2", InternalFormat::COMPRESSED_RG_RGTC2 },
+                { "COMPRESSED_SIGNED_RG_RGTC2", InternalFormat::COMPRESSED_SIGNED_RG_RGTC2 },
+
+                { "COMPRESSED_RGBA_BPTC_UNORM", InternalFormat::COMPRESSED_RGBA_BPTC_UNORM },
+                { "COMPRESSED_SRGB_ALPHA_BPTC_UNORM", InternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM },
+                { "COMPRESSED_RGB_BPTC_SIGNED_FLOAT", InternalFormat::COMPRESSED_RGB_BPTC_SIGNED_FLOAT },
+                { "COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT", InternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT },
+
+                { "COMPRESSED_RGB8_ETC2", InternalFormat::COMPRESSED_RGB8_ETC2 },
+                { "COMPRESSED_SRGB8_ETC2", InternalFormat::COMPRESSED_SRGB8_ETC2 },
+                { "COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2", InternalFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 },
+                { "COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2", InternalFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 },
+                { "COMPRESSED_RGBA8_ETC2_EAC", InternalFormat::COMPRESSED_RGBA8_ETC2_EAC },
+                { "COMPRESSED_SRGB8_ALPHA8_ETC2_EAC", InternalFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC },
+
+                { "COMPRESSED_R11_EAC", InternalFormat::COMPRESSED_R11_EAC },
+                { "COMPRESSED_SIGNED_R11_EAC", InternalFormat::COMPRESSED_SIGNED_R11_EAC },
+                { "COMPRESSED_RG11_EAC", InternalFormat::COMPRESSED_RG11_EAC },
+                { "COMPRESSED_SIGNED_RG11_EAC", InternalFormat::COMPRESSED_SIGNED_RG11_EAC }
+            };
+
+            inline const char* toString(InternalFormat format) {
+                for (auto& pair : nameToFormat) {
+                    if (pair.second == format) {
+                        return pair.first.data();
+                    }
+                }
+                return nullptr;
+            }
+
+            inline bool fromString(const char* name, InternalFormat* format) {
+                auto it = nameToFormat.find(name);
+                if (it == nameToFormat.end()) {
+                    return false;
+                }
+                *format = it->second;
+                return true;
+            }
+
             inline uint8_t evalUncompressedBlockBitSize(InternalFormat format) {
                 switch (format) {
                     case InternalFormat::R8:
diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp
index 416920d43f..716d2aeab1 100644
--- a/libraries/model-networking/src/model-networking/ModelCache.cpp
+++ b/libraries/model-networking/src/model-networking/ModelCache.cpp
@@ -63,9 +63,10 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
     PROFILE_ASYNC_BEGIN(resource_parse_geometry, "GeometryMappingResource::downloadFinished", _url.toString(),
                          { { "url", _url.toString() } });
 
-    auto mapping = FSTReader::readMapping(data);
+    // store parsed contents of FST file
+    _mapping = FSTReader::readMapping(data);
 
-    QString filename = mapping.value("filename").toString();
+    QString filename = _mapping.value("filename").toString();
 
     if (filename.isNull()) {
         qCDebug(modelnetworking) << "Mapping file" << _url << "has no \"filename\" field";
@@ -73,7 +74,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
     } else {
         QUrl url = _url.resolved(filename);
 
-        QString texdir = mapping.value(TEXDIR_FIELD).toString();
+        QString texdir = _mapping.value(TEXDIR_FIELD).toString();
         if (!texdir.isNull()) {
             if (!texdir.endsWith('/')) {
                 texdir += '/';
@@ -83,15 +84,16 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
             _textureBaseUrl = url.resolved(QUrl("."));
         }
 
-        auto scripts = FSTReader::getScripts(_url, mapping);
+        auto scripts = FSTReader::getScripts(_url, _mapping);
         if (scripts.size() > 0) {
-            mapping.remove(SCRIPT_FIELD);
+            _mapping.remove(SCRIPT_FIELD);
             for (auto &scriptPath : scripts) {
-                mapping.insertMulti(SCRIPT_FIELD, scriptPath);
+                _mapping.insertMulti(SCRIPT_FIELD, scriptPath);
             }
         }
 
-        auto animGraphVariant = mapping.value("animGraphUrl");
+        auto animGraphVariant = _mapping.value("animGraphUrl");
+
         if (animGraphVariant.isValid()) {
             QUrl fstUrl(animGraphVariant.toString());
             if (fstUrl.isValid()) {
@@ -104,7 +106,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
         }
 
         auto modelCache = DependencyManager::get<ModelCache>();
-        GeometryExtra extra{ mapping, _textureBaseUrl, false };
+        GeometryExtra extra{ _mapping, _textureBaseUrl, false };
 
         // Get the raw GeometryResource
         _geometryResource = modelCache->getResource(url, QUrl(), &extra).staticCast<GeometryResource>();
@@ -379,6 +381,7 @@ Geometry::Geometry(const Geometry& geometry) {
     }
 
     _animGraphOverrideUrl = geometry._animGraphOverrideUrl;
+    _mapping = geometry._mapping;
 }
 
 void Geometry::setTextures(const QVariantMap& textureMap) {
@@ -534,10 +537,11 @@ QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& textu
         // Inlined file: cache under the fbx file to avoid namespace clashes
         // NOTE: We cannot resolve the path because filename may be an absolute path
         assert(texture.filename.size() > 0);
+        auto baseUrlStripped = baseUrl.toDisplayString(QUrl::RemoveFragment | QUrl::RemoveQuery | QUrl::RemoveUserInfo);
         if (texture.filename.at(0) == '/') {
-            return baseUrl.toString() + texture.filename;
+            return baseUrlStripped + texture.filename;
         } else {
-            return baseUrl.toString() + '/' + texture.filename;
+            return baseUrlStripped + '/' + texture.filename;
         }
     }
 }
diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h
index 9532f39ce0..7e911bc9bf 100644
--- a/libraries/model-networking/src/model-networking/ModelCache.h
+++ b/libraries/model-networking/src/model-networking/ModelCache.h
@@ -55,6 +55,7 @@ public:
 
     virtual bool areTexturesLoaded() const;
     const QUrl& getAnimGraphOverrideUrl() const { return _animGraphOverrideUrl; }
+    const QVariantHash& getMapping() const { return _mapping; }
 
 protected:
     friend class GeometryMappingResource;
@@ -68,6 +69,7 @@ protected:
     NetworkMaterials _materials;
 
     QUrl _animGraphOverrideUrl;
+    QVariantHash _mapping;  // parsed contents of FST file.
 
 private:
     mutable bool _areTexturesLoaded { false };
@@ -144,6 +146,9 @@ public:
      * API to manage model cache resources.
      * @namespace ModelCache
      *
+     * @hifi-interface
+     * @hifi-client-entity
+     *
      * @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
      * @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
      * @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
@@ -156,7 +161,7 @@ public:
     /**jsdoc
      * Get the list of all resource URLs.
      * @function ModelCache.getResourceList
-     * @return {string[]}
+     * @returns {string[]}
      */
 
     /**jsdoc
@@ -170,10 +175,11 @@ public:
      */
 
     /**jsdoc
+     * Prefetches a resource.
      * @function ModelCache.prefetch
-     * @param {string} url
-     * @param {object} extra
-     * @returns {object}
+     * @param {string} url - URL of the resource to prefetch.
+     * @param {object} [extra=null]
+     * @returns {Resource}
      */
 
     /**jsdoc
@@ -182,14 +188,7 @@ public:
      * @param {string} url - URL of the resource to load.
      * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
      * @param {} [extra=null]
-     * @return {Resource}
-     */
-
-    /**jsdoc
-     * Prefetches a resource.
-     * @function ModelCache.prefetch
-     * @param {string} url - URL of the resource to prefetch.
-     * @return {Resource}
+     * @returns {Resource}
      */
 
 
diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp
index 04696cea1a..ed21fd35bc 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.cpp
+++ b/libraries/model-networking/src/model-networking/TextureCache.cpp
@@ -48,6 +48,8 @@
 #include <Trace.h>
 #include <StatTracker.h>
 
+#include <TextureMeta.h>
+
 Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image")
 Q_LOGGING_CATEGORY(trace_resource_parse_image_raw, "trace.resource.parse.image.raw")
 Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.ktx")
@@ -293,7 +295,6 @@ int networkTexturePointerMetaTypeId = qRegisterMetaType<QWeakPointer<NetworkText
 NetworkTexture::NetworkTexture(const QUrl& url) :
 Resource(url),
 _type(),
-_sourceIsKTX(false),
 _maxNumPixels(100)
 {
     _textureSource = std::make_shared<gpu::TextureSource>(url);
@@ -309,17 +310,25 @@ static bool isLocalUrl(const QUrl& url) {
 NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) :
     Resource(url),
     _type(type),
-    _sourceIsKTX(url.path().endsWith(".ktx")),
     _maxNumPixels(maxNumPixels)
 {
     _textureSource = std::make_shared<gpu::TextureSource>(url, (int)type);
     _lowestRequestedMipLevel = 0;
 
-    _shouldFailOnRedirect = !_sourceIsKTX;
+    auto fileNameLowercase = url.fileName().toLower();
+    if (fileNameLowercase.endsWith(TEXTURE_META_EXTENSION)) {
+        _currentlyLoadingResourceType = ResourceType::META;
+    } else if (fileNameLowercase.endsWith(".ktx")) {
+        _currentlyLoadingResourceType = ResourceType::KTX;
+    } else {
+        _currentlyLoadingResourceType = ResourceType::ORIGINAL;
+    }
+
+    _shouldFailOnRedirect = _currentlyLoadingResourceType != ResourceType::KTX;
 
     if (type == image::TextureUsage::CUBE_TEXTURE) {
         setLoadPriority(this, SKYBOX_LOAD_PRIORITY);
-    } else if (_sourceIsKTX) {
+    } else if (_currentlyLoadingResourceType == ResourceType::KTX) {
         setLoadPriority(this, HIGH_MIPS_LOAD_PRIORITY);
     }
 
@@ -330,7 +339,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type,
     // if we have content, load it after we have our self pointer
     if (!content.isEmpty()) {
         _startedLoading = true;
-        QMetaObject::invokeMethod(this, "loadContent", Qt::QueuedConnection, Q_ARG(const QByteArray&, content));
+        QMetaObject::invokeMethod(this, "downloadFinished", Qt::QueuedConnection, Q_ARG(const QByteArray&, content));
     }
 }
 
@@ -393,12 +402,12 @@ NetworkTexture::~NetworkTexture() {
 
 const uint16_t NetworkTexture::NULL_MIP_LEVEL = std::numeric_limits<uint16_t>::max();
 void NetworkTexture::makeRequest() {
-    if (!_sourceIsKTX) {
+    if (_currentlyLoadingResourceType != ResourceType::KTX) {
         Resource::makeRequest();
         return;
     }
 
-    if (isLocalUrl(_url)) {
+    if (isLocalUrl(_activeUrl)) {
         auto self = _self;
         QtConcurrent::run(QThreadPool::globalInstance(), [self] {
             auto resource = self.lock();
@@ -466,12 +475,12 @@ void NetworkTexture::handleLocalRequestCompleted() {
 }
 
 void NetworkTexture::makeLocalRequest() {
-    const QString scheme = _url.scheme();
+    const QString scheme = _activeUrl.scheme();
     QString path;
     if (scheme == URL_SCHEME_FILE) {
-        path = PathUtils::expandToLocalDataAbsolutePath(_url).toLocalFile();
+        path = PathUtils::expandToLocalDataAbsolutePath(_activeUrl).toLocalFile();
     } else {
-        path = ":" + _url.path();
+        path = ":" + _activeUrl.path();
     }
 
     connect(this, &Resource::finished, this, &NetworkTexture::handleLocalRequestCompleted);
@@ -497,7 +506,7 @@ void NetworkTexture::makeLocalRequest() {
         });
 
         if (found == ktxDescriptor->keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) {
-            hash = _url.toString().toLocal8Bit().toHex().toStdString();
+            hash = _activeUrl.toString().toLocal8Bit().toHex().toStdString();
         } else {
             // at this point the source hash is in binary 16-byte form
             // and we need it in a hexadecimal string
@@ -536,11 +545,13 @@ void NetworkTexture::makeLocalRequest() {
 }
 
 bool NetworkTexture::handleFailedRequest(ResourceRequest::Result result) {
-    if (!_sourceIsKTX && result == ResourceRequest::Result::RedirectFail) {
+    if (_currentlyLoadingResourceType != ResourceType::KTX
+        && result == ResourceRequest::Result::RedirectFail) {
+
         auto newPath = _request->getRelativePathUrl();
         if (newPath.fileName().endsWith(".ktx")) {
             qDebug() << "Redirecting to" << newPath << "from" << _url;
-            _sourceIsKTX = true;
+            _currentlyLoadingResourceType = ResourceType::KTX;
             _activeUrl = newPath;
             _shouldFailOnRedirect = false;
             makeRequest();
@@ -930,11 +941,75 @@ void NetworkTexture::handleFinishedInitialLoad() {
 }
 
 void NetworkTexture::downloadFinished(const QByteArray& data) {
-    loadContent(data);
+    if (_currentlyLoadingResourceType == ResourceType::META) {
+        loadMetaContent(data);
+    } else if (_currentlyLoadingResourceType == ResourceType::ORIGINAL) {
+        loadTextureContent(data);
+    } else {
+        TextureCache::requestCompleted(_self);
+        Resource::handleFailedRequest(ResourceRequest::Error);
+    }
 }
 
-void NetworkTexture::loadContent(const QByteArray& content) {
-    if (_sourceIsKTX) {
+void NetworkTexture::loadMetaContent(const QByteArray& content) {
+    if (_currentlyLoadingResourceType != ResourceType::META) {
+        qWarning() << "Trying to load meta content when current resource type is not META";
+        assert(false);
+        return;
+    }
+
+    TextureMeta meta;
+    if (!TextureMeta::deserialize(content, &meta)) {
+        qWarning() << "Failed to read texture meta from " << _url;
+        return;
+    }
+
+
+    auto& backend = DependencyManager::get<TextureCache>()->getGPUContext()->getBackend();
+    for (auto pair : meta.availableTextureTypes) {
+        gpu::Element elFormat;
+
+        if (gpu::Texture::getCompressedFormat(pair.first, elFormat)) {
+            if (backend->supportedTextureFormat(elFormat)) {
+                auto url = pair.second;
+                if (url.fileName().endsWith(TEXTURE_META_EXTENSION)) {
+                    qWarning() << "Found a texture meta URL inside of the texture meta file at" << _activeUrl;
+                    continue;
+                }
+
+                _currentlyLoadingResourceType = ResourceType::KTX;
+                _activeUrl = _activeUrl.resolved(url);
+                auto textureCache = DependencyManager::get<TextureCache>();
+                auto self = _self.lock();
+                if (!self) {
+                    return;
+                }
+                QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection);
+                return;
+            }
+        }
+    }
+
+    if (!meta.original.isEmpty()) {
+        _currentlyLoadingResourceType = ResourceType::ORIGINAL;
+        _activeUrl = _activeUrl.resolved(meta.original);
+
+        auto textureCache = DependencyManager::get<TextureCache>();
+        auto self = _self.lock();
+        if (!self) {
+            return;
+        }
+        QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection);
+        return;
+    }
+
+    qWarning() << "Failed to find supported texture type in " << _activeUrl;
+    Resource::handleFailedRequest(ResourceRequest::NotFound);
+}
+
+void NetworkTexture::loadTextureContent(const QByteArray& content) {
+    if (_currentlyLoadingResourceType != ResourceType::ORIGINAL) {
+        qWarning() << "Trying to load texture content when current resource type is not ORIGINAL";
         assert(false);
         return;
     }
diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h
index 3f46dc3074..898f0e3d3a 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.h
+++ b/libraries/model-networking/src/model-networking/TextureCache.h
@@ -24,7 +24,9 @@
 #include <graphics/TextureMap.h>
 #include <image/Image.h>
 #include <ktx/KTX.h>
+#include <TextureMeta.h>
 
+#include <gpu/Context.h>
 #include "KTXCache.h"
 
 namespace gpu {
@@ -75,11 +77,13 @@ protected:
 
     virtual bool isCacheable() const override { return _loaded; }
 
-    virtual void downloadFinished(const QByteArray& data) override;
+    Q_INVOKABLE virtual void downloadFinished(const QByteArray& data) override;
 
     bool handleFailedRequest(ResourceRequest::Result result) override;
 
-    Q_INVOKABLE void loadContent(const QByteArray& content);
+    Q_INVOKABLE void loadMetaContent(const QByteArray& content);
+    Q_INVOKABLE void loadTextureContent(const QByteArray& content);
+
     Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight);
 
     Q_INVOKABLE void startRequestForNextMipLevel();
@@ -93,6 +97,14 @@ private:
 
     image::TextureUsage::Type _type;
 
+    enum class ResourceType {
+        META,
+        ORIGINAL,
+        KTX
+    };
+
+    ResourceType _currentlyLoadingResourceType { ResourceType::META };
+
     static const uint16_t NULL_MIP_LEVEL;
     enum KTXResourceState {
         PENDING_INITIAL_LOAD = 0,
@@ -103,7 +115,6 @@ private:
         FAILED_TO_LOAD
     };
 
-    bool _sourceIsKTX { false };
     KTXResourceState _ktxResourceState { PENDING_INITIAL_LOAD };
 
     // The current mips that are currently being requested w/ _ktxMipRequest
@@ -151,6 +162,9 @@ public:
     * API to manage texture cache resources.
     * @namespace TextureCache
     *
+    * @hifi-interface
+    * @hifi-client-entity
+    *
     * @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
     * @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
     * @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
@@ -160,44 +174,38 @@ public:
 
     // Functions are copied over from ResourceCache (see ResourceCache.h for reason).
 
-   /**jsdoc
-    * Get the list of all resource URLs.
-    * @function TextureCache.getResourceList
-    * @return {string[]}
-    */
+    /**jsdoc
+     * Get the list of all resource URLs.
+     * @function TextureCache.getResourceList
+     * @returns {string[]}
+     */
 
-   /**jsdoc
-    * @function TextureCache.dirty
-    * @returns {Signal}
-    */
+    /**jsdoc
+     * @function TextureCache.dirty
+     * @returns {Signal}
+     */
 
-   /**jsdoc
-    * @function TextureCache.updateTotalSize
-    * @param {number} deltaSize
-    */
+    /**jsdoc
+     * @function TextureCache.updateTotalSize
+     * @param {number} deltaSize
+     */
 
-   /**jsdoc
-    * @function TextureCache.prefetch
-    * @param {string} url
-    * @param {object} extra
-    * @returns {object}
-    */
+    /**jsdoc
+     * Prefetches a resource.
+     * @function TextureCache.prefetch
+     * @param {string} url - URL of the resource to prefetch.
+     * @param {object} [extra=null]
+     * @returns {Resource}
+     */
 
-   /**jsdoc
-    * Asynchronously loads a resource from the specified URL and returns it.
-    * @function TextureCache.getResource
-    * @param {string} url - URL of the resource to load.
-    * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
-    * @param {} [extra=null]
-    * @return {Resource}
-    */
-
-   /**jsdoc
-    * Prefetches a resource.
-    * @function TextureCache.prefetch
-    * @param {string} url - URL of the resource to prefetch.
-    * @return {Resource}
-    */
+    /**jsdoc
+     * Asynchronously loads a resource from the specified URL and returns it.
+     * @function TextureCache.getResource
+     * @param {string} url - URL of the resource to load.
+     * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
+     * @param {} [extra=null]
+     * @returns {Resource}
+     */
 
 
     /// Returns the ID of the permutation/normal texture used for Perlin noise shader programs.  This texture
@@ -236,6 +244,9 @@ public:
     static const int DEFAULT_SPECTATOR_CAM_WIDTH { 2048 };
     static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 };
 
+    void setGPUContext(const gpu::ContextPointer& context) { _gpuContext = context; }
+    gpu::ContextPointer getGPUContext() const { return _gpuContext; }
+
 signals:
     /**jsdoc 
      * @function TextureCache.spectatorCameraFramebufferReset
@@ -246,10 +257,11 @@ signals:
 protected:
     
     /**jsdoc
-     * @function TextureCache.prefect
+     * @function TextureCache.prefetch
      * @param {string} url
      * @param {number} type
      * @param {number} [maxNumPixels=67108864]
+     * @returns {Resource}
      */
     // Overload ResourceCache::prefetch to allow specifying texture type for loads
     Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);
@@ -268,6 +280,8 @@ private:
     static const std::string KTX_DIRNAME;
     static const std::string KTX_EXT;
 
+    gpu::ContextPointer _gpuContext { nullptr };
+
     std::shared_ptr<cache::FileCache> _ktxCache { std::make_shared<KTXCache>(KTX_DIRNAME, KTX_EXT) };
 
     // Map from image hashes to texture weak pointers
diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index edb2992128..dd6a7fffe9 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AddressManager.h"
+
 #include <QGuiApplication>
 #include <QClipboard>
 #include <QDebug>
@@ -23,7 +25,6 @@
 #include <SettingHandle.h>
 #include <UUID.h>
 
-#include "AddressManager.h"
 #include "NodeList.h"
 #include "NetworkLogging.h"
 #include "UserActivityLogger.h"
diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index 94eff46bda..8e2553779b 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -33,11 +33,14 @@ const QString GET_PLACE = "/api/v1/places/%1";
  * The location API provides facilities related to your current location in the metaverse.
  *
  * @namespace location
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-assignment-client
+ *
  * @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not
  *     connected to the domain or are in a serverless domain.
  *     <em>Read-only.</em>
- * @property {Uuid} domainId - Synonym for <code>domainId</code>. <em>Read-only.</em> <strong>Deprecated:</strong> This property
- *     is deprecated and will soon be removed.
  * @property {string} hostname - The name of the domain for your current metaverse address (e.g., <code>"AvatarIsland"</code>,
  *     <code>localhost</code>, or an IP address). Is blank if you're in a serverless domain.
  *     <em>Read-only.</em>
@@ -68,7 +71,6 @@ class AddressManager : public QObject, public Dependency {
     Q_PROPERTY(QString pathname READ currentPath)
     Q_PROPERTY(QString placename READ getPlaceName)
     Q_PROPERTY(QString domainID READ getDomainID)
-    Q_PROPERTY(QString domainId READ getDomainID)
 public:
     using PositionGetter = std::function<glm::vec3()>;
     using OrientationGetter = std::function<glm::quat()>;
diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp
index 58a4446aa6..71a3cfb269 100644
--- a/libraries/networking/src/Assignment.cpp
+++ b/libraries/networking/src/Assignment.cpp
@@ -9,17 +9,18 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "udt/PacketHeaders.h"
-#include "SharedUtil.h"
-#include "UUID.h"
+#include "Assignment.h"
 
 #include <QtCore/QDataStream>
 
 #include <BuildInfo.h>
-#include "Assignment.h"
 #include <QtCore/QStandardPaths>
 #include <QtCore/QDir>
 
+#include "udt/PacketHeaders.h"
+#include "SharedUtil.h"
+#include "UUID.h"
+
 Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) {
     switch (nodeType) {
         case NodeType::AudioMixer:
diff --git a/libraries/networking/src/AtpReply.cpp b/libraries/networking/src/AtpReply.cpp
index 6417478005..b2b7e8bee7 100644
--- a/libraries/networking/src/AtpReply.cpp
+++ b/libraries/networking/src/AtpReply.cpp
@@ -9,9 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "ResourceManager.h"
 #include "AtpReply.h"
 
+#include "ResourceManager.h"
+
 AtpReply::AtpReply(const QUrl& url, QObject* parent) :
     _resourceRequest(DependencyManager::get<ResourceManager>()->createResourceRequest(parent, url)) {
     setOperation(QNetworkAccessManager::GetOperation);
diff --git a/libraries/networking/src/BandwidthRecorder.cpp b/libraries/networking/src/BandwidthRecorder.cpp
index d43d4cf21f..5ad3494017 100644
--- a/libraries/networking/src/BandwidthRecorder.cpp
+++ b/libraries/networking/src/BandwidthRecorder.cpp
@@ -11,9 +11,9 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QDateTime>
 #include "BandwidthRecorder.h"
 
+#include <QDateTime>
 
 BandwidthRecorder::Channel::Channel() {
 }
diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp
index 51f93d13b0..8756a0cc4b 100644
--- a/libraries/networking/src/DataServerAccountInfo.cpp
+++ b/libraries/networking/src/DataServerAccountInfo.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "DataServerAccountInfo.h"
+
 #include <openssl/rsa.h>
 #include <openssl/x509.h>
 
@@ -20,7 +22,6 @@
 #include <UUID.h>
 
 #include "NetworkLogging.h"
-#include "DataServerAccountInfo.h"
 
 #ifdef __clang__
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h
index 2d4712209d..08908dbaf6 100644
--- a/libraries/networking/src/DomainHandler.h
+++ b/libraries/networking/src/DomainHandler.h
@@ -97,7 +97,7 @@ public:
 
     int getCheckInPacketsSinceLastReply() const { return _checkInPacketsSinceLastReply; }
     void sentCheckInPacket();
-    void domainListReceived() { _checkInPacketsSinceLastReply = 0; }
+    void clearPendingCheckins() { _checkInPacketsSinceLastReply = 0; }
 
     /**jsdoc
      * <p>The reasons that you may be refused connection to a domain are defined by numeric values:</p>
diff --git a/libraries/networking/src/HMACAuth.cpp b/libraries/networking/src/HMACAuth.cpp
new file mode 100644
index 0000000000..515af9a070
--- /dev/null
+++ b/libraries/networking/src/HMACAuth.cpp
@@ -0,0 +1,117 @@
+//
+//  HMACAuth.cpp
+//  libraries/networking/src
+//
+//  Created by Simon Walton on 3/19/2018.
+//  Copyright 2018 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 "HMACAuth.h"
+
+#include <openssl/opensslv.h>
+#include <openssl/hmac.h>
+
+#include <QUuid>
+#include "NetworkLogging.h"
+#include <cassert>
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+HMACAuth::HMACAuth(AuthMethod authMethod)
+    : _hmacContext(HMAC_CTX_new())
+    , _authMethod(authMethod) { }
+
+HMACAuth::~HMACAuth()
+{
+    HMAC_CTX_free(_hmacContext);
+}
+
+#else
+
+HMACAuth::HMACAuth(AuthMethod authMethod)
+    : _hmacContext(new HMAC_CTX())
+    , _authMethod(authMethod) {
+    HMAC_CTX_init(_hmacContext);
+}
+
+HMACAuth::~HMACAuth() {
+    HMAC_CTX_cleanup(_hmacContext);
+    delete _hmacContext;
+}
+#endif
+
+bool HMACAuth::setKey(const char* keyValue, int keyLen) {
+    const EVP_MD* sslStruct = nullptr;
+
+    switch (_authMethod) {
+    case MD5:
+        sslStruct = EVP_md5();
+        break;
+
+    case SHA1:
+        sslStruct = EVP_sha1();
+        break;
+
+    case SHA224:
+        sslStruct = EVP_sha224();
+        break;
+
+    case SHA256:
+        sslStruct = EVP_sha256();
+        break;
+
+    case RIPEMD160:
+        sslStruct = EVP_ripemd160();
+        break;
+
+    default:
+        return false;
+    }
+
+    QMutexLocker lock(&_lock);
+    return (bool) HMAC_Init_ex(_hmacContext, keyValue, keyLen, sslStruct, nullptr);
+}
+
+bool HMACAuth::setKey(const QUuid& uidKey) {
+    const QByteArray rfcBytes(uidKey.toRfc4122());
+    return setKey(rfcBytes.constData(), rfcBytes.length());
+}
+
+bool HMACAuth::addData(const char* data, int dataLen) {
+    QMutexLocker lock(&_lock);
+    return (bool) HMAC_Update(_hmacContext, reinterpret_cast<const unsigned char*>(data), dataLen);
+}
+
+HMACAuth::HMACHash HMACAuth::result() {
+    HMACHash hashValue(EVP_MAX_MD_SIZE);
+    unsigned int hashLen;
+    QMutexLocker lock(&_lock);
+    
+    auto hmacResult = HMAC_Final(_hmacContext, &hashValue[0], &hashLen);
+    
+    if (hmacResult) {
+        hashValue.resize((size_t)hashLen);
+    } else {
+        // the HMAC_FINAL call failed - should not be possible to get into this state
+        qCWarning(networking) << "Error occured calling HMAC_Final";
+        assert(hmacResult);
+    }
+
+    // Clear state for possible reuse.
+    HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
+    return hashValue;
+}
+
+bool HMACAuth::calculateHash(HMACHash& hashResult, const char* data, int dataLen) {
+    QMutexLocker lock(&_lock);
+    if (!addData(data, dataLen)) {
+        qCWarning(networking) << "Error occured calling HMACAuth::addData()";
+        assert(false);
+        return false;
+    }
+
+    hashResult = result();
+    return true;
+}
diff --git a/libraries/networking/src/HMACAuth.h b/libraries/networking/src/HMACAuth.h
new file mode 100644
index 0000000000..1346bdee17
--- /dev/null
+++ b/libraries/networking/src/HMACAuth.h
@@ -0,0 +1,47 @@
+//
+//  HMACAuth.h
+//  libraries/networking/src
+//
+//  Created by Simon Walton on 3/19/2018.
+//  Copyright 2018 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_HMACAuth_h
+#define hifi_HMACAuth_h
+
+#include <vector>
+#include <memory>
+#include <QtCore/QMutex>
+
+class QUuid;
+
+class HMACAuth {
+public:
+    enum AuthMethod { MD5, SHA1, SHA224, SHA256, RIPEMD160 };
+    using HMACHash = std::vector<unsigned char>;
+    
+    explicit HMACAuth(AuthMethod authMethod = MD5);
+    ~HMACAuth();
+
+    bool setKey(const char* keyValue, int keyLen);
+    bool setKey(const QUuid& uidKey);
+    // Calculate complete hash in one.
+    bool calculateHash(HMACHash& hashResult, const char* data, int dataLen);
+
+    // Append to data to be hashed.
+    bool addData(const char* data, int dataLen);
+    // Get the resulting hash from calls to addData().
+    // Note that only one hash may be calculated at a time for each
+    // HMACAuth instance if this interface is used.
+    HMACHash result();
+
+private:
+    QMutex _lock { QMutex::Recursive };
+    struct hmac_ctx_st* _hmacContext;
+    AuthMethod _authMethod;
+};
+
+#endif  // hifi_HMACAuth_h
diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp
index e2a3e79c79..a1bfcdd275 100644
--- a/libraries/networking/src/HifiSockAddr.cpp
+++ b/libraries/networking/src/HifiSockAddr.cpp
@@ -9,11 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "HifiSockAddr.h"
+
 #include <qdatastream.h>
 #include <qhostinfo.h>
 #include <qnetworkinterface.h>
 
-#include "HifiSockAddr.h"
 #include "NetworkLogging.h"
 
 int hifiSockAddrMetaTypeId = qRegisterMetaType<HifiSockAddr>();
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 0660f02fd6..012a891698 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -36,6 +36,7 @@
 #include "HifiSockAddr.h"
 #include "NetworkLogging.h"
 #include "udt/Packet.h"
+#include "HMACAuth.h"
 
 static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
 
@@ -332,15 +333,20 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
             if (verifiedPacket && !ignoreVerification) {
 
                 QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
-                QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
+                QByteArray expectedHash;
+                auto sourceNodeHMACAuth = sourceNode->getAuthenticateHash();
+                if (sourceNode->getAuthenticateHash()) {
+                    expectedHash = NLPacket::hashForPacketAndHMAC(packet, *sourceNodeHMACAuth);
+                }
 
-                // check if the md5 hash in the header matches the hash we would expect
-                if (packetHeaderHash != expectedHash) {
+                // check if the HMAC-md5 hash in the header matches the hash we would expect
+                if (!sourceNodeHMACAuth || packetHeaderHash != expectedHash) {
                     static QMultiMap<QUuid, PacketType> hashDebugSuppressMap;
 
                     if (!hashDebugSuppressMap.contains(sourceID, headerType)) {
-                        qCDebug(networking) << packetHeaderHash << expectedHash;
                         qCDebug(networking) << "Packet hash mismatch on" << headerType << "- Sender" << sourceID;
+                        qCDebug(networking) << "Packet len:" << packet.getDataSize() << "Expected hash:" <<
+                            expectedHash.toHex() << "Actual:" << packetHeaderHash.toHex();
 
                         hashDebugSuppressMap.insert(sourceID, headerType);
                     }
@@ -372,15 +378,15 @@ void LimitedNodeList::collectPacketStats(const NLPacket& packet) {
     _numCollectedBytes += packet.getDataSize();
 }
 
-void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret) {
+void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth) {
     if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) {
         packet.writeSourceID(getSessionLocalID());
     }
 
-    if (!connectionSecret.isNull()
+    if (hmacAuth
         && !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
         && !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
-        packet.writeVerificationHashGivenSecret(connectionSecret);
+        packet.writeVerificationHash(*hmacAuth);
     }
 }
 
@@ -396,17 +402,17 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node&
     emit dataSent(destinationNode.getType(), packet.getDataSize());
     destinationNode.recordBytesSent(packet.getDataSize());
 
-    return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret());
+    return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getAuthenticateHash());
 }
 
 qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
-                                             const QUuid& connectionSecret) {
+        HMACAuth* hmacAuth) {
     Q_ASSERT(!packet.isPartOfMessage());
     Q_ASSERT_X(!packet.isReliable(), "LimitedNodeList::sendUnreliablePacket",
                "Trying to send a reliable packet unreliably.");
 
     collectPacketStats(packet);
-    fillPacketHeader(packet, connectionSecret);
+    fillPacketHeader(packet, hmacAuth);
 
     return _nodeSocket.writePacket(packet, sockAddr);
 }
@@ -419,7 +425,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
         emit dataSent(destinationNode.getType(), packet->getDataSize());
         destinationNode.recordBytesSent(packet->getDataSize());
 
-        return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret());
+        return sendPacket(std::move(packet), *activeSocket, destinationNode.getAuthenticateHash());
     } else {
         qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
         return ERROR_SENDING_PACKET_BYTES;
@@ -427,18 +433,18 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
 }
 
 qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
-                                   const QUuid& connectionSecret) {
+                                   HMACAuth* hmacAuth) {
     Q_ASSERT(!packet->isPartOfMessage());
     if (packet->isReliable()) {
         collectPacketStats(*packet);
-        fillPacketHeader(*packet, connectionSecret);
+        fillPacketHeader(*packet, hmacAuth);
 
         auto size = packet->getDataSize();
         _nodeSocket.writePacket(std::move(packet), sockAddr);
 
         return size;
     } else {
-        return sendUnreliablePacket(*packet, sockAddr, connectionSecret);
+        return sendUnreliablePacket(*packet, sockAddr, hmacAuth);
     }
 }
 
@@ -447,13 +453,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
 
     if (activeSocket) {
         qint64 bytesSent = 0;
-        auto connectionSecret = destinationNode.getConnectionSecret();
+        auto connectionHash = destinationNode.getAuthenticateHash();
 
         // close the last packet in the list
         packetList.closeCurrentPacket();
 
         while (!packetList._packets.empty()) {
-            bytesSent += sendPacket(packetList.takeFront<NLPacket>(), *activeSocket, connectionSecret);
+            bytesSent += sendPacket(packetList.takeFront<NLPacket>(), *activeSocket,
+                connectionHash);
         }
 
         emit dataSent(destinationNode.getType(), bytesSent);
@@ -466,14 +473,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
 }
 
 qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
-                                                          const QUuid& connectionSecret) {
+                                                          HMACAuth* hmacAuth) {
     qint64 bytesSent = 0;
 
     // close the last packet in the list
     packetList.closeCurrentPacket();
 
     while (!packetList._packets.empty()) {
-        bytesSent += sendPacket(packetList.takeFront<NLPacket>(), sockAddr, connectionSecret);
+        bytesSent += sendPacket(packetList.takeFront<NLPacket>(), sockAddr, hmacAuth);
     }
 
     return bytesSent;
@@ -501,7 +508,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr<NLPacketList> packetList,
         for (std::unique_ptr<udt::Packet>& packet : packetList->_packets) {
             NLPacket* nlPacket = static_cast<NLPacket*>(packet.get());
             collectPacketStats(*nlPacket);
-            fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret());
+            fillPacketHeader(*nlPacket, destinationNode.getAuthenticateHash());
         }
 
         return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
@@ -524,7 +531,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
     auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket()
                                                              : overridenSockAddr;
 
-    return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret());
+    return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getAuthenticateHash());
 }
 
 int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h
index 3d6fd0cd91..05374bbfbb 100644
--- a/libraries/networking/src/LimitedNodeList.h
+++ b/libraries/networking/src/LimitedNodeList.h
@@ -138,19 +138,17 @@ public:
     // use sendUnreliablePacket to send an unreliable packet (that you do not need to move)
     // either to a node (via its active socket) or to a manual sockaddr
     qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
-    qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
-                                const QUuid& connectionSecret = QUuid());
+    qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
 
     // use sendPacket to send a moved unreliable or reliable NL packet to a node's active socket or manual sockaddr
     qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode);
-    qint64 sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
-                      const QUuid& connectionSecret = QUuid());
+    qint64 sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
 
     // use sendUnreliableUnorderedPacketList to unreliably send separate packets from the packet list
     // either to a node's active socket or to a manual sockaddr
     qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const Node& destinationNode);
     qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
-                          const QUuid& connectionSecret = QUuid());
+        HMACAuth* hmacAuth = nullptr);
 
     // use sendPacketList to send reliable packet lists (ordered or unordered) to a node's active socket
     // or to a manual sock addr
@@ -372,7 +370,7 @@ protected:
     qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr,
                        const QUuid& connectionSecret = QUuid());
     void collectPacketStats(const NLPacket& packet);
-    void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret = QUuid());
+    void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr);
 
     void setLocalSocket(const HifiSockAddr& sockAddr);
 
diff --git a/libraries/networking/src/LocationScriptingInterface.cpp b/libraries/networking/src/LocationScriptingInterface.cpp
index aae1da73ba..39845558a8 100644
--- a/libraries/networking/src/LocationScriptingInterface.cpp
+++ b/libraries/networking/src/LocationScriptingInterface.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "AddressManager.h"
-
 #include "LocationScriptingInterface.h"
 
+#include "AddressManager.h"
+
 LocationScriptingInterface* LocationScriptingInterface::getInstance() {
     static LocationScriptingInterface sharedInstance;
     return &sharedInstance;
diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h
index 6ef3777d8c..f2ccfe33f4 100644
--- a/libraries/networking/src/MessagesClient.h
+++ b/libraries/networking/src/MessagesClient.h
@@ -37,6 +37,11 @@
  * </ul>
  *
  * @namespace Messages
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
  */
 class MessagesClient : public QObject, public Dependency {
     Q_OBJECT
diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp
index ac3fbc966b..f946e97bf4 100644
--- a/libraries/networking/src/NLPacket.cpp
+++ b/libraries/networking/src/NLPacket.cpp
@@ -11,6 +11,8 @@
 
 #include "NLPacket.h"
 
+#include "HMACAuth.h"
+
 int NLPacket::localHeaderSize(PacketType type) {
     bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type);
     bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type);
@@ -150,18 +152,16 @@ QByteArray NLPacket::verificationHashInHeader(const udt::Packet& packet) {
     return QByteArray(packet.getData() + offset, NUM_BYTES_MD5_HASH);
 }
 
-QByteArray NLPacket::hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret) {
-    QCryptographicHash hash(QCryptographicHash::Md5);
-    
+QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash) {
     int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
         + NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
     
     // add the packet payload and the connection UUID
-    hash.addData(packet.getData() + offset, packet.getDataSize() - offset);
-    hash.addData(connectionSecret.toRfc4122());
-    
-    // return the hash
-    return hash.result();
+    HMACAuth::HMACHash hashResult;
+    if (!hash.calculateHash(hashResult, packet.getData() + offset, packet.getDataSize() - offset)) {
+        return QByteArray();
+    }
+    return QByteArray((const char*) hashResult.data(), (int) hashResult.size());
 }
 
 void NLPacket::writeTypeAndVersion() {
@@ -214,14 +214,14 @@ void NLPacket::writeSourceID(LocalID sourceID) const {
     _sourceID = sourceID;
 }
 
-void NLPacket::writeVerificationHashGivenSecret(const QUuid& connectionSecret) const {
+void NLPacket::writeVerificationHash(HMACAuth& hmacAuth) const {
     Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) &&
              !PacketTypeEnum::getNonVerifiedPackets().contains(_type));
     
     auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
                 + NUM_BYTES_LOCALID;
 
-    QByteArray verificationHash = hashForPacketAndSecret(*this, connectionSecret);
+    QByteArray verificationHash = hashForPacketAndHMAC(*this, hmacAuth);
     
     memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size());
 }
diff --git a/libraries/networking/src/NLPacket.h b/libraries/networking/src/NLPacket.h
index 9b07bc6581..4103f2068e 100644
--- a/libraries/networking/src/NLPacket.h
+++ b/libraries/networking/src/NLPacket.h
@@ -18,6 +18,8 @@
 
 #include "udt/Packet.h"
 
+class HMACAuth;
+
 class NLPacket : public udt::Packet {
     Q_OBJECT
 public:
@@ -69,7 +71,7 @@ public:
     
     static LocalID sourceIDInHeader(const udt::Packet& packet);
     static QByteArray verificationHashInHeader(const udt::Packet& packet);
-    static QByteArray hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret);
+    static QByteArray hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash);
     
     PacketType getType() const { return _type; }
     void setType(PacketType type);
@@ -78,9 +80,9 @@ public:
     void setVersion(PacketVersion version);
 
     LocalID getSourceID() const { return _sourceID; }
-
+    
     void writeSourceID(LocalID sourceID) const;
-    void writeVerificationHashGivenSecret(const QUuid& connectionSecret) const;
+    void writeVerificationHash(HMACAuth& hmacAuth) const;
 
 protected:
     
diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp
index fd356c3e94..f73243e675 100644
--- a/libraries/networking/src/NetworkAccessManager.cpp
+++ b/libraries/networking/src/NetworkAccessManager.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "NetworkAccessManager.h"
+
 #include <QThreadStorage>
 
 #include "AtpReply.h"
-#include "NetworkAccessManager.h"
 #include <QtNetwork/QNetworkProxy>
 
 QThreadStorage<QNetworkAccessManager*> networkAccessManagers;
diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp
index 73b7c44e7e..5bbff103dd 100644
--- a/libraries/networking/src/Node.cpp
+++ b/libraries/networking/src/Node.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Node.h"
+
 #include <cstring>
 #include <stdio.h>
 
@@ -21,8 +23,6 @@
 #include "NodePermissions.h"
 #include "SharedUtil.h"
 
-#include "Node.h"
-
 const QString UNKNOWN_NodeType_t_NAME = "Unknown";
 
 int NodePtrMetaTypeId = qRegisterMetaType<Node*>("Node*");
@@ -86,7 +86,7 @@ NodeType_t NodeType::fromString(QString type) {
 
 
 Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
-           const HifiSockAddr& localSocket, QObject* parent) :
+    const HifiSockAddr& localSocket, QObject* parent) :
     NetworkPeer(uuid, publicSocket, localSocket, parent),
     _type(type),
     _pingMs(-1),  // "Uninitialized"
@@ -108,6 +108,7 @@ void Node::setType(char type) {
     _symmetricSocket.setObjectName(typeString);
 }
 
+
 void Node::updateClockSkewUsec(qint64 clockSkewSample) {
     _clockSkewMovingPercentile.updatePercentile(clockSkewSample);
     _clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
@@ -194,3 +195,16 @@ QDebug operator<<(QDebug debug, const Node& node) {
     debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket();
     return debug.nospace();
 }
+
+void Node::setConnectionSecret(const QUuid& connectionSecret) {
+    if (_connectionSecret == connectionSecret) {
+        return;
+    }
+
+    if (!_authenticateHash) {
+        _authenticateHash.reset(new HMACAuth());
+    }
+
+    _connectionSecret = connectionSecret;
+    _authenticateHash->setKey(_connectionSecret);
+}
diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h
index 93b6a649d4..bcfe525aa8 100644
--- a/libraries/networking/src/Node.h
+++ b/libraries/networking/src/Node.h
@@ -33,6 +33,7 @@
 #include "SimpleMovingAverage.h"
 #include "MovingPercentile.h"
 #include "NodePermissions.h"
+#include "HMACAuth.h"
 
 class Node : public NetworkPeer {
     Q_OBJECT
@@ -55,7 +56,8 @@ public:
     void setIsUpstream(bool isUpstream) { _isUpstream = isUpstream; }
 
     const QUuid& getConnectionSecret() const { return _connectionSecret; }
-    void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
+    void setConnectionSecret(const QUuid& connectionSecret);
+    HMACAuth* getAuthenticateHash() const { return _authenticateHash.get(); }
 
     NodeData* getLinkedData() const { return _linkedData.get(); }
     void setLinkedData(std::unique_ptr<NodeData> linkedData) { _linkedData = std::move(linkedData); }
@@ -97,6 +99,7 @@ private:
     NodeType_t _type;
 
     QUuid _connectionSecret;
+    std::unique_ptr<HMACAuth> _authenticateHash { nullptr };
     std::unique_ptr<NodeData> _linkedData;
     bool _isReplicated { false };
     int _pingMs;
diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp
index 04e32f50cb..4920ea97c7 100644
--- a/libraries/networking/src/NodeList.cpp
+++ b/libraries/networking/src/NodeList.cpp
@@ -594,6 +594,8 @@ void NodeList::processDomainServerConnectionTokenPacket(QSharedPointer<ReceivedM
     }
     // read in the connection token from the packet, then send domain-server checkin
     _domainHandler.setConnectionToken(QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)));
+
+    _domainHandler.clearPendingCheckins();
     sendDomainServerCheckIn();
 }
 
@@ -605,7 +607,7 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
     }
 
     // this is a packet from the domain server, reset the count of un-replied check-ins
-    _domainHandler.domainListReceived();
+    _domainHandler.clearPendingCheckins();
 
     // emit our signal so listeners know we just heard from the DS
     emit receivedDomainServerList();
diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h
index 9595c5da84..c5cf5e9524 100644
--- a/libraries/networking/src/NodeList.h
+++ b/libraries/networking/src/NodeList.h
@@ -167,7 +167,7 @@ private:
     HifiSockAddr _assignmentServerSocket;
     bool _isShuttingDown { false };
     QTimer _keepAlivePingTimer;
-    bool _requestsDomainListData;
+    bool _requestsDomainListData { false };
 
     mutable QReadWriteLock _ignoredSetLock;
     tbb::concurrent_unordered_set<QUuid, UUIDHasher> _ignoredNodeIDs;
diff --git a/libraries/networking/src/OAuthAccessToken.cpp b/libraries/networking/src/OAuthAccessToken.cpp
index 0c14e5e074..44db2a799e 100644
--- a/libraries/networking/src/OAuthAccessToken.cpp
+++ b/libraries/networking/src/OAuthAccessToken.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QtCore/QDataStream>
-
 #include "OAuthAccessToken.h"
 
+#include <QtCore/QDataStream>
+
 OAuthAccessToken::OAuthAccessToken() :
     token(),
     refreshToken(),
diff --git a/libraries/networking/src/OAuthNetworkAccessManager.cpp b/libraries/networking/src/OAuthNetworkAccessManager.cpp
index a30786efa4..272ff47a49 100644
--- a/libraries/networking/src/OAuthNetworkAccessManager.cpp
+++ b/libraries/networking/src/OAuthNetworkAccessManager.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OAuthNetworkAccessManager.h"
+
 #include <QNetworkAccessManager>
 #include <QNetworkRequest>
 #include <QThreadStorage>
@@ -18,8 +20,6 @@
 #include "NetworkingConstants.h"
 #include "SharedUtil.h"
 
-#include "OAuthNetworkAccessManager.h"
-
 QThreadStorage<OAuthNetworkAccessManager*> oauthNetworkAccessManagers;
 
 OAuthNetworkAccessManager* OAuthNetworkAccessManager::getInstance() {
diff --git a/libraries/networking/src/PacketSender.cpp b/libraries/networking/src/PacketSender.cpp
index 02c4815f1f..6288743c46 100644
--- a/libraries/networking/src/PacketSender.cpp
+++ b/libraries/networking/src/PacketSender.cpp
@@ -9,12 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "PacketSender.h"
+
 #include <algorithm>
 #include <math.h>
 #include <stdint.h>
 
 #include "NodeList.h"
-#include "PacketSender.h"
 #include "SharedUtil.h"
 
 const quint64 PacketSender::USECS_PER_SECOND = 1000 * 1000;
diff --git a/libraries/networking/src/RSAKeypairGenerator.cpp b/libraries/networking/src/RSAKeypairGenerator.cpp
index a98cf74564..8ca8b81ea3 100644
--- a/libraries/networking/src/RSAKeypairGenerator.cpp
+++ b/libraries/networking/src/RSAKeypairGenerator.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "RSAKeypairGenerator.h"
+
 #include <openssl/err.h>
 #include <openssl/rsa.h>
 #include <openssl/x509.h>
@@ -16,8 +18,6 @@
 #include <qdebug.h>
 
 #include "NetworkLogging.h"
-
-#include "RSAKeypairGenerator.h"
 #ifdef __clang__
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 #endif
diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp
index c18d4ed1e8..7145744206 100644
--- a/libraries/networking/src/ReceivedPacketProcessor.cpp
+++ b/libraries/networking/src/ReceivedPacketProcessor.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ReceivedPacketProcessor.h"
+
 #include <NumericalConstants.h>
 
 #include "NodeList.h"
-#include "ReceivedPacketProcessor.h"
 #include "SharedUtil.h"
 
 ReceivedPacketProcessor::ReceivedPacketProcessor() {
diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h
index f71abce1f1..6c590ec54d 100644
--- a/libraries/networking/src/ReceivedPacketProcessor.h
+++ b/libraries/networking/src/ReceivedPacketProcessor.h
@@ -14,8 +14,12 @@
 
 #include <QWaitCondition>
 
+#include "NodeList.h"
+
 #include "GenericThread.h"
 
+class ReceivedMessage;
+
 /// Generalized threaded processor for handling received inbound packets.
 class ReceivedPacketProcessor : public GenericThread {
     Q_OBJECT
diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp
index d3583687e8..28266d0a7f 100644
--- a/libraries/networking/src/ResourceCache.cpp
+++ b/libraries/networking/src/ResourceCache.cpp
@@ -581,6 +581,7 @@ void Resource::refresh() {
         ResourceCache::requestCompleted(_self);
     }
     
+    _activeUrl = _url;
     init();
     ensureLoading();
     emit onRefresh();
@@ -618,7 +619,6 @@ void Resource::init(bool resetLoaded) {
         _loaded = false;
     }
     _attempts = 0;
-    _activeUrl = _url;
     
     if (_url.isEmpty()) {
         _startedLoading = _loaded = true;
@@ -724,7 +724,7 @@ void Resource::handleReplyFinished() {
     auto result = _request->getResult();
     if (result == ResourceRequest::Success) {
         auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString());
-        qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
+        qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_activeUrl.toDisplayString(), extraInfo);
 
         auto relativePathURL = _request->getRelativePathUrl();
         if (!relativePathURL.isEmpty()) {
diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h
index 609483bc56..e2df582aa3 100644
--- a/libraries/networking/src/ResourceCache.h
+++ b/libraries/networking/src/ResourceCache.h
@@ -89,6 +89,12 @@ class ScriptableResource : public QObject {
 
     /**jsdoc
      * @constructor Resource
+     *
+     * @hifi-interface
+     * @hifi-client-entity
+     * @hifi-server-entity
+     * @hifi-assignment-client
+     *
      * @property {string} url - URL of this resource.
      * @property {Resource.State} state - Current loading state.
      */
@@ -209,7 +215,7 @@ public:
     /**jsdoc
      * Get the list of all resource URLs.
      * @function ResourceCache.getResourceList
-     * @return {string[]}
+     * @returns {string[]}
      */
     Q_INVOKABLE QVariantList getResourceList();
 
@@ -251,10 +257,11 @@ protected slots:
     void updateTotalSize(const qint64& deltaSize);
 
     /**jsdoc
+     * Prefetches a resource.
      * @function ResourceCache.prefetch
-     * @param {string} url
-     * @param {object} extra
-     * @returns {object}
+     * @param {string} url - URL of the resource to prefetch.
+     * @param {object} [extra=null]
+     * @returns {Resource}
      */
     // Prefetches a resource to be held by the QScriptEngine.
     // Left as a protected member so subclasses can overload prefetch
@@ -267,7 +274,7 @@ protected slots:
      * @param {string} url - URL of the resource to load.
      * @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
      * @param {} [extra=null]
-     * @return {Resource}
+     * @returns {Resource}
      */
     /// Loads a resource from the specified URL and returns it.
     /// If the caller is on a different thread than the ResourceCache,
@@ -285,12 +292,7 @@ protected:
     // Pointers created through this method should be owned by the caller,
     // which should be a QScriptEngine with ScriptableResource registered, so that
     // the QScriptEngine will delete the pointer when it is garbage collected.
-    /**jsdoc
-     * Prefetches a resource.
-     * @function ResourceCache.prefetch
-     * @param {string} url - URL of the resource to prefetch.
-     * @return {Resource}
-     */
+    // JSDoc is provided on more general function signature.
     Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr); }
 
     /// Creates a new resource.
diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp
index 8b6de7da11..9a69d9b3d8 100644
--- a/libraries/networking/src/ThreadedAssignment.cpp
+++ b/libraries/networking/src/ThreadedAssignment.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ThreadedAssignment.h"
+
 #include <QtCore/QCoreApplication>
 #include <QtCore/QJsonArray>
 #include <QtCore/QJsonObject>
@@ -17,8 +19,6 @@
 
 #include <LogHandler.h>
 
-#include "ThreadedAssignment.h"
-
 #include "NetworkLogging.h"
 
 ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) :
diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp
index 0cfd1e09e7..7a92d4bad9 100644
--- a/libraries/networking/src/UserActivityLogger.cpp
+++ b/libraries/networking/src/UserActivityLogger.cpp
@@ -9,16 +9,17 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "UserActivityLogger.h"
+
 #include <QEventLoop>
 #include <QJsonDocument>
 #include <QHttpMultiPart>
 #include <QTimer>
 
-#include "NetworkLogging.h"
-
-#include "UserActivityLogger.h"
 #include <DependencyManager.h>
+
 #include "AddressManager.h"
+#include "NetworkLogging.h"
 
 UserActivityLogger::UserActivityLogger() {
     _timer.start();
diff --git a/libraries/networking/src/WalletTransaction.cpp b/libraries/networking/src/WalletTransaction.cpp
index 0c823555fd..2bb66c67d0 100644
--- a/libraries/networking/src/WalletTransaction.cpp
+++ b/libraries/networking/src/WalletTransaction.cpp
@@ -9,13 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "WalletTransaction.h"
+
 #include <QtCore/QJsonObject>
 
 #include <UUID.h>
 
-#include "WalletTransaction.h"
-
-
 WalletTransaction::WalletTransaction() :
     _uuid(),
     _destinationUUID(),
diff --git a/libraries/networking/src/udt/ConnectionStats.cpp b/libraries/networking/src/udt/ConnectionStats.cpp
index e7efe3d5af..986da062f2 100644
--- a/libraries/networking/src/udt/ConnectionStats.cpp
+++ b/libraries/networking/src/udt/ConnectionStats.cpp
@@ -11,6 +11,8 @@
 
 #include "ConnectionStats.h"
 
+#include <QtCore/QDebug>
+
 using namespace udt;
 using namespace std::chrono;
 
@@ -112,3 +114,31 @@ void ConnectionStats::recordPacketSendPeriod(int sample) {
     _currentSample.packetSendPeriod = sample;
     _total.packetSendPeriod = (int)((_total.packetSendPeriod * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT));
 }
+
+QDebug& operator<<(QDebug&& debug, const udt::ConnectionStats::Stats& stats) {
+    debug << "Connection stats:\n";
+#define HIFI_LOG_EVENT(x) << "    " #x " events: " << stats.events[ConnectionStats::Stats::Event::x] << "\n"
+    debug
+    HIFI_LOG_EVENT(SentACK)
+    HIFI_LOG_EVENT(ReceivedACK)
+    HIFI_LOG_EVENT(ProcessedACK)
+    HIFI_LOG_EVENT(SentLightACK)
+    HIFI_LOG_EVENT(ReceivedLightACK)
+    HIFI_LOG_EVENT(SentACK2)
+    HIFI_LOG_EVENT(ReceivedACK2)
+    HIFI_LOG_EVENT(SentNAK)
+    HIFI_LOG_EVENT(ReceivedNAK)
+    HIFI_LOG_EVENT(SentTimeoutNAK)
+    HIFI_LOG_EVENT(ReceivedTimeoutNAK)
+    HIFI_LOG_EVENT(Retransmission)
+    HIFI_LOG_EVENT(Duplicate)
+    ;
+#undef HIFI_LOG_EVENT
+
+    debug << "    Sent packets: " << stats.sentPackets;
+    debug << "\n     Received packets: " << stats.receivedPackets;
+    debug << "\n     Sent util bytes: " << stats.sentUtilBytes;
+    debug << "\n     Sent bytes: " << stats.sentBytes;
+    debug << "\n     Received bytes: " << stats.receivedBytes << "\n";
+    return debug;
+}
diff --git a/libraries/networking/src/udt/ConnectionStats.h b/libraries/networking/src/udt/ConnectionStats.h
index 84cd6b2486..7ec7b163ee 100644
--- a/libraries/networking/src/udt/ConnectionStats.h
+++ b/libraries/networking/src/udt/ConnectionStats.h
@@ -101,4 +101,7 @@ private:
     
 }
 
+class QDebug;
+QDebug& operator<<(QDebug&& debug, const udt::ConnectionStats::Stats& stats);
+
 #endif // hifi_ConnectionStats_h
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index 98b0e1d892..02853543ba 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -34,7 +34,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
         case PacketType::EntityPhysics:
             return static_cast<PacketVersion>(EntityVersion::MaterialData);
         case PacketType::EntityQuery:
-            return static_cast<PacketVersion>(EntityQueryPacketVersion::RemovedJurisdictions);
+            return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
         case PacketType::AvatarIdentity:
         case PacketType::AvatarData:
         case PacketType::BulkAvatarData:
@@ -59,11 +59,10 @@ PacketVersion versionForPacketType(PacketType packetType) {
             return 17;
         case PacketType::AssetMappingOperation:
         case PacketType::AssetMappingOperationReply:
-            return static_cast<PacketVersion>(AssetServerPacketVersion::RedirectedMappings);
         case PacketType::AssetGetInfo:
         case PacketType::AssetGet:
         case PacketType::AssetUpload:
-            return static_cast<PacketVersion>(AssetServerPacketVersion::RangeRequestSupport);
+            return static_cast<PacketVersion>(AssetServerPacketVersion::BakingTextureMeta);
         case PacketType::NodeIgnoreRequest:
             return 18; // Introduction of node ignore request (which replaced an unused packet tpye)
 
@@ -90,8 +89,10 @@ PacketVersion versionForPacketType(PacketType packetType) {
             return 18;  // replace min_avatar_scale and max_avatar_scale with min_avatar_height and max_avatar_height
         case PacketType::Ping:
             return static_cast<PacketVersion>(PingVersion::IncludeConnectionID);
+        case PacketType::AvatarQuery:
+            return static_cast<PacketVersion>(AvatarQueryVersion::ConicalFrustums);
         default:
-            return 20;
+            return 21;
     }
 }
 
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index e6b133c158..7d374f3625 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -103,7 +103,7 @@ public:
         RadiusIgnoreRequest,
         UsernameFromIDRequest,
         UsernameFromIDReply,
-        ViewFrustum,
+        AvatarQuery,
         RequestsDomainListData,
         PerAvatarGainSet,
         EntityScriptGetStatus,
@@ -244,13 +244,16 @@ enum class EntityQueryPacketVersion: PacketVersion {
     JSONFilter = 18,
     JSONFilterWithFamilyTree = 19,
     ConnectionIdentifier = 20,
-    RemovedJurisdictions = 21
+    RemovedJurisdictions = 21,
+    MultiFrustumQuery = 22,
+    ConicalFrustums = 23
 };
 
 enum class AssetServerPacketVersion: PacketVersion {
     VegasCongestionControl = 19,
     RangeRequestSupport,
-    RedirectedMappings
+    RedirectedMappings,
+    BakingTextureMeta
 };
 
 enum class AvatarMixerPacketVersion : PacketVersion {
@@ -327,4 +330,9 @@ enum class PingVersion : PacketVersion {
     IncludeConnectionID = 18
 };
 
+enum class AvatarQueryVersion : PacketVersion {
+    SendMultipleFrustums = 21,
+    ConicalFrustums = 22
+};
+
 #endif // hifi_PacketHeaders_h
diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp
index 2efd32f2e8..5f943fabf2 100644
--- a/libraries/octree/src/Octree.cpp
+++ b/libraries/octree/src/Octree.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Octree.h"
+
 #include <cstring>
 #include <cstdio>
 #include <cmath>
@@ -43,7 +45,6 @@
 #include <PathUtils.h>
 #include <ViewFrustum.h>
 
-#include "Octree.h"
 #include "OctreeConstants.h"
 #include "OctreeLogging.h"
 #include "OctreeQueryNode.h"
diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h
index a2ad834e18..b827ed7cd0 100644
--- a/libraries/octree/src/Octree.h
+++ b/libraries/octree/src/Octree.h
@@ -59,7 +59,6 @@ const int LOW_RES_MOVING_ADJUST  = 1;
 
 class EncodeBitstreamParams {
 public:
-    ViewFrustum viewFrustum;
     bool includeExistsBits;
     NodeData* nodeData;
 
diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp
index 4f10c9bf79..d8d3041270 100644
--- a/libraries/octree/src/OctreeEditPacketSender.cpp
+++ b/libraries/octree/src/OctreeEditPacketSender.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctreeEditPacketSender.h"
+
 #include <assert.h>
 
 #include <PerfStat.h>
@@ -16,13 +18,11 @@
 #include <OctalCode.h>
 #include <udt/PacketHeaders.h>
 #include "OctreeLogging.h"
-#include "OctreeEditPacketSender.h"
 
 const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::DEFAULT_PACKETS_PER_SECOND;
 
 
 OctreeEditPacketSender::OctreeEditPacketSender() :
-    _shouldSend(true),
     _maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES),
     _releaseQueuedMessagesPending(false)
 {
@@ -145,10 +145,6 @@ void OctreeEditPacketSender::queuePendingPacketToNodes(std::unique_ptr<NLPacket>
 }
 
 void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr<NLPacket> packet) {
-    if (!_shouldSend) {
-        return; // bail early
-    }
-
     assert(serversExist()); // we must have servers to be here!!
 
     auto node = DependencyManager::get<NodeList>()->soloNodeOfType(getMyNodeType());
@@ -161,10 +157,6 @@ void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr<NLPacket> packet
 // NOTE: editMessage - is JUST the octcode/color and does not contain the packet header
 void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, QByteArray& editMessage) {
 
-    if (!_shouldSend) {
-        return; // bail early
-    }
-
     // If we don't have servers, then we will simply queue up all of these packets and wait till we have
     // servers for processing
     if (!serversExist()) {
diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h
index 8a33eb8b73..1dad5b74c7 100644
--- a/libraries/octree/src/OctreeEditPacketSender.h
+++ b/libraries/octree/src/OctreeEditPacketSender.h
@@ -41,12 +41,10 @@ public:
 
     /// are we in sending mode. If we're not in sending mode then all packets and messages will be ignored and
     /// not queued and not sent
-    bool getShouldSend() const { return _shouldSend; }
 
     /// set sending mode. By default we are set to shouldSend=TRUE and packets will be sent. If shouldSend=FALSE, then we'll
     /// switch to not sending mode, and all packets and messages will be ignored, not queued, and not sent. This might be used
     /// in an application like interface when all octree features are disabled.
-    void setShouldSend(bool shouldSend) { _shouldSend = shouldSend; }
 
     /// if you're running in non-threaded mode, you must call this method regularly
     virtual bool process() override;
@@ -76,7 +74,6 @@ public slots:
 protected:
     using EditMessagePair = std::pair<PacketType, QByteArray>;
 
-    bool _shouldSend;
     void queuePacketToNode(const QUuid& nodeID, std::unique_ptr<NLPacket> packet);
     void queuePacketListToNode(const QUuid& nodeUUID, std::unique_ptr<NLPacketList> packetList);
 
diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp
index a666ba0426..b94d0d57e1 100644
--- a/libraries/octree/src/OctreeElement.cpp
+++ b/libraries/octree/src/OctreeElement.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctreeElement.h"
+
 #include <assert.h>
 #include <cmath>
 #include <cstring>
@@ -21,17 +23,16 @@
 #include <LogHandler.h>
 #include <NodeList.h>
 #include <PerfStat.h>
+#include <Trace.h>
 
 #include "AACube.h"
 #include "Logging.h"
 #include "OctalCode.h"
 #include "Octree.h"
 #include "OctreeConstants.h"
-#include "OctreeElement.h"
 #include "OctreeLogging.h"
 #include "OctreeUtils.h"
 #include "SharedUtil.h"
-#include <Trace.h>
 
 AtomicUIntStat OctreeElement::_octreeMemoryUsage { 0 };
 AtomicUIntStat OctreeElement::_octcodeMemoryUsage { 0 };
diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp
index 7108f9a4e4..b938850684 100644
--- a/libraries/octree/src/OctreePacketData.cpp
+++ b/libraries/octree/src/OctreePacketData.cpp
@@ -9,11 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctreePacketData.h"
+
 #include <GLMHelpers.h>
 #include <PerfStat.h>
 
 #include "OctreeLogging.h"
-#include "OctreePacketData.h"
 #include "NumericalConstants.h"
 
 bool OctreePacketData::_debug = false;
diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp
index e6c28f75e8..e6afccab47 100644
--- a/libraries/octree/src/OctreePersistThread.cpp
+++ b/libraries/octree/src/OctreePersistThread.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctreePersistThread.h"
+
 #include <chrono>
 #include <thread>
 
@@ -30,7 +32,6 @@
 #include <PathUtils.h>
 
 #include "OctreeLogging.h"
-#include "OctreePersistThread.h"
 #include "OctreeUtils.h"
 #include "OctreeDataUtils.h"
 
diff --git a/libraries/octree/src/OctreeProcessor.cpp b/libraries/octree/src/OctreeProcessor.cpp
index 43019c7acc..db78e985e6 100644
--- a/libraries/octree/src/OctreeProcessor.cpp
+++ b/libraries/octree/src/OctreeProcessor.cpp
@@ -8,16 +8,17 @@
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
+#include "OctreeProcessor.h"
+
+#include <stdint.h>
 
 #include <glm/glm.hpp>
-#include <stdint.h>
 
 #include <NumericalConstants.h>
 #include <PerfStat.h>
 #include <SharedUtil.h>
 
 #include "OctreeLogging.h"
-#include "OctreeProcessor.h"
 
 OctreeProcessor::OctreeProcessor() :
     _tree(NULL),
diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp
index 18766dd7f6..3f730d4458 100644
--- a/libraries/octree/src/OctreeQuery.cpp
+++ b/libraries/octree/src/OctreeQuery.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctreeQuery.h"
+
 #include <random>
 
 #include <QtCore/QJsonDocument>
@@ -16,23 +18,7 @@
 #include <GLMHelpers.h>
 #include <udt/PacketHeaders.h>
 
-#include "OctreeConstants.h"
-#include "OctreeQuery.h"
-
-const float DEFAULT_FOV = 45.0f; // degrees
-const float DEFAULT_ASPECT_RATIO = 1.0f;
-const float DEFAULT_NEAR_CLIP = 0.1f;
-const float DEFAULT_FAR_CLIP = 3.0f;
-
-OctreeQuery::OctreeQuery(bool randomizeConnectionID) :
-    _cameraFov(DEFAULT_FOV),
-    _cameraAspectRatio(DEFAULT_ASPECT_RATIO),
-    _cameraNearClip(DEFAULT_NEAR_CLIP),
-    _cameraFarClip(DEFAULT_FAR_CLIP),
-    _cameraCenterRadius(DEFAULT_FAR_CLIP)
-{
-    _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
-
+OctreeQuery::OctreeQuery(bool randomizeConnectionID) {
     if (randomizeConnectionID) {
         // randomize our initial octree query connection ID using random_device
         // the connection ID is 16 bits so we take a generated 32 bit value from random device and chop off the top
@@ -47,26 +33,14 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
     // pack the connection ID so the server can detect when we start a new connection
     memcpy(destinationBuffer, &_connectionID, sizeof(_connectionID));
     destinationBuffer += sizeof(_connectionID);
-    
-    // back a boolean (cut to 1 byte) to designate if this query uses the sent view frustum
-    memcpy(destinationBuffer, &_usesFrustum, sizeof(_usesFrustum));
-    destinationBuffer += sizeof(_usesFrustum);
-    
-    if (_usesFrustum) {
-        // TODO: DRY this up to a shared method
-        // that can pack any type given the number of bytes
-        // and return the number of bytes to push the pointer
-        
-        // camera details
-        memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
-        destinationBuffer += sizeof(_cameraPosition);
-        destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _cameraOrientation);
-        destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _cameraFov);
-        destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _cameraAspectRatio);
-        destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraNearClip);
-        destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraFarClip);
-        memcpy(destinationBuffer, &_cameraEyeOffsetPosition, sizeof(_cameraEyeOffsetPosition));
-        destinationBuffer += sizeof(_cameraEyeOffsetPosition);
+
+    // Number of frustums
+    uint8_t numFrustums = (uint8_t)_conicalViews.size();
+    memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums));
+    destinationBuffer += sizeof(numFrustums);
+
+    for (const auto& view : _conicalViews) {
+        destinationBuffer += view.serialize(destinationBuffer);
     }
     
     // desired Max Octree PPS
@@ -80,9 +54,6 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
     // desired boundaryLevelAdjust
     memcpy(destinationBuffer, &_boundaryLevelAdjust, sizeof(_boundaryLevelAdjust));
     destinationBuffer += sizeof(_boundaryLevelAdjust);
-
-    memcpy(destinationBuffer, &_cameraCenterRadius, sizeof(_cameraCenterRadius));
-    destinationBuffer += sizeof(_cameraCenterRadius);
     
     // create a QByteArray that holds the binary representation of the JSON parameters
     QByteArray binaryParametersDocument;
@@ -133,20 +104,15 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
     }
 
     // check if this query uses a view frustum
-    memcpy(&_usesFrustum, sourceBuffer, sizeof(_usesFrustum));
-    sourceBuffer += sizeof(_usesFrustum);
-    
-    if (_usesFrustum) {
-        // unpack camera details
-        memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
-        sourceBuffer += sizeof(_cameraPosition);
-        sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _cameraOrientation);
-        sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_cameraFov);
-        sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer,_cameraAspectRatio);
-        sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraNearClip);
-        sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraFarClip);
-        memcpy(&_cameraEyeOffsetPosition, sourceBuffer, sizeof(_cameraEyeOffsetPosition));
-        sourceBuffer += sizeof(_cameraEyeOffsetPosition);
+    uint8_t numFrustums = 0;
+    memcpy(&numFrustums, sourceBuffer, sizeof(numFrustums));
+    sourceBuffer += sizeof(numFrustums);
+
+    _conicalViews.clear();
+    for (int i = 0; i < numFrustums; ++i) {
+        ConicalViewFrustum view;
+        sourceBuffer += view.deserialize(sourceBuffer);
+        _conicalViews.push_back(view);
     }
 
     // desired Max Octree PPS
@@ -161,9 +127,6 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
     memcpy(&_boundaryLevelAdjust, sourceBuffer, sizeof(_boundaryLevelAdjust));
     sourceBuffer += sizeof(_boundaryLevelAdjust);
     
-    memcpy(&_cameraCenterRadius, sourceBuffer, sizeof(_cameraCenterRadius));
-    sourceBuffer += sizeof(_cameraCenterRadius);
-    
     // check if we have a packed JSON filter
     uint16_t binaryParametersBytes;
     memcpy(&binaryParametersBytes, sourceBuffer, sizeof(binaryParametersBytes));
@@ -184,8 +147,3 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
     
     return sourceBuffer - startPosition;
 }
-
-glm::vec3 OctreeQuery::calculateCameraDirection() const {
-    glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FORWARD, 0.0f));
-    return direction;
-}
diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h
index 21ce2e7fac..7dfc1cfaaf 100644
--- a/libraries/octree/src/OctreeQuery.h
+++ b/libraries/octree/src/OctreeQuery.h
@@ -12,16 +12,13 @@
 #ifndef hifi_OctreeQuery_h
 #define hifi_OctreeQuery_h
 
-#include <inttypes.h>
-
-#include <glm/glm.hpp>
-#include <glm/gtc/quaternion.hpp>
-
 #include <QtCore/QJsonObject>
 #include <QtCore/QReadWriteLock>
 
 #include <NodeData.h>
+#include <shared/ConicalViewFrustum.h>
 
+#include "OctreeConstants.h"
 
 class OctreeQuery : public NodeData {
     Q_OBJECT
@@ -30,31 +27,16 @@ public:
     OctreeQuery(bool randomizeConnectionID = false);
     virtual ~OctreeQuery() {}
 
+    OctreeQuery(const OctreeQuery&) = delete;
+    OctreeQuery& operator=(const OctreeQuery&) = delete;
+
     int getBroadcastData(unsigned char* destinationBuffer);
     int parseData(ReceivedMessage& message) override;
 
-    // getters for camera details
-    const glm::vec3& getCameraPosition() const { return _cameraPosition; }
-    const glm::quat& getCameraOrientation() const { return _cameraOrientation; }
-    float getCameraFov() const { return _cameraFov; }
-    float getCameraAspectRatio() const { return _cameraAspectRatio; }
-    float getCameraNearClip() const { return _cameraNearClip; }
-    float getCameraFarClip() const { return _cameraFarClip; }
-    const glm::vec3& getCameraEyeOffsetPosition() const { return _cameraEyeOffsetPosition; }
-    float getCameraCenterRadius() const { return _cameraCenterRadius; }
+    bool hasConicalViews() const { return !_conicalViews.empty(); }
+    void setConicalViews(ConicalViewFrustums views) { _conicalViews = views; }
+    void clearConicalViews() { _conicalViews.clear(); }
 
-    glm::vec3 calculateCameraDirection() const;
-
-    // setters for camera details
-    void setCameraPosition(const glm::vec3& position) { _cameraPosition = position; }
-    void setCameraOrientation(const glm::quat& orientation) { _cameraOrientation = orientation; }
-    void setCameraFov(float fov) { _cameraFov = fov; }
-    void setCameraAspectRatio(float aspectRatio) { _cameraAspectRatio = aspectRatio; }
-    void setCameraNearClip(float nearClip) { _cameraNearClip = nearClip; }
-    void setCameraFarClip(float farClip) { _cameraFarClip = farClip; }
-    void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; }
-    void setCameraCenterRadius(float radius) { _cameraCenterRadius = radius; }
-    
     // getters/setters for JSON filter
     QJsonObject getJSONParameters() { QReadLocker locker { &_jsonParametersLock }; return _jsonParameters; }
     void setJSONParameters(const QJsonObject& jsonParameters)
@@ -64,9 +46,6 @@ public:
     int getMaxQueryPacketsPerSecond() const { return _maxQueryPPS; }
     float getOctreeSizeScale() const { return _octreeElementSizeScale; }
     int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
-    
-    bool getUsesFrustum() { return _usesFrustum; }
-    void setUsesFrustum(bool usesFrustum) { _usesFrustum = usesFrustum; }
 
     void incrementConnectionID() { ++_connectionID; }
 
@@ -81,33 +60,19 @@ public slots:
     void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
 
 protected:
-    // camera details for the avatar
-    glm::vec3 _cameraPosition { glm::vec3(0.0f) };
-    glm::quat _cameraOrientation { glm::quat() };
-    float _cameraFov;
-    float _cameraAspectRatio;
-    float _cameraNearClip;
-    float _cameraFarClip;
-    float _cameraCenterRadius;
-    glm::vec3 _cameraEyeOffsetPosition { glm::vec3(0.0f) };
+    ConicalViewFrustums _conicalViews;
 
     // octree server sending items
     int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
     float _octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE; /// used for LOD calculations
     int _boundaryLevelAdjust = 0; /// used for LOD calculations
-    
-    uint8_t _usesFrustum = true;
+
     uint16_t _connectionID; // query connection ID, randomized to start, increments with each new connection to server
     
     QJsonObject _jsonParameters;
     QReadWriteLock _jsonParametersLock;
 
     bool _hasReceivedFirstQuery { false };
-    
-private:
-    // privatize the copy constructor and assignment operator so they cannot be called
-    OctreeQuery(const OctreeQuery&);
-    OctreeQuery& operator= (const OctreeQuery&);
 };
 
 #endif // hifi_OctreeQuery_h
diff --git a/libraries/octree/src/OctreeQueryNode.cpp b/libraries/octree/src/OctreeQueryNode.cpp
index 16542b697e..2d1d89a11c 100644
--- a/libraries/octree/src/OctreeQueryNode.cpp
+++ b/libraries/octree/src/OctreeQueryNode.cpp
@@ -139,81 +139,61 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by
     }
 }
 
-void OctreeQueryNode::copyCurrentViewFrustum(ViewFrustum& viewOut) const {
-    QMutexLocker viewLocker(&_viewMutex);
-    viewOut = _currentViewFrustum;
-}
-
 bool OctreeQueryNode::updateCurrentViewFrustum() {
     // if shutting down, return immediately
     if (_isShuttingDown) {
         return false;
     }
     
-    if (!_usesFrustum) {
+    if (!hasConicalViews()) {
         // this client does not use a view frustum so the view frustum for this query has not changed
         return false;
-    } else {
-        bool currentViewFrustumChanged = false;
-        
-        ViewFrustum newestViewFrustum;
-        // get position and orientation details from the camera
-        newestViewFrustum.setPosition(getCameraPosition());
-        newestViewFrustum.setOrientation(getCameraOrientation());
-        
-        newestViewFrustum.setCenterRadius(getCameraCenterRadius());
-        
-        // Also make sure it's got the correct lens details from the camera
-        float originalFOV = getCameraFov();
-        float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
-        
-        if (0.0f != getCameraAspectRatio() &&
-            0.0f != getCameraNearClip() &&
-            0.0f != getCameraFarClip() &&
-            getCameraNearClip() != getCameraFarClip()) {
-            newestViewFrustum.setProjection(glm::perspective(
-                                                             glm::radians(wideFOV), // hack
-                                                             getCameraAspectRatio(),
-                                                             getCameraNearClip(),
-                                                             getCameraFarClip()));
-            newestViewFrustum.calculate();
-        }
-        
-        
-        { // if there has been a change, then recalculate
-            QMutexLocker viewLocker(&_viewMutex);
-            if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) {
-                _currentViewFrustum = newestViewFrustum;
-                currentViewFrustumChanged = true;
-            }
-        }
-        
-        // Also check for LOD changes from the client
-        if (_lodInitialized) {
-            if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) {
-                _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
-                _lodChanged = true;
-            }
-            if (_lastClientOctreeSizeScale != getOctreeSizeScale()) {
-                _lastClientOctreeSizeScale = getOctreeSizeScale();
-                _lodChanged = true;
+    }
+
+    bool currentViewFrustumChanged = false;
+
+    { // if there has been a change, then recalculate
+        QMutexLocker viewLocker(&_viewMutex);
+
+        if (_conicalViews.size() == _currentConicalViews.size()) {
+            for (size_t i = 0; i < _conicalViews.size(); ++i) {
+                if (!_conicalViews[i].isVerySimilar(_currentConicalViews[i])) {
+                    _currentConicalViews = _conicalViews;
+                    currentViewFrustumChanged = true;
+                    break;
+                }
             }
         } else {
-            _lodInitialized = true;
-            _lastClientOctreeSizeScale = getOctreeSizeScale();
-            _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
-            _lodChanged = false;
+            _currentConicalViews = _conicalViews;
+            currentViewFrustumChanged = true;
         }
-        
-        // When we first detect that the view stopped changing, we record this.
-        // but we don't change it back to false until we've completely sent this
-        // scene.
-        if (_viewFrustumChanging && !currentViewFrustumChanged) {
-            _viewFrustumJustStoppedChanging = true;
-        }
-        _viewFrustumChanging = currentViewFrustumChanged;
-        return currentViewFrustumChanged;
     }
+
+    // Also check for LOD changes from the client
+    if (_lodInitialized) {
+        if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) {
+            _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
+            _lodChanged = true;
+        }
+        if (_lastClientOctreeSizeScale != getOctreeSizeScale()) {
+            _lastClientOctreeSizeScale = getOctreeSizeScale();
+            _lodChanged = true;
+        }
+    } else {
+        _lodInitialized = true;
+        _lastClientOctreeSizeScale = getOctreeSizeScale();
+        _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
+        _lodChanged = false;
+    }
+
+    // When we first detect that the view stopped changing, we record this.
+    // but we don't change it back to false until we've completely sent this
+    // scene.
+    if (_viewFrustumChanging && !currentViewFrustumChanged) {
+        _viewFrustumJustStoppedChanging = true;
+    }
+    _viewFrustumChanging = currentViewFrustumChanged;
+    return currentViewFrustumChanged;
 }
 
 void OctreeQueryNode::setViewSent(bool viewSent) {
diff --git a/libraries/octree/src/OctreeQueryNode.h b/libraries/octree/src/OctreeQueryNode.h
index 640a7c7ddc..d984e048c1 100644
--- a/libraries/octree/src/OctreeQueryNode.h
+++ b/libraries/octree/src/OctreeQueryNode.h
@@ -14,14 +14,14 @@
 
 #include <iostream>
 
-#include <NodeData.h>
+#include <qqueue.h>
+
 #include "OctreeConstants.h"
 #include "OctreeElementBag.h"
 #include "OctreePacketData.h"
 #include "OctreeQuery.h"
 #include "OctreeSceneStats.h"
 #include "SentPacketHistory.h"
-#include <qqueue.h>
 
 class OctreeSendThread;
 class OctreeServer;
@@ -49,7 +49,7 @@ public:
 
     OctreeElementExtraEncodeData extraEncodeData;
 
-    void copyCurrentViewFrustum(ViewFrustum& viewOut) const;
+    const ConicalViewFrustums& getCurrentViews() const { return _currentConicalViews; }
 
     // These are not classic setters because they are calculating and maintaining state
     // which is set asynchronously through the network receive
@@ -87,9 +87,6 @@ public:
     void setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; }
 
 private:
-    OctreeQueryNode(const OctreeQueryNode &);
-    OctreeQueryNode& operator= (const OctreeQueryNode&);
-
     bool _viewSent { false };
     std::unique_ptr<NLPacket> _octreePacket;
     bool _octreePacketWaiting;
@@ -99,7 +96,7 @@ private:
     quint64 _firstSuppressedPacket { usecTimestampNow() };
 
     mutable QMutex _viewMutex { QMutex::Recursive };
-    ViewFrustum _currentViewFrustum;
+    ConicalViewFrustums _currentConicalViews;
     bool _viewFrustumChanging { false };
     bool _viewFrustumJustStoppedChanging { true };
 
diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp
index b2efdfd595..d8ff6ba0ec 100644
--- a/libraries/octree/src/OctreeSceneStats.cpp
+++ b/libraries/octree/src/OctreeSceneStats.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctreeSceneStats.h"
+
 #include <limits>
 #include <QString>
 #include <QStringList>
@@ -20,8 +22,6 @@
 #include "OctreePacketData.h"
 #include "OctreeElement.h"
 #include "OctreeLogging.h"
-#include "OctreeSceneStats.h"
-
 
 const int samples = 100;
 OctreeSceneStats::OctreeSceneStats() :
diff --git a/libraries/octree/src/OctreeScriptingInterface.cpp b/libraries/octree/src/OctreeScriptingInterface.cpp
index 618e8ac469..b1729c649e 100644
--- a/libraries/octree/src/OctreeScriptingInterface.cpp
+++ b/libraries/octree/src/OctreeScriptingInterface.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QCoreApplication>
-
 #include "OctreeScriptingInterface.h"
 
+#include <QCoreApplication>
+
 OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packetSender) :
     _packetSender(packetSender),
     _managedPacketSender(false),
diff --git a/libraries/octree/src/OctreeUtils.cpp b/libraries/octree/src/OctreeUtils.cpp
index 8980504431..8eaf22e198 100644
--- a/libraries/octree/src/OctreeUtils.cpp
+++ b/libraries/octree/src/OctreeUtils.cpp
@@ -16,6 +16,7 @@
 #include <glm/glm.hpp>
 
 #include <AABox.h>
+#include <AACube.h>
 
 float calculateRenderAccuracy(const glm::vec3& position,
         const AABox& bounds,
@@ -73,4 +74,10 @@ float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust
     // Smallest visible element is 1cm
     const float smallestSize = 0.01f;
     return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale);
-}
\ No newline at end of file
+}
+
+bool isAngularSizeBigEnough(glm::vec3 position, const AACube& cube, float lodScaleFactor, float minDiameter) {
+    float distance = glm::distance(cube.calcCenter(), position) + MIN_VISIBLE_DISTANCE;
+    float angularDiameter = cube.getScale() / distance;
+    return angularDiameter > minDiameter * lodScaleFactor;
+}
diff --git a/libraries/octree/src/OctreeUtils.h b/libraries/octree/src/OctreeUtils.h
index d5008376ea..dff56cad64 100644
--- a/libraries/octree/src/OctreeUtils.h
+++ b/libraries/octree/src/OctreeUtils.h
@@ -12,9 +12,12 @@
 #ifndef hifi_OctreeUtils_h
 #define hifi_OctreeUtils_h
 
+#include <NumericalConstants.h>
+
 #include "OctreeConstants.h"
 
 class AABox;
+class AACube;
 class QJsonDocument;
 
 /// renderAccuracy represents a floating point "visibility" of an object based on it's view from the camera. At a simple
@@ -32,8 +35,9 @@ float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust
 // MIN_ELEMENT_ANGULAR_DIAMETER = angular diameter of 1x1x1m cube at 400m = sqrt(3) / 400 = 0.0043301 radians ~= 0.25 degrees
 const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians
 // NOTE: the entity bounding cube is larger than the smallest possible containing octree element by sqrt(3)
-const float SQRT_THREE = 1.73205080f;
 const float MIN_ENTITY_ANGULAR_DIAMETER = MIN_ELEMENT_ANGULAR_DIAMETER * SQRT_THREE;
 const float MIN_VISIBLE_DISTANCE = 0.0001f; // helps avoid divide-by-zero check
 
+bool isAngularSizeBigEnough(glm::vec3 position, const AACube& cube, float lodScaleFactor, float minDiameter);
+
 #endif // hifi_OctreeUtils_h
diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index 0a8f2b6f20..5effe4422c 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "EntityMotionState.h"
+
 #include <glm/gtx/norm.hpp>
 
 #include <EntityItem.h>
@@ -19,7 +21,6 @@
 #include <Profile.h>
 
 #include "BulletUtil.h"
-#include "EntityMotionState.h"
 #include "PhysicsEngine.h"
 #include "PhysicsHelpers.h"
 #include "PhysicsLogging.h"
@@ -356,7 +357,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
 
     if (_numInactiveUpdates > 0) {
         const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
-        if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) {
+        if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES || isServerlessMode()) {
             // clear local ownership (stop sending updates) and let the server clear itself
             _entity->clearSimulationOwnership();
             return false;
@@ -733,7 +734,9 @@ void EntityMotionState::measureBodyAcceleration() {
         // hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt
         glm::vec3 velocity = getBodyLinearVelocityGTSigma();
 
-        _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt;
+        const float MIN_DAMPING_FACTOR = 0.01f;
+        float invDampingAttenuationFactor = 1.0f / glm::max(powf(1.0f - _body->getLinearDamping(), dt), MIN_DAMPING_FACTOR);
+        _measuredAcceleration = (velocity * invDampingAttenuationFactor - _lastVelocity) * invDt;
         _lastVelocity = velocity;
         if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) {
             // we fall in here when _lastMeasureStep is old: the body has just become active
@@ -849,3 +852,9 @@ void EntityMotionState::clearObjectVelocities() const {
     }
     _entity->setAcceleration(glm::vec3(0.0f));
 }
+
+bool EntityMotionState::isServerlessMode() {
+    EntityTreeElementPointer element = _entity->getElement();
+    EntityTreePointer tree = element ? element->getTree() : nullptr;
+    return tree ? tree->isServerlessMode() : false;
+}
diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h
index 21ade734a8..e2b81fa054 100644
--- a/libraries/physics/src/EntityMotionState.h
+++ b/libraries/physics/src/EntityMotionState.h
@@ -159,6 +159,8 @@ protected:
     uint8_t _bidPriority { 0 };
     uint8_t _region { workload::Region::INVALID };
     bool _serverVariablesSet { false };
+
+    bool isServerlessMode();
 };
 
 #endif // hifi_EntityMotionState_h
diff --git a/libraries/physics/src/MeshMassProperties.cpp b/libraries/physics/src/MeshMassProperties.cpp
index a6a33932aa..ad4208e6a1 100644
--- a/libraries/physics/src/MeshMassProperties.cpp
+++ b/libraries/physics/src/MeshMassProperties.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "MeshMassProperties.h"
+
 #include <assert.h>
 #include <stdint.h>
 
-#include "MeshMassProperties.h"
-
 // this method is included for unit test verification
 void computeBoxInertia(btScalar mass, const btVector3& diagonal, btMatrix3x3& inertia) {
     // formula for box inertia tensor:
diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp
index 87732ded03..dfcf1aba33 100644
--- a/libraries/physics/src/ObjectAction.cpp
+++ b/libraries/physics/src/ObjectAction.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "EntitySimulation.h"
-
 #include "ObjectAction.h"
 
+#include "EntitySimulation.h"
+
 #include "PhysicsLogging.h"
 
 
diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp
index e90862266b..4c2ed35f8e 100644
--- a/libraries/physics/src/ObjectActionOffset.cpp
+++ b/libraries/physics/src/ObjectActionOffset.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "QVariantGLM.h"
-
 #include "ObjectActionOffset.h"
 
+#include "QVariantGLM.h"
+
 #include "PhysicsLogging.h"
 
 
diff --git a/libraries/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp
index 9b2da22665..4235bbd616 100644
--- a/libraries/physics/src/ObjectActionTractor.cpp
+++ b/libraries/physics/src/ObjectActionTractor.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "QVariantGLM.h"
-
 #include "ObjectActionTractor.h"
 
+#include "QVariantGLM.h"
+
 #include "PhysicsLogging.h"
 
 const float TRACTOR_MAX_SPEED = 10.0f;
@@ -397,7 +397,7 @@ void ObjectActionTractor::deserialize(QByteArray serializedArguments) {
 
     EntityDynamicType type;
     dataStream >> type;
-    assert(type == getType());
+    assert(type == getType() || type == DYNAMIC_TYPE_SPRING);
 
     QUuid id;
     dataStream >> id;
diff --git a/libraries/physics/src/ObjectActionTravelOriented.cpp b/libraries/physics/src/ObjectActionTravelOriented.cpp
index accade8695..c93cce2482 100644
--- a/libraries/physics/src/ObjectActionTravelOriented.cpp
+++ b/libraries/physics/src/ObjectActionTravelOriented.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ObjectActionTravelOriented.h"
+
 #include <glm/gtc/quaternion.hpp>
 
 #include "QVariantGLM.h"
-#include "ObjectActionTravelOriented.h"
 #include "PhysicsLogging.h"
 
 const uint16_t ObjectActionTravelOriented::actionVersion = 1;
diff --git a/libraries/physics/src/ObjectConstraint.cpp b/libraries/physics/src/ObjectConstraint.cpp
index 54fd4777e0..38467b1d83 100644
--- a/libraries/physics/src/ObjectConstraint.cpp
+++ b/libraries/physics/src/ObjectConstraint.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "EntitySimulation.h"
-
 #include "ObjectConstraint.h"
 
+#include "EntitySimulation.h"
+
 #include "PhysicsLogging.h"
 
 ObjectConstraint::ObjectConstraint(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
diff --git a/libraries/physics/src/ObjectConstraintBallSocket.cpp b/libraries/physics/src/ObjectConstraintBallSocket.cpp
index 4736f2c9e2..b7a186e187 100644
--- a/libraries/physics/src/ObjectConstraintBallSocket.cpp
+++ b/libraries/physics/src/ObjectConstraintBallSocket.cpp
@@ -9,12 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ObjectConstraintBallSocket.h"
+
 #include <LogHandler.h>
 
 #include "QVariantGLM.h"
 
 #include "EntityTree.h"
-#include "ObjectConstraintBallSocket.h"
 #include "PhysicsLogging.h"
 
 
diff --git a/libraries/physics/src/ObjectConstraintConeTwist.cpp b/libraries/physics/src/ObjectConstraintConeTwist.cpp
index 47228c1c16..e2b86a9e0f 100644
--- a/libraries/physics/src/ObjectConstraintConeTwist.cpp
+++ b/libraries/physics/src/ObjectConstraintConeTwist.cpp
@@ -9,12 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ObjectConstraintConeTwist.h"
+
 #include <LogHandler.h>
 
 #include "QVariantGLM.h"
 
 #include "EntityTree.h"
-#include "ObjectConstraintConeTwist.h"
 #include "PhysicsLogging.h"
 
 const uint16_t CONE_TWIST_VERSION_WITH_UNUSED_PAREMETERS = 1;
diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp
index 4793741391..0a01f413dc 100644
--- a/libraries/physics/src/ObjectConstraintHinge.cpp
+++ b/libraries/physics/src/ObjectConstraintHinge.cpp
@@ -9,12 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ObjectConstraintHinge.h"
+
 #include <LogHandler.h>
 
 #include "QVariantGLM.h"
 
 #include "EntityTree.h"
-#include "ObjectConstraintHinge.h"
 #include "PhysicsLogging.h"
 
 
diff --git a/libraries/physics/src/ObjectConstraintSlider.cpp b/libraries/physics/src/ObjectConstraintSlider.cpp
index da5bba7f4d..4776e0e4a6 100644
--- a/libraries/physics/src/ObjectConstraintSlider.cpp
+++ b/libraries/physics/src/ObjectConstraintSlider.cpp
@@ -9,12 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ObjectConstraintSlider.h"
+
 #include <LogHandler.h>
 
 #include "QVariantGLM.h"
 
 #include "EntityTree.h"
-#include "ObjectConstraintSlider.h"
 #include "PhysicsLogging.h"
 
 
diff --git a/libraries/physics/src/ObjectDynamic.cpp b/libraries/physics/src/ObjectDynamic.cpp
index 5bbb5981d1..3341025a8f 100644
--- a/libraries/physics/src/ObjectDynamic.cpp
+++ b/libraries/physics/src/ObjectDynamic.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "EntitySimulation.h"
-
 #include "ObjectDynamic.h"
 
+#include "EntitySimulation.h"
+
 #include "PhysicsLogging.h"
 
 
diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp
index b11e21366e..64d2368207 100644
--- a/libraries/physics/src/ObjectMotionState.cpp
+++ b/libraries/physics/src/ObjectMotionState.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ObjectMotionState.h"
+
 #include <math.h>
 
 #include "BulletUtil.h"
-#include "ObjectMotionState.h"
 #include "PhysicsEngine.h"
 #include "PhysicsHelpers.h"
 #include "PhysicsLogging.h"
diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index 4f8e8342c8..0ab757df51 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -354,10 +354,18 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta
 }
 
 void PhysicalEntitySimulation::addOwnershipBid(EntityMotionState* motionState) {
-    motionState->initForBid();
-    motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
-    _bids.push_back(motionState);
-    _nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry());
+    if (getEntityTree()->isServerlessMode()) {
+        EntityItemPointer entity = motionState->getEntity();
+        auto nodeList = DependencyManager::get<NodeList>();
+        auto sessionID = nodeList->getSessionUUID();
+        entity->setSimulationOwner(SimulationOwner(sessionID, SCRIPT_GRAB_SIMULATION_PRIORITY));
+        _owned.push_back(motionState);
+    } else {
+        motionState->initForBid();
+        motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
+        _bids.push_back(motionState);
+        _nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry());
+    }
 }
 
 void PhysicalEntitySimulation::addOwnership(EntityMotionState* motionState) {
diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp
index 76753d1dee..47971b238a 100644
--- a/libraries/physics/src/PhysicsEngine.cpp
+++ b/libraries/physics/src/PhysicsEngine.cpp
@@ -9,18 +9,18 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <PhysicsCollisionGroups.h>
+#include "PhysicsEngine.h"
 
 #include <functional>
 
 #include <QFile>
 
 #include <PerfStat.h>
+#include <PhysicsCollisionGroups.h>
 #include <Profile.h>
 
 #include "CharacterController.h"
 #include "ObjectMotionState.h"
-#include "PhysicsEngine.h"
 #include "PhysicsHelpers.h"
 #include "PhysicsDebugDraw.h"
 #include "ThreadSafeDynamicsWorld.h"
diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp
index 5abeb022aa..8057eb0e0c 100644
--- a/libraries/physics/src/ShapeFactory.cpp
+++ b/libraries/physics/src/ShapeFactory.cpp
@@ -9,11 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ShapeFactory.h"
+
 #include <glm/gtx/norm.hpp>
 
 #include <SharedUtil.h> // for MILLIMETERS_PER_METER
 
-#include "ShapeFactory.h"
 #include "BulletUtil.h"
 
 
diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp
index 97b9e5dab1..ef7a4a1749 100644
--- a/libraries/physics/src/ShapeManager.cpp
+++ b/libraries/physics/src/ShapeManager.cpp
@@ -9,12 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QDebug>
+#include "ShapeManager.h"
 
 #include <glm/gtx/norm.hpp>
 
+#include <QDebug>
+
 #include "ShapeFactory.h"
-#include "ShapeManager.h"
 
 ShapeManager::ShapeManager() {
 }
diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp
index 5b8c0d5843..3f24851dce 100644
--- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp
+++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp
@@ -15,9 +15,10 @@
  * Copied and modified from btDiscreteDynamicsWorld.cpp by AndrewMeadows on 2014.11.12.
  * */
 
+#include "ThreadSafeDynamicsWorld.h"
+
 #include <LinearMath/btQuickprof.h>
 
-#include "ThreadSafeDynamicsWorld.h"
 #include "Profile.h"
 
 ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp
index c155d5bd7f..19c4a62443 100644
--- a/libraries/procedural/src/procedural/Procedural.cpp
+++ b/libraries/procedural/src/procedural/Procedural.cpp
@@ -19,7 +19,7 @@
 #include <SharedUtil.h>
 #include <NumericalConstants.h>
 #include <GLMHelpers.h>
-
+#include <NetworkingConstants.h>
 #include "ProceduralCommon_frag.h"
 
 #include "Logging.h"
@@ -178,6 +178,8 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) {
                 return;
             }
             _shaderPath = shaderUrl.toLocalFile();
+        } else if (shaderUrl.scheme() == URL_SCHEME_QRC) {
+            _shaderPath = ":" + shaderUrl.path();
         } else {
             _networkShader = ShaderCache::instance().getShader(shaderUrl);
         }
diff --git a/libraries/qml/src/qml/Logging.h b/libraries/qml/src/qml/Logging.h
index 487566701d..48efdd1da9 100644
--- a/libraries/qml/src/qml/Logging.h
+++ b/libraries/qml/src/qml/Logging.h
@@ -6,8 +6,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#ifndef hifi_Controllers_Logging_h
-#define hifi_Controllers_Logging_h
+#ifndef hifi_QML_Logging_h
+#define hifi_QML_Logging_h
 
 #include <QLoggingCategory>
 
diff --git a/libraries/recording/src/recording/ClipCache.cpp b/libraries/recording/src/recording/ClipCache.cpp
index 0fbbf1bc8e..c63350de7f 100644
--- a/libraries/recording/src/recording/ClipCache.cpp
+++ b/libraries/recording/src/recording/ClipCache.cpp
@@ -6,11 +6,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ClipCache.h"
+
 #include <QThread>
 
 #include <shared/QtHelpers.h>
 
-#include "ClipCache.h"
 #include "impl/PointerClip.h"
 #include "Logging.h"
 
diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h
index 54fdc903ca..b90e291da5 100644
--- a/libraries/render-utils/src/AbstractViewStateInterface.h
+++ b/libraries/render-utils/src/AbstractViewStateInterface.h
@@ -24,6 +24,8 @@ class Transform;
 class QThread;
 class ViewFrustum;
 class PickRay;
+class ConicalViewFrustum;
+using ConicalViewFrustums = std::vector<ConicalViewFrustum>;
 
 /// Interface provided by Application to other objects that need access to the current view state details
 class AbstractViewStateInterface {
@@ -31,6 +33,8 @@ public:
     /// copies the current view frustum for rendering the view state
     virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const = 0;
 
+    virtual const ConicalViewFrustums& getConicalViews() const = 0;
+
     virtual QThread* getMainThread() = 0;
 
     virtual PickRay computePickRay(float x, float y) const = 0;
diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp
index c526f16b75..2ac8e77898 100644
--- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp
+++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-
-#include <glm/gtc/random.hpp>
+#include "AmbientOcclusionEffect.h"
 
 #include <algorithm> //min max and more
 
+#include <glm/gtc/random.hpp>
 
 #include <PathUtils.h>
 #include <SharedUtil.h>
@@ -22,7 +22,6 @@
 #include "RenderUtilsLogging.h"
 
 #include "DeferredLightingEffect.h"
-#include "AmbientOcclusionEffect.h"
 #include "TextureCache.h"
 #include "FramebufferCache.h"
 #include "DependencyManager.h"
@@ -543,4 +542,4 @@ void DebugAmbientOcclusion::run(const render::RenderContextPointer& renderContex
     });
 
 }
- 
\ No newline at end of file
+ 
diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp
index c9aa1b8f71..2173aef76a 100644
--- a/libraries/render-utils/src/AntialiasingEffect.cpp
+++ b/libraries/render-utils/src/AntialiasingEffect.cpp
@@ -9,6 +9,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AntialiasingEffect.h"
 
 #include <glm/gtc/random.hpp>
 
@@ -17,7 +18,6 @@
 #include <gpu/Context.h>
 #include <gpu/StandardShaderLib.h>
 
-#include "AntialiasingEffect.h"
 #include "StencilMaskPass.h"
 #include "TextureCache.h"
 #include "DependencyManager.h"
diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp
index 854ff71e20..ceac4ae3c8 100644
--- a/libraries/render-utils/src/LightStage.cpp
+++ b/libraries/render-utils/src/LightStage.cpp
@@ -9,12 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "ViewFrustum.h"
-
 #include "LightStage.h"
 
 #include <cmath>
 
+#include "ViewFrustum.h"
+
 std::string LightStage::_stageName { "LIGHT_STAGE"};
 const glm::mat4 LightStage::Shadow::_biasMatrix{
     0.5, 0.0, 0.0, 0.0,
diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh
index 6a5982f1e8..8abdb5cbf9 100644
--- a/libraries/render-utils/src/LightingModel.slh
+++ b/libraries/render-utils/src/LightingModel.slh
@@ -14,10 +14,10 @@
 <@func declareLightingModel()@>
 
 struct LightingModel {
-    PRECISIONQ vec4 _UnlitEmissiveLightmapBackground;
-    PRECISIONQ vec4 _ScatteringDiffuseSpecularAlbedo;
-    PRECISIONQ vec4 _AmbientDirectionalPointSpot;
-    PRECISIONQ vec4 _ShowContourObscuranceWireframe;
+    vec4 _UnlitEmissiveLightmapBackground;
+    vec4 _ScatteringDiffuseSpecularAlbedo;
+    vec4 _AmbientDirectionalPointSpot;
+    vec4 _ShowContourObscuranceWireframe;
 };
 
 uniform lightingModelBuffer{
diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp
index a3abb24afe..b02266e67b 100644
--- a/libraries/render-utils/src/RenderPipelines.cpp
+++ b/libraries/render-utils/src/RenderPipelines.cpp
@@ -10,10 +10,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "RenderPipelines.h"
+
 #include <functional>
 
 #include <gpu/Context.h>
 #include <gpu/StandardShaderLib.h>
+#include <model-networking/TextureCache.h>
 
 #include "StencilMaskPass.h"
 #include "DeferredLightingEffect.h"
@@ -615,9 +618,6 @@ void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state) {
         skinModelShadowFadeDualQuatProgram, state);
 }
 
-#include "RenderPipelines.h"
-#include <model-networking/TextureCache.h>
-
 // FIXME find a better way to setup the default textures
 void RenderPipelines::bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) {
     if (!material) {
diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp
index 69c5b3c689..fbb4bba263 100644
--- a/libraries/render-utils/src/RenderShadowTask.cpp
+++ b/libraries/render-utils/src/RenderShadowTask.cpp
@@ -149,9 +149,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
         batch.setStateScissorRect(viewport);
 
         batch.setFramebuffer(fbo);
-        batch.clearFramebuffer(
-            gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH,
-            vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true);
+        batch.clearDepthFramebuffer(1.0, false);
 
         glm::mat4 projMat;
         Transform viewMat;
@@ -232,12 +230,11 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
     const auto queryResolution = setupOutput.getN<RenderShadowSetup::Outputs>(2);
     // Fetch and cull the items from the scene
 
-    // Enable models to not cast shadows (otherwise, models will always cast shadows)
-    static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask).withShadowCaster();
+    static const auto shadowCasterReceiverFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask);
 
-    const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying();
+    const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterReceiverFilter, queryResolution).asVarying();
     const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowTree", fetchInput);
-    const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying();
+    const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterReceiverFilter).asVarying();
     const auto shadowItems = task.addJob<FetchSpatialSelection>("FetchShadowSelection", selectionInputs);
 
     // Cull objects that are not visible in camera view. Hopefully the cull functor only performs LOD culling, not
@@ -261,21 +258,22 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
         char jobName[64];
         sprintf(jobName, "ShadowCascadeSetup%d", i);
         const auto cascadeSetupOutput = task.addJob<RenderShadowCascadeSetup>(jobName, i, _cullFunctor, tagBits, tagMask);
-        const auto shadowFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(0);
+        const auto shadowRenderFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(0);
+        const auto shadowBoundsFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
         auto antiFrustum = render::Varying(ViewFrustumPointer());
-        cascadeFrustums[i] = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
+        cascadeFrustums[i] = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(2);
         if (i > 1) {
             antiFrustum = cascadeFrustums[i - 2];
         }
 
         // CPU jobs: finer grained culling
-        const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying();
+        const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowRenderFilter, shadowBoundsFilter, antiFrustum).asVarying();
         const auto culledShadowItemsAndBounds = task.addJob<CullShapeBounds>("CullShadowCascade", cullInputs, shadowCullFunctor, RenderDetails::SHADOW);
 
         // GPU jobs: Render to shadow map
         sprintf(jobName, "RenderShadowMap%d", i);
         task.addJob<RenderShadowMap>(jobName, culledShadowItemsAndBounds, shapePlumber, i);
-        task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", shadowFilter);
+        task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", shadowRenderFilter);
     }
 
     task.addJob<RenderShadowTeardown>("ShadowTeardown", setupOutput);
@@ -406,7 +404,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
 
     const auto globalShadow = lightStage->getCurrentKeyShadow();
     if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
-        output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask).withShadowCaster();
+        auto baseFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
+        // Second item filter is to filter items to keep in shadow frustum computation (here we need to keep shadow receivers)
+        output.edit1() = baseFilter;
+        // First item filter is to filter items to render in shadow map (so only keep casters)
+        output.edit0() = baseFilter.withShadowCaster();
 
         // Set the keylight render args
         auto& cascade = globalShadow->getCascade(_cascadeIndex);
@@ -419,10 +421,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
         texelSize *= minTexelCount;
         _cullFunctor._minSquareSize = texelSize * texelSize;
 
-        output.edit1() = cascadeFrustum;
+        output.edit2() = cascadeFrustum;
     } else {
         output.edit0() = ItemFilter::Builder::nothing();
-        output.edit1() = ViewFrustumPointer();
+        output.edit1() = ItemFilter::Builder::nothing();
+        output.edit2() = ViewFrustumPointer();
     }
 }
 
diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h
index 98b70c0c9f..19ffcb4234 100644
--- a/libraries/render-utils/src/RenderShadowTask.h
+++ b/libraries/render-utils/src/RenderShadowTask.h
@@ -118,7 +118,7 @@ private:
 
 class RenderShadowCascadeSetup {
 public:
-    using Outputs = render::VaryingSet2<render::ItemFilter, ViewFrustumPointer>;
+    using Outputs = render::VaryingSet3<render::ItemFilter, render::ItemFilter, ViewFrustumPointer>;
     using JobModel = render::Job::ModelO<RenderShadowCascadeSetup, Outputs>;
 
     RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) : 
diff --git a/libraries/render-utils/src/forward_simple.slf b/libraries/render-utils/src/forward_simple.slf
index 587fcbde73..1ac44750a7 100644
--- a/libraries/render-utils/src/forward_simple.slf
+++ b/libraries/render-utils/src/forward_simple.slf
@@ -16,11 +16,21 @@
 <@include ForwardGlobalLight.slh@>
 <$declareEvalSkyboxGlobalColor()$>
 
+
 // the interpolated normal
 in vec3 _normalWS;
+in vec3 _normalMS;
 in vec4 _color;
+in vec2 _texCoord0;
+in vec4 _positionMS;
 in vec4 _positionES;
 
+// For retro-compatibility
+#define _normal      _normalWS
+#define _modelNormal _normalMS
+#define _position    _positionMS
+#define _eyePosition _positionES
+
 layout(location = 0) out vec4 _fragColor0;
 
 //PROCEDURAL_COMMON_BLOCK
diff --git a/libraries/render-utils/src/forward_simple_transparent.slf b/libraries/render-utils/src/forward_simple_transparent.slf
index f40ba2ed4f..8be2759571 100644
--- a/libraries/render-utils/src/forward_simple_transparent.slf
+++ b/libraries/render-utils/src/forward_simple_transparent.slf
@@ -18,9 +18,18 @@
 
 // the interpolated normal
 in vec3 _normalWS;
+in vec3 _normalMS;
 in vec4 _color;
+in vec2 _texCoord0;
+in vec4 _positionMS;
 in vec4 _positionES;
 
+// For retro-compatibility
+#define _normal      _normalWS
+#define _modelNormal _normalMS
+#define _position    _positionMS
+#define _eyePosition _positionES
+
 layout(location = 0) out vec4 _fragColor0;
 
 //PROCEDURAL_COMMON_BLOCK
diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf
index ed77777184..7591dc1882 100644
--- a/libraries/render-utils/src/simple.slf
+++ b/libraries/render-utils/src/simple.slf
@@ -16,7 +16,17 @@
 
 // the interpolated normal
 in vec3 _normalWS;
+in vec3 _normalMS;
 in vec4 _color;
+in vec2 _texCoord0;
+in vec4 _positionMS;
+in vec4 _positionES;
+
+// For retro-compatibility
+#define _normal      _normalWS
+#define _modelNormal _normalMS
+#define _position    _positionMS
+#define _eyePosition _positionES
 
 //PROCEDURAL_COMMON_BLOCK
 
diff --git a/libraries/render-utils/src/simple_fade.slf b/libraries/render-utils/src/simple_fade.slf
index 6e7aee2894..0710c3e10b 100644
--- a/libraries/render-utils/src/simple_fade.slf
+++ b/libraries/render-utils/src/simple_fade.slf
@@ -19,9 +19,19 @@
 
 // the interpolated normal
 in vec3 _normalWS;
+in vec3 _normalMS;
 in vec4 _color;
+in vec2 _texCoord0;
+in vec4 _positionMS;
+in vec4 _positionES;
 in vec4 _positionWS;
 
+// For retro-compatibility
+#define _normal      _normalWS
+#define _modelNormal _normalMS
+#define _position    _positionMS
+#define _eyePosition _positionES
+
 //PROCEDURAL_COMMON_BLOCK
 
 #line 1001
diff --git a/libraries/render-utils/src/simple_transparent.slf b/libraries/render-utils/src/simple_transparent.slf
index c9815e8a80..ee79d2c0c4 100644
--- a/libraries/render-utils/src/simple_transparent.slf
+++ b/libraries/render-utils/src/simple_transparent.slf
@@ -16,7 +16,17 @@
 
 // the interpolated normal
 in vec3 _normalWS;
+in vec3 _normalMS;
 in vec4 _color;
+in vec2 _texCoord0;
+in vec4 _positionMS;
+in vec4 _positionES;
+
+// For retro-compatibility
+#define _normal      _normalWS
+#define _modelNormal _normalMS
+#define _position    _positionMS
+#define _eyePosition _positionES
 
 //PROCEDURAL_COMMON_BLOCK
 
diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp
index f04427540a..b5819f114f 100644
--- a/libraries/render/src/render/CullTask.cpp
+++ b/libraries/render/src/render/CullTask.cpp
@@ -368,17 +368,19 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
     RenderArgs* args = renderContext->args;
 
     const auto& inShapes = inputs.get0();
-    const auto& filter = inputs.get1();
-    const auto& antiFrustum = inputs.get2();
+	const auto& cullFilter = inputs.get1();
+	const auto& boundsFilter = inputs.get2();
+	const auto& antiFrustum = inputs.get3();
     auto& outShapes = outputs.edit0();
     auto& outBounds = outputs.edit1();
 
     outShapes.clear();
     outBounds = AABox();
 
-    if (!filter.selectsNothing()) {
+    if (!cullFilter.selectsNothing() || !boundsFilter.selectsNothing()) {
         auto& details = args->_details.edit(_detailType);
         Test test(_cullFunctor, args, details, antiFrustum);
+		auto scene = args->_scene;
 
         for (auto& inItems : inShapes) {
             auto key = inItems.first;
@@ -393,16 +395,26 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
             if (antiFrustum == nullptr) {
                 for (auto& item : inItems.second) {
                     if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) {
-                        outItems->second.emplace_back(item);
-                        outBounds += item.bound;
+						const auto shapeKey = scene->getItem(item.id).getKey();
+						if (cullFilter.test(shapeKey)) {
+							outItems->second.emplace_back(item);
+						}
+						if (boundsFilter.test(shapeKey)) {
+							outBounds += item.bound;
+						}
                     }
                 }
             } else {
                 for (auto& item : inItems.second) {
                     if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) {
-                        outItems->second.emplace_back(item);
-                        outBounds += item.bound;
-                    }
+						const auto shapeKey = scene->getItem(item.id).getKey();
+						if (cullFilter.test(shapeKey)) {
+							outItems->second.emplace_back(item);
+						}
+						if (boundsFilter.test(shapeKey)) {
+							outBounds += item.bound;
+						}
+					}
                 }
             }
             details._rendered += (int)outItems->second.size();
diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h
index 3c5a30de89..47abe8a960 100644
--- a/libraries/render/src/render/CullTask.h
+++ b/libraries/render/src/render/CullTask.h
@@ -110,7 +110,7 @@ namespace render {
 
     class CullShapeBounds {
     public:
-        using Inputs = render::VaryingSet3<ShapeBounds, ItemFilter, ViewFrustumPointer>;
+        using Inputs = render::VaryingSet4<ShapeBounds, ItemFilter, ItemFilter, ViewFrustumPointer>;
         using Outputs = render::VaryingSet2<ShapeBounds, AABox>;
         using JobModel = Job::ModelIO<CullShapeBounds, Inputs, Outputs>;
 
diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp
index 8aabffea46..3a7555f790 100755
--- a/libraries/render/src/render/DrawTask.cpp
+++ b/libraries/render/src/render/DrawTask.cpp
@@ -9,19 +9,19 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <LogHandler.h>
-
 #include "DrawTask.h"
-#include "Logging.h"
 
 #include <algorithm>
 #include <assert.h>
 
+#include <LogHandler.h>
 #include <PerfStat.h>
 #include <ViewFrustum.h>
 #include <gpu/Context.h>
 #include <gpu/StandardShaderLib.h>
 
+#include "Logging.h"
+
 #include "drawItemBounds_vert.h"
 #include "drawItemBounds_frag.h"
 
diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp
index 35cc66315b..703acc5fa6 100644
--- a/libraries/render/src/render/ShapePipeline.cpp
+++ b/libraries/render/src/render/ShapePipeline.cpp
@@ -9,12 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "DependencyManager.h"
-#include "Logging.h"
 #include "ShapePipeline.h"
 
 #include <PerfStat.h>
 
+#include "DependencyManager.h"
+#include "Logging.h"
+
 using namespace render;
 
 ShapePipeline::CustomFactoryMap ShapePipeline::_globalCustomFactoryMap;
@@ -182,4 +183,4 @@ const ShapePipelinePointer ShapePlumber::pickPipeline(RenderArgs* args, const Ke
     }
 
     return shapePipeline;
-}
\ No newline at end of file
+}
diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp
index 4a06dee391..f64dbeffd6 100644
--- a/libraries/script-engine/src/ArrayBufferClass.cpp
+++ b/libraries/script-engine/src/ArrayBufferClass.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ArrayBufferClass.h"
+
 #include <QDebug>
 
 #include "ArrayBufferPrototype.h"
@@ -16,8 +18,6 @@
 #include "ScriptEngine.h"
 #include "TypedArrays.h"
 
-#include "ArrayBufferClass.h"
-
 
 static const QString CLASS_NAME = "ArrayBuffer";
 
diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp
index 9739f67381..d75482aa2e 100644
--- a/libraries/script-engine/src/ArrayBufferPrototype.cpp
+++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp
@@ -9,13 +9,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ArrayBufferPrototype.h"
+
 #include <glm/glm.hpp>
 
 #include <QBuffer>
 #include <QImage>
 
 #include "ArrayBufferClass.h"
-#include "ArrayBufferPrototype.h"
 
 static const int QCOMPRESS_HEADER_POSITION = 0;
 static const int QCOMPRESS_HEADER_SIZE = 4;
diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h
index 5cb1136b74..7f7a3a68b0 100644
--- a/libraries/script-engine/src/AssetScriptingInterface.h
+++ b/libraries/script-engine/src/AssetScriptingInterface.h
@@ -27,6 +27,11 @@
 /**jsdoc
  * The Assets API allows you to communicate with the Asset Browser.
  * @namespace Assets
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
  */
 class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable {
     Q_OBJECT
@@ -181,36 +186,36 @@ public:
 
     /**jsdoc
      * @function Assets.deleteAsset
-     * @property {} options
-     * @property {} scope
-     * @property {} [callback = ""]
+     * @param {} options
+     * @param {} scope
+     * @param {} [callback = ""]
      */
 
     Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
     
     /**jsdoc
      * @function Assets.resolveAsset
-     * @property {} options
-     * @property {} scope
-     * @property {} [callback = ""]
+     * @param {} options
+     * @param {} scope
+     * @param {} [callback = ""]
      */
 
     Q_INVOKABLE void resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
     
     /**jsdoc
      * @function Assets.decompressData
-     * @property {} options
-     * @property {} scope
-     * @property {} [callback = ""]
+     * @param {} options
+     * @param {} scope
+     * @param {} [callback = ""]
      */
 
     Q_INVOKABLE void decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
     
     /**jsdoc
      * @function Assets.compressData
-     * @property {} options
-     * @property {} scope
-     * @property {} [callback = ""]
+     * @param {} options
+     * @param {} scope
+     * @param {} [callback = ""]
      */
 
     Q_INVOKABLE void compressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
@@ -224,7 +229,7 @@ public:
     
     /**jsdoc
      * @function Assets.canWriteCacheValue
-     * @property {string} url
+     * @param {string} url
      * @returns {boolean}
      */
 
@@ -232,8 +237,8 @@ public:
     
     /**jsdoc
      * @function Assets.getCacheStatus
-     * @property {} scope
-     * @property {} [callback=undefined]
+     * @param {} scope
+     * @param {} [callback=undefined]
      */
 
     Q_INVOKABLE void getCacheStatus(QScriptValue scope, QScriptValue callback = QScriptValue()) {
@@ -242,38 +247,38 @@ public:
 
     /**jsdoc
      * @function Assets.queryCacheMeta
-     * @property {} options
-     * @property {} scope
-     * @property {} [callback=undefined]
+     * @param {} options
+     * @param {} scope
+     * @param {} [callback=undefined]
      */
 
     Q_INVOKABLE void queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
     
     /**jsdoc
      * @function Assets.loadFromCache
-     * @property {} options
-     * @property {} scope
-     * @property {} [callback=undefined]
+     * @param {} options
+     * @param {} scope
+     * @param {} [callback=undefined]
      */
 
     Q_INVOKABLE void loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
     
     /**jsdoc
      * @function Assets.saveToCache
-     * @property {} options
-     * @property {} scope
-     * @property {} [callback=undefined]
+     * @param {} options
+     * @param {} scope
+     * @param {} [callback=undefined]
      */
 
     Q_INVOKABLE void saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue());
     
     /**jsdoc
      * @function Assets.saveToCache
-     * @property {} url
-     * @property {} data
-     * @property {} metadata
-     * @property {} scope
-     * @property {} [callback=undefined]
+     * @param {} url
+     * @param {} data
+     * @param {} metadata
+     * @param {} scope
+     * @param {} [callback=undefined]
      */
 
     Q_INVOKABLE void saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& metadata,
diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp
index 0c65d5c6f0..4e2943d536 100644
--- a/libraries/script-engine/src/BatchLoader.cpp
+++ b/libraries/script-engine/src/BatchLoader.cpp
@@ -9,15 +9,17 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "BatchLoader.h"
+
 #include <QNetworkAccessManager>
 #include <QNetworkReply>
-
 #include <QFile>
 #include <QPointer>
-#include "ScriptEngineLogging.h"
-#include "BatchLoader.h"
+
 #include <NetworkAccessManager.h>
 #include <SharedUtil.h>
+
+#include "ScriptEngineLogging.h"
 #include "ResourceManager.h"
 #include "ScriptEngines.h"
 #include "ScriptCache.h"
diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.cpp b/libraries/script-engine/src/ConsoleScriptingInterface.cpp
index b4ef98938d..60de04aa9e 100644
--- a/libraries/script-engine/src/ConsoleScriptingInterface.cpp
+++ b/libraries/script-engine/src/ConsoleScriptingInterface.cpp
@@ -15,8 +15,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QtCore/QDateTime>
 #include "ConsoleScriptingInterface.h"
+
+#include <QtCore/QDateTime>
+
 #include "ScriptEngine.h"
 
 #define INDENTATION 4 // 1 Tab - 4 spaces
diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp
index a65bdff617..3cc5443973 100644
--- a/libraries/script-engine/src/DataViewClass.cpp
+++ b/libraries/script-engine/src/DataViewClass.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "DataViewPrototype.h"
-
 #include "DataViewClass.h"
 
+#include "DataViewPrototype.h"
+
 Q_DECLARE_METATYPE(QByteArray*)
 
 static const QString DATA_VIEW_NAME = "DataView";
@@ -91,4 +91,4 @@ QString DataViewClass::name() const {
 
 QScriptValue DataViewClass::prototype() const {
     return _proto;
-}
\ No newline at end of file
+}
diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp
index 8bab574f33..ef757a5cb4 100644
--- a/libraries/script-engine/src/DataViewPrototype.cpp
+++ b/libraries/script-engine/src/DataViewPrototype.cpp
@@ -8,14 +8,15 @@
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
+
+#include "DataViewPrototype.h"
+
 #include <QDebug>
 
 #include <glm/glm.hpp>
 
 #include "DataViewClass.h"
 
-#include "DataViewPrototype.h"
-
 Q_DECLARE_METATYPE(QByteArray*)
 
 DataViewPrototype::DataViewPrototype(QObject* parent) : QObject(parent) {
diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp
index abdd934e5a..94c074d44e 100644
--- a/libraries/script-engine/src/EventTypes.cpp
+++ b/libraries/script-engine/src/EventTypes.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "EventTypes.h"
+
 #include "KeyEvent.h"
 #include "MouseEvent.h"
 #include "SpatialEvent.h"
@@ -16,8 +18,6 @@
 #include "TouchEvent.h"
 #include "WheelEvent.h"
 
-#include "EventTypes.h"
-
 void registerEventTypes(QScriptEngine* engine) {
     qScriptRegisterMetaType(engine, KeyEvent::toScriptValue, KeyEvent::fromScriptValue);
     qScriptRegisterMetaType(engine, MouseEvent::toScriptValue, MouseEvent::fromScriptValue);
diff --git a/libraries/script-engine/src/KeyEvent.cpp b/libraries/script-engine/src/KeyEvent.cpp
index 581f9a816b..b0e622a774 100644
--- a/libraries/script-engine/src/KeyEvent.cpp
+++ b/libraries/script-engine/src/KeyEvent.cpp
@@ -9,13 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "KeyEvent.h"
+
 #include <qdebug.h>
 #include <qscriptengine.h>
 
 #include "ScriptEngineLogging.h"
 
-#include "KeyEvent.h"
-
 KeyEvent::KeyEvent() :
     key(0),
     text(""),
diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp
index 15015782e2..3e75d815c3 100644
--- a/libraries/script-engine/src/Mat4.cpp
+++ b/libraries/script-engine/src/Mat4.cpp
@@ -9,14 +9,17 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QDebug>
-#include <GLMHelpers.h>
+#include "Mat4.h"
+
 #include <glm/glm.hpp>
 #include <glm/gtx/string_cast.hpp>
 #include <glm/gtc/type_ptr.hpp>
+
+#include <QDebug>
+#include <GLMHelpers.h>
+
 #include "ScriptEngineLogging.h"
 #include "ScriptEngine.h"
-#include "Mat4.h"
 
 glm::mat4 Mat4::multiply(const glm::mat4& m1, const glm::mat4& m2) const {
     return m1 * m2;
diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp
index 40254eeccb..2662ba406d 100644
--- a/libraries/script-engine/src/MenuItemProperties.cpp
+++ b/libraries/script-engine/src/MenuItemProperties.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QDebug>
-#include <RegisteredMetaTypes.h>
 #include "MenuItemProperties.h"
 
+#include <QDebug>
+#include <RegisteredMetaTypes.h>
 
 
 MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& menuItemName,
diff --git a/libraries/script-engine/src/MouseEvent.cpp b/libraries/script-engine/src/MouseEvent.cpp
index 20bac96087..1bace0425f 100644
--- a/libraries/script-engine/src/MouseEvent.cpp
+++ b/libraries/script-engine/src/MouseEvent.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "MouseEvent.h"
+
 #include <qscriptengine.h>
 #include <qscriptvalue.h>
 
-#include "MouseEvent.h"
-
 MouseEvent::MouseEvent() :
     x(0.0f),
     y(0.0f),
@@ -104,4 +104,4 @@ QScriptValue MouseEvent::toScriptValue(QScriptEngine* engine, const MouseEvent&
 
 void MouseEvent::fromScriptValue(const QScriptValue& object, MouseEvent& event) {
     // nothing for now...
-}
\ No newline at end of file
+}
diff --git a/libraries/script-engine/src/MouseEvent.h b/libraries/script-engine/src/MouseEvent.h
index 0fbc688e5f..d9b00a8e01 100644
--- a/libraries/script-engine/src/MouseEvent.h
+++ b/libraries/script-engine/src/MouseEvent.h
@@ -13,6 +13,9 @@
 #define hifi_MouseEvent_h
 
 #include <QMouseEvent>
+#include <QScriptValue>
+
+class QScriptEngine;
 
 class MouseEvent {
 public:
@@ -38,4 +41,4 @@ public:
 
 Q_DECLARE_METATYPE(MouseEvent)
 
-#endif // hifi_MouseEvent_h
\ No newline at end of file
+#endif // hifi_MouseEvent_h
diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp
index a6f7acffc8..afff0a6b03 100644
--- a/libraries/script-engine/src/Quat.cpp
+++ b/libraries/script-engine/src/Quat.cpp
@@ -9,16 +9,17 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Quat.h"
+
 #include <glm/gtx/vector_angle.hpp>
+#include <glm/gtx/string_cast.hpp>
 
 #include <QDebug>
 
 #include <OctreeConstants.h>
-#include <GLMHelpers.h>
-#include <glm/gtx/string_cast.hpp>
+
 #include "ScriptEngineLogging.h"
 #include "ScriptEngine.h"
-#include "Quat.h"
 
 quat Quat::normalize(const glm::quat& q) {
     return glm::normalize(q);
diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h
index e6e395d9bf..1ccdfdbf31 100644
--- a/libraries/script-engine/src/Quat.h
+++ b/libraries/script-engine/src/Quat.h
@@ -20,6 +20,8 @@
 #include <QString>
 #include <QtScript/QScriptable>
 
+#include <GLMHelpers.h>
+
 /**jsdoc
  * A quaternion value. See also the {@link Quat(0)|Quat} object.
  * @typedef {object} Quat
@@ -35,6 +37,12 @@
  * of gimbal lock.
  * @namespace Quat
  * @variation 0
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
  * @property IDENTITY {Quat} <code>{ x: 0, y: 0, z: 0, w: 1 }</code> : The identity rotation, i.e., no rotation.
  *     <em>Read-only.</em>
  * @example <caption>Print the <code>IDENTITY</code> value.</caption>
diff --git a/libraries/script-engine/src/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h
index 0e4f90b928..29d9b31049 100644
--- a/libraries/script-engine/src/RecordingScriptingInterface.h
+++ b/libraries/script-engine/src/RecordingScriptingInterface.h
@@ -25,6 +25,10 @@ class QScriptValue;
 
 /**jsdoc
  * @namespace Recording
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-assignment-client
  */
 class RecordingScriptingInterface : public QObject, public Dependency {
     Q_OBJECT
diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h
index c69cd7090d..fdfbc6f6c0 100644
--- a/libraries/script-engine/src/SceneScriptingInterface.h
+++ b/libraries/script-engine/src/SceneScriptingInterface.h
@@ -112,6 +112,10 @@ namespace SceneScripting {
     
     /**jsdoc
      * @class Scene.Stage
+     *
+     * @hifi-interface
+     * @hifi-client-entity
+     *
      * @property {string} backgroundMode
      * @property {Scene.Stage.KeyLight} keyLight
      * @property {Scene.Stage.Location} location
@@ -171,6 +175,10 @@ namespace SceneScripting {
 
 /**jsdoc
  * @namespace Scene
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  * @property {boolean} shouldRenderAvatars
  * @property {boolean} shouldRenderEntities
  * @property {Scene.Stage} stage
diff --git a/libraries/script-engine/src/ScriptAudioInjector.cpp b/libraries/script-engine/src/ScriptAudioInjector.cpp
index 516f62401f..8b51377bff 100644
--- a/libraries/script-engine/src/ScriptAudioInjector.cpp
+++ b/libraries/script-engine/src/ScriptAudioInjector.cpp
@@ -9,9 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "ScriptEngineLogging.h"
 #include "ScriptAudioInjector.h"
 
+#include "ScriptEngineLogging.h"
+
 QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in) {
     // The AudioScriptingInterface::playSound method can return null, so we need to account for that.
     if (!in) {
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 4915a2dc8b..23ffbabe77 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ScriptEngine.h"
+
 #include <chrono>
 #include <thread>
 
@@ -67,7 +69,6 @@
 #include "ScriptAvatarData.h"
 #include "ScriptCache.h"
 #include "ScriptEngineLogging.h"
-#include "ScriptEngine.h"
 #include "TypedArrays.h"
 #include "XMLHttpRequestClass.h"
 #include "WebSocketClass.h"
@@ -100,8 +101,6 @@ int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature
 
 int scriptEnginePointerMetaID = qRegisterMetaType<ScriptEnginePointer>();
 
-Q_LOGGING_CATEGORY(scriptengineScript, "hifi.scriptengine.script")
-
 static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
     QString message = "";
     for (int i = 0; i < context->argumentCount(); i++) {
@@ -114,9 +113,9 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
     if (ScriptEngine *scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
         scriptEngine->print(message);
         // prefix the script engine name to help disambiguate messages in the main debug log
-        qCDebug(scriptengineScript, "[%s] %s", qUtf8Printable(scriptEngine->getFilename()), qUtf8Printable(message));
+        qCDebug(scriptengine_script, "[%s] %s", qUtf8Printable(scriptEngine->getFilename()), qUtf8Printable(message));
     } else {
-        qCDebug(scriptengineScript, "%s", qUtf8Printable(message));
+        qCDebug(scriptengine_script, "%s", qUtf8Printable(message));
     }
 
     return QScriptValue();
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index bc7e847cdf..3001666b5d 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -89,6 +89,12 @@ public:
 
 /**jsdoc
  * @namespace Script
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
  * @property {string} context
  */
 class ScriptEngine : public BaseScriptEngine, public EntitiesScriptEngineProvider {
@@ -520,6 +526,9 @@ public:
     void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
     bool isUserLoaded() const { return _isUserLoaded; }
 
+    void setQuitWhenFinished(const bool quitWhenFinished) { _quitWhenFinished = quitWhenFinished; }
+    bool isQuitWhenFinished() const { return _quitWhenFinished; }
+
     // NOTE - this is used by the TypedArray implementation. we need to review this for thread safety
     ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
 
@@ -762,6 +771,8 @@ protected:
     std::atomic<bool> _isUserLoaded { false };
     bool _isReloading { false };
 
+    std::atomic<bool> _quitWhenFinished;
+
     ArrayBufferClass* _arrayBufferClass;
 
     AssetScriptingInterface* _assetScriptingInterface;
diff --git a/libraries/script-engine/src/ScriptEngineLogging.cpp b/libraries/script-engine/src/ScriptEngineLogging.cpp
index 392bc05129..b51d7c3780 100644
--- a/libraries/script-engine/src/ScriptEngineLogging.cpp
+++ b/libraries/script-engine/src/ScriptEngineLogging.cpp
@@ -13,3 +13,4 @@
 
 Q_LOGGING_CATEGORY(scriptengine, "hifi.scriptengine")
 Q_LOGGING_CATEGORY(scriptengine_module, "hifi.scriptengine.module")
+Q_LOGGING_CATEGORY(scriptengine_script, "hifi.scriptengine.script")
diff --git a/libraries/script-engine/src/ScriptEngineLogging.h b/libraries/script-engine/src/ScriptEngineLogging.h
index 62e46632a6..3096dd4927 100644
--- a/libraries/script-engine/src/ScriptEngineLogging.h
+++ b/libraries/script-engine/src/ScriptEngineLogging.h
@@ -16,6 +16,7 @@
 
 Q_DECLARE_LOGGING_CATEGORY(scriptengine)
 Q_DECLARE_LOGGING_CATEGORY(scriptengine_module)
+Q_DECLARE_LOGGING_CATEGORY(scriptengine_script)
 
 #endif // hifi_ScriptEngineLogging_h
 
diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp
index f2ed296b63..ad6e1debe9 100644
--- a/libraries/script-engine/src/ScriptEngines.cpp
+++ b/libraries/script-engine/src/ScriptEngines.cpp
@@ -347,7 +347,8 @@ void ScriptEngines::saveScripts() {
     {
         QReadLocker lock(&_scriptEnginesHashLock);
         for (auto it = _scriptEnginesHash.begin(); it != _scriptEnginesHash.end(); ++it) {
-            if (it.value() && it.value()->isUserLoaded()) {
+            // Save user-loaded scripts, only if they are set to quit when finished
+            if (it.value() && it.value()->isUserLoaded()  && !it.value()->isQuitWhenFinished()) {
                 auto normalizedUrl = normalizeScriptURL(it.key());
                 list.append(normalizedUrl.toString());
             }
@@ -456,7 +457,7 @@ void ScriptEngines::reloadAllScripts() {
 }
 
 ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor,
-                                        bool activateMainWindow, bool reload) {
+                                              bool activateMainWindow, bool reload, bool quitWhenFinished) {
     if (thread() != QThread::currentThread()) {
         ScriptEnginePointer result { nullptr };
         BLOCKING_INVOKE_METHOD(this, "loadScript", Q_RETURN_ARG(ScriptEnginePointer, result),
@@ -488,6 +489,7 @@ ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool i
     scriptEngine = ScriptEnginePointer(new ScriptEngine(_context, NO_SCRIPT, "about:" + scriptFilename.fileName()));
     addScriptEngine(scriptEngine);
     scriptEngine->setUserLoaded(isUserLoaded);
+    scriptEngine->setQuitWhenFinished(quitWhenFinished);
 
     if (scriptFilename.isEmpty() || !scriptUrl.isValid()) {
         launchScriptEngine(scriptEngine);
@@ -496,6 +498,11 @@ ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool i
         connect(scriptEngine.data(), &ScriptEngine::scriptLoaded, this, &ScriptEngines::onScriptEngineLoaded);
         connect(scriptEngine.data(), &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError);
 
+        // Shutdown Interface when script finishes, if requested
+        if (quitWhenFinished) {
+            connect(scriptEngine.data(), &ScriptEngine::finished, this, &ScriptEngines::quitWhenFinished);
+        }
+
         // get the script engine object to load the script at the designated script URL
         scriptEngine->loadURL(scriptUrl, reload);
     }
@@ -536,6 +543,10 @@ void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) {
     emit scriptCountChanged();
 }
 
+void ScriptEngines::quitWhenFinished() {
+    qApp->quit();
+}
+
 int ScriptEngines::runScriptInitializers(ScriptEnginePointer scriptEngine) {
     int ii=0;
     for (auto initializer : _scriptInitializers) {
diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h
index cf081860d8..4d5964e462 100644
--- a/libraries/script-engine/src/ScriptEngines.h
+++ b/libraries/script-engine/src/ScriptEngines.h
@@ -29,6 +29,10 @@ class ScriptEngine;
 
 /**jsdoc
  * @namespace ScriptDiscoveryService
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  * @property {string} debugScriptUrl
  * @property {string} defaultScriptsPath
  * @property {ScriptsModel} scriptsModel
@@ -84,10 +88,11 @@ public:
      * @param {boolean} [loadScriptFromEditor=false]
      * @param {boolean} [activateMainWindow=false]
      * @param {boolean} [reload=false]
+     * @param {boolean} [quitWhenFinished=false]
      * @returns {boolean}
      */
     Q_INVOKABLE ScriptEnginePointer loadScript(const QUrl& scriptFilename = QString(),
-        bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false);
+        bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false, bool quitWhenFinished = false);
 
     /**jsdoc
      * @function ScriptDiscoveryService.stopScript
@@ -262,6 +267,7 @@ protected:
     ScriptEnginePointer reloadScript(const QString& scriptName, bool isUserLoaded = true) { return loadScript(scriptName, isUserLoaded, false, false, true); }
     void removeScriptEngine(ScriptEnginePointer);
     void onScriptEngineLoaded(const QString& scriptFilename);
+    void quitWhenFinished();
     void onScriptEngineError(const QString& scriptFilename);
     void launchScriptEngine(ScriptEnginePointer);
 
diff --git a/libraries/script-engine/src/ScriptUUID.cpp b/libraries/script-engine/src/ScriptUUID.cpp
index ee15f1a760..f88803c87c 100644
--- a/libraries/script-engine/src/ScriptUUID.cpp
+++ b/libraries/script-engine/src/ScriptUUID.cpp
@@ -11,11 +11,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ScriptUUID.h"
+
 #include <QDebug>
 
 #include "ScriptEngineLogging.h"
 #include "ScriptEngine.h"
-#include "ScriptUUID.h"
 
 QUuid ScriptUUID::fromString(const QString& s) {
     return QUuid(s);
diff --git a/libraries/script-engine/src/ScriptUUID.h b/libraries/script-engine/src/ScriptUUID.h
index 303a871d1d..0af0c1cf8e 100644
--- a/libraries/script-engine/src/ScriptUUID.h
+++ b/libraries/script-engine/src/ScriptUUID.h
@@ -15,6 +15,7 @@
 #define hifi_ScriptUUID_h
 
 #include <QUuid>
+#include <QObject>
 #include <QtScript/QScriptable>
 
 /**jsdoc
@@ -23,6 +24,12 @@
  * hexadecimal digits.
  *
  * @namespace Uuid
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
  * @property NULL {Uuid} The null UUID, <code>{00000000-0000-0000-0000-000000000000}</code>.
  */
 
diff --git a/libraries/script-engine/src/ScriptsModel.h b/libraries/script-engine/src/ScriptsModel.h
index a4ffc192f9..2466347baa 100644
--- a/libraries/script-engine/src/ScriptsModel.h
+++ b/libraries/script-engine/src/ScriptsModel.h
@@ -68,6 +68,9 @@ public:
  * <p>Has properties and functions below in addition to those of <a href="http://doc.qt.io/qt-5/qabstractitemmodel.html">
  * http://doc.qt.io/qt-5/qabstractitemmodel.html</a>.</p>
  * @class ScriptsModel
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 class ScriptsModel : public QAbstractItemModel {
     Q_OBJECT
diff --git a/libraries/script-engine/src/ScriptsModelFilter.h b/libraries/script-engine/src/ScriptsModelFilter.h
index 26efde02e8..05a76334bb 100644
--- a/libraries/script-engine/src/ScriptsModelFilter.h
+++ b/libraries/script-engine/src/ScriptsModelFilter.h
@@ -20,6 +20,9 @@
  * <p>Has properties and functions per <a href="http://doc.qt.io/qt-5/qsortfilterproxymodel.html">
  * http://doc.qt.io/qt-5/qsortfilterproxymodel.html</a>.</p>
  * @class ScriptsModelFilter
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 class ScriptsModelFilter : public QSortFilterProxyModel {
     Q_OBJECT
diff --git a/libraries/script-engine/src/SpatialEvent.cpp b/libraries/script-engine/src/SpatialEvent.cpp
index f20a0c2b1e..d06cc556d3 100644
--- a/libraries/script-engine/src/SpatialEvent.cpp
+++ b/libraries/script-engine/src/SpatialEvent.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <RegisteredMetaTypes.h>
-
 #include "SpatialEvent.h"
 
+#include <RegisteredMetaTypes.h>
+
 SpatialEvent::SpatialEvent() :
     locTranslation(0.0f),
     locRotation(),
@@ -43,4 +43,4 @@ QScriptValue SpatialEvent::toScriptValue(QScriptEngine* engine, const SpatialEve
 
 void SpatialEvent::fromScriptValue(const QScriptValue& object,SpatialEvent& event) {
     // nothing for now...
-}
\ No newline at end of file
+}
diff --git a/libraries/script-engine/src/TouchEvent.cpp b/libraries/script-engine/src/TouchEvent.cpp
index 097639d4e8..6ff591decf 100644
--- a/libraries/script-engine/src/TouchEvent.cpp
+++ b/libraries/script-engine/src/TouchEvent.cpp
@@ -9,15 +9,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "TouchEvent.h"
+
 #include <qscriptengine.h>
 #include <qscriptvalue.h>
-#include <QTouchEvent>
 
 #include <RegisteredMetaTypes.h>
 #include <NumericalConstants.h>
 
-#include "TouchEvent.h"
-
 TouchEvent::TouchEvent() :
     x(0.0f),
     y(0.0f),
diff --git a/libraries/script-engine/src/TouchEvent.h b/libraries/script-engine/src/TouchEvent.h
index d9eedf50d0..62cb1b1801 100644
--- a/libraries/script-engine/src/TouchEvent.h
+++ b/libraries/script-engine/src/TouchEvent.h
@@ -13,8 +13,13 @@
 #define hifi_TouchEvent_h
 
 #include <glm/glm.hpp>
+
+#include <QVector>
 #include <QTouchEvent>
 
+class QScriptValue;
+class QScriptEngine;
+
 class TouchEvent {
 public:
     TouchEvent();
@@ -54,4 +59,4 @@ private:
 
 Q_DECLARE_METATYPE(TouchEvent)
 
-#endif // hifi_TouchEvent_h
\ No newline at end of file
+#endif // hifi_TouchEvent_h
diff --git a/libraries/script-engine/src/TypedArrayPrototype.cpp b/libraries/script-engine/src/TypedArrayPrototype.cpp
index 4de948e806..a1f3ff87e8 100644
--- a/libraries/script-engine/src/TypedArrayPrototype.cpp
+++ b/libraries/script-engine/src/TypedArrayPrototype.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "TypedArrays.h"
-
 #include "TypedArrayPrototype.h"
 
+#include "TypedArrays.h"
+
 Q_DECLARE_METATYPE(QByteArray*)
 
 TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) {
diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp
index 4d5181ff33..f2c3d3fd3d 100644
--- a/libraries/script-engine/src/TypedArrays.cpp
+++ b/libraries/script-engine/src/TypedArrays.cpp
@@ -9,13 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "TypedArrays.h"
+
 #include <glm/glm.hpp>
 
 #include "ScriptEngine.h"
 #include "TypedArrayPrototype.h"
 
-#include "TypedArrays.h"
-
 Q_DECLARE_METATYPE(QByteArray*)
 
 TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferViewClass(scriptEngine) {
diff --git a/libraries/script-engine/src/UndoStackScriptingInterface.cpp b/libraries/script-engine/src/UndoStackScriptingInterface.cpp
index 17bf8b1aa6..1171625c04 100644
--- a/libraries/script-engine/src/UndoStackScriptingInterface.cpp
+++ b/libraries/script-engine/src/UndoStackScriptingInterface.cpp
@@ -9,13 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "UndoStackScriptingInterface.h"
+
 #include <QDebug>
 #include <QScriptValue>
 #include <QScriptValueList>
 #include <QScriptEngine>
 
-#include "UndoStackScriptingInterface.h"
-
 UndoStackScriptingInterface::UndoStackScriptingInterface(QUndoStack* undoStack) : _undoStack(undoStack) {
 }
 
diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h
index 6728c471f6..e80d38239f 100644
--- a/libraries/script-engine/src/UsersScriptingInterface.h
+++ b/libraries/script-engine/src/UsersScriptingInterface.h
@@ -18,7 +18,12 @@
 
 /**jsdoc
  * @namespace Users
- * @property {boolean} canKick - <code>true</code> if the domain server allows the node or avatar to kick (ban) avatars, 
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-assignment-client
+ *
+ * @property {boolean} canKick - <code>true</code> if the domain server allows the node or avatar to kick (ban) avatars,
  *     otherwise <code>false</code>. <em>Read-only.</em>
  * @property {boolean} requestsDomainListData - <code>true</code> if the avatar requests extra data from the mixers (such as 
  *     positional data of an avatar you've ignored). <em>Read-only.</em>
@@ -101,10 +106,11 @@ public slots:
     void mute(const QUuid& nodeID);
 
     /**jsdoc
-    * Get the user name and machine fingerprint associated with the given UUID. This will only do anything if you're an admin 
-    * of the domain you're in.
-    * @function Users.getUsernameFromID
-    * @param {Uuid} nodeID The node or session ID of the user whose username you want.
+    * Request the user name and machine fingerprint associated with the given UUID. The user name will be returned in a 
+    * {@link Users.usernameFromIDReply|usernameFromIDReply} signal. This will only do anything if you're an admin of the domain 
+    * you're in.
+    * @function Users.requestUsernameFromID
+    * @param {Uuid} nodeID The node or session ID of the user whose user name you want.
     */
     void requestUsernameFromID(const QUuid& nodeID);
 
@@ -165,7 +171,8 @@ signals:
     void enteredIgnoreRadius();
 
     /**jsdoc
-     * Notifies scripts of the user name and machine fingerprint associated with a UUID.
+     * Triggered in response to a {@link Users.requestUsernameFromID|requestUsernameFromID} call. Provides the user name and 
+     * machine fingerprint associated with a UUID.
      * Username and machineFingerprint will be their default constructor output if the requesting user isn't an admin.
      * @function Users.usernameFromIDReply
      * @param {Uuid} nodeID
diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp
index c21f96cd47..2d3d4454c3 100644
--- a/libraries/script-engine/src/Vec3.cpp
+++ b/libraries/script-engine/src/Vec3.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "Vec3.h"
+
 #include <glm/gtx/vector_angle.hpp>
 
 #include <QDebug>
@@ -16,11 +18,10 @@
 #include <GLMHelpers.h>
 #include <glm/gtx/string_cast.hpp>
 
-#include "ScriptEngineLogging.h"
 #include "NumericalConstants.h"
-#include "Vec3.h"
-
 #include "ScriptEngine.h"
+#include "ScriptEngineLogging.h"
+
 
 float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) {
     float radians = glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3));
diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h
index 635f2a530c..eb9438c5c2 100644
--- a/libraries/script-engine/src/Vec3.h
+++ b/libraries/script-engine/src/Vec3.h
@@ -47,6 +47,12 @@
  *
  * @namespace Vec3
  * @variation 0
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
  * @property {Vec3} UNIT_X - <code>{ x: 1, y: 0, z: 0 }</code> : Unit vector in the x-axis direction. <em>Read-only.</em>
  * @property {Vec3} UNIT_Y - <code>{ x: 0, y: 1, z: 0 }</code> : Unit vector in the y-axis direction. <em>Read-only.</em>
  * @property {Vec3} UNIT_Z - <code>{ x: 0, y: 0, z: 1 }</code> : Unit vector in the z-axis direction. <em>Read-only.</em>
diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp
index 76faaab415..56753f07d1 100644
--- a/libraries/script-engine/src/WebSocketClass.cpp
+++ b/libraries/script-engine/src/WebSocketClass.cpp
@@ -12,9 +12,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "ScriptEngine.h"
 #include "WebSocketClass.h"
 
+#include "ScriptEngine.h"
+
 WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) :
     _webSocket(new QWebSocket()),
     _engine(engine)
diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp
index 3b723d5b3f..860170a3f9 100644
--- a/libraries/script-engine/src/WebSocketServerClass.cpp
+++ b/libraries/script-engine/src/WebSocketServerClass.cpp
@@ -11,9 +11,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "ScriptEngine.h"
 #include "WebSocketServerClass.h"
 
+#include "ScriptEngine.h"
+
 WebSocketServerClass::WebSocketServerClass(QScriptEngine* engine, const QString& serverName, const quint16 port) :
     _webSocketServer(serverName, QWebSocketServer::SslMode::NonSecureMode),
     _engine(engine)
diff --git a/libraries/script-engine/src/WheelEvent.cpp b/libraries/script-engine/src/WheelEvent.cpp
index 70004d0d3f..a0a897c991 100644
--- a/libraries/script-engine/src/WheelEvent.cpp
+++ b/libraries/script-engine/src/WheelEvent.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <qscriptengine.h>
-#include <qscriptvalue.h>
-
 #include "WheelEvent.h"
 
+#include <QScriptEngine>
+#include <QScriptValue>
+
 WheelEvent::WheelEvent() :
     x(0.0f),
     y(0.0f),
@@ -99,4 +99,4 @@ QScriptValue WheelEvent::toScriptValue(QScriptEngine* engine, const WheelEvent&
 
 void WheelEvent::fromScriptValue(const QScriptValue& object, WheelEvent& event) {
     // nothing for now...
-}
\ No newline at end of file
+}
diff --git a/libraries/script-engine/src/WheelEvent.h b/libraries/script-engine/src/WheelEvent.h
index edac4bc3c3..88ac828578 100644
--- a/libraries/script-engine/src/WheelEvent.h
+++ b/libraries/script-engine/src/WheelEvent.h
@@ -12,8 +12,12 @@
 #ifndef hifi_WheelEvent_h
 #define hifi_WheelEvent_h
 
+#include <QString>
 #include <QWheelEvent>
 
+class QScriptValue;
+class QScriptEngine;
+
 class WheelEvent {
 public:
     WheelEvent();
@@ -37,4 +41,4 @@ public:
 
 Q_DECLARE_METATYPE(WheelEvent)
 
-#endif // hifi_WheelEvent_h
\ No newline at end of file
+#endif // hifi_WheelEvent_h
diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp
index 62384f9d97..ebc459b2d1 100644
--- a/libraries/script-engine/src/XMLHttpRequestClass.cpp
+++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp
@@ -12,6 +12,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "XMLHttpRequestClass.h"
+
 #include <QEventLoop>
 #include <qurlquery.h>
 
@@ -20,7 +22,6 @@
 #include <NetworkingConstants.h>
 
 #include "ScriptEngine.h"
-#include "XMLHttpRequestClass.h"
 
 const QString METAVERSE_API_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/";
 
diff --git a/libraries/shared/src/AACube.cpp b/libraries/shared/src/AACube.cpp
index 8cff3255b3..7dd2f8cb5b 100644
--- a/libraries/shared/src/AACube.cpp
+++ b/libraries/shared/src/AACube.cpp
@@ -9,8 +9,9 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "AABox.h"
 #include "AACube.h"
+
+#include "AABox.h"
 #include "Extents.h"
 #include "GeometryUtil.h"
 #include "NumericalConstants.h"
diff --git a/libraries/shared/src/CrashHelpers.cpp b/libraries/shared/src/CrashHelpers.cpp
new file mode 100644
index 0000000000..f8ca90bc4c
--- /dev/null
+++ b/libraries/shared/src/CrashHelpers.cpp
@@ -0,0 +1,77 @@
+//
+//  CrashHelpers.cpp
+//  libraries/shared/src
+//
+//  Created by Clement Brisset on 4/30/18.
+//  Copyright 2018 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 "CrashHelpers.h"
+
+namespace crash {
+
+class B;
+class A {
+public:
+    A(B* b) : _b(b) { }
+    ~A();
+    virtual void virtualFunction() = 0;
+
+private:
+    B* _b;
+};
+
+class B : public A {
+public:
+    B() : A(this) { }
+    virtual void virtualFunction() override { }
+};
+
+A::~A() {
+    _b->virtualFunction();
+}
+
+void pureVirtualCall() {
+    qCDebug(shared) << "About to make a pure virtual call";
+    B b;
+}
+
+void doubleFree() {
+    qCDebug(shared) << "About to double delete memory";
+    int* blah = new int(200);
+    delete blah;
+    delete blah;
+}
+
+void nullDeref() {
+    qCDebug(shared) << "About to dereference a null pointer";
+    int* p = nullptr;
+    *p = 1;
+}
+
+void doAbort() {
+    qCDebug(shared) << "About to abort";
+    abort();
+}
+
+void outOfBoundsVectorCrash() {
+    qCDebug(shared) << "std::vector out of bounds crash!";
+    std::vector<int> v;
+    v[0] = 42;
+}
+
+void newFault() {
+    qCDebug(shared) << "About to crash inside new fault";
+
+    // Force crash with multiple large allocations
+    while (true) {
+        const size_t GIGABYTE = 1024 * 1024 * 1024;
+        new char[GIGABYTE];
+    }
+
+}
+    
+}
diff --git a/libraries/shared/src/CrashHelpers.h b/libraries/shared/src/CrashHelpers.h
index 1cc6749182..ad988c8906 100644
--- a/libraries/shared/src/CrashHelpers.h
+++ b/libraries/shared/src/CrashHelpers.h
@@ -18,66 +18,12 @@
 
 namespace crash {
 
-class B;
-class A {
-public:
-    A(B* b) : _b(b) { }
-    ~A();
-    virtual void virtualFunction() = 0;
-
-private:
-    B* _b;
-};
-
-class B : public A {
-public:
-    B() : A(this) { }
-    virtual void virtualFunction() override { }
-};
-
-A::~A() {
-    _b->virtualFunction();
-}
-
-void pureVirtualCall() {
-    qCDebug(shared) << "About to make a pure virtual call";
-    B b;
-}
-    
-void doubleFree() {
-    qCDebug(shared) << "About to double delete memory";
-    int* blah = new int(200);
-    delete blah;
-    delete blah;
-}
-
-void nullDeref() {
-    qCDebug(shared) << "About to dereference a null pointer";
-    int* p = nullptr;
-    *p = 1;
-}
-
-void doAbort() {
-    qCDebug(shared) << "About to abort";
-    abort();
-}
-
-void outOfBoundsVectorCrash() {
-    qCDebug(shared) << "std::vector out of bounds crash!";
-    std::vector<int> v;
-    v[0] = 42;
-}
-
-void newFault() {
-    qCDebug(shared) << "About to crash inside new fault";
-
-    // Force crash with multiple large allocations
-    while (true) {
-        const size_t GIGABYTE = 1024 * 1024 * 1024;
-        new char[GIGABYTE];
-    }
-
-}
+void pureVirtualCall();
+void doubleFree();
+void nullDeref();
+void doAbort();
+void outOfBoundsVectorCrash();
+void newFault();
 
 }
 
diff --git a/libraries/shared/src/CubeProjectedPolygon.cpp b/libraries/shared/src/CubeProjectedPolygon.cpp
index 04d6e8bb4e..acd2fc11d6 100644
--- a/libraries/shared/src/CubeProjectedPolygon.cpp
+++ b/libraries/shared/src/CubeProjectedPolygon.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "CubeProjectedPolygon.h"
+
 #include <algorithm>
 
 #include <QtCore/QDebug>
@@ -16,8 +18,6 @@
 #include "GeometryUtil.h"
 #include "SharedUtil.h"
 #include "SharedLogging.h"
-#include "CubeProjectedPolygon.h"
-
 
 glm::vec2 BoundingRectangle::getVertex(int vertexNumber) const {
     switch (vertexNumber) {
diff --git a/libraries/shared/src/DebugDraw.h b/libraries/shared/src/DebugDraw.h
index 64327585fb..7dd19415c9 100644
--- a/libraries/shared/src/DebugDraw.h
+++ b/libraries/shared/src/DebugDraw.h
@@ -25,6 +25,11 @@
  * Helper functions to render ephemeral debug markers and lines.
  * DebugDraw markers and lines are only visible locally, they are not visible by other users.
  * @namespace DebugDraw
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
  */
 class DebugDraw : public QObject {
     Q_OBJECT
diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp
index 72710a6a7d..75446754d5 100644
--- a/libraries/shared/src/GLMHelpers.cpp
+++ b/libraries/shared/src/GLMHelpers.cpp
@@ -76,13 +76,15 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float proportion) {
 
 // Allows sending of fixed-point numbers: radix 1 makes 15.1 number, radix 8 makes 8.8 number, etc
 int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix) {
-    int16_t outVal = (int16_t)(scalar * (float)(1 << radix));
-    memcpy(buffer, &outVal, sizeof(uint16_t));
-    return sizeof(uint16_t);
+    int16_t twoByteFixed = (int16_t)(scalar * (float)(1 << radix));
+    memcpy(buffer, &twoByteFixed, sizeof(int16_t));
+    return sizeof(int16_t);
 }
 
 int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, float* destinationPointer, int radix) {
-    *destinationPointer = *byteFixedPointer / (float)(1 << radix);
+    int16_t twoByteFixed;
+    memcpy(&twoByteFixed, byteFixedPointer, sizeof(int16_t));
+    *destinationPointer = twoByteFixed / (float)(1 << radix);
     return sizeof(int16_t);
 }
 
@@ -102,18 +104,19 @@ int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm
     return sourceBuffer - startPosition;
 }
 
-
 int packFloatAngleToTwoByte(unsigned char* buffer, float degrees) {
     const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.0f);
 
-    uint16_t angleHolder = floorf((degrees + 180.0f) * ANGLE_CONVERSION_RATIO);
-    memcpy(buffer, &angleHolder, sizeof(uint16_t));
+    uint16_t twoByteAngle = floorf((degrees + 180.0f) * ANGLE_CONVERSION_RATIO);
+    memcpy(buffer, &twoByteAngle, sizeof(uint16_t));
 
     return sizeof(uint16_t);
 }
 
 int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer) {
-    *destinationPointer = (*byteAnglePointer / (float) std::numeric_limits<uint16_t>::max()) * 360.0f - 180.0f;
+    uint16_t twoByteAngle;
+    memcpy(&twoByteAngle, byteAnglePointer, sizeof(uint16_t));
+    *destinationPointer = (twoByteAngle / (float) std::numeric_limits<uint16_t>::max()) * 360.0f - 180.0f;
     return sizeof(uint16_t);
 }
 
@@ -222,6 +225,12 @@ int unpackOrientationQuatFromSixBytes(const unsigned char* buffer, glm::quat& qu
     return 6;
 }
 
+bool closeEnough(float a, float b, float relativeError) {
+    assert(relativeError >= 0.0f);
+    // NOTE: we add EPSILON to the denominator so we can avoid checking for division by zero.
+    // This method works fine when: fabsf(a + b) >> EPSILON
+    return fabsf(a - b) / (0.5f * fabsf(a + b) + EPSILON) < relativeError;
+}
 
 //  Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
 //  http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h
index 5c9a8b5ca1..0e1af27cd2 100644
--- a/libraries/shared/src/GLMHelpers.h
+++ b/libraries/shared/src/GLMHelpers.h
@@ -137,6 +137,8 @@ int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, flo
 int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
 int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix);
 
+bool closeEnough(float a, float b, float relativeError);
+
 /// \return vec3 with euler angles in radians
 glm::vec3 safeEulerAngles(const glm::quat& q);
 
diff --git a/libraries/shared/src/GPUIdent.cpp b/libraries/shared/src/GPUIdent.cpp
index 309cb30728..3b7a6cee40 100644
--- a/libraries/shared/src/GPUIdent.cpp
+++ b/libraries/shared/src/GPUIdent.cpp
@@ -8,8 +8,7 @@
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 
-#include <QtCore/QtGlobal>
-
+#include "GPUIdent.h"
 
 #ifdef Q_OS_WIN
 #include <string>
@@ -24,8 +23,9 @@
 #include <OpenGL/OpenGL.h>
 #endif
 
+#include <QtCore/QtGlobal>
+
 #include "SharedLogging.h"
-#include "GPUIdent.h"
 
 GPUIdent GPUIdent::_instance {};
 
diff --git a/libraries/shared/src/GPUIdent.h b/libraries/shared/src/GPUIdent.h
index 8615e61b08..f780a4ddbd 100644
--- a/libraries/shared/src/GPUIdent.h
+++ b/libraries/shared/src/GPUIdent.h
@@ -16,6 +16,8 @@
 
 #include <cstdint>
 
+#include <QString>
+
 class GPUIdent
 {
 public:
diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp
index 230b9590f1..e35c74e68a 100644
--- a/libraries/shared/src/GenericThread.cpp
+++ b/libraries/shared/src/GenericThread.cpp
@@ -9,11 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QDebug>
-#include <QtCore/QCoreApplication>
-
 #include "GenericThread.h"
 
+#include <QDebug>
+#include <QtCore/QCoreApplication>
 
 GenericThread::GenericThread() :
     _stopThread(false),
diff --git a/libraries/shared/src/Gzip.cpp b/libraries/shared/src/Gzip.cpp
index 25e214fffb..06b499b88a 100644
--- a/libraries/shared/src/Gzip.cpp
+++ b/libraries/shared/src/Gzip.cpp
@@ -9,9 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <zlib.h>
 #include "Gzip.h"
 
+#include <zlib.h>
+
 const int GZIP_WINDOWS_BIT = 31;
 const int GZIP_CHUNK_SIZE = 4096;
 const int DEFAULT_MEM_LEVEL = 8;
diff --git a/libraries/shared/src/LogUtils.cpp b/libraries/shared/src/LogUtils.cpp
index 73667116a0..11a4665ab1 100644
--- a/libraries/shared/src/LogUtils.cpp
+++ b/libraries/shared/src/LogUtils.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QtGlobal>
-
 #include "LogUtils.h"
 
+#include <QtGlobal>
+
 void LogUtils::init() {
 #ifdef Q_OS_WIN
     // Windows applications buffer stdout/err hard when not run from a terminal,
diff --git a/libraries/shared/src/NumericalConstants.h b/libraries/shared/src/NumericalConstants.h
index e2d2409d56..4c24a73226 100644
--- a/libraries/shared/src/NumericalConstants.h
+++ b/libraries/shared/src/NumericalConstants.h
@@ -48,6 +48,8 @@ const int BYTES_PER_KILOBYTE = 1000;
 const int BYTES_PER_KILOBIT = BYTES_PER_KILOBYTE / BITS_IN_BYTE;
 const int KILO_PER_MEGA = 1000;
 
+const float SQRT_THREE = 1.73205080f;
+
 #define KB_TO_BYTES_SHIFT 10
 #define MB_TO_BYTES_SHIFT 20
 #define GB_TO_BYTES_SHIFT 30
diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp
index c7ad4a790d..7f7d03c335 100644
--- a/libraries/shared/src/OctalCode.cpp
+++ b/libraries/shared/src/OctalCode.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OctalCode.h"
+
 #include <algorithm> // std:min
 #include <cassert>
 #include <cmath>
@@ -17,7 +19,6 @@
 #include <QtCore/QDebug>
 
 #include "NumericalConstants.h"
-#include "OctalCode.h"
 #include "SharedUtil.h"
 
 int numberOfThreeBitSectionsInCode(const unsigned char* octalCode, int maxBytes) {
diff --git a/libraries/shared/src/PIDController.cpp b/libraries/shared/src/PIDController.cpp
index 790c26ac25..5850e345cb 100644
--- a/libraries/shared/src/PIDController.cpp
+++ b/libraries/shared/src/PIDController.cpp
@@ -9,11 +9,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <glm/glm.hpp>
-#include <QDebug>
-#include "SharedLogging.h"
 #include "PIDController.h"
 
+#include <glm/glm.hpp>
+
+#include <QDebug>
+
+#include "SharedLogging.h"
+
 float PIDController::update(float measuredValue, float dt, bool resetAccumulator) {
     const float error = getMeasuredValueSetpoint() - measuredValue;   // Sign is the direction we want measuredValue to go. Positive means go higher.
 
diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h
index d879ac968d..fc933b6b8c 100644
--- a/libraries/shared/src/PathUtils.h
+++ b/libraries/shared/src/PathUtils.h
@@ -22,6 +22,10 @@
  * The Paths API provides absolute paths to the scripts and resources directories.
  *
  * @namespace Paths
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  * @deprecated The Paths API is deprecated. Use {@link Script.resolvePath} and {@link Script.resourcesPath} instead.
  * @readonly
  * @property {string} defaultScripts - The path to the scripts directory. <em>Read-only.</em>
diff --git a/libraries/shared/src/PerfStat.cpp b/libraries/shared/src/PerfStat.cpp
index 13b3d44eda..c3bc44b7d3 100644
--- a/libraries/shared/src/PerfStat.cpp
+++ b/libraries/shared/src/PerfStat.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "PerfStat.h"
+
 #include <cstdio>
 #include <map>
 #include <string>
@@ -16,8 +18,6 @@
 #include <QDebug>
 #include <QThread>
 
-#include "PerfStat.h"
-
 #include "NumericalConstants.h"
 #include "SharedLogging.h"
 
diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h
index 279fa42ea4..34ec074d45 100644
--- a/libraries/shared/src/PrioritySortUtil.h
+++ b/libraries/shared/src/PrioritySortUtil.h
@@ -15,7 +15,7 @@
 #include <queue>
 
 #include "NumericalConstants.h"
-#include "ViewFrustum.h"
+#include "shared/ConicalViewFrustum.h"
 
 /*   PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum.  To use:
 
@@ -84,14 +84,12 @@ namespace PrioritySortUtil {
     class PriorityQueue {
     public:
         PriorityQueue() = delete;
-
-        PriorityQueue(const ViewFrustum& view) : _view(view) { }
-
-        PriorityQueue(const ViewFrustum& view, float angularWeight, float centerWeight, float ageWeight)
-                : _view(view), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
+        PriorityQueue(const ConicalViewFrustums& views) : _views(views) { }
+        PriorityQueue(const ConicalViewFrustums& views, float angularWeight, float centerWeight, float ageWeight)
+                : _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
         { }
 
-        void setView(const ViewFrustum& view) { _view = view; }
+        void setViews(const ConicalViewFrustums& views) { _views = views; }
 
         void setWeights(float angularWeight, float centerWeight, float ageWeight) {
             _angularWeight = angularWeight;
@@ -109,7 +107,18 @@ namespace PrioritySortUtil {
         bool empty() const { return _queue.empty(); }
 
     private:
+
         float computePriority(const T& thing) const {
+            float priority = std::numeric_limits<float>::min();
+
+            for (const auto& view : _views) {
+                priority = std::max(priority, computePriority(view, thing));
+            }
+
+            return priority;
+        }
+
+        float computePriority(const ConicalViewFrustum& view, const T& thing) const {
             // priority = weighted linear combination of multiple values:
             //   (a) angular size
             //   (b) proximity to center of view
@@ -117,11 +126,11 @@ namespace PrioritySortUtil {
             // where the relative "weights" are tuned to scale the contributing values into units of "priority".
 
             glm::vec3 position = thing.getPosition();
-            glm::vec3 offset = position - _view.getPosition();
+            glm::vec3 offset = position - view.getPosition();
             float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
             const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance)
             float radius = glm::min(thing.getRadius(), MIN_RADIUS);
-            float cosineAngle = (glm::dot(offset, _view.getDirection()) / distance);
+            float cosineAngle = (glm::dot(offset, view.getDirection()) / distance);
             float age = (float)(usecTimestampNow() - thing.getTimestamp());
 
             // we modulatate "age" drift rate by the cosineAngle term to make periphrial objects sort forward
@@ -134,8 +143,8 @@ namespace PrioritySortUtil {
                 + _ageWeight * cosineAngleFactor * age;
 
             // decrement priority of things outside keyhole
-            if (distance - radius > _view.getCenterRadius()) {
-                if (!_view.sphereIntersectsFrustum(position, radius)) {
+            if (distance - radius > view.getRadius()) {
+                if (!view.intersects(offset, distance, radius)) {
                     constexpr float OUT_OF_VIEW_PENALTY = -10.0f;
                     priority += OUT_OF_VIEW_PENALTY;
                 }
@@ -143,7 +152,7 @@ namespace PrioritySortUtil {
             return priority;
         }
 
-        ViewFrustum _view;
+        ConicalViewFrustums _views;
         std::priority_queue<T> _queue;
         float _angularWeight { DEFAULT_ANGULAR_COEF };
         float _centerWeight { DEFAULT_CENTER_COEF };
diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h
index 689d1a3f42..467d6374a5 100644
--- a/libraries/shared/src/RegisteredMetaTypes.h
+++ b/libraries/shared/src/RegisteredMetaTypes.h
@@ -361,6 +361,12 @@ using MeshPointer = std::shared_ptr<graphics::Mesh>;
 /**jsdoc
  * A handle for a mesh in an entity, such as returned by {@link Entities.getMeshes}.
  * @class MeshProxy
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
  * @deprecated Use the {@link Graphics} API instead.
  */
 class MeshProxy : public QObject {
diff --git a/libraries/shared/src/SimpleMovingAverage.cpp b/libraries/shared/src/SimpleMovingAverage.cpp
index f75180afb5..9bcc6b732f 100644
--- a/libraries/shared/src/SimpleMovingAverage.cpp
+++ b/libraries/shared/src/SimpleMovingAverage.cpp
@@ -9,9 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "SharedUtil.h"
 #include "SimpleMovingAverage.h"
 
+#include "SharedUtil.h"
+
 SimpleMovingAverage::SimpleMovingAverage(int numSamplesToAverage) :
     _numSamples(0),
     _lastEventTimestamp(0),
diff --git a/libraries/shared/src/StDev.cpp b/libraries/shared/src/StDev.cpp
index 23afd12b98..99280ba42e 100644
--- a/libraries/shared/src/StDev.cpp
+++ b/libraries/shared/src/StDev.cpp
@@ -9,12 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "StDev.h"
+
 #include <limits>   
 #include <cmath>
 #include <cstring>
 
-#include "StDev.h"
-
 StDev::StDev() :
     _sampleCount(0)
 {
diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp
index 876de2e698..9ed0e24593 100644
--- a/libraries/shared/src/StreamUtils.cpp
+++ b/libraries/shared/src/StreamUtils.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QDataStream>
+#include "StreamUtils.h"
 
 #include <glm/gtc/type_ptr.hpp>
 
-#include "StreamUtils.h"
+#include <QDataStream>
 
 
 void StreamUtil::dump(std::ostream& s, const QByteArray& buffer) {
diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp
index 3f8f748720..d7f685f8d3 100644
--- a/libraries/shared/src/TriangleSet.cpp
+++ b/libraries/shared/src/TriangleSet.cpp
@@ -9,9 +9,9 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include "GLMHelpers.h"
 #include "TriangleSet.h"
 
+#include "GLMHelpers.h"
 
 void TriangleSet::insert(const Triangle& t) {
     _isBalanced = false;
diff --git a/libraries/shared/src/VariantMapToScriptValue.cpp b/libraries/shared/src/VariantMapToScriptValue.cpp
index 00fc2cd682..008c3a5d9b 100644
--- a/libraries/shared/src/VariantMapToScriptValue.cpp
+++ b/libraries/shared/src/VariantMapToScriptValue.cpp
@@ -9,10 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QDebug>
-#include "SharedLogging.h"
 #include "VariantMapToScriptValue.h"
 
+#include <QDebug>
+
+#include "SharedLogging.h"
 
 QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine) {
     switch(qValue.type()) {
diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp
index 2a2eebc0a7..3e03c13fa4 100644
--- a/libraries/shared/src/ViewFrustum.cpp
+++ b/libraries/shared/src/ViewFrustum.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ViewFrustum.h"
+
 #include <algorithm>
 #include <array>
 
@@ -16,15 +18,13 @@
 #include <glm/gtx/quaternion.hpp>
 #include <glm/gtx/transform.hpp>
 #include <glm/gtx/vector_angle.hpp>
-#include <QtCore/QDebug>
 
+#include <QtCore/QDebug>
 
 #include "GeometryUtil.h"
 #include "GLMHelpers.h"
 #include "NumericalConstants.h"
 #include "SharedLogging.h"
-//#include "OctreeConstants.h"
-#include "ViewFrustum.h"
 
 using namespace std;
 
@@ -75,6 +75,10 @@ void ViewFrustum::setProjection(const glm::mat4& projection) {
     _width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x;
 }
 
+void ViewFrustum::setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip) {
+    setProjection(glm::perspective(glm::radians(cameraFov), cameraAspectRatio, cameraNearClip, cameraFarClip));
+}
+
 // ViewFrustum::calculate()
 //
 // Description: this will calculate the view frustum bounds for a given position and direction
@@ -134,71 +138,6 @@ const char* ViewFrustum::debugPlaneName (int plane) const {
     return "Unknown";
 }
 
-void ViewFrustum::fromByteArray(const QByteArray& input) {
-
-    // From the wire!
-    glm::vec3 cameraPosition;
-    glm::quat cameraOrientation;
-    float cameraCenterRadius;
-    float cameraFov;
-    float cameraAspectRatio;
-    float cameraNearClip;
-    float cameraFarClip;
-
-    const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(input.constData());
-    const unsigned char* sourceBuffer = startPosition;
-
-    // camera details
-    memcpy(&cameraPosition, sourceBuffer, sizeof(cameraPosition));
-    sourceBuffer += sizeof(cameraPosition);
-    sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, cameraOrientation);
-    sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &cameraFov);
-    sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, cameraAspectRatio);
-    sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, cameraNearClip);
-    sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, cameraFarClip);
-    memcpy(&cameraCenterRadius, sourceBuffer, sizeof(cameraCenterRadius));
-    sourceBuffer += sizeof(cameraCenterRadius);
-
-    setPosition(cameraPosition);
-    setOrientation(cameraOrientation);
-    setCenterRadius(cameraCenterRadius);
-
-    // Also make sure it's got the correct lens details from the camera
-    if (0.0f != cameraAspectRatio &&
-        0.0f != cameraNearClip &&
-        0.0f != cameraFarClip &&
-        cameraNearClip != cameraFarClip) {
-        setProjection(glm::perspective(
-            glm::radians(cameraFov),
-            cameraAspectRatio,
-            cameraNearClip,
-            cameraFarClip));
-
-        calculate();
-    }
-}
-
-
-QByteArray ViewFrustum::toByteArray() {
-    static const int LARGE_ENOUGH = 1024;
-    QByteArray viewFrustumDataByteArray(LARGE_ENOUGH, 0);
-    unsigned char* destinationBuffer = reinterpret_cast<unsigned char*>(viewFrustumDataByteArray.data());
-    unsigned char* startPosition = destinationBuffer;
-
-    // camera details
-    memcpy(destinationBuffer, &_position, sizeof(_position));
-    destinationBuffer += sizeof(_position);
-    destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _orientation);
-    destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _fieldOfView);
-    destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _aspectRatio);
-    destinationBuffer += packClipValueToTwoByte(destinationBuffer, _nearClip);
-    destinationBuffer += packClipValueToTwoByte(destinationBuffer, _farClip);
-    memcpy(destinationBuffer, &_centerSphereRadius, sizeof(_centerSphereRadius));
-    destinationBuffer += sizeof(_centerSphereRadius);
-
-    return viewFrustumDataByteArray.left(destinationBuffer - startPosition);
-}
-
 ViewFrustum::intersection ViewFrustum::calculateCubeFrustumIntersection(const AACube& cube) const {
     // only check against frustum
     ViewFrustum::intersection result = INSIDE;
@@ -336,13 +275,6 @@ bool ViewFrustum::boxIntersectsKeyhole(const AABox& box) const {
     return true;
 }
 
-bool closeEnough(float a, float b, float relativeError) {
-    assert(relativeError >= 0.0f);
-    // NOTE: we add EPSILON to the denominator so we can avoid checking for division by zero.
-    // This method works fine when: fabsf(a + b) >> EPSILON
-    return fabsf(a - b) / (0.5f * fabsf(a + b) + EPSILON) < relativeError;
-}
-
 // TODO: the slop and relative error should be passed in by argument rather than hard-coded.
 bool ViewFrustum::isVerySimilar(const ViewFrustum& other) const {
     const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared
diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h
index 981aabe70c..eada65468d 100644
--- a/libraries/shared/src/ViewFrustum.h
+++ b/libraries/shared/src/ViewFrustum.h
@@ -48,6 +48,7 @@ public:
 
     // setters for lens attributes
     void setProjection(const glm::mat4 & projection);
+    void setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip);
     void setFocalLength(float focalLength) { _focalLength = focalLength; }
     bool isPerspective() const;
 
@@ -146,9 +147,6 @@ public:
 
     void invalidate(); // causes all reasonable intersection tests to fail
 
-    QByteArray toByteArray();
-    void fromByteArray(const QByteArray& input);
-
 private:
     glm::mat4 _view;
     glm::mat4 _projection;
@@ -188,5 +186,6 @@ private:
 
 };
 using ViewFrustumPointer = std::shared_ptr<ViewFrustum>;
+using ViewFrustums = std::vector<ViewFrustum>;
 
 #endif // hifi_ViewFrustum_h
diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h
index ea2e9cddab..32e753d0f9 100644
--- a/libraries/shared/src/shared/Camera.h
+++ b/libraries/shared/src/shared/Camera.h
@@ -40,6 +40,10 @@ class Camera : public QObject {
      * The Camera API provides access to the "camera" that defines your view in desktop and HMD display modes.
      *
      * @namespace Camera
+     *
+     * @hifi-interface
+     * @hifi-client-entity
+     *
      * @property position {Vec3} The position of the camera. You can set this value only when the camera is in independent mode.
      * @property orientation {Quat} The orientation of the camera. You can set this value only when the camera is in independent
      *     mode.
diff --git a/libraries/shared/src/shared/ConicalViewFrustum.cpp b/libraries/shared/src/shared/ConicalViewFrustum.cpp
new file mode 100644
index 0000000000..2ef096e3a8
--- /dev/null
+++ b/libraries/shared/src/shared/ConicalViewFrustum.cpp
@@ -0,0 +1,146 @@
+//
+//  ConicalViewFrustum.cpp
+//  libraries/shared/src/shared
+//
+//  Created by Clement Brisset 4/26/18
+//  Copyright 2017 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 "ConicalViewFrustum.h"
+
+
+#include "../NumericalConstants.h"
+#include "../ViewFrustum.h"
+
+void ConicalViewFrustum::set(const ViewFrustum& viewFrustum) {
+    // The ConicalViewFrustum has two parts: a central sphere (same as ViewFrustum) and a circular cone that bounds the frustum part.
+    // Why?  Because approximate intersection tests are much faster to compute for a cone than for a frustum.
+    _position = viewFrustum.getPosition();
+    _radius = viewFrustum.getCenterRadius();
+    _farClip = viewFrustum.getFarClip();
+
+    auto topLeft = viewFrustum.getNearTopLeft() - _position;
+    auto topRight = viewFrustum.getNearTopRight() - _position;
+    auto bottomLeft = viewFrustum.getNearBottomLeft() - _position;
+    auto bottomRight = viewFrustum.getNearBottomRight() - _position;
+    auto centerAxis = 0.25f * (topLeft + topRight + bottomLeft + bottomRight); // Take the average
+
+    _direction = glm::normalize(centerAxis);
+    _angle = std::max(std::max(angleBetween(_direction, topLeft),
+                               angleBetween(_direction, topRight)),
+                      std::max(angleBetween(_direction, bottomLeft),
+                               angleBetween(_direction, bottomRight)));
+}
+
+void ConicalViewFrustum::calculate() {
+    // Pre-compute cos and sin for faster checks
+    _cosAngle = cosf(_angle);
+    _sinAngle = sqrtf(1.0f - _cosAngle * _cosAngle);
+}
+
+bool ConicalViewFrustum::isVerySimilar(const ConicalViewFrustum& other) const {
+    const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared
+    const float MIN_ANGLE_BETWEEN = 0.174533f; // radian angle between 2 vectors 10 degrees apart
+    const float MIN_RELATIVE_ERROR = 0.01f; // 1%
+
+    return glm::distance2(_position, other._position) < MIN_POSITION_SLOP_SQUARED &&
+            angleBetween(_direction, other._direction) < MIN_ANGLE_BETWEEN &&
+            closeEnough(_angle, other._angle, MIN_RELATIVE_ERROR) &&
+            closeEnough(_farClip, other._farClip, MIN_RELATIVE_ERROR) &&
+            closeEnough(_radius, other._radius, MIN_RELATIVE_ERROR);
+}
+
+bool ConicalViewFrustum::intersects(const AACube& cube) const {
+    auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
+    auto position = cube.calcCenter() - _position; // position of bounding sphere in view-frame
+    float distance = glm::length(position);
+
+    return intersects(position, distance, radius);
+}
+
+bool ConicalViewFrustum::intersects(const AABox& box) const {
+    auto radius = 0.5f * glm::length(box.getScale()); // radius of bounding sphere
+    auto position = box.calcCenter() - _position; // position of bounding sphere in view-frame
+    float distance = glm::length(position);
+
+    return intersects(position, distance, radius);
+}
+
+bool ConicalViewFrustum::getAngularSize(const AACube& cube) const {
+    auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
+    auto position = cube.calcCenter() - _position; // position of bounding sphere in view-frame
+    float distance = glm::length(position);
+
+    return getAngularSize(distance, radius);
+}
+
+bool ConicalViewFrustum::getAngularSize(const AABox& box) const {
+    auto radius = 0.5f * glm::length(box.getScale()); // radius of bounding sphere
+    auto position = box.calcCenter() - _position; // position of bounding sphere in view-frame
+    float distance = glm::length(position);
+
+    return getAngularSize(distance, radius);
+}
+
+
+bool ConicalViewFrustum::intersects(const glm::vec3& relativePosition, float distance, float radius) const {
+    if (distance < _radius + radius) {
+        // Inside keyhole radius
+        return true;
+    }
+    if (distance > _farClip + radius) {
+        // Past far clip
+        return false;
+    }
+
+    // We check the angle between the center of the cube and the _direction of the view.
+    // If it is less than the sum of the half-angle from center of cone to outer edge plus
+    // the half apparent angle of the bounding sphere then it is in view.
+    //
+    // The math here is left as an exercise for the reader with the following hints:
+    // (1) We actually check the dot product of the cube's local position rather than the angle and
+    // (2) we take advantage of this trig identity: cos(A+B) = cos(A)*cos(B) - sin(A)*sin(B)
+    return glm::dot(relativePosition, _direction) >
+           sqrtf(distance * distance - radius * radius) * _cosAngle - radius * _sinAngle;
+}
+
+bool ConicalViewFrustum::getAngularSize(float distance, float radius) const {
+    const float AVOID_DIVIDE_BY_ZERO = 0.001f;
+    float angularSize = radius / (distance + AVOID_DIVIDE_BY_ZERO);
+    return angularSize;
+}
+
+int ConicalViewFrustum::serialize(unsigned char* destinationBuffer) const {
+    const unsigned char* startPosition = destinationBuffer;
+
+    memcpy(destinationBuffer, &_position, sizeof(_position));
+    destinationBuffer += sizeof(_position);
+    memcpy(destinationBuffer, &_direction, sizeof(_direction));
+    destinationBuffer += sizeof(_direction);
+    destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _angle);
+    destinationBuffer += packClipValueToTwoByte(destinationBuffer, _farClip);
+    memcpy(destinationBuffer, &_radius, sizeof(_radius));
+    destinationBuffer += sizeof(_radius);
+
+    return destinationBuffer - startPosition;
+}
+
+int ConicalViewFrustum::deserialize(const unsigned char* sourceBuffer) {
+    const unsigned char* startPosition = sourceBuffer;
+
+    memcpy(&_position, sourceBuffer, sizeof(_position));
+    sourceBuffer += sizeof(_position);
+    memcpy(&_direction, sourceBuffer, sizeof(_direction));
+    sourceBuffer += sizeof(_direction);
+    sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &_angle);
+    sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, _farClip);
+    memcpy(&_radius, sourceBuffer, sizeof(_radius));
+    sourceBuffer += sizeof(_radius);
+
+    calculate();
+
+    return sourceBuffer - startPosition;
+}
diff --git a/libraries/shared/src/shared/ConicalViewFrustum.h b/libraries/shared/src/shared/ConicalViewFrustum.h
new file mode 100644
index 0000000000..dc372d560e
--- /dev/null
+++ b/libraries/shared/src/shared/ConicalViewFrustum.h
@@ -0,0 +1,70 @@
+//
+//  ConicalViewFrustum.h
+//  libraries/shared/src/shared
+//
+//  Created by Clement Brisset 4/26/18
+//  Copyright 2017 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_ConicalViewFrustum_h
+#define hifi_ConicalViewFrustum_h
+
+#include <vector>
+
+#include <glm/glm.hpp>
+
+class AACube;
+class AABox;
+class ViewFrustum;
+using ViewFrustums = std::vector<ViewFrustum>;
+
+const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
+const float DEFAULT_VIEW_ANGLE = 1.0f;
+const float DEFAULT_VIEW_RADIUS = 10.0f;
+const float DEFAULT_VIEW_FAR_CLIP = 100.0f;
+
+// ConicalViewFrustum is an approximation of a ViewFrustum for fast calculation of sort priority.
+class ConicalViewFrustum {
+public:
+    ConicalViewFrustum() = default;
+    ConicalViewFrustum(const ViewFrustum& viewFrustum) { set(viewFrustum); }
+
+    void set(const ViewFrustum& viewFrustum);
+    void calculate();
+
+    const glm::vec3& getPosition() const { return _position; }
+    const glm::vec3& getDirection() const { return _direction; }
+    float getAngle() const { return _angle; }
+    float getRadius() const { return _radius; }
+    float getFarClip() const { return _farClip; }
+
+    bool isVerySimilar(const ConicalViewFrustum& other) const;
+
+    bool intersects(const AACube& cube) const;
+    bool intersects(const AABox& box) const;
+    bool getAngularSize(const AACube& cube) const;
+    bool getAngularSize(const AABox& box) const;
+
+    bool intersects(const glm::vec3& relativePosition, float distance, float radius) const;
+    bool getAngularSize(float distance, float radius) const;
+
+    int serialize(unsigned char* destinationBuffer) const;
+    int deserialize(const unsigned char* sourceBuffer);
+
+private:
+    glm::vec3 _position { 0.0f, 0.0f, 0.0f };
+    glm::vec3 _direction { 0.0f, 0.0f, 1.0f };
+    float _angle { DEFAULT_VIEW_ANGLE };
+    float _radius { DEFAULT_VIEW_RADIUS };
+    float _farClip { DEFAULT_VIEW_FAR_CLIP };
+
+    float _sinAngle { SQRT_TWO_OVER_TWO };
+    float _cosAngle { SQRT_TWO_OVER_TWO };
+};
+using ConicalViewFrustums = std::vector<ConicalViewFrustum>;
+
+
+#endif /* hifi_ConicalViewFrustum_h */
diff --git a/libraries/shared/src/shared/FileLogger.cpp b/libraries/shared/src/shared/FileLogger.cpp
index 8ceb378574..6a10629ee5 100644
--- a/libraries/shared/src/shared/FileLogger.cpp
+++ b/libraries/shared/src/shared/FileLogger.cpp
@@ -36,17 +36,14 @@ protected:
 private:
     const FileLogger& _logger;
     QMutex _fileMutex;
-    uint64_t _lastRollTime;
 };
 
-
-
 static const QString FILENAME_FORMAT = "hifi-log_%1%2.txt";
 static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss";
 static const QString LOGS_DIRECTORY = "Logs";
-static const QString IPADDR_WILDCARD = "[0-9]*.[0-9]*.[0-9]*.[0-9]*";
-static const QString DATETIME_WILDCARD = "20[0-9][0-9]-[0,1][0-9]-[0-3][0-9]_[0-2][0-9].[0-6][0-9].[0-6][0-9]";
-static const QString FILENAME_WILDCARD = "hifi-log_" + IPADDR_WILDCARD + "_" + DATETIME_WILDCARD + ".txt";
+static const QString DATETIME_WILDCARD = "20[0-9][0-9]-[01][0-9]-[0-3][0-9]_[0-2][0-9]\\.[0-6][0-9]\\.[0-6][0-9]";
+static const QString SESSION_WILDCARD = "[0-9a-z]{8}(-[0-9a-z]{4}){3}-[0-9a-z]{12}";
+static QRegExp LOG_FILENAME_REGEX { "hifi-log_" + DATETIME_WILDCARD + "(_" + SESSION_WILDCARD + ")?\\.txt" };
 static QUuid SESSION_ID;
 
 // Max log size is 512 KB. We send log files to our crash reporter, so we want to keep this relatively
@@ -54,8 +51,6 @@ static QUuid SESSION_ID;
 static const qint64 MAX_LOG_SIZE = 512 * 1024;
 // Max log files found in the log directory is 100.
 static const qint64 MAX_LOG_DIR_SIZE = 512 * 1024 * 100;
-// Max log age is 1 hour
-static const uint64_t MAX_LOG_AGE_USECS = USECS_PER_SECOND * 3600;
 
 static FilePersistThread* _persistThreadInstance;
 
@@ -86,38 +81,34 @@ FilePersistThread::FilePersistThread(const FileLogger& logger) : _logger(logger)
     if (file.exists()) {
         rollFileIfNecessary(file, false);
     }
-    _lastRollTime = usecTimestampNow();
 }
 
 void FilePersistThread::rollFileIfNecessary(QFile& file, bool notifyListenersIfRolled) {
-    uint64_t now = usecTimestampNow();
-    if ((file.size() > MAX_LOG_SIZE) || (now - _lastRollTime) > MAX_LOG_AGE_USECS) {
+    if (file.size() > MAX_LOG_SIZE) {
         QString newFileName = getLogRollerFilename();
         if (file.copy(newFileName)) {
             file.open(QIODevice::WriteOnly | QIODevice::Truncate);
             file.close();
-            qCDebug(shared) << "Rolled log file:" << newFileName;
 
             if (notifyListenersIfRolled) {
                 emit rollingLogFile(newFileName);
             }
-
-            _lastRollTime = now;
         }
-        QStringList nameFilters;
-        nameFilters << FILENAME_WILDCARD;
 
-        QDir logQDir(FileUtils::standardPath(LOGS_DIRECTORY));
-        logQDir.setNameFilters(nameFilters);
-        logQDir.setSorting(QDir::Time);
-        QFileInfoList filesInDir = logQDir.entryInfoList();
+        QDir logDir(FileUtils::standardPath(LOGS_DIRECTORY));
+        logDir.setSorting(QDir::Time);
+        logDir.setFilter(QDir::Files);
         qint64 totalSizeOfDir = 0;
-        foreach(QFileInfo dirItm, filesInDir){
-            if (totalSizeOfDir < MAX_LOG_DIR_SIZE){
-                totalSizeOfDir += dirItm.size();
-            } else {
-                QFile file(dirItm.filePath());
-                file.remove();
+        QFileInfoList filesInDir = logDir.entryInfoList();
+        for (auto& fileInfo : filesInDir) {
+            if (!LOG_FILENAME_REGEX.exactMatch(fileInfo.fileName())) {
+                continue;
+            }
+            totalSizeOfDir += fileInfo.size();
+            if (totalSizeOfDir > MAX_LOG_DIR_SIZE){
+                qDebug() << "Removing log file: " << fileInfo.fileName();
+                QFile oldLogFile(fileInfo.filePath());
+                oldLogFile.remove();
             }
         }
     }
@@ -129,7 +120,7 @@ bool FilePersistThread::processQueueItems(const Queue& messages) {
     rollFileIfNecessary(file);
     if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
         QTextStream out(&file);
-        foreach(const QString& message, messages) {
+        for (const QString& message : messages) {
             out << message;
         }
     }
diff --git a/libraries/shared/src/shared/StringHelpers.cpp b/libraries/shared/src/shared/StringHelpers.cpp
index 1c1730bd5a..39ac23e510 100644
--- a/libraries/shared/src/shared/StringHelpers.cpp
+++ b/libraries/shared/src/shared/StringHelpers.cpp
@@ -6,10 +6,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <QStringList>
-
 #include "StringHelpers.h"
 
+#include <QStringList>
+
 /// Note: this will not preserve line breaks in the original input.
 QString simpleWordWrap(const QString& input, int maxCharactersPerLine) {
     QStringList words = input.split(QRegExp("\\s+"));
diff --git a/libraries/test-utils/CMakeLists.txt b/libraries/test-utils/CMakeLists.txt
new file mode 100644
index 0000000000..2c23e96c1e
--- /dev/null
+++ b/libraries/test-utils/CMakeLists.txt
@@ -0,0 +1,3 @@
+set(TARGET_NAME test-utils)
+setup_hifi_library(Network Gui)
+
diff --git a/libraries/test-utils/src/test-utils/FileDownloader.cpp b/libraries/test-utils/src/test-utils/FileDownloader.cpp
new file mode 100644
index 0000000000..09049e3e0c
--- /dev/null
+++ b/libraries/test-utils/src/test-utils/FileDownloader.cpp
@@ -0,0 +1,21 @@
+#include "FileDownloader.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtNetwork/QNetworkReply>
+
+FileDownloader::FileDownloader(QUrl url, const Handler& handler, QObject* parent) : QObject(parent), _handler(handler) {
+    connect(&_accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*)));
+    _accessManager.get(QNetworkRequest(url));
+}
+
+void FileDownloader::waitForDownload() {
+    while (!_complete) {
+        QCoreApplication::processEvents();
+    }
+}
+
+void FileDownloader::fileDownloaded(QNetworkReply* pReply) {
+    _handler(pReply->readAll());
+    pReply->deleteLater();
+    _complete = true;
+}
diff --git a/libraries/test-utils/src/test-utils/FileDownloader.h b/libraries/test-utils/src/test-utils/FileDownloader.h
new file mode 100644
index 0000000000..5a618fcf45
--- /dev/null
+++ b/libraries/test-utils/src/test-utils/FileDownloader.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <QtCore/QObject>
+#include <QtNetwork/QNetworkAccessManager>
+
+class FileDownloader : public QObject {
+    Q_OBJECT
+
+public:
+    using Handler = std::function<void(const QByteArray& data)>;
+
+    FileDownloader(QUrl url, const Handler& handler, QObject* parent = 0);
+
+    void waitForDownload();
+
+private slots:
+    void fileDownloaded(QNetworkReply* pReply);
+
+private:
+    QNetworkAccessManager _accessManager;
+    Handler _handler;
+    bool _complete { false };
+};
diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h
index da9ea46cf4..5df20ff30c 100644
--- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h
+++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h
@@ -54,6 +54,12 @@ public:
     void setFullscreen(const QScreen* targetScreen, bool hideMenu = false);
     void unsetFullscreen(const QScreen* avoidScreen = nullptr);
 
+    // FIXME remove access tot he menu from the plugin container
+    // Instead let display plugins expose a structure about the kinds
+    // of actions and menu items they want to have appear when they are
+    // active and allow the application to act on that when the display
+    // plugin becomes active (or when the UI is initialized, and a 
+    // display plugin is already active)
     virtual ui::Menu* getPrimaryMenu() = 0;
     virtual void showDisplayPluginsTools(bool show = true) = 0;
     virtual void requestReset() = 0;
diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h
index bab15fc7b6..e74b846f02 100644
--- a/libraries/ui/src/ui/TabletScriptingInterface.h
+++ b/libraries/ui/src/ui/TabletScriptingInterface.h
@@ -40,6 +40,9 @@ class OffscreenQmlSurface;
 
 /**jsdoc
  * @namespace Tablet
+ *
+ * @hifi-interface
+ * @hifi-client-entity
  */
 class TabletScriptingInterface : public QObject, public Dependency {
     Q_OBJECT
@@ -176,6 +179,10 @@ Q_DECLARE_METATYPE(TabletButtonsProxyModel*);
 
 /**jsdoc
  * @class TabletProxy
+  *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  * @property {string} name - Name of this tablet. <em>Read-only.</em>
  * @property {boolean} toolbarMode - Used to transition this tablet into and out of toolbar mode.
  *     When tablet is in toolbar mode, all its buttons will appear in a floating toolbar.
@@ -410,6 +417,10 @@ Q_DECLARE_METATYPE(TabletProxy*);
 
 /**jsdoc
  * @class TabletButtonProxy
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
  * @property {Uuid} uuid - Uniquely identifies this button. <em>Read-only.</em>
  * @property {TabletButtonProxy.ButtonProperties} properties
  */
diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp
index d8f2db75c9..7e337070d3 100644
--- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp
+++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp
@@ -166,12 +166,11 @@ void OculusBaseDisplayPlugin::deactivateSession() {
     //_session = nullptr;
 }
 void OculusBaseDisplayPlugin::updatePresentPose() {
-    //mat4 sensorResetMat;
-    //_currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
-    //_currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, _currentFrame->frameIndex);
-    //auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse);
-    //_currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose);
-    _currentPresentFrameInfo.presentPose = _currentPresentFrameInfo.renderPose;
+    _currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
+    _currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, 0);
+    auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse);
+    _currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose);
+    _currentPresentFrameInfo.renderPose = _currentPresentFrameInfo.presentPose;
 }
 
 OculusBaseDisplayPlugin::~OculusBaseDisplayPlugin() {
diff --git a/plugins/pcmCodec/src/PCMCodecManager.cpp b/plugins/pcmCodec/src/PCMCodecManager.cpp
index 051f3973a8..04adb367af 100644
--- a/plugins/pcmCodec/src/PCMCodecManager.cpp
+++ b/plugins/pcmCodec/src/PCMCodecManager.cpp
@@ -9,12 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "PCMCodecManager.h"
+
 #include <QtCore/QCoreApplication>
 
 #include <PerfStat.h>
 
-#include "PCMCodecManager.h"
-
 const char* PCMCodec::NAME { "pcm" };
 
 void PCMCodec::init() {
diff --git a/scripts/system/+android/radar.js b/scripts/system/+android/radar.js
index 455299dd5f..5d93ed4db1 100644
--- a/scripts/system/+android/radar.js
+++ b/scripts/system/+android/radar.js
@@ -21,7 +21,8 @@ function printd(str) {
 }
 
 var radar = false;
-var radarHeight = 10; // camera position meters above the avatar
+var RADAR_HEIGHT_INIT_DELTA = 10;
+var radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA; // camera position (absolute y)
 var tablet;
 
 var RADAR_CAMERA_OFFSET = -1; // 1 meter below the avatar
@@ -45,12 +46,12 @@ var uniqueColor;
 
 function moveTo(position) {
     if (radar) {
-        MyAvatar.position = position;
-        Camera.position = Vec3.sum(MyAvatar.position, {
-            x : 0,
+        MyAvatar.goToLocation(position, false);
+        Camera.position = {
+            x : position.x,
             y : radarHeight,
-            z : 0
-        });
+            z : position.z
+        };
     }
 }
 
@@ -89,46 +90,6 @@ function keyPressEvent(event) {
     }
 }
 
-function actionOnObjectFromEvent(event) {
-    var rayIntersection = findRayIntersection(Camera.computePickRay(event.x,
-            event.y));
-    if (rayIntersection && rayIntersection.intersects
-            && rayIntersection.overlayID) {
-        printd("found overlayID touched " + rayIntersection.overlayID);
-        if (entitiesByOverlayID[rayIntersection.overlayID]) {
-            var entity = Entities.getEntityProperties(
-                    entitiesByOverlayID[rayIntersection.overlayID],
-                    [ "sourceUrl" ]);
-            App.openUrl(entity.sourceUrl);
-            return true;
-        }
-    }
-    if (rayIntersection && rayIntersection.intersects
-            && rayIntersection.entityID && rayIntersection.properties) {
-        printd("found " + rayIntersection.entityID + " of type "
-                + rayIntersection.properties.type);
-        if (rayIntersection.properties.type == "Web") {
-            printd("found web element to "
-                    + rayIntersection.properties.sourceUrl);
-            App.openUrl(rayIntersection.properties.sourceUrl);
-            return true;
-        }
-    }
-    return false;
-}
-
-function mousePress(event) {
-    mousePressOrTouchEnd(event);
-}
-
-function mousePressOrTouchEnd(event) {
-    if (radar) {
-        if (actionOnObjectFromEvent(event)) {
-            return;
-        }
-    }
-}
-
 function toggleRadarMode() {
     if (radar) {
         endRadar();
@@ -229,9 +190,6 @@ function touchEnd(event) {
     if (analyzeDoubleTap(event))
         return; // double tap detected, finish
 
-    if (radar) {
-        mousePressOrTouchEnd(event);
-    }
 }
 
 /**
@@ -386,12 +344,13 @@ function pinchUpdate(event) {
             radarHeight -= pinchIncrement;
         }
     }
-    var deltaHeight = avatarY + radarHeight - Camera.position.y;
-    Camera.position = Vec3.sum(Camera.position, {
-        x : 0,
-        y : deltaHeight,
-        z : 0
-    });
+
+    Camera.position = { 
+        x : Camera.position.x, 
+        y : radarHeight, 
+        z : Camera.position.z 
+    };
+
     if (!draggingCamera) {
         startedDraggingCamera = true;
         draggingCamera = true;
@@ -401,7 +360,8 @@ function pinchUpdate(event) {
 }
 
 function isInsideSquare(coords0, coords1, halfside) {
-    return Math.abs(coords0.x - coords1.x) <= halfside
+    return coords0 != undefined && coords1 != undefined &&
+            Math.abs(coords0.x - coords1.x) <= halfside
             && Math.abs(coords0.y - coords1.y) <= halfside;
 }
 
@@ -412,7 +372,7 @@ function dragScrollUpdate(event) {
     // drag management
     var pickRay = Camera.computePickRay(event.x, event.y);
     var dragAt = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction,
-            radarHeight));
+            radarHeight - MyAvatar.position.y));
 
     if (lastDragAt === undefined || lastDragAt === null) {
         lastDragAt = dragAt;
@@ -654,6 +614,7 @@ function Teleporter() {
             return;
         }
 
+
         Camera.position = Vec3.sum(Camera.position, {
             x : xDelta,
             y : 0,
@@ -722,7 +683,7 @@ function Teleporter() {
     return {
         dragTeleportBegin : function(event) {
             printd("[newTeleport] TELEPORT began");
-            var overlayDimensions = entityIconModelDimensions();
+            var overlayDimensions = teleportIconModelDimensions(MyAvatar.position.y);
             // var destination = computeDestination(event, MyAvatar.position,
             // Camera.position, radarHeight);
             // Dimension teleport and cancel overlays (not show them yet)
@@ -843,7 +804,7 @@ var avatarIconDimensionsVal = {
 };
 function avatarIconPlaneDimensions() {
     // given the current height, give a size
-    var xy = -0.003531 * radarHeight + 0.1;
+    var xy = -0.003531 * (radarHeight - MyAvatar.position.y) + 0.1;
     avatarIconDimensionsVal.x = Math.abs(xy);
     avatarIconDimensionsVal.y = Math.abs(xy);
     // reuse object
@@ -1121,172 +1082,20 @@ function renderAllOthersAvatarIcons() {
     }
 }
 
-function entityAdded(entityID) {
-    printd("Entity added " + entityID);
-    var props = Entities.getEntityProperties(entityID, [ "type" ]);
-    printd("Entity added " + entityID + " PROPS " + JSON.stringify(props));
-    if (props && props.type == "Web") {
-        printd("Entity Web added " + entityID);
-        saveEntityData(entityID, true);
-    }
-}
-
-function entityRemoved(entityID) {
-    printd("Entity removed " + entityID);
-    var props = Entities.getEntityProperties(entityID, [ "type" ]);
-    if (props && props.type == "Web") {
-        print("Entity Web removed " + entityID);
-        removeEntityData(entityID);
-    }
-}
-
-/*******************************************************************************
- * Entities (to remark) cache structure for showing entities markers
- ******************************************************************************/
-
-var entitiesData = {}; // by entityID
-var entitiesByOverlayID = {}; // by overlayID
-var entitiesIcons = []; // a parallel list of icons (overlays) to easily run
-                        // through
-
-var ICON_ENTITY_WEB_MODEL_URL = Script.resolvePath("../assets/images/web.svg");
-var ICON_ENTITY_IMG_MODEL_URL = Script
-        .resolvePath("../assets/models/teleport-cancel.fbx"); // FIXME - use
-                                                                // correct
-                                                                // model&texture
 var ICON_ENTITY_DEFAULT_DIMENSIONS = {
     x : 0.10,
     y : 0.00001,
     z : 0.10
 };
 
-var entityIconModelDimensionsVal = {
-    x : 0,
-    y : 0.00001,
-    z : 0
-};
-function entityIconModelDimensions() {
-    // given the current height, give a size
-    var xz = -0.002831 * radarHeight + 0.1;
-    entityIconModelDimensionsVal.x = xz;
-    entityIconModelDimensionsVal.z = xz;
+
+function teleportIconModelDimensions(y) {
+    var teleportModelDimensions = ICON_ENTITY_DEFAULT_DIMENSIONS;
+    var xz = -0.002831 * (radarHeight - y) + 0.1;
+    teleportModelDimensions.x = xz;
+    teleportModelDimensions.z = xz;
     // reuse object
-    return entityIconModelDimensionsVal;
-}
-/*
- * entityIconPlaneDimensions: similar to entityIconModelDimensions but using xy
- * plane
- */
-function entityIconPlaneDimensions() {
-    var dim = entityIconModelDimensions();
-    var z = dim.z;
-    dim.z = dim.y;
-    dim.y = z;
-    return dim;
-}
-
-function currentOverlayForEntity(QUuid) {
-    if (entitiesData[QUuid] != undefined) {
-        return entitiesData[QUuid].icon;
-    } else {
-        return null;
-    }
-}
-
-function saveEntityData(QUuid, planar) {
-    if (QUuid == null)
-        return;
-    var entity = Entities.getEntityProperties(QUuid, [ "position" ]);
-    printd("entity added save entity " + QUuid);
-    if (entitiesData[QUuid] != undefined) {
-        entitiesData[QUuid].position = entity.position;
-    } else {
-        var entityIcon = Overlays.addOverlay("image3d", {
-            subImage : {
-                x : 0,
-                y : 0,
-                width : 150,
-                height : 150
-            },
-            url : ICON_ENTITY_WEB_MODEL_URL,
-            dimensions : ICON_ENTITY_DEFAULT_DIMENSIONS,
-            visible : false,
-            ignoreRayIntersection : false,
-            orientation : Quat.fromPitchYawRollDegrees(-90, 0, 0)
-        });
-
-        entitiesIcons.push(entityIcon);
-        entitiesData[QUuid] = {
-            position : entity.position,
-            icon : entityIcon
-        };
-        entitiesByOverlayID[entityIcon] = QUuid;
-    }
-}
-
-function removeEntityData(QUuid) {
-    if (QUuid == null)
-        return;
-
-    var itsOverlay = currentOverlayForEntity(QUuid);
-    if (itsOverlay != null) {
-        Overlays.deleteOverlay(itsOverlay);
-        delete entitiesByOverlayID[itsOverlay];
-    }
-    var idx = entitiesIcons.indexOf(itsOverlay);
-    entitiesIcons.splice(idx, 1);
-
-    delete entitiesData[QUuid];
-}
-
-/*******************************************************************************
- * Entities to remark Icon/Markers rendering
- ******************************************************************************/
-
-function hideAllEntitiesIcons() {
-    var len = entitiesIcons.length;
-    for (var i = 0; i < len; i++) {
-        Overlays.editOverlay(entitiesIcons[i], {
-            visible : false
-        });
-    }
-}
-
-function renderAllEntitiesIcons() {
-    var entityPos;
-    var entityProps;
-    var iconDimensions = entityIconModelDimensions();
-    var planeDimensions = entityIconPlaneDimensions(); // plane overlays uses
-                                                        // xy instead of xz
-    for ( var QUuid in entitiesData) {
-        if (entitiesData.hasOwnProperty(QUuid)) {
-            entityProps = Entities.getEntityProperties(QUuid, [ "position",
-                    "visible" ]);
-            if (entityProps != null) {
-                entityPos = entityProps.position;
-                if (entitiesData[QUuid].icon != undefined && entityPos) {
-                    var iconPos = findLineToHeightIntersectionCoords(
-                            entityPos.x,
-                            entityPos.y
-                                    + RADAR_ICONS_APPARENT_DISTANCE_TO_AVATAR_BASE,
-                            entityPos.z, Camera.position.x, Camera.position.y,
-                            Camera.position.z, Camera.position.y
-                                    - RADAR_CAMERA_DISTANCE_TO_ICONS);
-                    if (!iconPos) {
-                        printd("entity icon pos bad for " + QUuid);
-                        continue;
-                    }
-                    var dimensions = entitiesData[QUuid].planar ? planeDimensions
-                            : iconDimensions;
-                    Overlays.editOverlay(entitiesData[QUuid].icon, {
-                        visible : entityProps.visible,
-                        dimensions : dimensions,
-                        position : iconPos
-                    });
-                }
-            }
-        }
-    }
+    return teleportModelDimensions;
 }
 
 /*******************************************************************************
@@ -1298,11 +1107,8 @@ function startRadar() {
     saveAllOthersAvatarsData();
     Camera.mode = "independent";
 
-    Camera.position = Vec3.sum(MyAvatar.position, {
-        x : 0,
-        y : radarHeight,
-        z : 0
-    });
+    initCameraOverMyAvatar();
+
     Camera.orientation = Quat.fromPitchYawRollDegrees(-90, 0, 0);
     radar = true;
 
@@ -1319,7 +1125,6 @@ function endRadar() {
     Controller.setVPadEnabled(true);
 
     disconnectRadarModeEvents();
-    hideAllEntitiesIcons();
     hideAllAvatarIcons();
 }
 
@@ -1353,12 +1158,10 @@ function updateRadar() {
     // Update avatar icons
     if (startedDraggingCamera) {
         hideAllAvatarIcons();
-        hideAllEntitiesIcons();
         startedDraggingCamera = false;
     } else if (!draggingCamera) {
         renderMyAvatarIcon();
         renderAllOthersAvatarIcons();
-        renderAllEntitiesIcons();
     }
 }
 
@@ -1366,48 +1169,41 @@ function valueIfDefined(value) {
     return value !== undefined ? value : "";
 }
 
-function entitiesAnalysis() {
-    var ids = Entities.findEntitiesInFrustum(Camera.frustum);
-    var entities = [];
-    for (var i = 0; i < ids.length; i++) {
-        var id = ids[i];
-        var properties = Entities.getEntityProperties(id);
-        entities.push({
-            id : id,
-            name : properties.name,
-            type : properties.type,
-            url : properties.type == "Model" ? properties.modelURL : "",
-            sourceUrl : properties.sourceUrl,
-            locked : properties.locked,
-            visible : properties.visible,
-            drawCalls : valueIfDefined(properties.renderInfo.drawCalls),
-            hasScript : properties.script !== ""
-        });
-    }
-}
-
 function connectRadarModeEvents() {
     Script.update.connect(updateRadar); // 60Hz loop
     Controller.keyPressEvent.connect(keyPressEvent);
-    Controller.mousePressEvent.connect(mousePress); // single click/touch
     Controller.touchUpdateEvent.connect(touchUpdate);
+    Window.domainChanged.connect(domainChanged);
     MyAvatar.positionGoneTo.connect(positionGoneTo);
 }
 
-function positionGoneTo() {
-    Camera.position = Vec3.sum(MyAvatar.position, {
-        x : 0,
+function initCameraOverMyAvatar() {
+   radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA;
+   Camera.position = {
+        x : MyAvatar.position.x,
         y : radarHeight,
-        z : 0
-    });
+        z : MyAvatar.position.z
+    };
+}
+
+function domainChanged() {
+    initCameraOverMyAvatar();
+}
+
+function positionGoneTo() {
+    Camera.position = {
+            x : MyAvatar.position.x,
+            y : radarHeight,
+            z : MyAvatar.position.z
+        };
 }
 
 function disconnectRadarModeEvents() {
     Script.update.disconnect(updateRadar);
     Controller.keyPressEvent.disconnect(keyPressEvent);
-    Controller.mousePressEvent.disconnect(mousePress);
     Controller.touchUpdateEvent.disconnect(touchUpdate);
     MyAvatar.positionGoneTo.disconnect(positionGoneTo);
+    Window.domainChanged.disconnect(domainChanged);
 }
 
 function init() {
@@ -1418,7 +1214,4 @@ function init() {
 
     AvatarList.avatarAddedEvent.connect(avatarAdded);
     AvatarList.avatarRemovedEvent.connect(avatarRemoved);
-
-    Entities.addingEntity.connect(entityAdded);
-    Entities.deletingEntity.connect(entityRemoved);
 }
diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js
index cc884cdbc7..66cd197abd 100644
--- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js
+++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js
@@ -564,6 +564,9 @@ Script.include("/~/system/libraries/Xform.js");
                     }
                 } else if (this.distanceRotating) {
                     this.distanceRotate(otherFarGrabModule);
+                } else if (this.highlightedEntity) {
+                    Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity);
+                    this.highlightedEntity = null;
                 }
             }
             return this.exitIfDisabled(controllerData);
diff --git a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js
index a66f7bd144..bc09ebee7a 100644
--- a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js
+++ b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js
@@ -56,6 +56,12 @@
             return canGrabEntity;
         };
 
+        this.clearAll = function() {
+            this.highlightedEntities.forEach(function(entity) {
+                dispatcherUtils.unhighlightTargetEntity(entity);
+            });
+        };
+
         this.hasHyperLink = function(props) {
             return (props.href !== "" && props.href !== undefined);
         };
@@ -89,7 +95,9 @@
                 }
                 if (this.isGrabable(controllerData, props) || this.hasHyperLink(props)) {
                     dispatcherUtils.highlightTargetEntity(props.id);
-                    newHighlightedEntities.push(props.id);
+                    if (newHighlightedEntities.indexOf(props.id) < 0) {
+                        newHighlightedEntities.push(props.id);
+                    }
                 }
             }
 
@@ -119,7 +127,6 @@
             if (channel === 'Hifi-unhighlight-entity') {
                 try {
                     data = JSON.parse(message);
-
                     var hand = data.hand;
                     if (hand === dispatcherUtils.LEFT_HAND) {
                         leftHighlightNearbyEntities.removeEntityFromHighlightList(data.entityID);
@@ -129,6 +136,9 @@
                 } catch (e) {
                     print("Failed to parse message");
                 }
+            } else if (channel === 'Hifi-unhighlight-all') {
+                leftHighlightNearbyEntities.clearAll();
+                rightHighlightNearbyEntities.clearAll();
             }
         }
     };
@@ -143,6 +153,7 @@
         dispatcherUtils.disableDispatcherModule("RightHighlightNearbyEntities");
     }
     Messages.subscribe('Hifi-unhighlight-entity');
+    Messages.subscribe('Hifi-unhighlight-all');
     Messages.messageReceived.connect(handleMessage);
     Script.scriptEnding.connect(cleanup);
 }());
diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js
index f7e86f9042..3ad7902d71 100644
--- a/scripts/system/controllers/controllerModules/inEditMode.js
+++ b/scripts/system/controllers/controllerModules/inEditMode.js
@@ -78,6 +78,7 @@ Script.include("/~/system/libraries/utils.js");
                 if (controllerData.triggerValues[this.hand] < TRIGGER_ON_VALUE) {
                     this.triggerClicked = false;
                 }
+                Messages.sendLocalMessage('Hifi-unhighlight-all', '');
                 return makeRunningValues(true, [], []);
             }
             this.triggerClicked = false;
diff --git a/scripts/system/controllers/controllerModules/mouseHighlightEntities.js b/scripts/system/controllers/controllerModules/mouseHighlightEntities.js
index 6af1055a69..ac57c8691f 100644
--- a/scripts/system/controllers/controllerModules/mouseHighlightEntities.js
+++ b/scripts/system/controllers/controllerModules/mouseHighlightEntities.js
@@ -12,14 +12,16 @@
 
 /* jslint bitwise: true */
 
-/* global Script, print, Entities, Picks, HMD */
+/* global Script, print, Entities, Picks, HMD, Controller, MyAvatar, isInEditMode*/
 
 
 (function() {
+    Script.include("/~/system/libraries/utils.js");
     var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js");
 
     function MouseHighlightEntities() {
         this.highlightedEntity = null;
+        this.grabbedEntity = null;
 
         this.parameters = dispatcherUtils.makeDispatcherModuleParameters(
             5,
@@ -27,13 +29,18 @@
             [],
             100);
 
+        this.setGrabbedEntity = function(entity) {
+            this.grabbedEntity = entity;
+            this.highlightedEntity = null;
+        };
+
         this.isReady = function(controllerData) {
             if (HMD.active) {
                 if (this.highlightedEntity) {
                     dispatcherUtils.unhighlightTargetEntity(this.highlightedEntity);
                     this.highlightedEntity = null;
                 }
-            } else {
+            } else if (!this.grabbedEntity && !isInEditMode()) {
                 var pickResult = controllerData.mouseRayPick;
                 if (pickResult.type === Picks.INTERSECTED_ENTITY) {
                     var targetEntityID = pickResult.objectID;
@@ -56,6 +63,9 @@
                             this.highlightedEntity = targetEntityID;
                         }
                     }
+                } else if (this.highlightedEntity) {
+                    dispatcherUtils.unhighlightTargetEntity(this.highlightedEntity);
+                    this.highlightedEntity = null;
                 }
             }
 
@@ -70,8 +80,29 @@
     var mouseHighlightEntities = new MouseHighlightEntities();
     dispatcherUtils.enableDispatcherModule("MouseHighlightEntities", mouseHighlightEntities);
 
+    var handleMessage = function(channel, message, sender) {
+        var data;
+        if (sender === MyAvatar.sessionUUID) {
+            if (channel === 'Hifi-Object-Manipulation') {
+                try {
+                    data = JSON.parse(message);
+                    if (data.action === 'grab') {
+                        var grabbedEntity = data.grabbedEntity;
+                        mouseHighlightEntities.setGrabbedEntity(grabbedEntity);
+                    } else if (data.action === 'release') {
+                        mouseHighlightEntities.setGrabbedEntity(null);
+                    }
+                } catch (e) {
+                    print("Warning: mouseHighlightEntities -- error parsing Hifi-Object-Manipulation: " + message);
+                }
+            }
+        }
+    };
+
     function cleanup() {
         dispatcherUtils.disableDispatcherModule("MouseHighlightEntities");
     }
+    Messages.subscribe('Hifi-Object-Manipulation');
+    Messages.messageReceived.connect(handleMessage);
     Script.scriptEnding.connect(cleanup);
 })();
diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js
index 8ae94a4caa..a835373e4d 100644
--- a/scripts/system/controllers/grab.js
+++ b/scripts/system/controllers/grab.js
@@ -409,7 +409,7 @@ Grabber.prototype.pressEvent = function(event) {
     var args = "mouse";
     Entities.callEntityMethod(this.entityID, "startDistanceGrab", args);
 
-    Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
+    Messages.sendLocalMessage('Hifi-Object-Manipulation', JSON.stringify({
         action: 'grab',
         grabbedEntity: this.entityID
     }));
@@ -450,7 +450,7 @@ Grabber.prototype.releaseEvent = function(event) {
         var args = "mouse";
         Entities.callEntityMethod(this.entityID, "releaseGrab", args);
 
-        Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
+        Messages.sendLocalMessage('Hifi-Object-Manipulation', JSON.stringify({
             action: 'release',
             grabbedEntity: this.entityID,
             joint: "mouse"
diff --git a/scripts/system/edit.js b/scripts/system/edit.js
index c99c8d401a..523582836f 100644
--- a/scripts/system/edit.js
+++ b/scripts/system/edit.js
@@ -471,6 +471,25 @@ var toolBar = (function () {
         }
     }
 
+    var entitiesToDelete = [];
+    var deletedEntityTimer = null;
+    var DELETE_ENTITY_TIMER_TIMEOUT = 100;
+
+    function checkDeletedEntityAndUpdate(entityID) {
+        // Allow for multiple entity deletes before updating the entity list.
+        entitiesToDelete.push(entityID);
+        if (deletedEntityTimer !== null) {
+            Script.clearTimeout(deletedEntityTimer);
+        }
+        deletedEntityTimer = Script.setTimeout(function () {
+            selectionManager.removeEntities(entitiesToDelete);
+            entityListTool.clearEntityList();
+            entityListTool.sendUpdate();
+            entitiesToDelete = [];
+            deletedEntityTimer = null;
+        }, DELETE_ENTITY_TIMER_TIMEOUT);
+    }
+
     function initialize() {
         Script.scriptEnding.connect(cleanup);
         Window.domainChanged.connect(function () {
@@ -493,8 +512,10 @@ var toolBar = (function () {
         Entities.canRezTmpChanged.connect(checkEditPermissionsAndUpdate);
         Entities.canRezCertifiedChanged.connect(checkEditPermissionsAndUpdate);
         Entities.canRezTmpCertifiedChanged.connect(checkEditPermissionsAndUpdate);
-
         var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified());
+
+        Entities.deletingEntity.connect(checkDeletedEntityAndUpdate);
+
         var createButtonIconRsrc = (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON);
         tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
         activeButton = tablet.addButton({
@@ -1144,17 +1165,11 @@ function setupModelMenus() {
         });
         modelMenuAddedDelete = true;
     }
-    Menu.addMenuItem({
-        menuName: "Edit",
-        menuItemName: "Entity List...",
-        afterItem: "Entities",
-        grouping: "Advanced"
-    });
 
     Menu.addMenuItem({
         menuName: "Edit",
         menuItemName: "Parent Entity to Last",
-        afterItem: "Entity List...",
+        afterItem: "Entities",
         grouping: "Advanced"
     });
 
@@ -1276,7 +1291,6 @@ function cleanupModelMenus() {
 
     Menu.removeMenuItem("Edit", "Parent Entity to Last");
     Menu.removeMenuItem("Edit", "Unparent Entity");
-    Menu.removeMenuItem("Edit", "Entity List...");
     Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
     Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
     Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
@@ -1638,8 +1652,6 @@ function handeMenuEvent(menuItem) {
             Window.promptTextChanged.connect(onPromptTextChanged);
             Window.promptAsync("URL of SVO to import", "");
         }
-    } else if (menuItem === "Entity List...") {
-        entityListTool.toggleVisible();
     } else if (menuItem === "Select All Entities In Box") {
         selectAllEtitiesInCurrentSelectionBox(false);
     } else if (menuItem === "Select All Entities Touching Box") {
diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js
index b9fd7f725c..2d4a2d3e97 100644
--- a/scripts/system/hmd.js
+++ b/scripts/system/hmd.js
@@ -18,7 +18,7 @@
 var headset; // The preferred headset. Default to the first one found in the following list.
 var displayMenuName = "Display";
 var desktopMenuItemName = "Desktop";
-['OpenVR (Vive)', 'Oculus Rift'].forEach(function (name) {
+['HTC Vive', 'Oculus Rift', 'WindowMS'].forEach(function (name) {
     if (!headset && Menu.menuItemExists(displayMenuName, name)) {
         headset = name;
     }
diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js
index 2322210522..e84600a64a 100644
--- a/scripts/system/libraries/entitySelectionTool.js
+++ b/scripts/system/libraries/entitySelectionTool.js
@@ -140,12 +140,23 @@ SelectionManager = (function() {
         that._update(true);
     };
 
-    that.removeEntity = function(entityID) {
+    function removeEntityByID(entityID) {
         var idx = that.selections.indexOf(entityID);
         if (idx >= 0) {
             that.selections.splice(idx, 1);
             Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID);
         }
+    }
+
+    that.removeEntity = function (entityID) {
+        removeEntityByID(entityID);
+        that._update(true);
+    };
+
+    that.removeEntities = function(entityIDs) {
+        for (var i = 0, length = entityIDs.length; i < length; i++) {
+            removeEntityByID(entityIDs[i]);
+        }
         that._update(true);
     };
 
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index a05778e2dd..dc4d5aa844 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -19,6 +19,7 @@ var selectionDisplay = null; // for gridTool.js to ignore
 
     Script.include("/~/system/libraries/WebTablet.js");
     Script.include("/~/system/libraries/gridTool.js");
+    Script.include("/~/system/libraries/connectionUtils.js");
 
     var METAVERSE_SERVER_URL = Account.metaverseServerURL;
     var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace";
@@ -789,10 +790,14 @@ var selectionDisplay = null; // for gridTool.js to ignore
     var savedDisablePreviewOptionLocked = false;
     var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview");;
     function maybeEnableHMDPreview() {
-        setTabletVisibleInSecondaryCamera(true);
-        DesktopPreviewProvider.setPreviewDisabledReason("USER");
-        Menu.setIsOptionChecked("Disable Preview", savedDisablePreviewOption);
-        savedDisablePreviewOptionLocked = false;
+        // Set a small timeout to prevent sensitive data from being shown during
+        // UI fade
+        Script.setTimeout(function () {
+            setTabletVisibleInSecondaryCamera(true);
+            DesktopPreviewProvider.setPreviewDisabledReason("USER");
+            Menu.setIsOptionChecked("Disable Preview", savedDisablePreviewOption);
+            savedDisablePreviewOptionLocked = false;
+        }, 150);
     }
 
     // Function Name: fromQml()
diff --git a/scripts/system/pal.js b/scripts/system/pal.js
index 0a01007ee9..c70c2729f5 100644
--- a/scripts/system/pal.js
+++ b/scripts/system/pal.js
@@ -251,6 +251,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
             });
         }
         break;
+    case 'refresh': // old name for refreshNearby
     case 'refreshNearby':
         data = {};
         ExtendedOverlay.some(function (overlay) { // capture the audio data
@@ -743,10 +744,13 @@ function receiveMessage(channel, messageString, senderID) {
     var message = JSON.parse(messageString);
     switch (message.method) {
     case 'select':
-        sendToQml(message); // Accepts objects, not just strings.
+        if (!onPalScreen) {
+            tablet.loadQMLSource(PAL_QML_SOURCE);
+            Script.setTimeout(function () { sendToQml(message); }, 1000);
+        } else {
+            sendToQml(message); // Accepts objects, not just strings.
+        }
         break;
-    default:
-        print('Unrecognized PAL message', messageString);
     }
 }
 
diff --git a/server-console/package-lock.json b/server-console/package-lock.json
index 4311fde51a..4f12f2fa00 100644
--- a/server-console/package-lock.json
+++ b/server-console/package-lock.json
@@ -20,7 +20,6 @@
       "version": "5.5.2",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
       "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
-      "dev": true,
       "requires": {
         "co": "4.6.0",
         "fast-deep-equal": "1.0.0",
@@ -41,11 +40,6 @@
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz",
       "integrity": "sha1-xQYbbg74qBd15Q9dZhUb9r83EQc="
     },
-    "ansi-styles": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
-    },
     "array-find-index": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.1.tgz",
@@ -118,16 +112,10 @@
       "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
       "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ="
     },
-    "async": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
-      "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
-    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
     },
     "author-regex": {
       "version": "1.0.0",
@@ -136,17 +124,14 @@
       "dev": true
     },
     "aws-sign2": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
-      "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8="
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
     },
     "aws4": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.3.2.tgz",
-      "integrity": "sha1-054L7kEs7Q6O2Uoj4xTzE6lbn9E=",
-      "requires": {
-        "lru-cache": "4.0.1"
-      }
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
+      "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w=="
     },
     "base64-js": {
       "version": "1.2.0",
@@ -204,11 +189,11 @@
       "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
     },
     "boom": {
-      "version": "2.10.1",
-      "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
-      "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
+      "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
       "requires": {
-        "hoek": "2.16.3"
+        "hoek": "4.2.1"
       }
     },
     "buffers": {
@@ -239,9 +224,9 @@
       }
     },
     "caseless": {
-      "version": "0.11.0",
-      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
-      "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c="
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
     },
     "chainsaw": {
       "version": "0.1.0",
@@ -252,18 +237,6 @@
         "traverse": "0.3.9"
       }
     },
-    "chalk": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-      "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-      "requires": {
-        "ansi-styles": "2.2.1",
-        "escape-string-regexp": "1.0.5",
-        "has-ansi": "2.0.0",
-        "strip-ansi": "3.0.1",
-        "supports-color": "2.0.0"
-      }
-    },
     "cheerio": {
       "version": "0.19.0",
       "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.19.0.tgz",
@@ -295,8 +268,7 @@
     "co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
-      "dev": true
+      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
     },
     "code-point-at": {
       "version": "1.0.0",
@@ -318,6 +290,7 @@
       "version": "2.9.0",
       "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
       "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
+      "dev": true,
       "requires": {
         "graceful-readlink": "1.0.1"
       }
@@ -373,11 +346,21 @@
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
     },
     "cryptiles": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
-      "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
+      "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
       "requires": {
-        "boom": "2.10.1"
+        "boom": "5.2.0"
+      },
+      "dependencies": {
+        "boom": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
+          "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
+          "requires": {
+            "hoek": "4.2.1"
+          }
+        }
       }
     },
     "css-select": {
@@ -728,7 +711,7 @@
             "minimist": "1.2.0",
             "pretty-bytes": "1.0.4",
             "progress-stream": "1.2.0",
-            "request": "2.71.0",
+            "request": "2.85.0",
             "single-line-log": "1.1.2",
             "throttleit": "0.0.2"
           },
@@ -828,11 +811,6 @@
       "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==",
       "dev": true
     },
-    "escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
-    },
     "extend": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz",
@@ -875,14 +853,12 @@
     "fast-deep-equal": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
-      "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
-      "dev": true
+      "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8="
     },
     "fast-json-stable-stringify": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
-      "dev": true
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
     },
     "fd-slicer": {
       "version": "1.0.1",
@@ -976,13 +952,23 @@
       "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
     },
     "form-data": {
-      "version": "1.0.0-rc4",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc4.tgz",
-      "integrity": "sha1-BaxrwiIntD5EYfSIFhVUaZ1Pi14=",
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
+      "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
       "requires": {
-        "async": "1.5.2",
-        "combined-stream": "1.0.5",
-        "mime-types": "2.1.10"
+        "asynckit": "0.4.0",
+        "combined-stream": "1.0.6",
+        "mime-types": "2.1.18"
+      },
+      "dependencies": {
+        "combined-stream": {
+          "version": "1.0.6",
+          "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
+          "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
+          "requires": {
+            "delayed-stream": "1.0.0"
+          }
+        }
       }
     },
     "fs-extra": {
@@ -1058,19 +1044,6 @@
         }
       }
     },
-    "generate-function": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
-      "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ="
-    },
-    "generate-object-property": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
-      "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
-      "requires": {
-        "is-property": "1.0.2"
-      }
-    },
     "get-package-info": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz",
@@ -1212,7 +1185,8 @@
     "graceful-readlink": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
-      "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU="
+      "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
+      "dev": true
     },
     "growly": {
       "version": "1.3.0",
@@ -1222,43 +1196,32 @@
     "har-schema": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
-      "dev": true
+      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
     },
     "har-validator": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
-      "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
+      "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
       "requires": {
-        "chalk": "1.1.3",
-        "commander": "2.9.0",
-        "is-my-json-valid": "2.13.1",
-        "pinkie-promise": "2.0.1"
-      }
-    },
-    "has-ansi": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
-      "requires": {
-        "ansi-regex": "2.0.0"
+        "ajv": "5.5.2",
+        "har-schema": "2.0.0"
       }
     },
     "hawk": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
-      "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
+      "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
       "requires": {
-        "boom": "2.10.1",
-        "cryptiles": "2.0.5",
-        "hoek": "2.16.3",
-        "sntp": "1.0.9"
+        "boom": "4.3.1",
+        "cryptiles": "3.1.2",
+        "hoek": "4.2.1",
+        "sntp": "2.1.0"
       }
     },
     "hoek": {
-      "version": "2.16.3",
-      "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
-      "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
+      "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA=="
     },
     "home-path": {
       "version": "1.0.5",
@@ -1301,13 +1264,20 @@
       }
     },
     "http-signature": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
-      "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
       "requires": {
-        "assert-plus": "0.2.0",
+        "assert-plus": "1.0.0",
         "jsprim": "1.2.2",
         "sshpk": "1.7.4"
+      },
+      "dependencies": {
+        "assert-plus": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+          "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+        }
       }
     },
     "indent-string": {
@@ -1388,28 +1358,12 @@
         "number-is-nan": "1.0.0"
       }
     },
-    "is-my-json-valid": {
-      "version": "2.13.1",
-      "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz",
-      "integrity": "sha1-1Vd4qC/rawlj/0vhEdXRaE6JBwc=",
-      "requires": {
-        "generate-function": "2.0.0",
-        "generate-object-property": "1.2.0",
-        "jsonpointer": "2.0.0",
-        "xtend": "4.0.1"
-      }
-    },
     "is-promise": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz",
       "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=",
       "dev": true
     },
-    "is-property": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
-      "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
-    },
     "is-typedarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@@ -1465,8 +1419,7 @@
     "json-schema-traverse": {
       "version": "0.3.1",
       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
-      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
-      "dev": true
+      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
     },
     "json-stringify-safe": {
       "version": "5.0.1",
@@ -1478,11 +1431,6 @@
       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz",
       "integrity": "sha1-4lK5mmr5AdPsQfMyWJyQUJp7xgU="
     },
-    "jsonpointer": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz",
-      "integrity": "sha1-OvHdIP6FRjkQ1GmjheMwF9KgMNk="
-    },
     "jsprim": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.2.2.tgz",
@@ -1569,15 +1517,6 @@
         "signal-exit": "2.1.2"
       }
     },
-    "lru-cache": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.1.tgz",
-      "integrity": "sha1-E0OVXtry432bnn7nJB4nxLn7cr4=",
-      "requires": {
-        "pseudomap": "1.0.2",
-        "yallist": "2.0.0"
-      }
-    },
     "map-obj": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
@@ -1603,16 +1542,16 @@
       }
     },
     "mime-db": {
-      "version": "1.22.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.22.0.tgz",
-      "integrity": "sha1-qyOmNy3J2G09yRIb0OvTgQWhkEo="
+      "version": "1.33.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
+      "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
     },
     "mime-types": {
-      "version": "2.1.10",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.10.tgz",
-      "integrity": "sha1-uTx8tDYuFtQQcqflRTj7TUMHCDc=",
+      "version": "2.1.18",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
+      "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
       "requires": {
-        "mime-db": "1.22.0"
+        "mime-db": "1.33.0"
       }
     },
     "minimist": {
@@ -1891,11 +1830,6 @@
         }
       }
     },
-    "node-uuid": {
-      "version": "1.4.7",
-      "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz",
-      "integrity": "sha1-baWhdmjEs91ZYjvaEc9/pMH2Cm8="
-    },
     "nodeify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/nodeify/-/nodeify-1.0.1.tgz",
@@ -1945,7 +1879,7 @@
         "minimist": "1.2.0",
         "pretty-bytes": "1.0.4",
         "progress-stream": "1.2.0",
-        "request": "2.71.0",
+        "request": "2.85.0",
         "single-line-log": "1.1.2",
         "throttleit": "0.0.2"
       },
@@ -1973,9 +1907,9 @@
       "integrity": "sha1-wCD1KcUoKt/dIz2R1LGBw9aG3Es="
     },
     "oauth-sign": {
-      "version": "0.8.1",
-      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.1.tgz",
-      "integrity": "sha1-GCQ5vbkTeL90YOdcZOpD5kSN7wY="
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+      "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
     },
     "object-assign": {
       "version": "4.0.1",
@@ -2099,8 +2033,7 @@
     "performance-now": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
-      "dev": true
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
     },
     "pify": {
       "version": "2.3.0",
@@ -2111,12 +2044,14 @@
     "pinkie": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+      "dev": true
     },
     "pinkie-promise": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
       "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+      "dev": true,
       "requires": {
         "pinkie": "2.0.4"
       }
@@ -2166,11 +2101,6 @@
         "is-promise": "1.0.1"
       }
     },
-    "pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
-    },
     "pump": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz",
@@ -2183,8 +2113,7 @@
     "punycode": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
-      "dev": true
+      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
     },
     "q": {
       "version": "1.5.1",
@@ -2193,9 +2122,9 @@
       "dev": true
     },
     "qs": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.1.0.tgz",
-      "integrity": "sha1-7B0WJrJCeNmfD99FSeUk4k7O6yY="
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
+      "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
     },
     "rc": {
       "version": "1.1.6",
@@ -2252,31 +2181,39 @@
       }
     },
     "request": {
-      "version": "2.71.0",
-      "resolved": "https://registry.npmjs.org/request/-/request-2.71.0.tgz",
-      "integrity": "sha1-bxRkPJxaZ8ruapXPjvBHfVYDvZE=",
+      "version": "2.85.0",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz",
+      "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==",
       "requires": {
-        "aws-sign2": "0.6.0",
-        "aws4": "1.3.2",
-        "bl": "1.1.2",
-        "caseless": "0.11.0",
+        "aws-sign2": "0.7.0",
+        "aws4": "1.7.0",
+        "caseless": "0.12.0",
         "combined-stream": "1.0.5",
-        "extend": "3.0.0",
+        "extend": "3.0.1",
         "forever-agent": "0.6.1",
-        "form-data": "1.0.0-rc4",
-        "har-validator": "2.0.6",
-        "hawk": "3.1.3",
-        "http-signature": "1.1.1",
+        "form-data": "2.3.2",
+        "har-validator": "5.0.3",
+        "hawk": "6.0.2",
+        "http-signature": "1.2.0",
         "is-typedarray": "1.0.0",
         "isstream": "0.1.2",
         "json-stringify-safe": "5.0.1",
-        "mime-types": "2.1.10",
-        "node-uuid": "1.4.7",
-        "oauth-sign": "0.8.1",
-        "qs": "6.1.0",
+        "mime-types": "2.1.18",
+        "oauth-sign": "0.8.2",
+        "performance-now": "2.1.0",
+        "qs": "6.5.1",
+        "safe-buffer": "5.1.1",
         "stringstream": "0.0.5",
-        "tough-cookie": "2.2.2",
-        "tunnel-agent": "0.4.2"
+        "tough-cookie": "2.3.4",
+        "tunnel-agent": "0.6.0",
+        "uuid": "3.2.1"
+      },
+      "dependencies": {
+        "extend": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
+          "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
+        }
       }
     },
     "request-progress": {
@@ -2308,8 +2245,7 @@
     "safe-buffer": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
-      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
-      "dev": true
+      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
     },
     "sanitize-filename": {
       "version": "1.6.1",
@@ -2347,11 +2283,11 @@
       }
     },
     "sntp": {
-      "version": "1.0.9",
-      "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
-      "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
+      "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
       "requires": {
-        "hoek": "2.16.3"
+        "hoek": "4.2.1"
       }
     },
     "spdx-correct": {
@@ -2483,11 +2419,6 @@
         }
       }
     },
-    "supports-color": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-      "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
-    },
     "tar-fs": {
       "version": "1.12.0",
       "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.12.0.tgz",
@@ -2585,9 +2516,12 @@
       }
     },
     "tough-cookie": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz",
-      "integrity": "sha1-yDoYMPTl7wuT7yo0iOck+N4Basc="
+      "version": "2.3.4",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
+      "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
+      "requires": {
+        "punycode": "1.4.1"
+      }
     },
     "traverse": {
       "version": "0.3.9",
@@ -2611,9 +2545,12 @@
       }
     },
     "tunnel-agent": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.2.tgz",
-      "integrity": "sha1-EQTj82rIcSXChycAZ9WC0YEzv+4="
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "requires": {
+        "safe-buffer": "5.1.1"
+      }
     },
     "tweetnacl": {
       "version": "0.14.3",
@@ -2644,6 +2581,11 @@
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
       "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
     },
+    "uuid": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
+      "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA=="
+    },
     "validate-npm-package-license": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
@@ -2710,11 +2652,6 @@
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
       "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
     },
-    "yallist": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.0.0.tgz",
-      "integrity": "sha1-MGxUODXwnuGkyyO3vOmrNByRzdQ="
-    },
     "yargs": {
       "version": "3.32.0",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz",
diff --git a/server-console/package.json b/server-console/package.json
index 2428d2574e..6dd39ea6f8 100644
--- a/server-console/package.json
+++ b/server-console/package.json
@@ -30,7 +30,7 @@
     "fs-extra": "^1.0.0",
     "node-notifier": "^5.2.1",
     "os-homedir": "^1.0.1",
-    "request": "^2.67.0",
+    "request": "^2.85.0",
     "request-progress": "1.0.2",
     "tar-fs": "^1.12.0",
     "yargs": "^3.30.0"
diff --git a/tests-manual/CMakeLists.txt b/tests-manual/CMakeLists.txt
new file mode 100644
index 0000000000..bc64183298
--- /dev/null
+++ b/tests-manual/CMakeLists.txt
@@ -0,0 +1,8 @@
+# add the manual test directories
+file(GLOB TEST_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*")
+foreach(DIR ${TEST_SUBDIRS})
+  if((IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}") AND (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}/CMakeLists.txt"))
+    set(TEST_PROJ_NAME ${DIR})
+    add_subdirectory(${DIR})
+  endif()
+endforeach()
diff --git a/tests/controllers/CMakeLists.txt b/tests-manual/controllers/CMakeLists.txt
similarity index 100%
rename from tests/controllers/CMakeLists.txt
rename to tests-manual/controllers/CMakeLists.txt
diff --git a/tests/controllers/qml/main.qml b/tests-manual/controllers/qml/main.qml
similarity index 100%
rename from tests/controllers/qml/main.qml
rename to tests-manual/controllers/qml/main.qml
diff --git a/tests/controllers/src/main.cpp b/tests-manual/controllers/src/main.cpp
similarity index 100%
rename from tests/controllers/src/main.cpp
rename to tests-manual/controllers/src/main.cpp
diff --git a/tests/entities/CMakeLists.txt b/tests-manual/entities/CMakeLists.txt
similarity index 100%
rename from tests/entities/CMakeLists.txt
rename to tests-manual/entities/CMakeLists.txt
diff --git a/tests/entities/packet.bin b/tests-manual/entities/packet.bin
similarity index 100%
rename from tests/entities/packet.bin
rename to tests-manual/entities/packet.bin
diff --git a/tests/entities/src/main.cpp b/tests-manual/entities/src/main.cpp
similarity index 100%
rename from tests/entities/src/main.cpp
rename to tests-manual/entities/src/main.cpp
diff --git a/tests/gl/CMakeLists.txt b/tests-manual/gl/CMakeLists.txt
similarity index 100%
rename from tests/gl/CMakeLists.txt
rename to tests-manual/gl/CMakeLists.txt
diff --git a/tests/gl/src/main.cpp b/tests-manual/gl/src/main.cpp
similarity index 100%
rename from tests/gl/src/main.cpp
rename to tests-manual/gl/src/main.cpp
diff --git a/tests-manual/gpu-textures/CMakeLists.txt b/tests-manual/gpu-textures/CMakeLists.txt
new file mode 100644
index 0000000000..c10f2eda3f
--- /dev/null
+++ b/tests-manual/gpu-textures/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(TARGET_NAME gpu-textures-tests)
+AUTOSCRIBE_SHADER_LIB(gpu graphics render-utils)
+# This is not a testcase -- just set it up as a regular hifi project
+setup_hifi_project(Quick Gui Script)
+setup_memory_debugger()
+set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
+link_hifi_libraries(
+    shared task networking gl 
+    ktx gpu octree  
+    ${PLATFORM_GL_BACKEND}
+)
+
+set(EXTRA_DEPLOY_OPTIONS "--qmldir  \"${PROJECT_SOURCE_DIR}/qml\"")
+package_libraries_for_deployment()
+
+target_nsight()
diff --git a/tests-manual/gpu-textures/qml/textureStats.qml b/tests-manual/gpu-textures/qml/textureStats.qml
new file mode 100644
index 0000000000..d3700c3eac
--- /dev/null
+++ b/tests-manual/gpu-textures/qml/textureStats.qml
@@ -0,0 +1,52 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.3
+
+Item {
+    width: 400
+    height: 600
+
+    Column {
+        spacing: 10
+        anchors.top: parent.top
+        anchors.left: parent.left
+        anchors.margins: 10
+
+        Text { text: qsTr("Total") }
+        Text { text: Stats.total + " MB" }
+        Text { text: qsTr("Allocated") }
+        Text { text: Stats.allocated }
+        Text { text: qsTr("Populated") }
+        Text { text: Stats.populated }
+        Text { text: qsTr("Pending") }
+        Text { text: Stats.pending }
+        Text { text: qsTr("Current Index") }
+        Text { text: Stats.index }
+        Text { text: qsTr("Current Source") }
+        Text { text: Stats.source }
+        Text { text: qsTr("Current Rez") }
+        Text { text: Stats.rez.width + " x " + Stats.rez.height }
+    }
+
+    Row {
+        id: row1
+        spacing: 10
+        anchors.bottom: row2.top
+        anchors.left: parent.left
+        anchors.margins: 10
+        Button { text: "1024"; onClicked: Stats.maxTextureMemory(1024); }
+        Button { text: "256"; onClicked: Stats.maxTextureMemory(256); }
+    }
+
+    Row {
+        id: row2
+        spacing: 10
+        anchors.bottom: parent.bottom
+        anchors.left: parent.left
+        anchors.margins: 10
+        Button { text: "Change Textures"; onClicked: Stats.changeTextures(); }
+        Button { text: "Next"; onClicked: Stats.nextTexture(); }
+        Button { text: "Previous"; onClicked: Stats.prevTexture(); }
+    }
+
+}
+
diff --git a/tests-manual/gpu-textures/src/TestHelpers.cpp b/tests-manual/gpu-textures/src/TestHelpers.cpp
new file mode 100644
index 0000000000..f952a4385f
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestHelpers.cpp
@@ -0,0 +1,26 @@
+//
+//  Created by Bradley Austin Davis on 2016/05/16
+//  Copyright 2014 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 "TestHelpers.h"
+#include <QtCore/QFileInfo>
+
+gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) {
+    auto vs = gpu::Shader::createVertex(vertexShaderSrc);
+    auto fs = gpu::Shader::createPixel(fragmentShaderSrc);
+    auto shader = gpu::Shader::createProgram(vs, fs);
+    if (!gpu::Shader::makeProgram(*shader, bindings)) {
+        printf("Could not compile shader\n");
+        exit(-1);
+    }
+    return shader;
+}
+
+QString projectRootDir() {
+    static QString projectRootPath = QFileInfo(QFileInfo(__FILE__).absolutePath() + "/..").absoluteFilePath();
+    return projectRootPath;
+}
diff --git a/tests-manual/gpu-textures/src/TestHelpers.h b/tests-manual/gpu-textures/src/TestHelpers.h
new file mode 100644
index 0000000000..17730c3642
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestHelpers.h
@@ -0,0 +1,40 @@
+//
+//  Created by Bradley Austin Davis on 2016/05/16
+//  Copyright 2014 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
+
+#include <vector>
+
+#include <GLMHelpers.h>
+#include <Transform.h>
+#include <NumericalConstants.h>
+
+#include <gpu/Resource.h>
+#include <gpu/Forward.h>
+#include <gpu/Shader.h>
+#include <gpu/Stream.h>
+
+struct RenderArgs {
+    gpu::ContextPointer _context;
+    ivec4 _viewport;
+    gpu::Batch* _batch;
+};
+
+class GpuTestBase : public QObject {
+public:
+    virtual ~GpuTestBase() {}
+    virtual bool isReady() const { return true; }
+    virtual size_t getTestCount() const { return 1; }
+    virtual void renderTest(size_t test, const RenderArgs& args) = 0;
+    virtual QObject * statsObject() { return nullptr; }
+    virtual QUrl statUrl() { return QUrl(); }
+};
+
+uint32_t toCompactColor(const glm::vec4& color);
+gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings);
+QString projectRootDir();
diff --git a/tests-manual/gpu-textures/src/TestTextures.cpp b/tests-manual/gpu-textures/src/TestTextures.cpp
new file mode 100644
index 0000000000..7aedb506da
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestTextures.cpp
@@ -0,0 +1,166 @@
+//
+//  Created by Bradley Austin Davis on 2016/05/16
+//  Copyright 2014 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 "TestTextures.h"
+
+#include <random>
+#include <algorithm>
+
+#include <QtCore/QDir>
+#include <QtQuick/QQuickView>
+#include <QtQml/QQmlContext>
+#include <gpu/Batch.h>
+#include <gpu/Context.h>
+
+#include "TestHelpers.h"
+
+std::string vertexShaderSource = R"SHADER(
+#line 14
+layout(location = 0) out vec2 outTexCoord0;
+
+const vec4 VERTICES[] = vec4[](
+    vec4(-1.0, -1.0, 0.0, 1.0), 
+    vec4( 1.0, -1.0, 0.0, 1.0),    
+    vec4(-1.0,  1.0, 0.0, 1.0),
+    vec4( 1.0,  1.0, 0.0, 1.0)
+);   
+
+void main() {
+    outTexCoord0 = VERTICES[gl_VertexID].xy;
+    outTexCoord0 += 1.0;
+    outTexCoord0 /= 2.0;
+    gl_Position = VERTICES[gl_VertexID];
+}
+)SHADER";
+
+std::string fragmentShaderSource = R"SHADER(
+#line 28
+
+uniform sampler2D tex;
+
+layout(location = 0) in vec2 inTexCoord0;
+layout(location = 0) out vec4 outFragColor;
+
+void main() {
+    outFragColor = texture(tex, inTexCoord0);
+    outFragColor.a = 1.0;
+    //outFragColor.rb = inTexCoord0;
+}
+
+)SHADER";
+
+#define STAT_UPDATE(name, src) \
+    { \
+        auto val = src; \
+        if (_##name != val) { \
+            _##name = val; \
+            emit name##Changed(); \
+        } \
+    }
+
+
+void TextureTestStats::update(int curIndex, const gpu::TexturePointer& texture) {
+    STAT_UPDATE(total, (int)BYTES_TO_MB(gpu::Context::getTextureGPUMemSize()));
+    STAT_UPDATE(allocated, (int)gpu::Context::getTextureResourceGPUMemSize());
+    STAT_UPDATE(pending, (int)gpu::Context::getTexturePendingGPUTransferMemSize());
+    STAT_UPDATE(populated, (int)gpu::Context::getTextureResourcePopulatedGPUMemSize());
+    STAT_UPDATE(source, texture->source().c_str());
+    STAT_UPDATE(index, curIndex);
+    auto dims = texture->getDimensions();
+    STAT_UPDATE(rez, QSize(dims.x, dims.y));
+}
+
+TexturesTest::TexturesTest() {
+    connect(&stats, &TextureTestStats::changeTextures, this, &TexturesTest::onChangeTextures);
+    connect(&stats, &TextureTestStats::nextTexture, this, &TexturesTest::onNextTexture);
+    connect(&stats, &TextureTestStats::prevTexture, this, &TexturesTest::onPrevTexture);
+    connect(&stats, &TextureTestStats::maxTextureMemory, this, &TexturesTest::onMaxTextureMemory);
+    {
+        auto VS = gpu::Shader::createVertex(vertexShaderSource);
+        auto PS = gpu::Shader::createPixel(fragmentShaderSource);
+        auto program = gpu::Shader::createProgram(VS, PS);
+        gpu::Shader::BindingSet slotBindings;
+        gpu::Shader::makeProgram(*program, slotBindings);
+        // If the pipeline did not exist, make it
+        auto state = std::make_shared<gpu::State>();
+        state->setCullMode(gpu::State::CULL_NONE);
+        state->setDepthTest({});
+        state->setBlendFunction({ false });
+        pipeline = gpu::Pipeline::create(program, state);
+    }
+
+    onChangeTextures();
+}
+
+
+void TexturesTest::renderTest(size_t testId, const RenderArgs& args) {
+    stats.update((int)index, textures[index]);
+    gpu::Batch& batch = *(args._batch);
+    batch.setPipeline(pipeline);
+    batch.setInputFormat(vertexFormat);
+    for (const auto& texture : textures) {
+        batch.setResourceTexture(0, texture);
+        batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
+    }
+    batch.setResourceTexture(0, textures[index]);
+    batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
+}
+
+#define LOAD_TEXTURE_COUNT 64
+
+void TexturesTest::onChangeTextures() {
+    static const QDir TEST_DIR("D:/ktx_texture_test");
+    static std::vector<std::string> ALL_TEXTURE_FILES;
+    if (ALL_TEXTURE_FILES.empty()) {
+        auto entryList = TEST_DIR.entryList({ "*.ktx" }, QDir::Filter::Files);
+        ALL_TEXTURE_FILES.reserve(entryList.size());
+        for (auto entry : entryList) {
+            auto textureFile = TEST_DIR.absoluteFilePath(entry).toStdString();
+            ALL_TEXTURE_FILES.push_back(textureFile);
+        }
+    }
+
+    oldTextures.clear();
+    oldTextures.swap(textures);
+
+#if 0
+    static const std::string bad = "D:/ktx_texture_test/b4beed38675dbc7a827ecd576399c1f4.ktx";
+    auto texture = gpu::Texture::unserialize(bad);
+    auto texelFormat = texture->getTexelFormat();
+    qDebug() << texture->getTexelFormat().getSemantic();
+    qDebug() << texture->getTexelFormat().getScalarCount();
+    textures.push_back(texture);
+#else
+    std::shuffle(ALL_TEXTURE_FILES.begin(), ALL_TEXTURE_FILES.end(), std::default_random_engine());
+    size_t newTextureCount = std::min<size_t>(ALL_TEXTURE_FILES.size(), LOAD_TEXTURE_COUNT);
+    for (size_t i = 0; i < newTextureCount; ++i) {
+        const auto& textureFile = ALL_TEXTURE_FILES[i];
+        auto texture = gpu::Texture::unserialize(textureFile);
+        qDebug() << textureFile.c_str();
+        qDebug() << texture->getTexelFormat().getSemantic();
+        qDebug() << texture->getTexelFormat().getScalarCount();
+        textures.push_back(texture);
+    }
+#endif
+    index = 0;
+    qDebug() << "Done";
+}
+
+void TexturesTest::onNextTexture() {
+    index += textures.size() + 1;
+    index %= textures.size();
+}
+
+void TexturesTest::onPrevTexture() {
+    index += textures.size() - 1;
+    index %= textures.size();
+}
+
+void TexturesTest::onMaxTextureMemory(int maxTextureMemory) {
+    gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(maxTextureMemory));
+}
diff --git a/tests-manual/gpu-textures/src/TestTextures.h b/tests-manual/gpu-textures/src/TestTextures.h
new file mode 100644
index 0000000000..e446ce6132
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestTextures.h
@@ -0,0 +1,74 @@
+//
+//  Created by Bradley Austin Davis on 2016/05/16
+//  Copyright 2014 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
+
+#include "TestHelpers.h"
+
+#define STATS_PROPERTY(type, name, initialValue) \
+    Q_PROPERTY(type name READ name NOTIFY name##Changed) \
+public: \
+    type name() { return _##name; }; \
+private: \
+    type _##name{ initialValue };
+
+
+class TextureTestStats : public QObject {
+    Q_OBJECT;
+    STATS_PROPERTY(int, pending, 0)
+    STATS_PROPERTY(int, total, 0)
+    STATS_PROPERTY(int, populated, 0)
+    STATS_PROPERTY(int, allocated, 0)
+    STATS_PROPERTY(int, index, 0)
+
+    STATS_PROPERTY(QString, source, QString())
+    STATS_PROPERTY(QSize, rez, QSize(0, 0))
+
+public:
+    void update(int index, const gpu::TexturePointer& texture);
+
+signals:
+    void pendingChanged();
+    void totalChanged();
+    void populatedChanged();
+    void allocatedChanged();
+    void changeTextures();
+    void rezChanged();
+    void indexChanged();
+    void sourceChanged();
+    void maxTextureMemory(int);
+
+    void nextTexture();
+    void prevTexture();
+};
+
+
+class TexturesTest : public GpuTestBase {
+    Q_OBJECT
+
+    gpu::Stream::FormatPointer vertexFormat { std::make_shared<gpu::Stream::Format>() };
+    std::vector<gpu::TexturePointer> textures;
+    std::vector<gpu::TexturePointer> oldTextures;
+    gpu::PipelinePointer pipeline;
+    TextureTestStats stats;
+    size_t index{ 0 };
+
+public:
+    TexturesTest();
+    QObject* statsObject() override { return &stats; }
+    QUrl statUrl() override { return QUrl::fromLocalFile(projectRootDir() + "/qml/textureStats.qml"); }
+    void renderTest(size_t testId, const RenderArgs& args) override;
+
+protected slots:
+    void onChangeTextures();
+    void onMaxTextureMemory(int newValue);
+    void onNextTexture();
+    void onPrevTexture();
+
+};
+
+
diff --git a/tests-manual/gpu-textures/src/TestWindow.cpp b/tests-manual/gpu-textures/src/TestWindow.cpp
new file mode 100644
index 0000000000..038b0b9b80
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestWindow.cpp
@@ -0,0 +1,117 @@
+//
+//  Created by Bradley Austin Davis on 2016/05/16
+//  Copyright 2014 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 "TestWindow.h"
+
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+
+#include <QtCore/QTimer>
+#include <QtGui/QResizeEvent>
+
+#include <gl/GLHelpers.h>
+
+#include <gpu/gl/GLBackend.h>
+
+TestWindow::TestWindow() {
+
+    auto timer = new QTimer(this);
+    timer->setTimerType(Qt::PreciseTimer);
+    timer->setInterval(5);
+    connect(timer, &QTimer::timeout, [&] { draw(); });
+    timer->start();
+
+    connect(qApp, &QCoreApplication::aboutToQuit, [this, timer] {
+        timer->stop();
+        _aboutToQuit = true;
+    });
+
+    setSurfaceType(QSurface::OpenGLSurface);
+
+    QSurfaceFormat format = getDefaultOpenGLSurfaceFormat();
+    format.setOption(QSurfaceFormat::DebugContext);
+    setFormat(format);
+    _glContext.setFormat(format);
+    _glContext.create();
+    _glContext.makeCurrent(this);
+
+    show();
+}
+
+void TestWindow::initGl() {
+    _glContext.makeCurrent(this);
+    gl::initModuleGl();
+    gpu::Context::init<gpu::gl::GLBackend>();
+    _renderArgs->_context = std::make_shared<gpu::Context>();
+    _glContext.makeCurrent(this);
+    resize(QSize(800, 600));
+}
+
+void TestWindow::resizeWindow(const QSize& size) {
+    _size = size;
+    _renderArgs->_viewport = ivec4(0, 0, _size.width(), _size.height());
+}
+
+void TestWindow::beginFrame() {
+    _renderArgs->_context->recycle();
+    _renderArgs->_context->beginFrame();
+    gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
+        batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f });
+        batch.clearDepthFramebuffer(1e4);
+        batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() });
+    });
+
+    gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
+        batch.setViewportTransform(_renderArgs->_viewport);
+        batch.setStateScissorRect(_renderArgs->_viewport);
+        batch.setProjectionTransform(_projectionMatrix);
+    });
+}
+
+void TestWindow::endFrame() {
+    gpu::doInBatch("TestWindow::endFrame::finish", _renderArgs->_context, [&](gpu::Batch& batch) {
+        batch.resetStages();
+    });
+    auto framePointer = _renderArgs->_context->endFrame();
+    _renderArgs->_context->consumeFrameUpdates(framePointer);
+    _renderArgs->_context->executeFrame(framePointer);
+    _glContext.swapBuffers(this);
+}
+
+void TestWindow::draw() {
+    if (_aboutToQuit) {
+        return;
+    }
+
+    // Attempting to draw before we're visible and have a valid size will
+    // produce GL errors.
+    if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) {
+        return;
+    }
+
+    if (!_glContext.makeCurrent(this)) {
+        return;
+    }
+
+    static std::once_flag once;
+    std::call_once(once, [&] { initGl(); });
+    beginFrame();
+
+    renderFrame();
+
+    endFrame();
+}
+
+void TestWindow::resizeEvent(QResizeEvent* ev) {
+    resizeWindow(ev->size());
+    float fov_degrees = 60.0f;
+    float aspect_ratio = (float)_size.width() / _size.height();
+    float near_clip = 0.1f;
+    float far_clip = 1000.0f;
+    _projectionMatrix = glm::perspective(glm::radians(fov_degrees), aspect_ratio, near_clip, far_clip);
+}
diff --git a/tests-manual/gpu-textures/src/TestWindow.h b/tests-manual/gpu-textures/src/TestWindow.h
new file mode 100644
index 0000000000..00bbf03836
--- /dev/null
+++ b/tests-manual/gpu-textures/src/TestWindow.h
@@ -0,0 +1,41 @@
+//
+//  Created by Bradley Austin Davis on 2016/05/16
+//  Copyright 2014 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
+
+#include <QtGui/QWindow>
+#include <QtCore/QTime>
+
+#include <GLMHelpers.h>
+#include <gl/QOpenGLContextWrapper.h>
+#include <gpu/Forward.h>
+#include "TestHelpers.h"
+
+#define DEFERRED_LIGHTING
+
+class TestWindow : public QWindow {
+protected:
+    QOpenGLContextWrapper _glContext;
+    QSize _size;
+    glm::mat4 _projectionMatrix;
+    bool _aboutToQuit { false };
+    std::shared_ptr<RenderArgs> _renderArgs{ std::make_shared<RenderArgs>() };
+
+    TestWindow();
+    virtual void initGl();
+    virtual void renderFrame() = 0;
+
+private:
+    void resizeWindow(const QSize& size);
+
+    void beginFrame();
+    void endFrame();
+    void draw();
+    void resizeEvent(QResizeEvent* ev) override;
+};
+
diff --git a/tests-manual/gpu-textures/src/main.cpp b/tests-manual/gpu-textures/src/main.cpp
new file mode 100644
index 0000000000..3d0051dc1d
--- /dev/null
+++ b/tests-manual/gpu-textures/src/main.cpp
@@ -0,0 +1,170 @@
+//
+//  main.cpp
+//  tests/gpu-test/src
+//
+//  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 <unordered_map>
+#include <memory>
+#include <cstdio>
+
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+
+#include <QtCore/QTime>
+#include <QtCore/QTimer>
+#include <QtCore/QDir>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QFile>
+#include <QtCore/QLoggingCategory>
+
+#include <QtGui/QDesktopServices>
+#include <QtGui/QResizeEvent>
+#include <QtGui/QWindow>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QImage>
+#include <QtGui/QScreen>
+
+#include <QtWidgets/QApplication>
+
+#include <gl/Config.h>
+
+#include <QtQuick/QQuickWindow>
+#include <QtQuick/QQuickView>
+#include <QtQml/QQmlContext>
+
+#include <gpu/Context.h>
+#include <gpu/Batch.h>
+#include <gpu/Stream.h>
+#include <gpu/gl/GLBackend.h>
+
+#include <gl/QOpenGLContextWrapper.h>
+#include <gl/GLHelpers.h>
+
+#include <GLMHelpers.h>
+#include <PathUtils.h>
+#include <NumericalConstants.h>
+
+#include <PerfStat.h>
+#include <PathUtils.h>
+#include <SharedUtil.h>
+#include <ViewFrustum.h>
+
+#include <gpu/Pipeline.h>
+#include <gpu/Context.h>
+
+#include "TestWindow.h"
+#include "TestTextures.h"
+
+using TestBuilder = std::function<GpuTestBase*()>;
+using TestBuilders = std::list<TestBuilder>;
+
+#define INTERACTIVE
+
+class MyTestWindow : public TestWindow {
+    using Parent = TestWindow;
+    TestBuilders _testBuilders;
+    GpuTestBase* _currentTest{ nullptr };
+    size_t _currentTestId{ 0 };
+    size_t _currentMaxTests{ 0 };
+    glm::mat4 _camera;
+    QTime _time;
+
+    void initGl() override {
+        Parent::initGl();
+        _time.start();
+        updateCamera();
+        _testBuilders = TestBuilders({
+            [] { return new TexturesTest(); },
+        });
+    }
+
+    void updateCamera() {
+        float t = _time.elapsed() * 1e-3f;
+        glm::vec3 unitscale{ 1.0f };
+        glm::vec3 up{ 0.0f, 1.0f, 0.0f };
+
+        float distance = 3.0f;
+        glm::vec3 camera_position{ distance * sinf(t), 0.5f, distance * cosf(t) };
+
+        static const vec3 camera_focus(0);
+        static const vec3 camera_up(0, 1, 0);
+        _camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up));
+
+        ViewFrustum frustum;
+        frustum.setPosition(camera_position);
+        frustum.setOrientation(glm::quat_cast(_camera));
+        frustum.setProjection(_projectionMatrix);
+    }
+
+    void renderFrame() override {
+        updateCamera();
+
+        while ((!_currentTest || (_currentTestId >= _currentMaxTests)) && !_testBuilders.empty()) {
+            if (_currentTest) {
+                delete _currentTest;
+                _currentTest = nullptr;
+            }
+
+            _currentTest = _testBuilders.front()();
+            _testBuilders.pop_front();
+
+            if (_currentTest) {
+                auto statsObject = _currentTest->statsObject();
+                QUrl url = _currentTest->statUrl();
+                if (statsObject) {
+                    auto screens = qApp->screens();
+                    auto primaryScreen = qApp->primaryScreen();
+                    auto targetScreen = primaryScreen;
+                    for (const auto& screen : screens) {
+                        if (screen == primaryScreen) {
+                            continue;
+                        }
+                        targetScreen = screen;
+                        break;
+                    }
+
+                    auto destPoint = targetScreen->availableGeometry().topLeft();
+                    QQuickView* view = new QQuickView();
+                    view->rootContext()->setContextProperty("Stats", statsObject);
+                    view->setSource(url);
+                    view->show();
+                    view->setPosition({ destPoint.x() + 100, destPoint.y() + 100 });
+                }
+                _currentMaxTests = _currentTest->getTestCount();
+                _currentTestId = 0;
+            }
+        }
+
+        if (!_currentTest && _testBuilders.empty()) {
+            qApp->quit();
+            return;
+        }
+
+        // Tests might need to wait for resources to download
+        if (!_currentTest->isReady()) {
+            return;
+        }
+
+        gpu::doInBatch("main::renderFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
+            batch.setViewTransform(_camera);
+            _renderArgs->_batch = &batch;
+            _currentTest->renderTest(_currentTestId, *_renderArgs);
+            _renderArgs->_batch = nullptr;
+        });
+    }
+};
+
+int main(int argc, char** argv) {
+    setupHifiApplication("GPU Test");
+    qputenv("HIFI_DEBUG_OPENGL", QByteArray("1"));
+    QApplication app(argc, argv);
+    MyTestWindow window;
+    app.exec();
+
+    return 0;
+}
diff --git a/tests/gpu-test/CMakeLists.txt b/tests-manual/gpu/CMakeLists.txt
similarity index 100%
rename from tests/gpu-test/CMakeLists.txt
rename to tests-manual/gpu/CMakeLists.txt
diff --git a/tests/gpu-test/src/TestFbx.cpp b/tests-manual/gpu/src/TestFbx.cpp
similarity index 100%
rename from tests/gpu-test/src/TestFbx.cpp
rename to tests-manual/gpu/src/TestFbx.cpp
diff --git a/tests/gpu-test/src/TestFbx.h b/tests-manual/gpu/src/TestFbx.h
similarity index 100%
rename from tests/gpu-test/src/TestFbx.h
rename to tests-manual/gpu/src/TestFbx.h
diff --git a/tests/gpu-test/src/TestFloorGrid.cpp b/tests-manual/gpu/src/TestFloorGrid.cpp
similarity index 100%
rename from tests/gpu-test/src/TestFloorGrid.cpp
rename to tests-manual/gpu/src/TestFloorGrid.cpp
diff --git a/tests/gpu-test/src/TestFloorGrid.h b/tests-manual/gpu/src/TestFloorGrid.h
similarity index 100%
rename from tests/gpu-test/src/TestFloorGrid.h
rename to tests-manual/gpu/src/TestFloorGrid.h
diff --git a/tests/gpu-test/src/TestFloorTexture.cpp b/tests-manual/gpu/src/TestFloorTexture.cpp
similarity index 100%
rename from tests/gpu-test/src/TestFloorTexture.cpp
rename to tests-manual/gpu/src/TestFloorTexture.cpp
diff --git a/tests/gpu-test/src/TestFloorTexture.h b/tests-manual/gpu/src/TestFloorTexture.h
similarity index 100%
rename from tests/gpu-test/src/TestFloorTexture.h
rename to tests-manual/gpu/src/TestFloorTexture.h
diff --git a/tests/gpu-test/src/TestHelpers.cpp b/tests-manual/gpu/src/TestHelpers.cpp
similarity index 100%
rename from tests/gpu-test/src/TestHelpers.cpp
rename to tests-manual/gpu/src/TestHelpers.cpp
diff --git a/tests/gpu-test/src/TestHelpers.h b/tests-manual/gpu/src/TestHelpers.h
similarity index 100%
rename from tests/gpu-test/src/TestHelpers.h
rename to tests-manual/gpu/src/TestHelpers.h
diff --git a/tests/gpu-test/src/TestInstancedShapes.cpp b/tests-manual/gpu/src/TestInstancedShapes.cpp
similarity index 100%
rename from tests/gpu-test/src/TestInstancedShapes.cpp
rename to tests-manual/gpu/src/TestInstancedShapes.cpp
diff --git a/tests/gpu-test/src/TestInstancedShapes.h b/tests-manual/gpu/src/TestInstancedShapes.h
similarity index 100%
rename from tests/gpu-test/src/TestInstancedShapes.h
rename to tests-manual/gpu/src/TestInstancedShapes.h
diff --git a/tests/gpu-test/src/TestShapes.cpp b/tests-manual/gpu/src/TestShapes.cpp
similarity index 100%
rename from tests/gpu-test/src/TestShapes.cpp
rename to tests-manual/gpu/src/TestShapes.cpp
diff --git a/tests/gpu-test/src/TestShapes.h b/tests-manual/gpu/src/TestShapes.h
similarity index 100%
rename from tests/gpu-test/src/TestShapes.h
rename to tests-manual/gpu/src/TestShapes.h
diff --git a/tests/gpu-test/src/TestWindow.cpp b/tests-manual/gpu/src/TestWindow.cpp
similarity index 100%
rename from tests/gpu-test/src/TestWindow.cpp
rename to tests-manual/gpu/src/TestWindow.cpp
diff --git a/tests/gpu-test/src/TestWindow.h b/tests-manual/gpu/src/TestWindow.h
similarity index 100%
rename from tests/gpu-test/src/TestWindow.h
rename to tests-manual/gpu/src/TestWindow.h
diff --git a/tests/gpu-test/src/main.cpp b/tests-manual/gpu/src/main.cpp
similarity index 100%
rename from tests/gpu-test/src/main.cpp
rename to tests-manual/gpu/src/main.cpp
diff --git a/tests/qml/CMakeLists.txt b/tests-manual/qml/CMakeLists.txt
similarity index 100%
rename from tests/qml/CMakeLists.txt
rename to tests-manual/qml/CMakeLists.txt
diff --git a/tests/qml/qml/controls/WebEntityView.qml b/tests-manual/qml/qml/controls/WebEntityView.qml
similarity index 100%
rename from tests/qml/qml/controls/WebEntityView.qml
rename to tests-manual/qml/qml/controls/WebEntityView.qml
diff --git a/tests/qml/qml/main.qml b/tests-manual/qml/qml/main.qml
similarity index 100%
rename from tests/qml/qml/main.qml
rename to tests-manual/qml/qml/main.qml
diff --git a/tests/qml/qml/qml/+android/UI.js b/tests-manual/qml/qml/qml/+android/UI.js
similarity index 100%
rename from tests/qml/qml/qml/+android/UI.js
rename to tests-manual/qml/qml/qml/+android/UI.js
diff --git a/tests/qml/qml/qml/+ios/UI.js b/tests-manual/qml/qml/qml/+ios/UI.js
similarity index 100%
rename from tests/qml/qml/qml/+ios/UI.js
rename to tests-manual/qml/qml/qml/+ios/UI.js
diff --git a/tests/qml/qml/qml/+osx/UI.js b/tests-manual/qml/qml/qml/+osx/UI.js
similarity index 100%
rename from tests/qml/qml/qml/+osx/UI.js
rename to tests-manual/qml/qml/qml/+osx/UI.js
diff --git a/tests/qml/qml/qml/ButtonPage.qml b/tests-manual/qml/qml/qml/ButtonPage.qml
similarity index 100%
rename from tests/qml/qml/qml/ButtonPage.qml
rename to tests-manual/qml/qml/qml/ButtonPage.qml
diff --git a/tests/qml/qml/qml/InputPage.qml b/tests-manual/qml/qml/qml/InputPage.qml
similarity index 100%
rename from tests/qml/qml/qml/InputPage.qml
rename to tests-manual/qml/qml/qml/InputPage.qml
diff --git a/tests/qml/qml/qml/ProgressPage.qml b/tests-manual/qml/qml/qml/ProgressPage.qml
similarity index 100%
rename from tests/qml/qml/qml/ProgressPage.qml
rename to tests-manual/qml/qml/qml/ProgressPage.qml
diff --git a/tests/qml/qml/qml/UI.js b/tests-manual/qml/qml/qml/UI.js
similarity index 100%
rename from tests/qml/qml/qml/UI.js
rename to tests-manual/qml/qml/qml/UI.js
diff --git a/tests/qml/src/main.cpp b/tests-manual/qml/src/main.cpp
similarity index 88%
rename from tests/qml/src/main.cpp
rename to tests-manual/qml/src/main.cpp
index 349ac55d88..d70bb52dde 100644
--- a/tests/qml/src/main.cpp
+++ b/tests-manual/qml/src/main.cpp
@@ -26,7 +26,7 @@
 #include <QtGui/QWindow>
 #include <QtGui/QGuiApplication>
 #include <QtGui/QImage>
-#include <QtGui/QOpenGLFunctions_4_5_Core>
+#include <QtGui/QOpenGLFunctions_4_1_Core>
 #include <QtGui/QOpenGLContext>
 #include <QtQuick/QQuickItem>
 #include <QtQml/QQmlContext>
@@ -88,7 +88,7 @@ private:
     OffscreenGLCanvas _sharedContext;
     std::array<std::array<QmlInfo, DIVISIONS_Y>, DIVISIONS_X> _surfaces;
 
-    QOpenGLFunctions_4_5_Core _glf;
+    QOpenGLFunctions_4_1_Core _glf;
     std::function<void(uint32_t, void*)> _discardLamdba;
     QSize _size;
     size_t _surfaceCount{ 0 };
@@ -109,14 +109,6 @@ TestWindow::TestWindow() {
     Setting::init();
 
     setSurfaceType(QSurface::OpenGLSurface);
-    QSurfaceFormat format;
-    format.setDepthBufferSize(24);
-    format.setStencilBufferSize(8);
-    format.setVersion(4, 5);
-    format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
-    format.setOption(QSurfaceFormat::DebugContext);
-    QSurfaceFormat::setDefaultFormat(format);
-    setFormat(format);
 
     qmlRegisterType<QTestItem>("Hifi", 1, 0, "TestItem");
 
@@ -145,7 +137,7 @@ void TestWindow::initGl() {
     gl::initModuleGl();
 
     _glf.initializeOpenGLFunctions();
-    _glf.glCreateFramebuffers(1, &_fbo);
+    _glf.glGenFramebuffers(1, &_fbo);
 
     if (!_sharedContext.create(&_glContext) || !_sharedContext.makeCurrent()) {
         qFatal("Unable to intialize Shared GL context");
@@ -196,6 +188,12 @@ void TestWindow::buildSurface(QmlInfo& qmlInfo, bool video) {
 
 void TestWindow::destroySurface(QmlInfo& qmlInfo) {
     auto& surface = qmlInfo.surface;
+    auto& currentTexture = qmlInfo.texture;
+    if (currentTexture) {
+        auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+        glFlush();
+        _discardLamdba(currentTexture, readFence);
+    }
     auto webView = surface->getRootItem();
     if (webView) {
         // stop loading
@@ -274,16 +272,19 @@ void TestWindow::draw() {
             if (!qmlInfo.surface || !qmlInfo.texture) {
                 continue;
             }
-            _glf.glNamedFramebufferTexture(_fbo, GL_COLOR_ATTACHMENT0, qmlInfo.texture, 0);
-            _glf.glBlitNamedFramebuffer(_fbo, 0,
-                                        // src coordinates
-                                        0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1,
-                                        // dst coordinates
-                                        incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1),
-                                        // blit mask and filter
-                                        GL_COLOR_BUFFER_BIT, GL_NEAREST);
+            _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo);
+            _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, qmlInfo.texture, 0);
+            _glf.glBlitFramebuffer(
+                // src coordinates
+                0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1,
+                // dst coordinates
+                incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1),
+                // blit mask and filter
+                GL_COLOR_BUFFER_BIT, GL_NEAREST);
         }
     }
+    _glf.glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+    _glf.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
     _glContext.swapBuffers(this);
 }
 
@@ -292,6 +293,16 @@ void TestWindow::resizeEvent(QResizeEvent* ev) {
 }
 
 int main(int argc, char** argv) {
+
+    QSurfaceFormat format;
+    format.setDepthBufferSize(24);
+    format.setStencilBufferSize(8);
+    format.setVersion(4, 1);
+    format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
+    format.setOption(QSurfaceFormat::DebugContext);
+    QSurfaceFormat::setDefaultFormat(format);
+    // setFormat(format);
+
     QGuiApplication app(argc, argv);
     TestWindow window;
     return app.exec();
diff --git a/tests/render-perf/CMakeLists.txt b/tests-manual/render-perf/CMakeLists.txt
similarity index 100%
rename from tests/render-perf/CMakeLists.txt
rename to tests-manual/render-perf/CMakeLists.txt
diff --git a/tests/render-perf/src/Camera.hpp b/tests-manual/render-perf/src/Camera.hpp
similarity index 100%
rename from tests/render-perf/src/Camera.hpp
rename to tests-manual/render-perf/src/Camera.hpp
diff --git a/tests/render-perf/src/main.cpp b/tests-manual/render-perf/src/main.cpp
similarity index 99%
rename from tests/render-perf/src/main.cpp
rename to tests-manual/render-perf/src/main.cpp
index 9249b3d957..33aea6dcc9 100644
--- a/tests/render-perf/src/main.cpp
+++ b/tests-manual/render-perf/src/main.cpp
@@ -34,6 +34,7 @@
 #include <QtWidgets/QMessageBox>
 #include <QtWidgets/QApplication>
 
+#include <shared/ConicalViewFrustum.h>
 #include <shared/RateCounter.h>
 #include <shared/NetworkUtils.h>
 #include <shared/FileLogger.h>
@@ -441,6 +442,10 @@ protected:
         viewOut = _viewFrustum;
     }
 
+    const ConicalViewFrustums& getConicalViews() const override {
+        return _view;
+    }
+
     QThread* getMainThread() override {
         return QThread::currentThread();
     }
@@ -1116,6 +1121,7 @@ private:
     graphics::LightPointer _globalLight { std::make_shared<graphics::Light>() };
     bool _ready { false };
     EntitySimulationPointer _entitySimulation;
+    ConicalViewFrustums _view;
 
     QStringList _commands;
     QString _commandPath;
diff --git a/tests/render-texture-load/CMakeLists.txt b/tests-manual/render-texture-load/CMakeLists.txt
similarity index 100%
rename from tests/render-texture-load/CMakeLists.txt
rename to tests-manual/render-texture-load/CMakeLists.txt
diff --git a/tests/render-texture-load/src/GLIHelpers.cpp b/tests-manual/render-texture-load/src/GLIHelpers.cpp
similarity index 100%
rename from tests/render-texture-load/src/GLIHelpers.cpp
rename to tests-manual/render-texture-load/src/GLIHelpers.cpp
diff --git a/tests/render-texture-load/src/GLIHelpers.h b/tests-manual/render-texture-load/src/GLIHelpers.h
similarity index 100%
rename from tests/render-texture-load/src/GLIHelpers.h
rename to tests-manual/render-texture-load/src/GLIHelpers.h
diff --git a/tests/render-texture-load/src/main.cpp b/tests-manual/render-texture-load/src/main.cpp
similarity index 100%
rename from tests/render-texture-load/src/main.cpp
rename to tests-manual/render-texture-load/src/main.cpp
diff --git a/tests/render-utils/CMakeLists.txt b/tests-manual/render-utils/CMakeLists.txt
similarity index 100%
rename from tests/render-utils/CMakeLists.txt
rename to tests-manual/render-utils/CMakeLists.txt
diff --git a/tests/render-utils/src/main.cpp b/tests-manual/render-utils/src/main.cpp
similarity index 100%
rename from tests/render-utils/src/main.cpp
rename to tests-manual/render-utils/src/main.cpp
diff --git a/tests/shaders/CMakeLists.txt b/tests-manual/shaders/CMakeLists.txt
similarity index 100%
rename from tests/shaders/CMakeLists.txt
rename to tests-manual/shaders/CMakeLists.txt
diff --git a/tests/shaders/src/main.cpp b/tests-manual/shaders/src/main.cpp
similarity index 100%
rename from tests/shaders/src/main.cpp
rename to tests-manual/shaders/src/main.cpp
diff --git a/tests/ui/qml/Palettes.qml b/tests-manual/ui/qml/Palettes.qml
similarity index 100%
rename from tests/ui/qml/Palettes.qml
rename to tests-manual/ui/qml/Palettes.qml
diff --git a/tests/ui/qml/ScrollingGraph.qml b/tests-manual/ui/qml/ScrollingGraph.qml
similarity index 100%
rename from tests/ui/qml/ScrollingGraph.qml
rename to tests-manual/ui/qml/ScrollingGraph.qml
diff --git a/tests/ui/qml/StubMenu.qml b/tests-manual/ui/qml/StubMenu.qml
similarity index 100%
rename from tests/ui/qml/StubMenu.qml
rename to tests-manual/ui/qml/StubMenu.qml
diff --git a/tests/ui/qml/Stubs.qml b/tests-manual/ui/qml/Stubs.qml
similarity index 100%
rename from tests/ui/qml/Stubs.qml
rename to tests-manual/ui/qml/Stubs.qml
diff --git a/tests/ui/qml/TestControllers.qml b/tests-manual/ui/qml/TestControllers.qml
similarity index 100%
rename from tests/ui/qml/TestControllers.qml
rename to tests-manual/ui/qml/TestControllers.qml
diff --git a/tests/ui/qml/TestDialog.qml b/tests-manual/ui/qml/TestDialog.qml
similarity index 100%
rename from tests/ui/qml/TestDialog.qml
rename to tests-manual/ui/qml/TestDialog.qml
diff --git a/tests/ui/qml/TestMenu.qml b/tests-manual/ui/qml/TestMenu.qml
similarity index 100%
rename from tests/ui/qml/TestMenu.qml
rename to tests-manual/ui/qml/TestMenu.qml
diff --git a/tests/ui/qml/TestRoot.qml b/tests-manual/ui/qml/TestRoot.qml
similarity index 100%
rename from tests/ui/qml/TestRoot.qml
rename to tests-manual/ui/qml/TestRoot.qml
diff --git a/tests/ui/qml/controlDemo/ButtonPage.qml b/tests-manual/ui/qml/controlDemo/ButtonPage.qml
similarity index 100%
rename from tests/ui/qml/controlDemo/ButtonPage.qml
rename to tests-manual/ui/qml/controlDemo/ButtonPage.qml
diff --git a/tests/ui/qml/controlDemo/InputPage.qml b/tests-manual/ui/qml/controlDemo/InputPage.qml
similarity index 100%
rename from tests/ui/qml/controlDemo/InputPage.qml
rename to tests-manual/ui/qml/controlDemo/InputPage.qml
diff --git a/tests/ui/qml/controlDemo/ProgressPage.qml b/tests-manual/ui/qml/controlDemo/ProgressPage.qml
similarity index 100%
rename from tests/ui/qml/controlDemo/ProgressPage.qml
rename to tests-manual/ui/qml/controlDemo/ProgressPage.qml
diff --git a/tests/ui/qml/controlDemo/main.qml b/tests-manual/ui/qml/controlDemo/main.qml
similarity index 100%
rename from tests/ui/qml/controlDemo/main.qml
rename to tests-manual/ui/qml/controlDemo/main.qml
diff --git a/tests/ui/qml/main.qml b/tests-manual/ui/qml/main.qml
similarity index 100%
rename from tests/ui/qml/main.qml
rename to tests-manual/ui/qml/main.qml
diff --git a/tests/ui/qmlscratch.pro b/tests-manual/ui/qmlscratch.pro
similarity index 100%
rename from tests/ui/qmlscratch.pro
rename to tests-manual/ui/qmlscratch.pro
diff --git a/tests/ui/src/main.cpp b/tests-manual/ui/src/main.cpp
similarity index 100%
rename from tests/ui/src/main.cpp
rename to tests-manual/ui/src/main.cpp
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index a8b0727e3d..bc11580979 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -27,9 +27,9 @@ set_target_properties("test-extensions" PROPERTIES FOLDER "Tests")
 #   set (ALL_TEST_TARGETS "${ALL_TEST_TARGETS}" PARENT_SCOPE) # copies this back to parent scope
 #
 add_custom_target("all-tests"
-    COMMAND ctest .
     DEPENDS "${ALL_TEST_TARGETS}")
-set_target_properties("all-tests" PROPERTIES FOLDER "hidden/test-targets")
+
 set_target_properties("all-tests" PROPERTIES
+    FOLDER "hidden/test-targets"
     EXCLUDE_FROM_DEFAULT_BUILD TRUE
     EXCLUDE_FROM_ALL TRUE)
diff --git a/tests/QTestExtensions.h b/tests/QTestExtensions.h
index b7b9795a9a..ac037d831b 100644
--- a/tests/QTestExtensions.h
+++ b/tests/QTestExtensions.h
@@ -13,8 +13,9 @@
 #define hifi_QTestExtensions_hpp
 
 #include <QtTest/QtTest>
+#include <QtCore/QFileInfo>
 #include <functional>
-
+#include <NumericalConstants.h>
 #include "GLMTestUtils.h"
 
 // Implements several extensions to QtTest.
@@ -302,3 +303,43 @@ inline auto errorTest (float actual, float expected, float acceptableRelativeErr
     QCOMPARE_WITH_LAMBDA(actual, expected, errorTest(actual, expected, relativeError))
 
 
+
+inline QString getTestResource(const QString& relativePath) {
+    static QDir dir;
+    static std::once_flag once;
+    std::call_once(once, []{
+        QFileInfo fileInfo(__FILE__);
+        auto parentDir = fileInfo.absoluteDir();
+        auto rootDir = parentDir.absoluteFilePath("..");
+        dir = QDir::cleanPath(rootDir); 
+    });
+        
+    return QDir::cleanPath(dir.absoluteFilePath(relativePath));
+}
+
+inline bool afterUsecs(quint64& startUsecs, quint64 maxIntervalUecs) {
+    auto now = usecTimestampNow();
+    auto interval = now - startUsecs;
+    if (interval > maxIntervalUecs) {
+        startUsecs = now;
+        return true;
+    }
+    return false;
+}
+
+inline bool afterSecs(quint64& startUsecs, quint64 maxIntervalSecs) {
+    return afterUsecs(startUsecs, maxIntervalSecs * USECS_PER_SECOND);
+}
+
+template <typename F>
+void doEvery(quint64& lastReportUsecs, quint64 secs, F lamdba) {
+    if (afterSecs(lastReportUsecs, secs)) {
+        lamdba();
+    }
+}
+
+inline void failAfter(quint64 startUsecs, quint64 secs, const char* message) {
+    if (afterSecs(startUsecs, secs)) {
+        QFAIL(message);
+    }
+}
diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp
index fe54ac3781..432129594a 100644
--- a/tests/animation/src/AnimTests.cpp
+++ b/tests/animation/src/AnimTests.cpp
@@ -39,6 +39,7 @@ void AnimTests::initTestCase() {
 
 void AnimTests::cleanupTestCase() {
     //DependencyManager::destroy<AnimationCache>();
+    DependencyManager::get<ResourceManager>()->cleanup();
 }
 
 void AnimTests::testClipInternalState() {
diff --git a/tests/gpu/CMakeLists.txt b/tests/gpu/CMakeLists.txt
new file mode 100644
index 0000000000..ad0eac5822
--- /dev/null
+++ b/tests/gpu/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Declare dependencies
+macro (setup_testcase_dependencies)
+  # link in the shared libraries
+  link_hifi_libraries(shared test-utils ktx gpu gl ${PLATFORM_GL_BACKEND})
+  package_libraries_for_deployment()
+  target_opengl()
+  target_zlib()
+  find_package(QuaZip REQUIRED)
+  target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS})
+  target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES})
+  if (WIN32)
+    add_paths_to_fixup_libs(${QUAZIP_DLL_PATH})
+    add_dependency_external_projects(wasapi)
+  endif ()
+endmacro ()
+
+setup_hifi_testcase()
diff --git a/tests/gpu/src/TextureTest.cpp b/tests/gpu/src/TextureTest.cpp
new file mode 100644
index 0000000000..18361af791
--- /dev/null
+++ b/tests/gpu/src/TextureTest.cpp
@@ -0,0 +1,284 @@
+//
+//  Created by Bradley Austin Davis on 2018/01/11
+//  Copyright 2014 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 "TextureTest.h"
+
+#include <iostream>
+#include <QtCore/QTemporaryFile>
+
+#include <gpu/Forward.h>
+#include <gl/Config.h>
+#include <gl/GLHelpers.h>
+#include <gpu/gl/GLBackend.h>
+#include <NumericalConstants.h>
+#include <test-utils/FileDownloader.h>
+
+#include <quazip5/quazip.h>
+#include <quazip5/JlCompress.h>
+
+#include "../../QTestExtensions.h"
+
+QTEST_MAIN(TextureTest)
+
+#define LOAD_TEXTURE_COUNT 40
+
+static const QString TEST_DATA("https://hifi-public.s3.amazonaws.com/austin/test_data/test_ktx.zip");
+static const QString TEST_DIR_NAME("{630b8f02-52af-4cdf-a896-24e472b94b28}");
+
+std::string vertexShaderSource = R"SHADER(
+#line 14
+layout(location = 0) out vec2 outTexCoord0;
+
+const vec4 VERTICES[] = vec4[](
+    vec4(-1.0, -1.0, 0.0, 1.0), 
+    vec4( 1.0, -1.0, 0.0, 1.0),    
+    vec4(-1.0,  1.0, 0.0, 1.0),
+    vec4( 1.0,  1.0, 0.0, 1.0)
+);   
+
+void main() {
+    outTexCoord0 = VERTICES[gl_VertexID].xy;
+    outTexCoord0 += 1.0;
+    outTexCoord0 /= 2.0;
+    gl_Position = VERTICES[gl_VertexID];
+}
+)SHADER";
+
+std::string fragmentShaderSource = R"SHADER(
+#line 28
+
+uniform sampler2D tex;
+
+layout(location = 0) in vec2 inTexCoord0;
+layout(location = 0) out vec4 outFragColor;
+
+void main() {
+    outFragColor = texture(tex, inTexCoord0);
+    outFragColor.a = 1.0;
+    //outFragColor.rb = inTexCoord0;
+}
+
+)SHADER";
+
+QtMessageHandler originalHandler;
+
+void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
+#if defined(Q_OS_WIN)
+    OutputDebugStringA(message.toStdString().c_str());
+    OutputDebugStringA("\n");
+#endif
+    originalHandler(type, context, message);
+}
+
+void TextureTest::initTestCase() {
+    originalHandler = qInstallMessageHandler(messageHandler);
+    _resourcesPath = getTestResource("interface/resources");
+    getDefaultOpenGLSurfaceFormat();
+    _canvas.create();
+    if (!_canvas.makeCurrent()) {
+        qFatal("Unable to make test GL context current");
+    }
+    gl::initModuleGl();
+    gpu::Context::init<gpu::gl::GLBackend>();
+    _gpuContext = std::make_shared<gpu::Context>();
+
+    _resourcesPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + TEST_DIR_NAME;
+    if (!QFileInfo(_resourcesPath).exists()) {
+        QDir(_resourcesPath).mkpath(".");
+        FileDownloader(TEST_DATA,
+                       [&](const QByteArray& data) {
+                           QTemporaryFile zipFile;
+                           if (zipFile.open()) {
+                               zipFile.write(data);
+                               zipFile.close();
+                           }
+                           JlCompress::extractDir(zipFile.fileName(), _resourcesPath);
+                       })
+            .waitForDownload();
+    }
+
+    _canvas.makeCurrent();
+    {
+        auto VS = gpu::Shader::createVertex(vertexShaderSource);
+        auto PS = gpu::Shader::createPixel(fragmentShaderSource);
+        auto program = gpu::Shader::createProgram(VS, PS);
+        gpu::Shader::BindingSet slotBindings;
+        gpu::Shader::makeProgram(*program, slotBindings);
+        // If the pipeline did not exist, make it
+        auto state = std::make_shared<gpu::State>();
+        state->setCullMode(gpu::State::CULL_NONE);
+        state->setDepthTest({});
+        state->setBlendFunction({ false });
+        _pipeline = gpu::Pipeline::create(program, state);
+    }
+
+    _framebuffer.reset(gpu::Framebuffer::create("cached", gpu::Element::COLOR_SRGBA_32, _size.x, _size.y));
+
+    // Find the test textures
+    {
+        QDir resourcesDir(_resourcesPath);
+        auto entryList = resourcesDir.entryList({ "*.ktx" }, QDir::Filter::Files);
+        _textureFiles.reserve(entryList.size());
+        for (auto entry : entryList) {
+            auto textureFile = resourcesDir.absoluteFilePath(entry).toStdString();
+            _textureFiles.push_back(textureFile);
+        }
+    }
+
+    // Load the test textures
+    {
+        size_t newTextureCount = std::min<size_t>(_textureFiles.size(), LOAD_TEXTURE_COUNT);
+        for (size_t i = 0; i < newTextureCount; ++i) {
+            const auto& textureFile = _textureFiles[i];
+            auto texture = gpu::Texture::unserialize(textureFile);
+            _textures.push_back(texture);
+        }
+    }
+}
+
+void TextureTest::cleanupTestCase() {
+    _framebuffer.reset();
+    _pipeline.reset();
+    _gpuContext->recycle();
+    _gpuContext.reset();
+}
+
+void TextureTest::beginFrame() {
+    _gpuContext->recycle();
+    _gpuContext->beginFrame();
+    gpu::doInBatch("TestWindow::beginFrame", _gpuContext, [&](gpu::Batch& batch) {
+        batch.setFramebuffer(_framebuffer);
+        batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f });
+        batch.clearDepthFramebuffer(1e4);
+        batch.setViewportTransform({ 0, 0, _size.x, _size.y });
+    });
+}
+
+void TextureTest::endFrame() {
+    gpu::doInBatch("TestWindow::endFrame::finish", _gpuContext, [&](gpu::Batch& batch) { batch.resetStages(); });
+    auto framePointer = _gpuContext->endFrame();
+    _gpuContext->consumeFrameUpdates(framePointer);
+    _gpuContext->executeFrame(framePointer);
+    // Simulate swapbuffers with a finish
+    glFinish();
+    QThread::msleep(10);
+}
+
+void TextureTest::renderFrame(const std::function<void(gpu::Batch&)>& renderLambda) {
+    beginFrame();
+    gpu::doInBatch("Test::body", _gpuContext, renderLambda);
+    endFrame();
+}
+
+void TextureTest::testTextureLoading() {
+    QVERIFY(_textures.size() > 0);
+    auto renderTexturesLamdba = [this](gpu::Batch& batch) {
+        batch.setPipeline(_pipeline);
+        for (const auto& texture : _textures) {
+            batch.setResourceTexture(0, texture);
+            batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
+        }
+    };
+
+    size_t expectedAllocation = 0;
+    for (const auto& texture : _textures) {
+        expectedAllocation += texture->evalTotalSize();
+    }
+    QVERIFY(_textures.size() > 0);
+
+    auto reportLambda = [=] {
+        qDebug() << "Allowed   " << gpu::Texture::getAllowedGPUMemoryUsage();
+        qDebug() << "Allocated " << gpu::Context::getTextureResourceGPUMemSize();
+        qDebug() << "Populated " << gpu::Context::getTextureResourcePopulatedGPUMemSize();
+        qDebug() << "Pending   " << gpu::Context::getTexturePendingGPUTransferMemSize();
+    };
+
+    auto allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
+    auto populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
+
+    // Cycle frames we're fully allocated
+    // We need to use the texture rendering lambda
+    auto lastReport = usecTimestampNow();
+    auto start = usecTimestampNow();
+    while (expectedAllocation != allocatedMemory) {
+        doEvery(lastReport, 4, reportLambda);
+        failAfter(start, 10, "Failed to allocate texture memory after 10 seconds");
+        renderFrame(renderTexturesLamdba);
+        allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
+        populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
+    }
+    QCOMPARE(allocatedMemory, expectedAllocation);
+
+    // Restart the timer
+    start = usecTimestampNow();
+    // Cycle frames we're fully populated
+    while (allocatedMemory != populatedMemory || 0 != gpu::Context::getTexturePendingGPUTransferMemSize()) {
+        doEvery(lastReport, 4, reportLambda);
+        failAfter(start, 10, "Failed to populate texture memory after 10 seconds");
+        renderFrame();
+        allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
+        populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
+    }
+    reportLambda();
+    QCOMPARE(populatedMemory, allocatedMemory);
+
+    // FIXME workaround a race condition in the difference between populated size and the actual _populatedMip value in the texture
+    for (size_t i = 0; i < _textures.size(); ++i) {
+        renderFrame();
+    }
+
+    // Test on-demand deallocation of memory
+    auto maxMemory = allocatedMemory / 2;
+    gpu::Texture::setAllowedGPUMemoryUsage(maxMemory);
+
+    // Restart the timer
+    start = usecTimestampNow();
+    // Cycle frames until the allocated memory is below the max memory
+    while (allocatedMemory > maxMemory || allocatedMemory != populatedMemory) {
+        doEvery(lastReport, 4, reportLambda);
+        failAfter(start, 10, "Failed to deallocate texture memory after 10 seconds");
+        renderFrame(renderTexturesLamdba);
+        allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
+        populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
+    }
+    reportLambda();
+
+    // Verify that the allocation is now below the target
+    QVERIFY(allocatedMemory <= maxMemory);
+    // Verify that populated memory is the same as allocated memory
+    QCOMPARE(populatedMemory, allocatedMemory);
+
+    // Restart the timer
+    start = usecTimestampNow();
+    // Reset the max memory to automatic
+    gpu::Texture::setAllowedGPUMemoryUsage(0);
+    // Cycle frames we're fully populated
+    while (allocatedMemory != expectedAllocation || allocatedMemory != populatedMemory) {
+        doEvery(lastReport, 4, reportLambda);
+        failAfter(start, 10, "Failed to populate texture memory after 10 seconds");
+        renderFrame();
+        allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
+        populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
+    }
+    reportLambda();
+    QCOMPARE(allocatedMemory, expectedAllocation);
+    QCOMPARE(populatedMemory, allocatedMemory);
+
+    _textures.clear();
+    // Cycle frames we're fully populated
+    while (allocatedMemory != 0) {
+        failAfter(start, 10, "Failed to clear texture memory after 10 seconds");
+        renderFrame();
+        allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
+        populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
+    }
+    QCOMPARE(allocatedMemory, 0);
+    QCOMPARE(populatedMemory, 0);
+    qDebug() << "Done";
+
+}
diff --git a/tests/gpu/src/TextureTest.h b/tests/gpu/src/TextureTest.h
new file mode 100644
index 0000000000..91f8a358ea
--- /dev/null
+++ b/tests/gpu/src/TextureTest.h
@@ -0,0 +1,40 @@
+//
+//  Created by Bradley Austin Davis on 2018/05/08
+//  Copyright 2013-2018 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
+
+#include <QtTest/QtTest>
+#include <QtCore/QTemporaryDir>
+
+#include <gpu/Forward.h>
+#include <gl/OffscreenGLCanvas.h>
+
+class TextureTest : public QObject {
+    Q_OBJECT
+
+private:
+    void beginFrame();
+    void endFrame();
+    void renderFrame(const std::function<void(gpu::Batch&)>& = [](gpu::Batch&) {});
+
+private slots:
+    void initTestCase();
+    void cleanupTestCase();
+    void testTextureLoading();
+
+private:
+    QString _resourcesPath;
+    OffscreenGLCanvas _canvas;
+    gpu::ContextPointer _gpuContext;
+    gpu::PipelinePointer _pipeline;
+    gpu::FramebufferPointer _framebuffer;
+    gpu::TexturePointer _colorBuffer, _depthBuffer;
+    const glm::uvec2 _size{ 640, 480 };
+    std::vector<std::string> _textureFiles;
+    std::vector<gpu::TexturePointer> _textures;
+};
diff --git a/tests/jitter/src/JitterTests.cpp b/tests/jitter/src/JitterTests.cpp
index b09cb40d3e..5c81177b88 100644
--- a/tests/jitter/src/JitterTests.cpp
+++ b/tests/jitter/src/JitterTests.cpp
@@ -6,6 +6,8 @@
 //  Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
 //
 
+#include "JitterTests.h"
+
 #include <iostream>
 #ifdef _WINDOWS
 #include <WS2tcpip.h>
@@ -23,8 +25,6 @@
 #include <SimpleMovingAverage.h>
 #include <StDev.h>
 
-#include "JitterTests.h"
-
 // Uncomment this to run manually
 //#define RUN_MANUALLY
 
diff --git a/tests/ktx/src/KtxTests.cpp b/tests/ktx/src/KtxTests.cpp
index 65d9cbec3d..5158ad2fd4 100644
--- a/tests/ktx/src/KtxTests.cpp
+++ b/tests/ktx/src/KtxTests.cpp
@@ -76,7 +76,9 @@ void KtxTests::testKtxEvalFunctions() {
 void KtxTests::testKtxSerialization() {
     const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png";
     QImage image(TEST_IMAGE);
-    gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true);
+    std::atomic<bool> abortSignal;
+    gpu::TexturePointer testTexture =
+        image::TextureUsage::process2DTextureColorFromImage(std::move(image), TEST_IMAGE.toStdString(), true, abortSignal);
     auto ktxMemory = gpu::Texture::serialize(*testTexture);
     QVERIFY(ktxMemory.get());
 
diff --git a/tests/networking/src/ResourceTests.cpp b/tests/networking/src/ResourceTests.cpp
index e83eeb66a0..6af3629bc7 100644
--- a/tests/networking/src/ResourceTests.cpp
+++ b/tests/networking/src/ResourceTests.cpp
@@ -7,20 +7,27 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ResourceTests.h"
+
 #include <QNetworkDiskCache>
 
-#include "ResourceCache.h"
-#include "NetworkAccessManager.h"
-#include "DependencyManager.h"
-
-#include "ResourceTests.h"
+#include <ResourceCache.h>
+#include <LimitedNodeList.h>
+#include <NodeList.h>
+#include <NetworkAccessManager.h>
+#include <DependencyManager.h>
+#include <StatTracker.h>
 
 QTEST_MAIN(ResourceTests)
 
 void ResourceTests::initTestCase() {
 
-    auto resourceCacheSharedItems = DependencyManager::set<ResourceCacheSharedItems>();
-
+    //DependencyManager::set<AddressManager>();
+    DependencyManager::set<StatTracker>();
+    DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
+    DependencyManager::set<NodeList>(NodeType::Agent, INVALID_PORT);
+    DependencyManager::set<ResourceCacheSharedItems>();
+    DependencyManager::set<ResourceManager>();
     const qint64 MAXIMUM_CACHE_SIZE = 1024 * 1024 * 1024; // 1GB
 
     // set up the file cache
@@ -34,6 +41,10 @@ void ResourceTests::initTestCase() {
     networkAccessManager.setCache(cache);
 }
 
+void ResourceTests::cleanupTestCase() {
+    DependencyManager::get<ResourceManager>()->cleanup();
+}
+
 static QSharedPointer<Resource> resource;
 
 
diff --git a/tests/networking/src/ResourceTests.h b/tests/networking/src/ResourceTests.h
index 32fc151982..59675816ba 100644
--- a/tests/networking/src/ResourceTests.h
+++ b/tests/networking/src/ResourceTests.h
@@ -18,6 +18,7 @@ private slots:
     void initTestCase();
     void downloadFirst();
     void downloadAgain();
+    void cleanupTestCase();
 };
 
 #endif // hifi_ResourceTests_h
diff --git a/tests/networking/src/SequenceNumberStatsTests.cpp b/tests/networking/src/SequenceNumberStatsTests.cpp
index aaaeea53fc..0f01fb5b66 100644
--- a/tests/networking/src/SequenceNumberStatsTests.cpp
+++ b/tests/networking/src/SequenceNumberStatsTests.cpp
@@ -9,12 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "SequenceNumberStatsTests.h"
+
 #include <limits>
 
 #include <SharedUtil.h>
 
-#include "SequenceNumberStatsTests.h"
-
 QTEST_MAIN(SequenceNumberStatsTests)
 
 const quint32 UINT16_RANGE = std::numeric_limits<quint16>::max() + 1;
diff --git a/tests/octree/src/AABoxCubeTests.cpp b/tests/octree/src/AABoxCubeTests.cpp
index 8180e6f674..4e0a75e3b9 100644
--- a/tests/octree/src/AABoxCubeTests.cpp
+++ b/tests/octree/src/AABoxCubeTests.cpp
@@ -9,11 +9,11 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "AABoxCubeTests.h"
+
 #include <AABox.h>
 #include <AACube.h>
 
-#include "AABoxCubeTests.h"
-
 QTEST_MAIN(AABoxCubeTests)
 
 void AABoxCubeTests::raycastOutHitsXMinFace() {
diff --git a/tests/octree/src/ModelTests.cpp b/tests/octree/src/ModelTests.cpp
index c2d170da9a..f3e17a56a5 100644
--- a/tests/octree/src/ModelTests.cpp
+++ b/tests/octree/src/ModelTests.cpp
@@ -12,6 +12,9 @@
 //    * need to add expected results and accumulation of test success/failure
 //
 
+#include "ModelTests.h" // needs to be EntityTests.h soon
+//#include "EntityTests.h"
+
 #include <QDebug>
 
 #include <EntityItem.h>
@@ -22,9 +25,6 @@
 #include <PropertyFlags.h>
 #include <SharedUtil.h>
 
-//#include "EntityTests.h"
-#include "ModelTests.h" // needs to be EntityTests.h soon
-
 QTEST_MAIN(EntityTests)
 
 /*
diff --git a/tests/octree/src/OctreeTests.cpp b/tests/octree/src/OctreeTests.cpp
index 81300a1293..ae04313a6a 100644
--- a/tests/octree/src/OctreeTests.cpp
+++ b/tests/octree/src/OctreeTests.cpp
@@ -12,6 +12,8 @@
 //    * need to add expected results and accumulation of test success/failure
 //
 
+#include "OctreeTests.h"
+
 #include <QDebug>
 
 #include <ByteCountCoding.h>
@@ -23,8 +25,6 @@
 #include <PropertyFlags.h>
 #include <SharedUtil.h>
 
-#include "OctreeTests.h"
-
 enum ExamplePropertyList {
     EXAMPLE_PROP_PAGED_PROPERTY,
     EXAMPLE_PROP_CUSTOM_PROPERTIES_INCLUDED,
diff --git a/tests/physics/src/ShapeInfoTests.cpp b/tests/physics/src/ShapeInfoTests.cpp
index 79d0092dc3..efc88a4032 100644
--- a/tests/physics/src/ShapeInfoTests.cpp
+++ b/tests/physics/src/ShapeInfoTests.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ShapeInfoTests.h"
+
 #include <iostream>
 
 #include <btBulletDynamicsCommon.h>
@@ -19,8 +21,6 @@
 #include <ShapeFactory.h>
 #include <StreamUtils.h>
 
-#include "ShapeInfoTests.h"
-
 QTEST_MAIN(ShapeInfoTests)
 
 // Enable this to manually run testHashCollisions
diff --git a/tests/physics/src/ShapeManagerTests.cpp b/tests/physics/src/ShapeManagerTests.cpp
index f214601a42..393bfdcd07 100644
--- a/tests/physics/src/ShapeManagerTests.cpp
+++ b/tests/physics/src/ShapeManagerTests.cpp
@@ -9,13 +9,14 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ShapeManagerTests.h"
+
 #include <iostream>
+
 #include <ShapeManager.h>
 #include <StreamUtils.h>
 #include <Extents.h>
 
-#include "ShapeManagerTests.h"
-
 QTEST_MAIN(ShapeManagerTests)
 
 void ShapeManagerTests::testShapeAccounting() {
diff --git a/tests/qt59/CMakeLists.txt b/tests/qt59/CMakeLists.txt
deleted file mode 100644
index e3450ae069..0000000000
--- a/tests/qt59/CMakeLists.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-
-set(TARGET_NAME qt59)
-
-if (WIN32)
-  SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217")
-endif()
-
-setup_memory_debugger()
-
-# This is not a testcase -- just set it up as a regular hifi project
-setup_hifi_project(Gui)
-set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
-
-
-# link in the shared libraries
-link_hifi_libraries(shared networking)
-
-if (WIN32)
-	add_dependency_external_projects(wasapi)
-endif()
-
-package_libraries_for_deployment()
diff --git a/tests/qt59/src/main.cpp b/tests/qt59/src/main.cpp
deleted file mode 100644
index 19b922de9f..0000000000
--- a/tests/qt59/src/main.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-//  Created by Bradley Austin Davis on 2017/06/06
-//  Copyright 2013-2017 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 <mutex>
-
-#include <QtCore/QCoreApplication>
-
-#include <NodeList.h>
-#include <AccountManager.h>
-#include <AddressManager.h>
-#include <MessagesClient.h>
-
-#include <BuildInfo.h>
-
-
-class Qt59TestApp : public QCoreApplication {
-    Q_OBJECT
-public:
-    Qt59TestApp(int argc, char* argv[]);
-    ~Qt59TestApp();
-
-private:
-    void finish(int exitCode);
-};
-
-
-
-Qt59TestApp::Qt59TestApp(int argc, char* argv[]) :
-    QCoreApplication(argc, argv)
-{
-    DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
-    DependencyManager::set<AccountManager>([&] { return QString("Mozilla/5.0 (HighFidelityACClient)"); });
-    DependencyManager::set<AddressManager>();
-    DependencyManager::set<NodeList>(NodeType::Agent, 0);
-    auto nodeList = DependencyManager::get<NodeList>();
-    nodeList->startThread();
-    auto messagesClient = DependencyManager::set<MessagesClient>();
-    messagesClient->startThread();
-    QTimer::singleShot(1000, [this] { finish(0); });
-}
-
-Qt59TestApp::~Qt59TestApp() {
-}
-
-
-void Qt59TestApp::finish(int exitCode) {
-    auto nodeList = DependencyManager::get<NodeList>();
-
-    // send the domain a disconnect packet, force stoppage of domain-server check-ins
-    nodeList->getDomainHandler().disconnect();
-    nodeList->setIsShuttingDown(true);
-    nodeList->getPacketReceiver().setShouldDropPackets(true);
-
-    // remove the NodeList from the DependencyManager
-    DependencyManager::destroy<NodeList>();
-    QCoreApplication::exit(exitCode);
-}
-
-
-int main(int argc, char * argv[]) {
-    setupHifiApplication("Qt59Test");
-
-    Qt59TestApp app(argc, argv);
-
-    return app.exec();
-}
-
-#include "main.moc"
diff --git a/tests/shared/src/AABoxTests.cpp b/tests/shared/src/AABoxTests.cpp
index 2e9dfab497..865a82e86c 100644
--- a/tests/shared/src/AABoxTests.cpp
+++ b/tests/shared/src/AABoxTests.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <iostream>
-
 #include "AABoxTests.h"
 
+#include <iostream>
+
 #include <GLMHelpers.h>
 #include <NumericalConstants.h>
 #include <StreamUtils.h>
diff --git a/tests/shared/src/AACubeTests.cpp b/tests/shared/src/AACubeTests.cpp
index 177daf89f1..4d684b4677 100644
--- a/tests/shared/src/AACubeTests.cpp
+++ b/tests/shared/src/AACubeTests.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <iostream>
-
 #include "AACubeTests.h"
 
+#include <iostream>
+
 #include <GLMHelpers.h>
 #include <NumericalConstants.h>
 #include <StreamUtils.h>
diff --git a/tests/shared/src/DualQuaternionTests.cpp b/tests/shared/src/DualQuaternionTests.cpp
index fe14d9d166..276eb44f64 100644
--- a/tests/shared/src/DualQuaternionTests.cpp
+++ b/tests/shared/src/DualQuaternionTests.cpp
@@ -8,10 +8,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <iostream>
-
 #include "DualQuaternionTests.h"
 
+#include <iostream>
+
 #include <DualQuaternion.h>
 #include <GLMHelpers.h>
 #include <NumericalConstants.h>
diff --git a/tests/shared/src/GeometryUtilTests.cpp b/tests/shared/src/GeometryUtilTests.cpp
index eb9be4987f..9b4f0f250f 100644
--- a/tests/shared/src/GeometryUtilTests.cpp
+++ b/tests/shared/src/GeometryUtilTests.cpp
@@ -9,10 +9,10 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#include <iostream>
-
 #include "GeometryUtilTests.h"
 
+#include <iostream>
+
 #include <GeometryUtil.h>
 #include <GLMHelpers.h>
 #include <NumericalConstants.h>
diff --git a/tests/ui/CMakeLists.txt b/tests/ui/CMakeLists.txt
deleted file mode 100644
index 0a5d6796e0..0000000000
--- a/tests/ui/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-# This folder contains code for testing the QML UI using Qt Creator.  It is not intended to be included in the CMake project
diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp
index 4711dc4102..cfede87c53 100644
--- a/tools/ac-client/src/ACClientApp.cpp
+++ b/tools/ac-client/src/ACClientApp.cpp
@@ -9,10 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ACClientApp.h"
+
 #include <QDataStream>
 #include <QThread>
 #include <QLoggingCategory>
 #include <QCommandLineParser>
+
 #include <NetworkLogging.h>
 #include <NetworkingConstants.h>
 #include <SharedLogging.h>
@@ -20,8 +23,6 @@
 #include <DependencyManager.h>
 #include <SettingHandle.h>
 
-#include "ACClientApp.h"
-
 ACClientApp::ACClientApp(int argc, char* argv[]) :
     QCoreApplication(argc, argv)
 {
diff --git a/tools/atp-client/src/ATPClientApp.cpp b/tools/atp-client/src/ATPClientApp.cpp
index 526065b2f7..c688ba9c82 100644
--- a/tools/atp-client/src/ATPClientApp.cpp
+++ b/tools/atp-client/src/ATPClientApp.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ATPClientApp.h"
+
 #include <QDataStream>
 #include <QTextStream>
 #include <QThread>
@@ -25,8 +27,6 @@
 #include <AssetUpload.h>
 #include <StatTracker.h>
 
-#include "ATPClientApp.h"
-
 #define HIGH_FIDELITY_ATP_CLIENT_USER_AGENT "Mozilla/5.0 (HighFidelityATPClient)"
 #define TIMEOUT_MILLISECONDS 8000
 
diff --git a/tools/auto-tester/src/Downloader.cpp b/tools/auto-tester/src/Downloader.cpp
index 030aa95a19..530a3b61bd 100644
--- a/tools/auto-tester/src/Downloader.cpp
+++ b/tools/auto-tester/src/Downloader.cpp
@@ -9,6 +9,8 @@
 //
 #include "Downloader.h"
 
+#include <QtWidgets/QMessageBox>
+
 Downloader::Downloader(QUrl imageUrl, QObject *parent) : QObject(parent) {
     connect(
         &_networkAccessManager, SIGNAL (finished(QNetworkReply*)),
@@ -20,6 +22,12 @@ Downloader::Downloader(QUrl imageUrl, QObject *parent) : QObject(parent) {
 }
 
 void Downloader::fileDownloaded(QNetworkReply* reply) {
+    QNetworkReply::NetworkError error = reply->error();
+    if (error != QNetworkReply::NetworkError::NoError) {
+        QMessageBox::information(0, "Test Aborted", "Failed to download image: " + reply->errorString());
+        return;
+    }
+
     _downloadedData = reply->readAll();
 
     //emit a signal
diff --git a/tools/auto-tester/src/Test.cpp b/tools/auto-tester/src/Test.cpp
index 99f9025fdd..078a66aa6a 100644
--- a/tools/auto-tester/src/Test.cpp
+++ b/tools/auto-tester/src/Test.cpp
@@ -27,7 +27,7 @@ Test::Test() {
     mismatchWindow.setModal(true);
 }
 
-bool Test::createTestResultsFolderPath(QString directory) {
+bool Test::createTestResultsFolderPath(const QString& directory) {
     QDateTime now = QDateTime::currentDateTime();
     testResultsFolderPath =  directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT);
     QDir testResultsFolder(testResultsFolderPath);
@@ -72,7 +72,7 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
         QImage expectedImage(expectedImagesFullFilenames[i]);
 
         if (resultImage.width() != expectedImage.width() || resultImage.height() != expectedImage.height()) {
-            messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Images are not the same size");
+            QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Images are not the same size");
             exit(-1);
         }
 
@@ -80,7 +80,7 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
         try {
             similarityIndex = imageComparer.compareImages(resultImage, expectedImage);
         } catch (...) {
-            messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Image not in expected format");
+            QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Image not in expected format");
             exit(-1);
         }
 
@@ -125,22 +125,22 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
     return success;
 }
 
-void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
+void Test::appendTestResultsToFile(const QString& testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
     if (!QDir().exists(testResultsFolderPath)) {
-        messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + testResultsFolderPath + " not found");
+        QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + testResultsFolderPath + " not found");
         exit(-1);
     }
 
     QString failureFolderPath { testResultsFolderPath + "/" + "Failure_" + QString::number(index) };
     if (!QDir().mkdir(failureFolderPath)) {
-        messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create folder " + failureFolderPath);
+        QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create folder " + failureFolderPath);
         exit(-1);
     }
     ++index;
 
     QFile descriptionFile(failureFolderPath + "/" + TEST_RESULTS_FILENAME);
     if (!descriptionFile.open(QIODevice::ReadWrite)) {
-        messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + TEST_RESULTS_FILENAME);
+        QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + TEST_RESULTS_FILENAME);
         exit(-1);
     }
 
@@ -160,49 +160,54 @@ void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure te
     sourceFile = testFailure._pathname + testFailure._expectedImageFilename;
     destinationFile = failureFolderPath + "/" + "Expected Image.jpg";
     if (!QFile::copy(sourceFile, destinationFile)) {
-        messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile);
+        QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile);
         exit(-1);
     }
 
     sourceFile = testFailure._pathname + testFailure._actualImageFilename;
     destinationFile = failureFolderPath + "/" + "Actual Image.jpg";
     if (!QFile::copy(sourceFile, destinationFile)) {
-        messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile);
+        QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile);
         exit(-1);
     }
 
     comparisonImage.save(failureFolderPath + "/" + "Difference Image.jpg");
 }
 
-void Test::startTestsEvaluation() {
+void Test::startTestsEvaluation(const QString& testFolder) {
     // Get list of JPEG images in folder, sorted by name
-    pathToTestResultsDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
-    if (pathToTestResultsDirectory == "") {
+    QString previousSelection = snapshotDirectory;
+
+    snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images",
+                                                          previousSelection, QFileDialog::ShowDirsOnly);
+
+    // If user cancelled then restore previous selection and return
+    if (snapshotDirectory == "") {
+        snapshotDirectory = previousSelection;
         return;
     }
 
     // Quit if test results folder could not be created
-    if (!createTestResultsFolderPath(pathToTestResultsDirectory)) {
+    if (!createTestResultsFolderPath(snapshotDirectory)) {
         return;
     }
 
     // Before any processing - all images are converted to PNGs, as this is the format stored on GitHub
-    QStringList sortedSnapshotFilenames = createListOfAll_imagesInDirectory("jpg", pathToTestResultsDirectory);
+    QStringList sortedSnapshotFilenames = createListOfAll_imagesInDirectory("jpg", snapshotDirectory);
     foreach(QString filename, sortedSnapshotFilenames) {
         QStringList stringParts = filename.split(".");
-        copyJPGtoPNG(
-            pathToTestResultsDirectory + "/" + stringParts[0] + ".jpg", 
-            pathToTestResultsDirectory + "/" + stringParts[0] + ".png"
+        copyJPGtoPNG(snapshotDirectory + "/" + stringParts[0] + ".jpg", 
+            snapshotDirectory + "/" + stringParts[0] + ".png"
         );
 
-        QFile::remove(pathToTestResultsDirectory + "/" + stringParts[0] + ".jpg");
+        QFile::remove(snapshotDirectory + "/" + stringParts[0] + ".jpg");
     }
 
     // Create two lists.  The first is the test results,  the second is the expected images
     // The expected images are represented as a URL to enable download from GitHub
     // Images that are in the wrong format are ignored.
 
-    QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", pathToTestResultsDirectory);
+    QStringList sortedTestResultsFilenames = createListOfAll_imagesInDirectory("png", snapshotDirectory);
     QStringList expectedImagesURLs;
 
     resultImagesFullFilenames.clear();
@@ -210,7 +215,7 @@ void Test::startTestsEvaluation() {
     expectedImagesFullFilenames.clear();
 
     foreach(QString currentFilename, sortedTestResultsFilenames) {
-        QString fullCurrentFilename = pathToTestResultsDirectory + "/" + currentFilename;
+        QString fullCurrentFilename = snapshotDirectory + "/" + currentFilename;
         if (isInSnapshotFilenameFormat("png", currentFilename)) {
             resultImagesFullFilenames << fullCurrentFilename;
 
@@ -221,8 +226,8 @@ void Test::startTestsEvaluation() {
             QString expectedImageFilenameTail = currentFilename.left(currentFilename.length() - 4).right(NUM_DIGITS);
             QString expectedImageStoredFilename = EXPECTED_IMAGE_PREFIX + expectedImageFilenameTail + ".png";
 
-            QString imageURLString("https://github.com/" + githubUser + "/hifi_tests/blob/" + gitHubBranch + "/" + 
-                expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename + "?raw=true");
+            QString imageURLString("https://raw.githubusercontent.com/" + githubUser + "/hifi_tests/" + gitHubBranch + "/" + 
+                expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename);
 
             expectedImagesURLs << imageURLString;
 
@@ -230,26 +235,28 @@ void Test::startTestsEvaluation() {
             QString expectedImageFilename = currentFilename.replace("/", "_").replace(".", "_EI.");
 
             expectedImagesFilenames << expectedImageFilename;
-            expectedImagesFullFilenames << pathToTestResultsDirectory + "/" + expectedImageFilename;
+            expectedImagesFullFilenames << snapshotDirectory + "/" + expectedImageFilename;
         }
     }
 
-    autoTester->downloadImages(expectedImagesURLs, pathToTestResultsDirectory, expectedImagesFilenames);
+    autoTester->downloadImages(expectedImagesURLs, snapshotDirectory, expectedImagesFilenames);
 }
 
-void Test::finishTestsEvaluation(bool interactiveMode, QProgressBar* progressBar) {
-    bool success = compareImageLists(interactiveMode, progressBar);
+void Test::finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar) {
+    bool success = compareImageLists((!isRunningFromCommandline && interactiveMode), progressBar);
     
-    if (success) {
-        messageBox.information(0, "Success", "All images are as expected");
-    } else {
-        messageBox.information(0, "Failure", "One or more images are not as expected");
+    if (!isRunningFromCommandline) {
+        if (success) {
+            QMessageBox::information(0, "Success", "All images are as expected");
+        } else {
+            QMessageBox::information(0, "Failure", "One or more images are not as expected");
+        }
     }
 
     zipAndDeleteTestResultsFolder();
 }
 
-bool Test::isAValidDirectory(QString pathname) {
+bool Test::isAValidDirectory(const QString& pathname) {
     // Only process directories
     QDir dir(pathname);
     if (!dir.exists()) {
@@ -264,7 +271,7 @@ bool Test::isAValidDirectory(QString pathname) {
     return true;
 }
 
-QString Test::extractPathFromTestsDown(QString fullPath) {
+QString Test::extractPathFromTestsDown(const QString& fullPath) {
     // `fullPath` includes the full path to the test.  We need the portion below (and including) `tests`
     QStringList pathParts = fullPath.split('/');
     int i{ 0 };
@@ -273,7 +280,7 @@ QString Test::extractPathFromTestsDown(QString fullPath) {
     }
 
     if (i == pathParts.length()) {
-        messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Bad testPathname");
+        QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Bad testPathname");
         exit(-1);
     }
 
@@ -295,25 +302,39 @@ void Test::importTest(QTextStream& textStream, const QString& testPathname) {
 // This script will run all text.js scripts in every applicable sub-folder
 void Test::createRecursiveScript() {
     // Select folder to start recursing from
-    QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", ".", QFileDialog::ShowDirsOnly);
-    if (topLevelDirectory == "") {
+    QString previousSelection = testDirectory;
+
+    testDirectory =
+        QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script",
+                                          previousSelection, QFileDialog::ShowDirsOnly);
+
+    // If user cancelled then restore previous selection and return
+    if (testDirectory == "") {
+        testDirectory = previousSelection;
         return;
     }
 
-    createRecursiveScript(topLevelDirectory, true);
+    createRecursiveScript(testDirectory, true);
 }
 
 // This method creates a `testRecursive.js` script in every sub-folder.
 void Test::createAllRecursiveScripts() {
     // Select folder to start recursing from
-    QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts", ".", QFileDialog::ShowDirsOnly);
-    if (topLevelDirectory == "") {
+    QString previousSelection = testDirectory;
+
+    testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts",
+                                                      previousSelection,
+                                                      QFileDialog::ShowDirsOnly);
+
+    // If user cancelled then restore previous selection and return
+    if (testDirectory == "") {
+        testDirectory = previousSelection;
         return;
     }
 
-    createRecursiveScript(topLevelDirectory, false);
+    createRecursiveScript(testDirectory, false);
 
-    QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
+    QDirIterator it(testDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
     while (it.hasNext()) {
         QString directory = it.next();
 
@@ -342,14 +363,14 @@ void Test::createAllRecursiveScripts() {
         }
     }
 
-    messageBox.information(0, "Success", "Scripts have been created");
+    QMessageBox::information(0, "Success", "Scripts have been created");
 }
 
-void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode) {
+void Test::createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode) {
     const QString recursiveTestsFilename("testRecursive.js");
     QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename);
     if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) {
-        messageBox.critical(0,
+        QMessageBox::critical(0,
             "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
             "Failed to create \"" + recursiveTestsFilename + "\" in directory \"" + topLevelDirectory + "\""
         );
@@ -358,12 +379,15 @@ void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode
     }
 
     QTextStream textStream(&allTestsFilename);
-    textStream << "// This is an automatically generated file, created by auto-tester" << endl << endl;
+
+    const QString DATE_TIME_FORMAT("MMM d yyyy, h:mm");
+    textStream << "// This is an automatically generated file, created by auto-tester on " << QDateTime::currentDateTime().toString(DATE_TIME_FORMAT) << endl << endl;
 
     textStream << "var autoTester = Script.require(\"https://github.com/" + githubUser + "/hifi_tests/blob/" 
-        + gitHubBranch + "/tests/utils/autoTester.js?raw=true\");" << endl;
+        + gitHubBranch + "/tests/utils/autoTester.js?raw=true\");" << endl << endl;
 
-    textStream << "autoTester.enableRecursive();" << endl << endl;
+    textStream << "autoTester.enableRecursive();" << endl;
+    textStream << "autoTester.enableAuto();" << endl << endl;
 
     QVector<QString> testPathnames;
 
@@ -398,7 +422,7 @@ void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode
     }
 
     if (interactiveMode && testPathnames.length() <= 0) {
-        messageBox.information(0, "Failure", "No \"" + TEST_FILENAME + "\" files found");
+        QMessageBox::information(0, "Failure", "No \"" + TEST_FILENAME + "\" files found");
         allTestsFilename.close();
         return;
     }
@@ -409,48 +433,61 @@ void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode
     allTestsFilename.close();
     
     if (interactiveMode) {
-        messageBox.information(0, "Success", "Script has been created");
+        QMessageBox::information(0, "Success", "Script has been created");
     }
 }
 
 void Test::createTest() {
     // Rename files sequentially, as ExpectedResult_00000.jpeg, ExpectedResult_00001.jpg and so on
     // Any existing expected result images will be deleted
-    QString imageSourceDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
-    if (imageSourceDirectory == "") {
+    QString previousSelection = snapshotDirectory;
+
+    snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images",
+                                                          previousSelection,
+                                                          QFileDialog::ShowDirsOnly);
+
+    // If user cancelled then restore previous selection and return
+    if (snapshotDirectory == "") {
+        snapshotDirectory = previousSelection;
         return;
     }
 
-    QString imageDestinationDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder to save the test images", ".", QFileDialog::ShowDirsOnly);
-    if (imageDestinationDirectory == "") {
+    previousSelection = testDirectory;
+
+    QString testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder to save the test images",
+                                                              previousSelection, QFileDialog::ShowDirsOnly);
+
+    // If user cancelled then restore previous selection and return
+    if (testDirectory == "") {
+        testDirectory = previousSelection;
         return;
     }
 
-    QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("jpg", imageSourceDirectory);
+    QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("jpg", snapshotDirectory);
 
     int i = 1; 
     const int maxImages = pow(10, NUM_DIGITS);
     foreach (QString currentFilename, sortedImageFilenames) {
-        QString fullCurrentFilename = imageSourceDirectory + "/" + currentFilename;
+        QString fullCurrentFilename = snapshotDirectory + "/" + currentFilename;
         if (isInSnapshotFilenameFormat("jpg", currentFilename)) {
             if (i >= maxImages) {
-                messageBox.critical(0, "Error", "More than " + QString::number(maxImages) + " images not supported");
+                QMessageBox::critical(0, "Error", "More than " + QString::number(maxImages) + " images not supported");
                 exit(-1);
             }
             QString newFilename = "ExpectedImage_" + QString::number(i - 1).rightJustified(5, '0') + ".png";
-            QString fullNewFileName = imageDestinationDirectory + "/" + newFilename;
+            QString fullNewFileName = testDirectory + "/" + newFilename;
 
             try {
                 copyJPGtoPNG(fullCurrentFilename, fullNewFileName);
             } catch (...) {
-                messageBox.critical(0, "Error", "Could not delete existing file: " + currentFilename + "\nTest creation aborted");
+                QMessageBox::critical(0, "Error", "Could not delete existing file: " + currentFilename + "\nTest creation aborted");
                 exit(-1);
             }
             ++i;
         }
     }
 
-    messageBox.information(0, "Success", "Test images have been created");
+    QMessageBox::information(0, "Success", "Test images have been created");
 }
 
 ExtractedText Test::getTestScriptLines(QString testFileName) {
@@ -459,7 +496,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
     QFile inputFile(testFileName);
     inputFile.open(QIODevice::ReadOnly);
     if (!inputFile.isOpen()) {
-        messageBox.critical(0,
+        QMessageBox::critical(0,
             "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
             "Failed to open \"" + testFileName
         );
@@ -478,25 +515,6 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
     QString regexTestTitle(ws + functionPerformName + "\\(" + quotedString + "\\," + ws + ownPath + "\\," + ws + functionParameter + ws + "{" + ".*");
     QRegularExpression lineContainingTitle = QRegularExpression(regexTestTitle);
 
-    // Assert platform checks that test is running on the correct OS
-    const QString functionAssertPlatform(ws + "autoTester" + ws + "\\." + ws + "assertPlatform");
-    const QString regexAssertPlatform(ws + functionAssertPlatform + ws + "\\(" + ws + quotedString + ".*");
-    const QRegularExpression lineAssertPlatform = QRegularExpression(regexAssertPlatform);
-
-    // Assert display checks that test is running on the correct display
-    const QString functionAssertDisplay(ws + "autoTester" + ws + "\\." + ws + "assertDisplay");
-    const QString regexAssertDisplay(ws + functionAssertDisplay + ws + "\\(" + ws + quotedString + ".*");
-    const QRegularExpression lineAssertDisplay = QRegularExpression(regexAssertDisplay);
-
-    // Assert CPU checks that test is running on the correct type of CPU 
-    const QString functionAssertCPU(ws + "autoTester" + ws + "\\." + ws + "assertCPU");
-    const QString regexAssertCPU(ws + functionAssertCPU + ws + "\\(" + ws + quotedString + ".*");
-    const QRegularExpression lineAssertCPU = QRegularExpression(regexAssertCPU);
-
-    // Assert GPU checks that test is running on the correct type of GPU 
-    const QString functionAssertGPU(ws + "autoTester" + ws + "\\." + ws + "assertGPU");
-    const QString regexAssertGPU(ws + functionAssertGPU + ws + "\\(" + ws + quotedString + ".*");
-    const QRegularExpression lineAssertGPU = QRegularExpression(regexAssertGPU);
 
     // Each step is either of the following forms:
     //        autoTester.addStepSnapshot("Take snapshot"...
@@ -506,7 +524,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
     const QRegularExpression lineStepSnapshot = QRegularExpression(regexStepSnapshot);
 
     const QString functionAddStepName(ws + "autoTester" + ws + "\\." + ws + "addStep");
-    const QString regexStep(ws + functionAddStepName + ws + "\\(" + ws + quotedString + ws + "\\)" + ".*");
+    const QString regexStep(ws + functionAddStepName + ws + "\\(" + ws + quotedString + ".*");
     const QRegularExpression lineStep = QRegularExpression(regexStep);
 
     while (!line.isNull()) {
@@ -514,18 +532,6 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
         if (lineContainingTitle.match(line).hasMatch()) {
             QStringList tokens = line.split('"');
             relevantTextFromTest.title = tokens[1];
-        } else if (lineAssertPlatform.match(line).hasMatch()) {
-            QStringList platforms = line.split('"');
-            relevantTextFromTest.platform = platforms[1];
-        } else if (lineAssertDisplay.match(line).hasMatch()) {
-            QStringList displays = line.split('"');
-            relevantTextFromTest.display = displays[1];
-        } else if (lineAssertCPU.match(line).hasMatch()) {
-            QStringList cpus = line.split('"');
-            relevantTextFromTest.cpu = cpus[1];
-        } else if (lineAssertGPU.match(line).hasMatch()) {
-            QStringList gpus = line.split('"');
-            relevantTextFromTest.gpu = gpus[1];
         } else if (lineStepSnapshot.match(line).hasMatch()) {
             QStringList tokens = line.split('"');
             QString nameOfStep = tokens[1];
@@ -534,6 +540,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
             step->text = nameOfStep;
             step->takeSnapshot = true;
             relevantTextFromTest.stepList.emplace_back(step);
+
         } else if (lineStep.match(line).hasMatch()) {
             QStringList tokens = line.split('"');
             QString nameOfStep = tokens[1];
@@ -554,29 +561,43 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
 // The folder selected must contain a script named "test.js", the file produced is named "test.md"
 void Test::createMDFile() {
     // Folder selection
-    QString testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test", ".", QFileDialog::ShowDirsOnly);
+    QString previousSelection = testDirectory;
+
+    testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test", previousSelection,
+                                                      QFileDialog::ShowDirsOnly);
+
+    // If user cancelled then restore previous selection and return
     if (testDirectory == "") {
+        testDirectory = previousSelection;
         return;
     }
 
     createMDFile(testDirectory);
+
+    QMessageBox::information(0, "Success", "MD file has been created");
 }
 
 void Test::createAllMDFiles() {
     // Select folder to start recursing from
-    QString topLevelDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the MD files", ".", QFileDialog::ShowDirsOnly);
-    if (topLevelDirectory == "") {
+    QString previousSelection = testDirectory;
+
+    testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the MD files",
+                                                      previousSelection, QFileDialog::ShowDirsOnly);
+
+    // If user cancelled then restore previous selection and return
+    if (testDirectory == "") {
+        testDirectory = previousSelection;
         return;
     }
 
     // First test if top-level folder has a test.js file
-    const QString testPathname{ topLevelDirectory + "/" + TEST_FILENAME };
+    const QString testPathname { testDirectory + "/" + TEST_FILENAME };
     QFileInfo fileInfo(testPathname);
     if (fileInfo.exists()) {
-        createMDFile(topLevelDirectory);
+        createMDFile(testDirectory);
     }
 
-    QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
+    QDirIterator it(testDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
     while (it.hasNext()) {
         QString directory = it.next();
 
@@ -593,15 +614,15 @@ void Test::createAllMDFiles() {
         }
     }
 
-    messageBox.information(0, "Success", "MD files have been created");
+    QMessageBox::information(0, "Success", "MD files have been created");
 }
 
-void Test::createMDFile(QString testDirectory) {
+void Test::createMDFile(const QString& testDirectory) {
     // Verify folder contains test.js file
     QString testFileName(testDirectory + "/" + TEST_FILENAME);
     QFileInfo testFileInfo(testFileName);
     if (!testFileInfo.exists()) {
-        messageBox.critical(0, "Error", "Could not find file: " + TEST_FILENAME);
+        QMessageBox::critical(0, "Error", "Could not find file: " + TEST_FILENAME);
         return;
     }
 
@@ -610,7 +631,7 @@ void Test::createMDFile(QString testDirectory) {
     QString mdFilename(testDirectory + "/" + "test.md");
     QFile mdFile(mdFilename);
     if (!mdFile.open(QIODevice::WriteOnly)) {
-        messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
+        QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
         exit(-1);
     }
 
@@ -628,50 +649,6 @@ void Test::createMDFile(QString testDirectory) {
     stream << "## Preconditions" << "\n";
     stream << "- In an empty region of a domain with editing rights." << "\n\n";
 
-    // Platform
-    QStringList  platforms = testScriptLines.platform.split(" ");;
-    stream << "## Platforms\n";
-    stream << "Run the test on each of the following platforms\n";
-    for (int i = 0; i < platforms.size(); ++i) {
-        // Note that the platforms parameter may include extra spaces, these appear as empty strings in the list
-        if (platforms[i] != QString()) {
-            stream << " - " << platforms[i] << "\n";
-        }
-    }
-
-    // Display
-    QStringList  displays = testScriptLines.display.split(" ");
-    stream << "## Displays\n";
-    stream << "Run the test on each of the following displays\n";
-    for (int i = 0; i < displays.size(); ++i) {
-        // Note that the displays parameter may include extra spaces, these appear as empty strings in the list
-        if (displays[i] != QString()) {
-            stream << " - " << displays[i] << "\n";
-        }
-    }
-
-    // CPU
-    QStringList  cpus = testScriptLines.cpu.split(" ");
-    stream << "## Processors\n";
-    stream << "Run the test on each of the following processors\n";
-    for (int i = 0; i < cpus.size(); ++i) {
-        // Note that the cpus parameter may include extra spaces, these appear as empty strings in the list
-        if (cpus[i] != QString()) {
-            stream << " - " << cpus[i] << "\n";
-        }
-    }
-
-    // GPU
-    QStringList  gpus = testScriptLines.gpu.split(" ");
-    stream << "## Graphics Cards\n";
-    stream << "Run the test on graphics cards from each of the following vendors\n";
-    for (int i = 0; i < gpus.size(); ++i) {
-        // Note that the gpus parameter may include extra spaces, these appear as empty strings in the list
-        if (gpus[i] != QString()) {
-            stream << " - " << gpus[i] << "\n";
-        }
-    }
-
     stream << "## Steps\n";
     stream << "Press space bar to advance step by step\n\n";
 
@@ -679,7 +656,7 @@ void Test::createMDFile(QString testDirectory) {
     for (size_t i = 0; i < testScriptLines.stepList.size(); ++i) {
         stream << "### Step " << QString::number(i + 1) << "\n";
         stream << "- " << testScriptLines.stepList[i]->text << "\n";
-        if (testScriptLines.stepList[i]->takeSnapshot) {
+        if ((i + 1 < testScriptLines.stepList.size()) && testScriptLines.stepList[i]->takeSnapshot) {
             stream << "- ![](./ExpectedImage_" << QString::number(snapShotIndex).rightJustified(5, '0') << ".png)\n";
             ++snapShotIndex;
         }
@@ -689,16 +666,22 @@ void Test::createMDFile(QString testDirectory) {
 }
 
 void Test::createTestsOutline() {
-    QString testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", ".", QFileDialog::ShowDirsOnly);
-    if (testsRootDirectory == "") {
+    QString previousSelection = testDirectory;
+
+    testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", previousSelection,
+                                                      QFileDialog::ShowDirsOnly);
+
+    // If user cancelled then restore previous selection and return
+    if (testDirectory == "") {
+        testDirectory = previousSelection;
         return;
     }
 
     const QString testsOutlineFilename { "testsOutline.md" };
-    QString mdFilename(testsRootDirectory + "/" + testsOutlineFilename);
+    QString mdFilename(testDirectory + "/" + testsOutlineFilename);
     QFile mdFile(mdFilename);
     if (!mdFile.open(QIODevice::WriteOnly)) {
-        messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
+        QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
         exit(-1);
     }
 
@@ -709,10 +692,10 @@ void Test::createTestsOutline() {
     stream << "Directories with an appended (*) have an automatic test\n\n";
 
     // We need to know our current depth, as this isn't given by QDirIterator
-    int rootDepth { testsRootDirectory.count('/') };
+    int rootDepth { testDirectory.count('/') };
 
     // Each test is shown as the folder name linking to the matching GitHub URL, and the path to the associated test.md file
-    QDirIterator it(testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
+    QDirIterator it(testDirectory.toStdString().c_str(), QDirIterator::Subdirectories);
     while (it.hasNext()) {
         QString directory = it.next();
 
@@ -756,10 +739,10 @@ void Test::createTestsOutline() {
 
     mdFile.close();
 
-    messageBox.information(0, "Success", "Test outline file " + testsOutlineFilename + " has been created");
+    QMessageBox::information(0, "Success", "Test outline file " + testsOutlineFilename + " has been created");
 }
 
-void Test::copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFullFilename) {
+void Test::copyJPGtoPNG(const QString& sourceJPGFullFilename, const QString& destinationPNGFullFilename) {
     QFile::remove(destinationPNGFullFilename);
 
     QImageReader reader;
@@ -772,7 +755,7 @@ void Test::copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFul
     writer.write(image);
 }
 
-QStringList Test::createListOfAll_imagesInDirectory(QString imageFormat, QString pathToImageDirectory) {
+QStringList Test::createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory) {
     imageDirectory = QDir(pathToImageDirectory);
     QStringList nameFilters;
     nameFilters << "*." + imageFormat;
@@ -785,7 +768,7 @@ QStringList Test::createListOfAll_imagesInDirectory(QString imageFormat, QString
 //      Filename (i.e. without extension) contains _tests_ (this is based on all test scripts being within the tests folder
 //      Last 5 characters in filename are digits
 //      Extension is jpg
-bool Test::isInSnapshotFilenameFormat(QString imageFormat, QString filename) {
+bool Test::isInSnapshotFilenameFormat(const QString& imageFormat, const QString& filename) {
     QStringList filenameParts = filename.split(".");
 
     bool filnameHasNoPeriods = (filenameParts.size() == 2);
@@ -802,7 +785,7 @@ bool Test::isInSnapshotFilenameFormat(QString imageFormat, QString filename) {
 // For a file named "D_GitHub_hifi-tests_tests_content_entity_zone_create_0.jpg", the test directory is
 // D:/GitHub/hifi-tests/tests/content/entity/zone/create
 // This method assumes the filename is in the correct format
-QString Test::getExpectedImageDestinationDirectory(QString filename) {
+QString Test::getExpectedImageDestinationDirectory(const QString& filename) {
     QString filenameWithoutExtension = filename.split(".")[0];
     QStringList filenameParts = filenameWithoutExtension.split("_");
 
@@ -819,7 +802,7 @@ QString Test::getExpectedImageDestinationDirectory(QString filename) {
 // is ...tests/content/entity/zone/create
 // This is used to create the full URL
 // This method assumes the filename is in the correct format
-QString Test::getExpectedImagePartialSourceDirectory(QString filename) {
+QString Test::getExpectedImagePartialSourceDirectory(const QString& filename) {
     QString filenameWithoutExtension = filename.split(".")[0];
     QStringList filenameParts = filenameWithoutExtension.split("_");
 
@@ -831,7 +814,7 @@ QString Test::getExpectedImagePartialSourceDirectory(QString filename) {
     }
 
     if (i < 0) {
-        messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Bad filename");
+        QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Bad filename");
         exit(-1);
     }
 
diff --git a/tools/auto-tester/src/Test.h b/tools/auto-tester/src/Test.h
index e69459fef2..0fb957d309 100644
--- a/tools/auto-tester/src/Test.h
+++ b/tools/auto-tester/src/Test.h
@@ -30,10 +30,6 @@ using StepList = std::vector<Step*>;
 class ExtractedText {
 public:
     QString title;
-    QString platform;
-    QString display;
-    QString cpu;
-    QString gpu;
     StepList stepList;
 };
 
@@ -41,61 +37,64 @@ class Test {
 public: 
     Test();
 
-    void startTestsEvaluation();
-    void finishTestsEvaluation(bool interactiveMode, QProgressBar* progressBar);
+    void startTestsEvaluation(const QString& testFolder = QString());
+    void finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar);
 
     void createRecursiveScript();
     void createAllRecursiveScripts();
-    void createRecursiveScript(QString topLevelDirectory, bool interactiveMode);
+    void createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode);
 
     void createTest();
     void createMDFile();
     void createAllMDFiles();
-    void createMDFile(QString topLevelDirectory);
+    void createMDFile(const QString& topLevelDirectory);
 
     void createTestsOutline();
 
     bool compareImageLists(bool isInteractiveMode, QProgressBar* progressBar);
 
-    QStringList createListOfAll_imagesInDirectory(QString imageFormat, QString pathToImageDirectory);
+    QStringList createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory);
 
-    bool isInSnapshotFilenameFormat(QString imageFormat, QString filename);
+    bool isInSnapshotFilenameFormat(const QString& imageFormat, const QString& filename);
 
     void importTest(QTextStream& textStream, const QString& testPathname);
 
-    void appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage);
+    void appendTestResultsToFile(const QString& testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage);
 
-    bool createTestResultsFolderPath(QString directory);
+    bool createTestResultsFolderPath(const QString& directory);
     void zipAndDeleteTestResultsFolder();
 
-    bool isAValidDirectory(QString pathname);
-	QString extractPathFromTestsDown(QString fullPath);
-    QString getExpectedImageDestinationDirectory(QString filename);
-    QString getExpectedImagePartialSourceDirectory(QString filename);
+    bool isAValidDirectory(const QString& pathname);
+	QString extractPathFromTestsDown(const QString& fullPath);
+    QString getExpectedImageDestinationDirectory(const QString& filename);
+    QString getExpectedImagePartialSourceDirectory(const QString& filename);
 
-    void copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFullFilename);
+    void copyJPGtoPNG(const QString& sourceJPGFullFilename, const QString& destinationPNGFullFilename);
 
 private:
     const QString TEST_FILENAME { "test.js" };
     const QString TEST_RESULTS_FOLDER { "TestResults" };
     const QString TEST_RESULTS_FILENAME { "TestResults.txt" };
 
-    QMessageBox messageBox;
-
     QDir imageDirectory;
 
     MismatchWindow mismatchWindow;
 
     ImageComparer imageComparer;
 
-    QString testResultsFolderPath { "" };
+    QString testResultsFolderPath;
     int index { 1 };
 
     // Expected images are in the format ExpectedImage_dddd.jpg (d == decimal digit)
     const int NUM_DIGITS { 5 };
     const QString EXPECTED_IMAGE_PREFIX { "ExpectedImage_" };
 
-    QString pathToTestResultsDirectory;
+    // We have two directories to work with.
+    // The first is the directory containing the test we are working with
+    // The second contains the snapshots taken for test runs that need to be evaluated
+    QString testDirectory;
+    QString snapshotDirectory;
+
     QStringList expectedImagesFilenames;
     QStringList expectedImagesFullFilenames;
     QStringList resultImagesFullFilenames;
diff --git a/tools/auto-tester/src/main.cpp b/tools/auto-tester/src/main.cpp
index cd0ce22b13..ffa7a0b237 100644
--- a/tools/auto-tester/src/main.cpp
+++ b/tools/auto-tester/src/main.cpp
@@ -13,10 +13,23 @@
 AutoTester* autoTester;
 
 int main(int argc, char *argv[]) {
+    // Only parameter is "--testFolder"
+    QString testFolder;
+    if (argc == 3) {
+        if (QString(argv[1]) == "--testFolder") {
+            testFolder = QString(argv[2]);
+        }
+    }
+
     QApplication application(argc, argv);
 
     autoTester = new AutoTester();
-    autoTester->show();
+
+    if (!testFolder.isNull()) {
+        autoTester->runFromCommandLine(testFolder);
+    } else {
+        autoTester->show();
+    }
 
     return application.exec();
 }
diff --git a/tools/auto-tester/src/ui/AutoTester.cpp b/tools/auto-tester/src/ui/AutoTester.cpp
index 21acfe9569..db9974a250 100644
--- a/tools/auto-tester/src/ui/AutoTester.cpp
+++ b/tools/auto-tester/src/ui/AutoTester.cpp
@@ -15,9 +15,17 @@ AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
     ui.checkBoxInteractiveMode->setChecked(true);
     ui.progressBar->setVisible(false);
 
-    test = new Test();
-
     signalMapper = new QSignalMapper();
+
+    connect(ui.actionClose, &QAction::triggered, this, &AutoTester::on_closeButton_clicked);
+    connect(ui.actionAbout, &QAction::triggered, this, &AutoTester::about);
+
+    test = new Test();
+}
+
+void AutoTester::runFromCommandLine(const QString& testFolder) {
+    isRunningFromCommandline = true;
+    test->startTestsEvaluation(testFolder);
 }
 
 void AutoTester::on_evaluateTestsButton_clicked() {
@@ -90,13 +98,21 @@ void AutoTester::saveImage(int index) {
     image = image.convertToFormat(QImage::Format_ARGB32);
 
     QString fullPathname = _directoryName + "/" + _filenames[index];
-    image.save(fullPathname, 0, 100);
+    if (!image.save(fullPathname, 0, 100)) {
+        QMessageBox::information(0, "Test Aborted", "Failed to save image: " + _filenames[index]);
+        ui.progressBar->setVisible(false);
+        return;
+    }
 
     ++_numberOfImagesDownloaded;
 
     if (_numberOfImagesDownloaded == _numberOfImagesToDownload) {
-        test->finishTestsEvaluation(ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
+        test->finishTestsEvaluation(isRunningFromCommandline, ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
     } else {
         ui.progressBar->setValue(_numberOfImagesDownloaded);
     }
 }
+
+void AutoTester::about() {
+    QMessageBox::information(0, "About", QString("Built ") + __DATE__ + " : " + __TIME__);
+}
diff --git a/tools/auto-tester/src/ui/AutoTester.h b/tools/auto-tester/src/ui/AutoTester.h
index 1788e97177..fe37f2298d 100644
--- a/tools/auto-tester/src/ui/AutoTester.h
+++ b/tools/auto-tester/src/ui/AutoTester.h
@@ -22,6 +22,9 @@ class AutoTester : public QMainWindow {
 
 public:
     AutoTester(QWidget *parent = Q_NULLPTR);
+
+    void runFromCommandLine(const QString& testFolder);
+
     void downloadImage(const QUrl& url);
     void downloadImages(const QStringList& URLs, const QString& directoryName, const QStringList& filenames);
 
@@ -37,6 +40,8 @@ private slots:
 
     void saveImage(int index);
 
+    void about();
+
 private:
     Ui::AutoTesterClass ui;
     Test* test;
@@ -50,9 +55,11 @@ private:
     // Used to enable passing a parameter to slots
     QSignalMapper* signalMapper;
 
-    int _numberOfImagesToDownload;
-    int _numberOfImagesDownloaded;
-    int _index;
+    int _numberOfImagesToDownload { 0 };
+    int _numberOfImagesDownloaded { 0 };
+    int _index { 0 };
+
+    bool isRunningFromCommandline { false };
 };
 
 #endif // hifi_AutoTester_h
\ No newline at end of file
diff --git a/tools/auto-tester/src/ui/AutoTester.ui b/tools/auto-tester/src/ui/AutoTester.ui
index 2eb1314481..8c534eb7c7 100644
--- a/tools/auto-tester/src/ui/AutoTester.ui
+++ b/tools/auto-tester/src/ui/AutoTester.ui
@@ -95,7 +95,7 @@
      <number>24</number>
     </property>
    </widget>
-   <widget class="QPushButton" name="createRecursiveScriptsRecursivelyButton">
+   <widget class="QPushButton" name="createAllRecursiveScriptsButton">
     <property name="geometry">
      <rect>
       <x>360</x>
@@ -157,6 +157,20 @@
      <height>21</height>
     </rect>
    </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionClose"/>
+   </widget>
+   <widget class="QMenu" name="menuHelp">
+    <property name="title">
+     <string>Help</string>
+    </property>
+    <addaction name="actionAbout"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuHelp"/>
   </widget>
   <widget class="QToolBar" name="mainToolBar">
    <attribute name="toolBarArea">
@@ -167,6 +181,16 @@
    </attribute>
   </widget>
   <widget class="QStatusBar" name="statusBar"/>
+  <action name="actionClose">
+   <property name="text">
+    <string>Close</string>
+   </property>
+  </action>
+  <action name="actionAbout">
+   <property name="text">
+    <string>About</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <resources/>
diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp
index f9e7a76142..0301fad6f4 100644
--- a/tools/ice-client/src/ICEClientApp.cpp
+++ b/tools/ice-client/src/ICEClientApp.cpp
@@ -9,15 +9,16 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ICEClientApp.h"
+
 #include <QDataStream>
 #include <QLoggingCategory>
 #include <QCommandLineParser>
+
 #include <PathUtils.h>
 #include <LimitedNodeList.h>
 #include <NetworkLogging.h>
 
-#include "ICEClientApp.h"
-
 ICEClientApp::ICEClientApp(int argc, char* argv[]) :
     QCoreApplication(argc, argv)
 {
diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp
index 35550cdca8..a7b8401269 100644
--- a/tools/oven/src/BakerCLI.cpp
+++ b/tools/oven/src/BakerCLI.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "BakerCLI.h"
+
 #include <QObject>
 #include <QImageReader>
 #include <QtCore/QDebug>
@@ -16,7 +18,6 @@
 
 #include "OvenCLIApplication.h"
 #include "ModelBakingLoggingCategory.h"
-#include "BakerCLI.h"
 #include "FBXBaker.h"
 #include "JSBaker.h"
 #include "TextureBaker.h"
diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp
index 3c6799db88..0a75c72f9a 100644
--- a/tools/oven/src/DomainBaker.cpp
+++ b/tools/oven/src/DomainBaker.cpp
@@ -464,7 +464,7 @@ bool DomainBaker::rewriteSkyboxURL(QJsonValueRef urlValue, TextureBaker* baker)
     if (oldSkyboxURL.matches(baker->getTextureURL(), QUrl::RemoveQuery | QUrl::RemoveFragment)) {
         // change the URL to point to the baked texture with its original query and fragment
 
-        auto newSkyboxURL = _destinationPath.resolved(baker->getBakedTextureFileName());
+        auto newSkyboxURL = _destinationPath.resolved(baker->getMetaTextureFileName());
         newSkyboxURL.setQuery(oldSkyboxURL.query());
         newSkyboxURL.setFragment(oldSkyboxURL.fragment());
         newSkyboxURL.setUserInfo(oldSkyboxURL.userInfo());
diff --git a/tools/oven/src/OvenCLIApplication.cpp b/tools/oven/src/OvenCLIApplication.cpp
index 2fb8ea03f2..ab3178db01 100644
--- a/tools/oven/src/OvenCLIApplication.cpp
+++ b/tools/oven/src/OvenCLIApplication.cpp
@@ -9,13 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OvenCLIApplication.h"
+
 #include <QtCore/QCommandLineParser>
 #include <QtCore/QUrl>
 
 #include "BakerCLI.h"
 
-#include "OvenCLIApplication.h"
-
 static const QString CLI_INPUT_PARAMETER = "i";
 static const QString CLI_OUTPUT_PARAMETER = "o";
 static const QString CLI_TYPE_PARAMETER = "t";
diff --git a/tools/oven/src/ui/BakeWidget.cpp b/tools/oven/src/ui/BakeWidget.cpp
index 43f4c50328..931ef1de43 100644
--- a/tools/oven/src/ui/BakeWidget.cpp
+++ b/tools/oven/src/ui/BakeWidget.cpp
@@ -9,12 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "BakeWidget.h"
+
 #include <QtWidgets/QStackedWidget>
 
 #include "../OvenGUIApplication.h"
 
-#include "BakeWidget.h"
-
 BakeWidget::BakeWidget(QWidget* parent, Qt::WindowFlags flags) :
     QWidget(parent, flags)
 {
diff --git a/tools/oven/src/ui/DomainBakeWidget.cpp b/tools/oven/src/ui/DomainBakeWidget.cpp
index bf79319458..1121041e39 100644
--- a/tools/oven/src/ui/DomainBakeWidget.cpp
+++ b/tools/oven/src/ui/DomainBakeWidget.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "DomainBakeWidget.h"
+
 #include <QtConcurrent>
 
 #include <QtWidgets/QCheckBox>
@@ -23,8 +25,6 @@
 
 #include "../OvenGUIApplication.h"
 
-#include "DomainBakeWidget.h"
-
 static const QString DOMAIN_NAME_SETTING_KEY = "domain_name";
 static const QString EXPORT_DIR_SETTING_KEY = "domain_export_directory";
 static const QString BROWSE_START_DIR_SETTING_KEY = "domain_search_directory";
diff --git a/tools/oven/src/ui/ModelBakeWidget.cpp b/tools/oven/src/ui/ModelBakeWidget.cpp
index f80185df0f..9fa586871e 100644
--- a/tools/oven/src/ui/ModelBakeWidget.cpp
+++ b/tools/oven/src/ui/ModelBakeWidget.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ModelBakeWidget.h"
+
 #include <QtWidgets/QFileDialog>
 #include <QtWidgets/QGridLayout>
 #include <QtWidgets/QLabel>
@@ -26,7 +28,6 @@
 #include "OvenMainWindow.h"
 #include "FBXBaker.h"
 #include "OBJBaker.h"
-#include "ModelBakeWidget.h"
 
 
 static const auto EXPORT_DIR_SETTING_KEY = "model_export_directory";
diff --git a/tools/oven/src/ui/ModesWidget.cpp b/tools/oven/src/ui/ModesWidget.cpp
index 624aa949cc..1fdfce2c97 100644
--- a/tools/oven/src/ui/ModesWidget.cpp
+++ b/tools/oven/src/ui/ModesWidget.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ModesWidget.h"
+
 #include <QtWidgets/QHBoxLayout>
 #include <QtWidgets/QPushButton>
 #include <QtWidgets/QStackedWidget>
@@ -17,8 +19,6 @@
 #include "ModelBakeWidget.h"
 #include "SkyboxBakeWidget.h"
 
-#include "ModesWidget.h"
-
 ModesWidget::ModesWidget(QWidget* parent, Qt::WindowFlags flags) :
     QWidget(parent, flags)
 {
diff --git a/tools/oven/src/ui/OvenMainWindow.cpp b/tools/oven/src/ui/OvenMainWindow.cpp
index bebc2fa7dc..59cad3aac5 100644
--- a/tools/oven/src/ui/OvenMainWindow.cpp
+++ b/tools/oven/src/ui/OvenMainWindow.cpp
@@ -9,12 +9,12 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "OvenMainWindow.h"
+
 #include <QtWidgets/QStackedWidget>
 
 #include "ModesWidget.h"
 
-#include "OvenMainWindow.h"
-
 OvenMainWindow::OvenMainWindow(QWidget *parent, Qt::WindowFlags flags) :
     QMainWindow(parent, flags)
 {
diff --git a/tools/oven/src/ui/ResultsWindow.cpp b/tools/oven/src/ui/ResultsWindow.cpp
index 3a37a328de..feb7fbc4f1 100644
--- a/tools/oven/src/ui/ResultsWindow.cpp
+++ b/tools/oven/src/ui/ResultsWindow.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "ResultsWindow.h"
+
 #include <QtCore/QUrl>
 #include <QtGui/QDesktopServices>
 #include <QtWidgets/QHeaderView>
@@ -17,8 +19,6 @@
 
 #include "OvenMainWindow.h"
 
-#include "ResultsWindow.h"
-
 ResultsWindow::ResultsWindow(QWidget* parent) :
     QWidget(parent)
 {
diff --git a/tools/oven/src/ui/SkyboxBakeWidget.cpp b/tools/oven/src/ui/SkyboxBakeWidget.cpp
index 369b06c39f..71ae0cbab0 100644
--- a/tools/oven/src/ui/SkyboxBakeWidget.cpp
+++ b/tools/oven/src/ui/SkyboxBakeWidget.cpp
@@ -9,6 +9,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "SkyboxBakeWidget.h"
+
 #include <QtWidgets/QFileDialog>
 #include <QtWidgets/QGridLayout>
 #include <QtWidgets/QLabel>
@@ -21,9 +23,9 @@
 #include <QtCore/QDebug>
 #include <QtCore/QThread>
 
-#include "../OvenGUIApplication.h"
+#include <TextureBaker.h>
 
-#include "SkyboxBakeWidget.h"
+#include "../OvenGUIApplication.h"
 
 static const auto EXPORT_DIR_SETTING_KEY = "skybox_export_directory";
 static const auto SELECTION_START_DIR_SETTING_KEY = "skybox_search_directory";
diff --git a/tools/vhacd-util/src/VHACDUtilApp.cpp b/tools/vhacd-util/src/VHACDUtilApp.cpp
index 4d48bdf2bf..c263dce609 100644
--- a/tools/vhacd-util/src/VHACDUtilApp.cpp
+++ b/tools/vhacd-util/src/VHACDUtilApp.cpp
@@ -9,10 +9,13 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include "VHACDUtilApp.h"
+
 #include <QCommandLineParser>
+
 #include <Trace.h>
 #include <VHACD.h>
-#include "VHACDUtilApp.h"
+
 #include "VHACDUtil.h"
 #include "PathUtils.h"