diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp
index 7922da8af4..7db06f12c0 100644
--- a/assignment-client/src/octree/OctreeSendThread.cpp
+++ b/assignment-client/src/octree/OctreeSendThread.cpp
@@ -443,13 +443,8 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
                                               (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
 
                     EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
-                                                 viewFrustumChanged,
-                                                 boundaryLevelAdjust, octreeSizeScale,
-                                                 nodeData->getLastTimeBagEmpty(),
-                                                 isFullScene, &nodeData->stats, _myServer->getJurisdiction(),
-                                                 &nodeData->extraEncodeData,
-                                                 nodeData->getUsesFrustum(),
-                                                 nodeData);
+                                                 viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
+                                                 isFullScene, _myServer->getJurisdiction(), nodeData);
                     nodeData->copyCurrentViewFrustum(params.viewFrustum);
                     if (viewFrustumChanged) {
                         nodeData->copyLastKnownViewFrustum(params.lastViewFrustum);
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index 163b4d9e45..878058ad13 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -125,7 +125,7 @@ public:
     void markAsChangedOnServer() { _changedOnServer = usecTimestampNow();  }
     quint64 getLastChangedOnServer() const { return _changedOnServer; }
 
-    // TODO: eventually only include properties changed since the params.lastQuerySent time
+    // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
     virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
 
     virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,
diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp
index 755c19e625..98287541e3 100644
--- a/libraries/entities/src/EntityTreeElement.cpp
+++ b/libraries/entities/src/EntityTreeElement.cpp
@@ -51,7 +51,10 @@ void EntityTreeElement::debugExtraEncodeData(EncodeBitstreamParams& params) cons
     qCDebug(entities) << "EntityTreeElement::debugExtraEncodeData()... ";
     qCDebug(entities) << "    element:" << _cube;
 
-    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
+    auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
+    assert(entityNodeData);
+
+    OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
     assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
 
     if (extraEncodeData->contains(this)) {
@@ -65,7 +68,11 @@ void EntityTreeElement::debugExtraEncodeData(EncodeBitstreamParams& params) cons
 
 void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params) {
 
-    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
+    auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
+    assert(entityNodeData);
+
+    OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
+
     assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
     // Check to see if this element yet has encode data... if it doesn't create it
     if (!extraEncodeData->contains(this)) {
@@ -93,7 +100,11 @@ void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params)
 }
 
 bool EntityTreeElement::shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const {
-    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
+
+    auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
+    assert(entityNodeData);
+
+    OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
     assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
     
     if (extraEncodeData->contains(this)) {
@@ -122,7 +133,10 @@ bool EntityTreeElement::shouldRecurseChildTree(int childIndex, EncodeBitstreamPa
 }
 
 bool EntityTreeElement::alreadyFullyEncoded(EncodeBitstreamParams& params) const {
-    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
+    auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
+    assert(entityNodeData);
+
+    OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
     assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
 
     if (extraEncodeData->contains(this)) {
@@ -137,8 +151,12 @@ bool EntityTreeElement::alreadyFullyEncoded(EncodeBitstreamParams& params) const
 }
 
 void EntityTreeElement::updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const {
-    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
+    auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
+    assert(entityNodeData);
+
+    OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
     assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
+
     if (extraEncodeData->contains(this)) {
         EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData
                         = std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
@@ -161,7 +179,10 @@ void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params) con
         qCDebug(entities) << "EntityTreeElement::elementEncodeComplete() element:" << _cube;
     }
 
-    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
+    auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
+    assert(entityNodeData);
+
+    OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
     assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
     assert(extraEncodeData->contains(this));
 
@@ -236,8 +257,12 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
 
     OctreeElement::AppendState appendElementState = OctreeElement::COMPLETED; // assume the best...
 
+    auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
+    Q_ASSERT_X(entityNodeData, "EntityTreeElement::appendElementData", "expected params.nodeData not to be null");
+
     // first, check the params.extraEncodeData to see if there's any partial re-encode data for this element
-    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;
+    OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
+
     EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData = NULL;
     bool hadElementExtraData = false;
     if (extraEncodeData && extraEncodeData->contains(this)) {
@@ -285,20 +310,17 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
         // need to handle the case where our sibling elements need encoding but we don't.
         if (!entityTreeElementExtraEncodeData->elementCompleted) {
 
-            QJsonObject jsonFilters;
-            auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
 
-            if (entityNodeData) {
-                // we have an EntityNodeData instance
-                // so we should assume that means we might have JSON filters to check
-                jsonFilters = entityNodeData->getJSONParameters();
-            }
+            // we have an EntityNodeData instance
+            // so we should assume that means we might have JSON filters to check
+            auto jsonFilters = entityNodeData->getJSONParameters();
+
 
             for (uint16_t i = 0; i < _entityItems.size(); i++) {
                 EntityItemPointer entity = _entityItems[i];
                 bool includeThisEntity = true;
 
-                if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastQuerySent) {
+                if (!params.forceSendScene && entity->getLastChangedOnServer() < entityNodeData->getLastTimeBagEmpty()) {
                     includeThisEntity = false;
                 }
 
@@ -330,7 +352,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
 
                 // we only check the bounds against our frustum and LOD if the query has asked us to check against the frustum
                 // which can sometimes not be the case when JSON filters are sent
-                if (params.usesFrustum && (includeThisEntity || params.recurseEverything)) {
+                if (entityNodeData->getUsesFrustum() && (includeThisEntity || params.recurseEverything)) {
 
                     // we want to use the maximum possible box for this, so that we don't have to worry about the nuance of
                     // simulation changing what's visible. consider the case where the entity contains an angular velocity
diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp
index e09822f028..bc8e55e118 100644
--- a/libraries/entities/src/LightEntityItem.cpp
+++ b/libraries/entities/src/LightEntityItem.cpp
@@ -174,7 +174,7 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
 }
 
 
-// TODO: eventually only include properties changed since the params.lastQuerySent time
+// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
 EntityPropertyFlags LightEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
     EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
     requestedProperties += PROP_IS_SPOTLIGHT;
diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp
index 8ace665616..80e3f65fb3 100644
--- a/libraries/entities/src/LineEntityItem.cpp
+++ b/libraries/entities/src/LineEntityItem.cpp
@@ -126,7 +126,7 @@ int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
 }
 
 
-// TODO: eventually only include properties changed since the params.lastQuerySent time
+// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
 EntityPropertyFlags LineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
     EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
     requestedProperties += PROP_COLOR;
diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h
index 8629c94eb4..ff64c7c17e 100644
--- a/libraries/entities/src/LineEntityItem.h
+++ b/libraries/entities/src/LineEntityItem.h
@@ -26,7 +26,7 @@ class LineEntityItem : public EntityItem {
     virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
     virtual bool setProperties(const EntityItemProperties& properties) override;
 
-    // TODO: eventually only include properties changed since the params.lastQuerySent time
+    // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
     virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
 
     virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp
index 911ff224b2..51d9c2ad25 100644
--- a/libraries/entities/src/ModelEntityItem.cpp
+++ b/libraries/entities/src/ModelEntityItem.cpp
@@ -160,7 +160,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
     return bytesRead;
 }
 
-// TODO: eventually only include properties changed since the params.lastQuerySent time
+// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
 EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
     EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
 
diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h
index e1cb5cd92c..5076a43892 100644
--- a/libraries/entities/src/ModelEntityItem.h
+++ b/libraries/entities/src/ModelEntityItem.h
@@ -29,7 +29,7 @@ public:
     virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
     virtual bool setProperties(const EntityItemProperties& properties) override;
 
-    // TODO: eventually only include properties changed since the params.lastQuerySent time
+    // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
     virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
 
     virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp
index 140522b00e..fc2d128bd1 100644
--- a/libraries/entities/src/ParticleEffectEntityItem.cpp
+++ b/libraries/entities/src/ParticleEffectEntityItem.cpp
@@ -469,7 +469,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
 }
 
 
-// TODO: eventually only include properties changed since the params.lastQuerySent time
+// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
 EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
     EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
 
diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp
index 7abafad627..473d9c1131 100644
--- a/libraries/entities/src/PolyLineEntityItem.cpp
+++ b/libraries/entities/src/PolyLineEntityItem.cpp
@@ -170,7 +170,7 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da
 }
 
 
-// TODO: eventually only include properties changed since the params.lastQuerySent time
+// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
 EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
     EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
     requestedProperties += PROP_COLOR;
diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h
index 5f9f9124cf..3b3cf6e6ba 100644
--- a/libraries/entities/src/PolyLineEntityItem.h
+++ b/libraries/entities/src/PolyLineEntityItem.h
@@ -26,7 +26,7 @@ class PolyLineEntityItem : public EntityItem {
     virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
     virtual bool setProperties(const EntityItemProperties& properties) override;
 
-    // TODO: eventually only include properties changed since the params.lastQuerySent time
+    // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
     virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
 
     virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp
index 90344d6c4b..dc794f1dcc 100644
--- a/libraries/entities/src/PolyVoxEntityItem.cpp
+++ b/libraries/entities/src/PolyVoxEntityItem.cpp
@@ -179,7 +179,7 @@ int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* dat
 }
 
 
-// TODO: eventually only include properties changed since the params.lastQuerySent time
+// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
 EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
     EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
     requestedProperties += PROP_VOXEL_VOLUME_SIZE;
diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h
index 311a002a4a..db30e54b61 100644
--- a/libraries/entities/src/PolyVoxEntityItem.h
+++ b/libraries/entities/src/PolyVoxEntityItem.h
@@ -26,7 +26,7 @@ class PolyVoxEntityItem : public EntityItem {
     virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
     virtual bool setProperties(const EntityItemProperties& properties) override;
 
-    // TODO: eventually only include properties changed since the params.lastQuerySent time
+    // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
     virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
 
     virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp
index 345d9e54ab..018d8c568a 100644
--- a/libraries/entities/src/ShapeEntityItem.cpp
+++ b/libraries/entities/src/ShapeEntityItem.cpp
@@ -137,7 +137,7 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
 }
 
 
-// TODO: eventually only include properties changed since the params.lastQuerySent time
+// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
 EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
     EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
     requestedProperties += PROP_SHAPE;
diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp
index fbb0bdc9cf..366fdc7aa2 100644
--- a/libraries/entities/src/TextEntityItem.cpp
+++ b/libraries/entities/src/TextEntityItem.cpp
@@ -99,7 +99,7 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
 }
 
 
-// TODO: eventually only include properties changed since the params.lastQuerySent time
+// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
 EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
     EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
     requestedProperties += PROP_TEXT;
diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h
index 633aa96bfa..c706423cbd 100644
--- a/libraries/entities/src/TextEntityItem.h
+++ b/libraries/entities/src/TextEntityItem.h
@@ -30,7 +30,7 @@ public:
     virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
     virtual bool setProperties(const EntityItemProperties& properties) override;
 
-    // TODO: eventually only include properties changed since the params.lastQuerySent time
+    // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
     virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
 
     virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp
index 182d58ba36..40da2a0af2 100644
--- a/libraries/entities/src/WebEntityItem.cpp
+++ b/libraries/entities/src/WebEntityItem.cpp
@@ -84,7 +84,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
 }
 
 
-// TODO: eventually only include properties changed since the params.lastQuerySent time
+// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
 EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
     EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
     requestedProperties += PROP_SOURCE_URL;
diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h
index 19a7b577fe..483c2bbc8a 100644
--- a/libraries/entities/src/WebEntityItem.h
+++ b/libraries/entities/src/WebEntityItem.h
@@ -29,7 +29,7 @@ public:
     virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
     virtual bool setProperties(const EntityItemProperties& properties) override;
 
-    // TODO: eventually only include properties changed since the params.lastQuerySent time
+    // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
     virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
 
     virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp
index 37b3be99a3..6d1facc979 100644
--- a/libraries/entities/src/ZoneEntityItem.cpp
+++ b/libraries/entities/src/ZoneEntityItem.cpp
@@ -137,7 +137,7 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
 }
 
 
-// TODO: eventually only include properties changed since the params.lastQuerySent time
+// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
 EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
     EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
 
diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h
index 2bef95e452..6a6326d067 100644
--- a/libraries/entities/src/ZoneEntityItem.h
+++ b/libraries/entities/src/ZoneEntityItem.h
@@ -30,7 +30,7 @@ public:
     virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
     virtual bool setProperties(const EntityItemProperties& properties) override;
 
-    // TODO: eventually only include properties changed since the params.lastQuerySent time
+    // TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
     virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
 
     virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp
index 58910c66bd..dfc6195f95 100644
--- a/libraries/octree/src/Octree.cpp
+++ b/libraries/octree/src/Octree.cpp
@@ -43,14 +43,15 @@
 #include <PathUtils.h>
 #include <ViewFrustum.h>
 
+#include "Octree.h"
 #include "OctreeConstants.h"
 #include "OctreeElementBag.h"
-#include "Octree.h"
-#include "OctreeUtils.h"
 #include "OctreeLogging.h"
+#include "OctreeQueryNode.h"
+#include "OctreeUtils.h"
 
 
-QVector<QString> PERSIST_EXTENSIONS = {"svo", "json", "json.gz"};
+QVector<QString> PERSIST_EXTENSIONS = {"json", "json.gz"};
 
 Octree::Octree(bool shouldReaverage) :
     _rootElement(NULL),
@@ -898,8 +899,16 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element,
         return bytesWritten;
     }
 
+    // you can't call this without a valid nodeData
+    auto octreeQueryNode = static_cast<OctreeQueryNode*>(params.nodeData);
+    if (!octreeQueryNode) {
+        qCDebug(octree, "WARNING! encodeTreeBitstream() called with nodeData=NULL");
+        params.stopReason = EncodeBitstreamParams::NULL_NODE_DATA;
+        return bytesWritten;
+    }
+
     // If we're at a element that is out of view, then we can return, because no nodes below us will be in view!
-    if (params.usesFrustum && !params.recurseEverything && !element->isInView(params.viewFrustum)) {
+    if (octreeQueryNode->getUsesFrustum() && !params.recurseEverything && !element->isInView(params.viewFrustum)) {
         params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
         return bytesWritten;
     }
@@ -935,9 +944,7 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element,
 
     // record some stats, this is the one element that we won't record below in the recursion function, so we need to
     // track it here
-    if (params.stats) {
-        params.stats->traversed(element);
-    }
+    octreeQueryNode->stats.traversed(element);
 
     ViewFrustum::intersection parentLocationThisView = ViewFrustum::INTERSECT; // assume parent is in view, but not fully
 
@@ -993,6 +1000,15 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
         return bytesAtThisLevel;
     }
 
+    // you can't call this without a valid nodeData
+    auto octreeQueryNode = static_cast<OctreeQueryNode*>(params.nodeData);
+    if (!octreeQueryNode) {
+        qCDebug(octree, "WARNING! encodeTreeBitstream() called with nodeData=NULL");
+        params.stopReason = EncodeBitstreamParams::NULL_NODE_DATA;
+        return bytesAtThisLevel;
+    }
+
+
     // Keep track of how deep we've encoded.
     currentEncodeLevel++;
 
@@ -1015,15 +1031,13 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
     }
 
     ViewFrustum::intersection nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside
-    if (params.usesFrustum && !params.recurseEverything) {
+    if (octreeQueryNode->getUsesFrustum() && !params.recurseEverything) {
         float boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
                                         params.octreeElementSizeScale);
 
         // If we're too far away for our render level, then just return
         if (element->distanceToCamera(params.viewFrustum) >= boundaryDistance) {
-            if (params.stats) {
-                params.stats->skippedDistance(element);
-            }
+            octreeQueryNode->stats.skippedDistance(element);
             params.stopReason = EncodeBitstreamParams::LOD_SKIP;
             return bytesAtThisLevel;
         }
@@ -1039,9 +1053,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
         // although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if
         // we're out of view
         if (nodeLocationThisView == ViewFrustum::OUTSIDE) {
-            if (params.stats) {
-                params.stats->skippedOutOfView(element);
-            }
+            octreeQueryNode->stats.skippedOutOfView(element);
             params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
             return bytesAtThisLevel;
         }
@@ -1066,7 +1078,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
             // as "was in view"...
             if (wasInView) {
                 float boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
-                                                                            params.octreeElementSizeScale);
+                                                                        params.octreeElementSizeScale);
                 if (element->distanceToCamera(params.lastViewFrustum) >= boundaryDistance) {
                     // This would have been invisible... but now should be visible (we wouldn't be here otherwise)...
                     wasInView = false;
@@ -1077,22 +1089,20 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
         // If we were previously in the view, then we normally will return out of here and stop recursing. But
         // if we're in deltaView mode, and this element has changed since it was last sent, then we do
         // need to send it.
-        if (wasInView && !(params.deltaView && element->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE))) {
-            if (params.stats) {
-                params.stats->skippedWasInView(element);
-            }
+        if (wasInView && !(params.deltaView && element->hasChangedSince(octreeQueryNode->getLastTimeBagEmpty() - CHANGE_FUDGE))) {
+            octreeQueryNode->stats.skippedWasInView(element);
             params.stopReason = EncodeBitstreamParams::WAS_IN_VIEW;
             return bytesAtThisLevel;
         }
     }
 
-    // If we're not in delta sending mode, and we weren't asked to do a force send, and the octree element hasn't changed,
+    // If we're not in delta sending mode, and we weren't asked to do a force send, and the voxel hasn't changed,
     // then we can also bail early and save bits
     if (!params.forceSendScene && !params.deltaView &&
-        !element->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE)) {
-        if (params.stats) {
-            params.stats->skippedNoChange(element);
-        }
+        !element->hasChangedSince(octreeQueryNode->getLastTimeBagEmpty() - CHANGE_FUDGE)) {
+
+        octreeQueryNode->stats.skippedNoChange(element);
+
         params.stopReason = EncodeBitstreamParams::NO_CHANGE;
         return bytesAtThisLevel;
     }
@@ -1164,8 +1174,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
 
         // track stats
         // must check childElement here, because it could be we got here with no childElement
-        if (params.stats && childElement) {
-            params.stats->traversed(childElement);
+        if (childElement) {
+            octreeQueryNode->stats.traversed(childElement);
         }
     }
 
@@ -1176,7 +1186,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
         int originalIndex = indexOfChildren[i];
 
         bool childIsInView  = (childElement &&
-                (params.recurseEverything || !params.usesFrustum ||
+                (params.recurseEverything || !octreeQueryNode->getUsesFrustum() ||
                  (nodeLocationThisView == ViewFrustum::INSIDE) || // parent was fully in view, we can assume ALL children are
                   (nodeLocationThisView == ViewFrustum::INTERSECT &&
                         childElement->isInView(params.viewFrustum)) // the parent intersects and the child is in view
@@ -1184,20 +1194,18 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
 
         if (!childIsInView) {
             // must check childElement here, because it could be we got here because there was no childElement
-            if (params.stats && childElement) {
-                params.stats->skippedOutOfView(childElement);
+            if (childElement) {
+                octreeQueryNode->stats.skippedOutOfView(childElement);
             }
         } else {
             // Before we consider this further, let's see if it's in our LOD scope...
-            float boundaryDistance = params.recurseEverything || !params.usesFrustum ? 1 :
+            float boundaryDistance = params.recurseEverything || !octreeQueryNode->getUsesFrustum() ? 1 :
                                     boundaryDistanceForRenderLevel(childElement->getLevel() + params.boundaryLevelAdjust,
                                             params.octreeElementSizeScale);
 
             if (!(distancesToChildren[i] < boundaryDistance)) {
                 // don't need to check childElement here, because we can't get here with no childElement
-                if (params.stats) {
-                    params.stats->skippedDistance(childElement);
-                }
+                octreeQueryNode->stats.skippedDistance(childElement);
             } else {
                 inViewCount++;
 
@@ -1211,20 +1219,18 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
 
                 bool childIsOccluded = false; // assume it's not occluded
 
-                bool shouldRender = params.recurseEverything || !params.usesFrustum ||
+                bool shouldRender = params.recurseEverything || !octreeQueryNode->getUsesFrustum() ||
                         childElement->calculateShouldRender(params.viewFrustum,
                                 params.octreeElementSizeScale, params.boundaryLevelAdjust);
 
                 // track some stats
-                if (params.stats) {
-                    // don't need to check childElement here, because we can't get here with no childElement
-                    if (!shouldRender && childElement->isLeaf()) {
-                        params.stats->skippedDistance(childElement);
-                    }
-                    // don't need to check childElement here, because we can't get here with no childElement
-                    if (childIsOccluded) {
-                        params.stats->skippedOccluded(childElement);
-                    }
+                // don't need to check childElement here, because we can't get here with no childElement
+                if (!shouldRender && childElement->isLeaf()) {
+                    octreeQueryNode->stats.skippedDistance(childElement);
+                }
+                // don't need to check childElement here, because we can't get here with no childElement
+                if (childIsOccluded) {
+                    octreeQueryNode->stats.skippedOccluded(childElement);
                 }
 
                 // track children with actual color, only if the child wasn't previously in view!
@@ -1247,19 +1253,17 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
                     // need to send it.
                     if (!childWasInView ||
                         (params.deltaView &&
-                         childElement->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE))){
+                         childElement->hasChangedSince(octreeQueryNode->getLastTimeBagEmpty() - CHANGE_FUDGE))){
 
                         childrenDataBits += (1 << (7 - originalIndex));
                         inViewWithColorCount++;
                     } else {
                         // otherwise just track stats of the items we discarded
                         // don't need to check childElement here, because we can't get here with no childElement
-                        if (params.stats) {
-                            if (childWasInView) {
-                                params.stats->skippedWasInView(childElement);
-                            } else {
-                                params.stats->skippedNoChange(childElement);
-                            }
+                        if (childWasInView) {
+                            octreeQueryNode->stats.skippedWasInView(childElement);
+                        } else {
+                            octreeQueryNode->stats.skippedNoChange(childElement);
                         }
                     }
                 }
@@ -1277,9 +1281,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
 
     assert(continueThisLevel); // since we used reserved bits, this really shouldn't fail
     bytesAtThisLevel += sizeof(childrenDataBits); // keep track of byte count
-    if (params.stats) {
-        params.stats->colorBitsWritten(); // really data bits not just color bits
-    }
+
+    octreeQueryNode->stats.colorBitsWritten(); // really data bits not just color bits
 
     // NOW might be a good time to give our tree subclass and this element a chance to set up and check any extra encode data
     element->initializeExtraEncodeData(params);
@@ -1349,8 +1352,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
                 bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child
 
                 // don't need to check childElement here, because we can't get here with no childElement
-                if (params.stats && (childAppendState != OctreeElement::NONE)) {
-                    params.stats->colorSent(childElement);
+                if (childAppendState != OctreeElement::NONE) {
+                    octreeQueryNode->stats.colorSent(childElement);
                 }
             }
         }
@@ -1377,9 +1380,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
         continueThisLevel = packetData->appendBitMask(childrenExistInTreeBits);
         if (continueThisLevel) {
             bytesAtThisLevel += sizeof(childrenExistInTreeBits); // keep track of byte count
-            if (params.stats) {
-                params.stats->existsBitsWritten();
-            }
+
+            octreeQueryNode->stats.existsBitsWritten();
         } else {
             qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to append childrenExistInTreeBits";
             qCDebug(octree) << "This is not expected!!!!  -- continueThisLevel=FALSE....";
@@ -1392,9 +1394,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
         continueThisLevel = packetData->appendBitMask(childrenExistInPacketBits);
         if (continueThisLevel) {
             bytesAtThisLevel += sizeof(childrenExistInPacketBits); // keep track of byte count
-            if (params.stats) {
-                params.stats->existsInPacketBitsWritten();
-            }
+
+            octreeQueryNode->stats.existsInPacketBitsWritten();
         } else {
             qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to append childrenExistInPacketBits";
             qCDebug(octree) << "This is not expected!!!!  -- continueThisLevel=FALSE....";
@@ -1451,7 +1452,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
                 // called databits), then we wouldn't send the children. So those types of Octree's should tell us to keep
                 // recursing, by returning TRUE in recurseChildrenWithData().
 
-                if (params.recurseEverything || !params.usesFrustum
+                if (params.recurseEverything || !octreeQueryNode->getUsesFrustum()
                     || recurseChildrenWithData() || !oneAtBit(childrenDataBits, originalIndex)) {
 
                     // Allow the datatype a chance to determine if it really wants to recurse this tree. Usually this
@@ -1502,8 +1503,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
                     }
 
                     // If this is the last of the child exists bits, then we're actually be rolling out the entire tree
-                    if (params.stats && childrenExistInPacketBits == 0) {
-                        params.stats->childBitsRemoved(params.includeExistsBits);
+                    if (childrenExistInPacketBits == 0) {
+                        octreeQueryNode->stats.childBitsRemoved(params.includeExistsBits);
                     }
 
                     if (!continueThisLevel) {
@@ -1558,9 +1559,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
         if (continueThisLevel) {
             bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child
 
-            if (params.stats) {
-                params.stats->colorSent(element);
-            }
+            octreeQueryNode->stats.colorSent(element);
         }
 
         if (!continueThisLevel) {
@@ -1595,9 +1594,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
         bag.insert(element);
 
         // don't need to check element here, because we can't get here with no element
-        if (params.stats) {
-            params.stats->didntFit(element);
-        }
+        octreeQueryNode->stats.didntFit(element);
 
         params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
         bytesAtThisLevel = 0; // didn't fit
@@ -1876,9 +1873,7 @@ bool Octree::writeToFile(const char* fileName, OctreeElementPointer element, QSt
     const char* cFileName = byteArray.constData();
 
     bool success = false;
-    if (persistAsFileType == "svo") {
-        success = writeToSVOFile(fileName, element);
-    } else if (persistAsFileType == "json") {
+    if (persistAsFileType == "json") {
         success = writeToJSONFile(cFileName, element);
     } else if (persistAsFileType == "json.gz") {
         success = writeToJSONFile(cFileName, element, true);
@@ -1936,95 +1931,6 @@ bool Octree::writeToJSONFile(const char* fileName, OctreeElementPointer element,
     return success;
 }
 
-bool Octree::writeToSVOFile(const char* fileName, OctreeElementPointer element) {
-    qWarning() << "SVO file format deprecated. Support for reading SVO files is no longer support and will be removed soon.";
-    bool success = false;
-
-    std::ofstream file(fileName, std::ios::out|std::ios::binary);
-
-    if(file.is_open()) {
-        qCDebug(octree, "Saving binary SVO to file %s...", fileName);
-
-        PacketType expectedPacketType = expectedDataPacketType();
-        int expectedIntType = (int) expectedPacketType;
-        PacketVersion expectedVersion = versionForPacketType(expectedPacketType);
-        bool hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
-
-        // before reading the file, check to see if this version of the Octree supports file versions
-        if (getWantSVOfileVersions()) {
-            // if so, read the first byte of the file and see if it matches the expected version code
-            file.write(reinterpret_cast<char*>(&expectedIntType), sizeof(expectedIntType));
-            file.write(&expectedVersion, sizeof(expectedVersion));
-            qCDebug(octree) << "SVO file type: " << expectedPacketType << " version: " << (int)expectedVersion;
-
-            hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
-        }
-        if (hasBufferBreaks) {
-            qCDebug(octree) << "    this version includes buffer breaks";
-        } else {
-            qCDebug(octree) << "    this version does not include buffer breaks";
-        }
-
-
-        OctreeElementBag elementBag;
-        OctreeElementExtraEncodeData extraEncodeData;
-        // If we were given a specific element, start from there, otherwise start from root
-        if (element) {
-            elementBag.insert(element);
-        } else {
-            elementBag.insert(_rootElement);
-        }
-
-        OctreePacketData packetData;
-        int bytesWritten = 0;
-        bool lastPacketWritten = false;
-
-        while (OctreeElementPointer subTree = elementBag.extract()) {
-            EncodeBitstreamParams params(INT_MAX, NO_EXISTS_BITS);
-            params.recurseEverything = true;
-            withReadLock([&] {
-                params.extraEncodeData = &extraEncodeData;
-                bytesWritten = encodeTreeBitstream(subTree, &packetData, elementBag, params);
-            });
-
-            // if the subTree couldn't fit, and so we should reset the packet and reinsert the element in our bag and try again
-            if (bytesWritten == 0 && (params.stopReason == EncodeBitstreamParams::DIDNT_FIT)) {
-                if (packetData.hasContent()) {
-                    // if this type of SVO file should have buffer breaks, then we will write a buffer size before each
-                    // buffer to allow the reader to read this file in chunks.
-                    if (hasBufferBreaks) {
-                        quint16 bufferSize = packetData.getFinalizedSize();
-                        file.write((const char*)&bufferSize, sizeof(bufferSize));
-                    }
-                    file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize());
-                    lastPacketWritten = true;
-                }
-                packetData.reset(); // is there a better way to do this? could we fit more?
-                elementBag.insert(subTree);
-            } else {
-                lastPacketWritten = false;
-            }
-        }
-
-        if (!lastPacketWritten) {
-            // if this type of SVO file should have buffer breaks, then we will write a buffer size before each
-            // buffer to allow the reader to read this file in chunks.
-            if (hasBufferBreaks) {
-                quint16 bufferSize = packetData.getFinalizedSize();
-                file.write((const char*)&bufferSize, sizeof(bufferSize));
-            }
-            file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize());
-        }
-
-        releaseSceneEncodeData(&extraEncodeData);
-
-        success = true;
-    }
-    file.close();
-
-    return success;
-}
-
 unsigned long Octree::getOctreeElementsCount() {
     unsigned long nodeCount = 0;
     recurseTreeWithOperation(countOctreeElementsOperation, &nodeCount);
diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h
index 7b2f303c0d..caae31eaa5 100644
--- a/libraries/octree/src/Octree.h
+++ b/libraries/octree/src/Octree.h
@@ -59,9 +59,7 @@ const bool DONT_COLLAPSE          = false;
 const int DONT_CHOP              = 0;
 const int NO_BOUNDARY_ADJUST     = 0;
 const int LOW_RES_MOVING_ADJUST  = 1;
-const quint64 IGNORE_LAST_SENT  = 0;
 
-#define IGNORE_SCENE_STATS       NULL
 #define IGNORE_COVERAGE_MAP      NULL
 #define IGNORE_JURISDICTION_MAP  NULL
 
@@ -69,7 +67,6 @@ class EncodeBitstreamParams {
 public:
     ViewFrustum viewFrustum;
     ViewFrustum lastViewFrustum;
-    quint64 lastQuerySent;
     int maxEncodeLevel;
     int maxLevelReached;
     bool includeExistsBits;
@@ -79,10 +76,7 @@ public:
     int boundaryLevelAdjust;
     float octreeElementSizeScale;
     bool forceSendScene;
-    OctreeSceneStats* stats;
     JurisdictionMap* jurisdictionMap;
-    OctreeElementExtraEncodeData* extraEncodeData;
-    bool usesFrustum;
     NodeData* nodeData;
 
     // output hints from the encode process
@@ -90,6 +84,7 @@ public:
         UNKNOWN,
         DIDNT_FIT,
         NULL_NODE,
+        NULL_NODE_DATA,
         TOO_DEEP,
         OUT_OF_JURISDICTION,
         LOD_SKIP,
@@ -107,14 +102,9 @@ public:
         bool useDeltaView = false,
         int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
         float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
-        quint64 lastQuerySent = IGNORE_LAST_SENT,
         bool forceSendScene = true,
-        OctreeSceneStats* stats = IGNORE_SCENE_STATS,
         JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP,
-        OctreeElementExtraEncodeData* extraEncodeData = nullptr,
-        bool usesFrustum = true,
         NodeData* nodeData = nullptr) :
-            lastQuerySent(lastQuerySent),
             maxEncodeLevel(maxEncodeLevel),
             maxLevelReached(0),
             includeExistsBits(includeExistsBits),
@@ -123,10 +113,7 @@ public:
             boundaryLevelAdjust(boundaryLevelAdjust),
             octreeElementSizeScale(octreeElementSizeScale),
             forceSendScene(forceSendScene),
-            stats(stats),
             jurisdictionMap(jurisdictionMap),
-            extraEncodeData(extraEncodeData),
-            usesFrustum(usesFrustum),
             nodeData(nodeData),
             stopReason(UNKNOWN)
     {
@@ -306,9 +293,8 @@ public:
     void loadOctreeFile(const char* fileName);
 
     // Octree exporters
-    bool writeToFile(const char* filename, OctreeElementPointer element = NULL, QString persistAsFileType = "svo");
+    bool writeToFile(const char* filename, OctreeElementPointer element = NULL, QString persistAsFileType = "json.gz");
     bool writeToJSONFile(const char* filename, OctreeElementPointer element = NULL, bool doGzip = false);
-    bool writeToSVOFile(const char* filename, OctreeElementPointer element = NULL);
     virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues,
                             bool skipThoseWithBadParents) = 0;
 
diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h
index f8215fb34a..927304e862 100644
--- a/libraries/octree/src/OctreePersistThread.h
+++ b/libraries/octree/src/OctreePersistThread.h
@@ -35,7 +35,7 @@ public:
 
     OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory,
                         int persistInterval = DEFAULT_PERSIST_INTERVAL, bool wantBackup = false,
-                        const QJsonObject& settings = QJsonObject(), bool debugTimestampNow = false, QString persistAsFileType="svo");
+                        const QJsonObject& settings = QJsonObject(), bool debugTimestampNow = false, QString persistAsFileType="json.gz");
 
     bool isInitialLoadComplete() const { return _initialLoadComplete; }
     quint64 getLoadElapsedTime() const { return _loadTimeUSecs; }