Remove more dead octree code

This commit is contained in:
Clement 2018-04-10 18:49:30 -07:00
parent 0a1f07755e
commit 2e8a6e1961
42 changed files with 66 additions and 1806 deletions

View file

@ -48,8 +48,6 @@ private:
void preDistributionProcessing() override;
bool hasSomethingToSend(OctreeQueryNode* nodeData) override { return !_sendQueue.empty(); }
bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) override { return viewFrustumChanged || _traversal.finished(); }
void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene) override {};
bool shouldTraverseAndSend(OctreeQueryNode* nodeData) override { return true; }
DiffTraversal _traversal;
EntityPriorityQueue _sendQueue;

View file

@ -304,23 +304,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
return numPackets;
}
void OctreeSendThread::preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene) {
// If we're starting a full scene, then definitely we want to empty the elementBag
if (isFullScene) {
nodeData->elementBag.deleteAll();
}
// This is the start of "resending" the scene.
bool dontRestartSceneOnMove = false; // this is experimental
if (dontRestartSceneOnMove) {
if (nodeData->elementBag.isEmpty()) {
nodeData->elementBag.insert(_myServer->getOctree()->getRoot());
}
} else {
nodeData->elementBag.insert(_myServer->getOctree()->getRoot());
}
}
/// Version of octree element distributor that sends the deepest LOD level at once
int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged) {
OctreeServer::didPacketDistributor(this);
@ -366,16 +349,8 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
// the current view frustum for things to send.
if (shouldStartNewTraversal(nodeData, viewFrustumChanged)) {
// if our view has changed, we need to reset these things...
if (viewFrustumChanged) {
if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) {
nodeData->dumpOutOfView();
}
}
// track completed scenes and send out the stats packet accordingly
nodeData->stats.sceneCompleted();
nodeData->setLastRootTimestamp(_myServer->getOctree()->getRoot()->getLastChanged());
_myServer->getOctree()->releaseSceneEncodeData(&nodeData->extraEncodeData);
// TODO: add these to stats page
@ -389,111 +364,74 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
// TODO: add these to stats page
//::startSceneSleepTime = _usleepTime;
nodeData->sceneStart(usecTimestampNow() - CHANGE_FUDGE);
// start tracking our stats
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot());
preStartNewScene(nodeData, isFullScene);
}
// If we have something in our elementBag, then turn them into packets and send them out...
if (shouldTraverseAndSend(nodeData)) {
quint64 start = usecTimestampNow();
quint64 start = usecTimestampNow();
_myServer->getOctree()->withReadLock([&]{
traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
});
_myServer->getOctree()->withReadLock([&]{
traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
});
// Here's where we can/should allow the server to send other data...
// send the environment packet
// TODO: should we turn this into a while loop to better handle sending multiple special packets
if (_myServer->hasSpecialPacketsToSend(node) && !nodeData->isShuttingDown()) {
int specialPacketsSent = 0;
int specialBytesSent = _myServer->sendSpecialPackets(node, nodeData, specialPacketsSent);
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
_truePacketsSent += specialPacketsSent;
_trueBytesSent += specialBytesSent;
_packetsSentThisInterval += specialPacketsSent;
// Here's where we can/should allow the server to send other data...
// send the environment packet
// TODO: should we turn this into a while loop to better handle sending multiple special packets
if (_myServer->hasSpecialPacketsToSend(node) && !nodeData->isShuttingDown()) {
int specialPacketsSent = 0;
int specialBytesSent = _myServer->sendSpecialPackets(node, nodeData, specialPacketsSent);
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
_truePacketsSent += specialPacketsSent;
_trueBytesSent += specialBytesSent;
_packetsSentThisInterval += specialPacketsSent;
_totalPackets += specialPacketsSent;
_totalBytes += specialBytesSent;
_totalPackets += specialPacketsSent;
_totalBytes += specialBytesSent;
_totalSpecialPackets += specialPacketsSent;
_totalSpecialBytes += specialBytesSent;
_totalSpecialPackets += specialPacketsSent;
_totalSpecialBytes += specialBytesSent;
}
// calculate max number of packets that can be sent during this interval
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
// Re-send packets that were nacked by the client
while (nodeData->hasNextNackedPacket() && _packetsSentThisInterval < maxPacketsPerInterval) {
const NLPacket* packet = nodeData->getNextNackedPacket();
if (packet) {
DependencyManager::get<NodeList>()->sendUnreliablePacket(*packet, *node);
int numBytes = packet->getDataSize();
_truePacketsSent++;
_trueBytesSent += numBytes;
_packetsSentThisInterval++;
_totalPackets++;
_totalBytes += numBytes;
_totalWastedBytes += udt::MAX_PACKET_SIZE - packet->getDataSize();
}
}
// calculate max number of packets that can be sent during this interval
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
quint64 end = usecTimestampNow();
int elapsedmsec = (end - start) / USECS_PER_MSEC;
OctreeServer::trackLoopTime(elapsedmsec);
// Re-send packets that were nacked by the client
while (nodeData->hasNextNackedPacket() && _packetsSentThisInterval < maxPacketsPerInterval) {
const NLPacket* packet = nodeData->getNextNackedPacket();
if (packet) {
DependencyManager::get<NodeList>()->sendUnreliablePacket(*packet, *node);
int numBytes = packet->getDataSize();
_truePacketsSent++;
_trueBytesSent += numBytes;
_packetsSentThisInterval++;
// if after sending packets we've emptied our bag, then we want to remember that we've sent all
// the octree elements from the current view frustum
if (!hasSomethingToSend(nodeData)) {
nodeData->setViewSent(true);
_totalPackets++;
_totalBytes += numBytes;
_totalWastedBytes += udt::MAX_PACKET_SIZE - packet->getDataSize();
}
// If this was a full scene then make sure we really send out a stats packet at this point so that
// the clients will know the scene is stable
if (isFullScene) {
nodeData->stats.sceneCompleted();
handlePacketSend(node, nodeData, true);
}
quint64 end = usecTimestampNow();
int elapsedmsec = (end - start) / USECS_PER_MSEC;
OctreeServer::trackLoopTime(elapsedmsec);
// if after sending packets we've emptied our bag, then we want to remember that we've sent all
// the octree elements from the current view frustum
if (!hasSomethingToSend(nodeData)) {
nodeData->updateLastKnownViewFrustum();
nodeData->setViewSent(true);
// If this was a full scene then make sure we really send out a stats packet at this point so that
// the clients will know the scene is stable
if (isFullScene) {
nodeData->stats.sceneCompleted();
handlePacketSend(node, nodeData, true);
}
}
} // end if bag wasn't empty, and so we sent stuff...
}
return _truePacketsSent;
}
bool OctreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) {
bool somethingToSend = false;
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(params.nodeData);
if (!nodeData->elementBag.isEmpty()) {
quint64 encodeStart = usecTimestampNow();
quint64 lockWaitStart = encodeStart;
_myServer->getOctree()->withReadLock([&]{
OctreeServer::trackTreeWaitTime((float)(usecTimestampNow() - lockWaitStart));
OctreeElementPointer subTree = nodeData->elementBag.extract();
if (subTree) {
// NOTE: this is where the tree "contents" are actually packed
nodeData->stats.encodeStarted();
_myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->elementBag, params);
nodeData->stats.encodeStopped();
somethingToSend = true;
}
});
OctreeServer::trackEncodeTime((float)(usecTimestampNow() - encodeStart));
} else {
OctreeServer::trackTreeWaitTime(OctreeServer::SKIP_TIME);
OctreeServer::trackEncodeTime(OctreeServer::SKIP_TIME);
}
return somethingToSend;
}
void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) {
// calculate max number of packets that can be sent during this interval
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
@ -502,21 +440,12 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
int extraPackingAttempts = 0;
// init params once outside the while loop
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
int boundaryLevelAdjust = boundaryLevelAdjustClient +
(viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
float octreeSizeScale = nodeData->getOctreeSizeScale();
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
isFullScene, nodeData);
EncodeBitstreamParams params(WANT_EXISTS_BITS, nodeData);
// Our trackSend() function is implemented by the server subclass, and will be called back as new entities/data elements are sent
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
_myServer->trackSend(dataID, dataEdited, _nodeUuid);
};
nodeData->copyCurrentViewFrustum(params.viewFrustum);
if (viewFrustumChanged) {
nodeData->copyLastKnownViewFrustum(params.lastViewFrustum);
}
bool somethingToSend = true; // assume we have something
bool hadSomething = hasSomethingToSend(nodeData);
@ -537,7 +466,7 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
}
// If the bag had contents but is now empty then we know we've sent the entire scene.
bool completedScene = hadSomething && nodeData->elementBag.isEmpty();
bool completedScene = hadSomething;
if (completedScene || lastNodeDidntFit) {
// we probably want to flush what has accumulated in nodeData but:
// do we have more data to send? and is there room?

View file

@ -54,7 +54,7 @@ protected:
virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
bool viewFrustumChanged, bool isFullScene);
virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters);
virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) = 0;
OctreePacketData _packetData;
QWeakPointer<Node> _node;
@ -63,14 +63,12 @@ protected:
private:
/// Called before a packetDistributor pass to allow for pre-distribution processing
virtual void preDistributionProcessing() {};
virtual void preDistributionProcessing() = 0;
int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, bool dontSuppressDuplicate = false);
int packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
virtual bool hasSomethingToSend(OctreeQueryNode* nodeData) { return !nodeData->elementBag.isEmpty(); }
virtual bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) { return viewFrustumChanged || !hasSomethingToSend(nodeData); }
virtual void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene);
virtual bool shouldTraverseAndSend(OctreeQueryNode* nodeData) { return hasSomethingToSend(nodeData); }
virtual bool hasSomethingToSend(OctreeQueryNode* nodeData) = 0;
virtual bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) = 0;
int _truePacketsSent { 0 }; // available for debug stats
int _trueBytesSent { 0 }; // available for debug stats

View file

@ -876,10 +876,6 @@ void OctreeServer::parsePayload() {
}
}
OctreeServer::UniqueSendThread OctreeServer::newSendThread(const SharedNodePointer& node) {
return std::unique_ptr<OctreeSendThread>(new OctreeSendThread(this, node));
}
OctreeServer::UniqueSendThread OctreeServer::createSendThread(const SharedNodePointer& node) {
auto sendThread = newSendThread(node);

View file

@ -174,7 +174,7 @@ protected:
void beginRunning(QByteArray replaceData);
UniqueSendThread createSendThread(const SharedNodePointer& node);
virtual UniqueSendThread newSendThread(const SharedNodePointer& node);
virtual UniqueSendThread newSendThread(const SharedNodePointer& node) = 0;
int _argc;
const char** _argv;

View file

@ -120,7 +120,6 @@ public:
void markAsChangedOnServer();
quint64 getLastChangedOnServer() const;
// 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,

View file

@ -88,7 +88,6 @@ public:
// These methods will allow the OctreeServer to send your tree inbound edit packets of your
// own definition. Implement these to allow your octree based server to support editing
virtual bool getWantSVOfileVersions() const override { return true; }
virtual PacketType expectedDataPacketType() const override { return PacketType::EntityData; }
virtual bool handlesEditPacketType(PacketType packetType) const override;
void fixupTerseEditLogging(EntityItemProperties& properties, QList<QString>& changedProperties);
@ -107,11 +106,7 @@ public:
virtual bool rootElementHasData() const override { return true; }
// the root at least needs to store the number of entities in the packet/buffer
virtual int minimumRequiredRootDataBytes() const override { return sizeof(uint16_t); }
virtual bool suppressEmptySubtrees() const override { return false; }
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const override;
virtual bool mustIncludeAllChildData() const override { return false; }
virtual void update() override { update(true); }

View file

@ -67,455 +67,6 @@ void EntityTreeElement::debugExtraEncodeData(EncodeBitstreamParams& params) cons
}
}
void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params) {
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)) {
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData { new EntityTreeElementExtraEncodeData() };
entityTreeElementExtraEncodeData->elementCompleted = (_entityItems.size() == 0);
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
EntityTreeElementPointer child = getChildAtIndex(i);
if (!child) {
entityTreeElementExtraEncodeData->childCompleted[i] = true; // if no child exists, it is completed
} else {
if (child->hasEntities()) {
entityTreeElementExtraEncodeData->childCompleted[i] = false; // HAS ENTITIES NEEDS ENCODING
} else {
entityTreeElementExtraEncodeData->childCompleted[i] = true; // child doesn't have enities, it is completed
}
}
}
forEachEntity([&](EntityItemPointer entity) {
entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
});
// TODO: some of these inserts might be redundant!!!
extraEncodeData->insert(this, entityTreeElementExtraEncodeData);
}
}
bool EntityTreeElement::shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const {
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]);
bool childCompleted = entityTreeElementExtraEncodeData->childCompleted[childIndex];
// If we haven't completely sent the child yet, then we should include it
return !childCompleted;
}
// I'm not sure this should ever happen, since we should have the extra encode data if we're considering
// the child data for this element
assert(false);
return false;
}
bool EntityTreeElement::shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const {
EntityTreeElementPointer childElement = getChildAtIndex(childIndex);
if (childElement->alreadyFullyEncoded(params)) {
return false;
}
return true; // if we don't know otherwise than recurse!
}
bool EntityTreeElement::alreadyFullyEncoded(EncodeBitstreamParams& params) const {
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]);
// If we know that ALL subtrees below us have already been recursed, then we don't
// need to recurse this child.
return entityTreeElementExtraEncodeData->subtreeCompleted;
}
return false;
}
void EntityTreeElement::updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const {
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]);
if (childAppendState == OctreeElement::COMPLETED) {
entityTreeElementExtraEncodeData->childCompleted[childIndex] = true;
}
} else {
assert(false); // this shouldn't happen!
}
}
void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params) const {
const bool wantDebug = false;
if (wantDebug) {
qCDebug(entities) << "EntityTreeElement::elementEncodeComplete() element:" << _cube;
}
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));
EntityTreeElementExtraEncodeDataPointer thisExtraEncodeData
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
// Note: this will be called when OUR element has finished running through encodeTreeBitstreamRecursion()
// which means, it's possible that our parent element hasn't finished encoding OUR data... so
// in this case, our children may be complete, and we should clean up their encode data...
// but not necessarily cleanup our own encode data...
//
// If we're really complete here's what must be true...
// 1) our own data must be complete
// 2) the data for all our immediate children must be complete.
// However, the following might also be the case...
// 1) it's ok for our child trees to not yet be fully encoded/complete...
// SO LONG AS... the our child's node is in the bag ready for encoding
bool someChildTreeNotComplete = false;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
EntityTreeElementPointer childElement = getChildAtIndex(i);
if (childElement) {
// why would this ever fail???
// If we've encoding this element before... but we're coming back a second time in an attempt to
// encode our parent... this might happen.
if (extraEncodeData->contains(childElement.get())) {
EntityTreeElementExtraEncodeDataPointer childExtraEncodeData
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[childElement.get()]);
if (wantDebug) {
qCDebug(entities) << "checking child: " << childElement->_cube;
qCDebug(entities) << " childElement->isLeaf():" << childElement->isLeaf();
qCDebug(entities) << " childExtraEncodeData->elementCompleted:" << childExtraEncodeData->elementCompleted;
qCDebug(entities) << " childExtraEncodeData->subtreeCompleted:" << childExtraEncodeData->subtreeCompleted;
}
if (childElement->isLeaf() && childExtraEncodeData->elementCompleted) {
if (wantDebug) {
qCDebug(entities) << " CHILD IS LEAF -- AND CHILD ELEMENT DATA COMPLETED!!!";
}
childExtraEncodeData->subtreeCompleted = true;
}
if (!childExtraEncodeData->elementCompleted || !childExtraEncodeData->subtreeCompleted) {
someChildTreeNotComplete = true;
}
}
}
}
if (wantDebug) {
qCDebug(entities) << "for this element: " << _cube;
qCDebug(entities) << " WAS elementCompleted:" << thisExtraEncodeData->elementCompleted;
qCDebug(entities) << " WAS subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted;
}
thisExtraEncodeData->subtreeCompleted = !someChildTreeNotComplete;
if (wantDebug) {
qCDebug(entities) << " NOW elementCompleted:" << thisExtraEncodeData->elementCompleted;
qCDebug(entities) << " NOW subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted;
if (thisExtraEncodeData->subtreeCompleted) {
qCDebug(entities) << " YEAH!!!!! >>>>>>>>>>>>>> NOW subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted;
}
}
}
OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData* packetData,
EncodeBitstreamParams& params) const {
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 = &entityNodeData->extraEncodeData;
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData = NULL;
bool hadElementExtraData = false;
if (extraEncodeData && extraEncodeData->contains(this)) {
entityTreeElementExtraEncodeData =
std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
hadElementExtraData = true;
} else {
// if there wasn't one already, then create one
entityTreeElementExtraEncodeData.reset(new EntityTreeElementExtraEncodeData());
entityTreeElementExtraEncodeData->elementCompleted = !hasContent();
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
EntityTreeElementPointer child = getChildAtIndex(i);
if (!child) {
entityTreeElementExtraEncodeData->childCompleted[i] = true; // if no child exists, it is completed
} else {
if (child->hasEntities()) {
entityTreeElementExtraEncodeData->childCompleted[i] = false;
} else {
// if the child doesn't have enities, it is completed
entityTreeElementExtraEncodeData->childCompleted[i] = true;
}
}
}
forEachEntity([&](EntityItemPointer entity) {
entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
});
}
//assert(extraEncodeData);
//assert(extraEncodeData->contains(this));
//entityTreeElementExtraEncodeData = std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
LevelDetails elementLevel = packetData->startLevel();
// write our entities out... first determine which of the entities are in view based on our params
uint16_t numberOfEntities = 0;
uint16_t actualNumberOfEntities = 0;
int numberOfEntitiesOffset = 0;
withReadLock([&] {
QVector<uint16_t> indexesOfEntitiesToInclude;
// It's possible that our element has been previous completed. In this case we'll simply not include any of our
// entities for encoding. This is needed because we encode the element data at the "parent" level, and so we
// need to handle the case where our sibling elements need encoding but we don't.
if (!entityTreeElementExtraEncodeData->elementCompleted) {
// 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() < entityNodeData->getLastTimeBagEmpty()) {
includeThisEntity = false;
}
// if this entity has been updated since our last full send and there are json filters, check them
if (includeThisEntity && !jsonFilters.isEmpty()) {
// if params include JSON filters, check if this entity matches
bool entityMatchesFilters = entity->matchesJSONFilters(jsonFilters);
if (entityMatchesFilters) {
// make sure this entity is in the set of entities sent last frame
entityNodeData->insertSentFilteredEntity(entity->getID());
} else if (entityNodeData->sentFilteredEntity(entity->getID())) {
// this entity matched in the previous frame - we send it still so the client realizes it just
// fell outside of their filter
entityNodeData->removeSentFilteredEntity(entity->getID());
} else if (!entityNodeData->isEntityFlaggedAsExtra(entity->getID())) {
// we don't send this entity because
// (1) it didn't match our filter
// (2) it didn't match our filter last frame
// (3) it isn't one the JSON query flags told us we should still include
includeThisEntity = false;
}
}
if (includeThisEntity && hadElementExtraData) {
includeThisEntity = entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID());
}
// 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 (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
// the entity may not be in view and then in view a frame later, let the client side handle it's view
// frustum culling on rendering.
bool success;
AACube entityCube = entity->getQueryAACube(success);
if (!success || !params.viewFrustum.cubeIntersectsKeyhole(entityCube)) {
includeThisEntity = false; // out of view, don't include it
} else {
// 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.
success = true;
// we can't cull a parent-entity by its dimensions because the child may be larger. we need to
// avoid sending details about a child but not the parent. the parent's queryAACube should have
// been adjusted to encompass the queryAACube of the child.
AABox entityBounds = entity->hasChildren() ? AABox(entityCube) : entity->getAABox(success);
if (!success) {
// if this entity is a child of an avatar, the entity-server wont be able to determine its
// AABox. If this happens, fall back to the queryAACube.
entityBounds = AABox(entityCube);
}
auto renderAccuracy = calculateRenderAccuracy(params.viewFrustum.getPosition(),
entityBounds,
params.octreeElementSizeScale,
params.boundaryLevelAdjust);
if (renderAccuracy <= 0.0f) {
includeThisEntity = false; // too small, don't include it
#ifdef WANT_LOD_DEBUGGING
qCDebug(entities) << "skipping entity - TOO SMALL - \n"
<< "......id:" << entity->getID() << "\n"
<< "....name:" << entity->getName() << "\n"
<< "..bounds:" << entityBounds << "\n"
<< "....cell:" << getAACube();
#endif
}
}
}
if (includeThisEntity) {
#ifdef WANT_LOD_DEBUGGING
qCDebug(entities) << "including entity - \n"
<< "......id:" << entity->getID() << "\n"
<< "....name:" << entity->getName() << "\n"
<< "....cell:" << getAACube();
#endif
indexesOfEntitiesToInclude << i;
numberOfEntities++;
} else {
// if the extra data included this entity, and we've decided to not include the entity, then
// we can treat it as if it was completed.
entityTreeElementExtraEncodeData->entities.remove(entity->getEntityItemID());
}
}
}
numberOfEntitiesOffset = packetData->getUncompressedByteOffset();
bool successAppendEntityCount = packetData->appendValue(numberOfEntities);
if (successAppendEntityCount) {
foreach(uint16_t i, indexesOfEntitiesToInclude) {
EntityItemPointer entity = _entityItems[i];
LevelDetails entityLevel = packetData->startLevel();
OctreeElement::AppendState appendEntityState = entity->appendEntityData(packetData,
params, entityTreeElementExtraEncodeData);
// If none of this entity data was able to be appended, then discard it
// and don't include it in our entity count
if (appendEntityState == OctreeElement::NONE) {
packetData->discardLevel(entityLevel);
} else {
// If either ALL or some of it got appended, then end the level (commit it)
// and include the entity in our final count of entities
packetData->endLevel(entityLevel);
actualNumberOfEntities++;
// If the entity item got completely appended, then we can remove it from the extra encode data
if (appendEntityState == OctreeElement::COMPLETED) {
entityTreeElementExtraEncodeData->entities.remove(entity->getEntityItemID());
}
}
// If any part of the entity items didn't fit, then the element is considered partial
// NOTE: if the entity item didn't fit or only partially fit, then the entity item should have
// added itself to the extra encode data.
if (appendEntityState != OctreeElement::COMPLETED) {
appendElementState = OctreeElement::PARTIAL;
}
}
} else {
// we we couldn't add the entity count, then we couldn't add anything for this element and we're in a NONE state
appendElementState = OctreeElement::NONE;
}
});
// If we were provided with extraEncodeData, and we allocated and/or got entityTreeElementExtraEncodeData
// then we need to do some additional processing, namely make sure our extraEncodeData is up to date for
// this octree element.
if (extraEncodeData && entityTreeElementExtraEncodeData) {
// After processing, if we are PARTIAL or COMPLETED then we need to re-include our extra data.
// Only our parent can remove our extra data in these cases and only after it knows that all of its
// children have been encoded.
//
// FIXME -- this comment seems wrong....
//
// If we weren't able to encode ANY data about ourselves, then we go ahead and remove our element data
// since that will signal that the entire element needs to be encoded on the next attempt
if (appendElementState == OctreeElement::NONE) {
if (!entityTreeElementExtraEncodeData->elementCompleted && entityTreeElementExtraEncodeData->entities.size() == 0) {
// TODO: we used to delete the extra encode data here. But changing the logic around
// this is now a dead code branch. Clean this up!
} else {
// TODO: some of these inserts might be redundant!!!
extraEncodeData->insert(this, entityTreeElementExtraEncodeData);
}
} else {
// If we weren't previously completed, check to see if we are
if (!entityTreeElementExtraEncodeData->elementCompleted) {
// If all of our items have been encoded, then we are complete as an element.
if (entityTreeElementExtraEncodeData->entities.size() == 0) {
entityTreeElementExtraEncodeData->elementCompleted = true;
}
}
// TODO: some of these inserts might be redundant!!!
extraEncodeData->insert(this, entityTreeElementExtraEncodeData);
}
}
// Determine if no entities at all were able to fit
bool noEntitiesFit = (numberOfEntities > 0 && actualNumberOfEntities == 0);
// If we wrote fewer entities than we expected, update the number of entities in our packet
bool successUpdateEntityCount = true;
if (numberOfEntities != actualNumberOfEntities) {
successUpdateEntityCount = packetData->updatePriorBytes(numberOfEntitiesOffset,
(const unsigned char*)&actualNumberOfEntities, sizeof(actualNumberOfEntities));
}
// If we weren't able to update our entity count, or we couldn't fit any entities, then
// we should discard our element and return a result of NONE
if (!successUpdateEntityCount) {
packetData->discardLevel(elementLevel);
appendElementState = OctreeElement::NONE;
} else {
if (noEntitiesFit) {
//appendElementState = OctreeElement::PARTIAL;
packetData->discardLevel(elementLevel);
appendElementState = OctreeElement::NONE;
} else {
packetData->endLevel(elementLevel);
}
}
return appendElementState;
}
bool EntityTreeElement::containsEntityBounds(EntityItemPointer entity) const {
bool success;
auto queryCube = entity->getQueryAACube(success);

View file

@ -121,17 +121,6 @@ public:
virtual bool requiresSplit() const override { return false; }
virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const override;
virtual void initializeExtraEncodeData(EncodeBitstreamParams& params) override;
virtual bool shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const override;
virtual bool shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const override;
virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const override;
virtual void elementEncodeComplete(EncodeBitstreamParams& params) const override;
bool alreadyFullyEncoded(EncodeBitstreamParams& params) const;
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
virtual OctreeElement::AppendState appendElementData(OctreePacketData* packetData,
EncodeBitstreamParams& params) const override;
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
/// from the network.

View file

@ -186,7 +186,6 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
}
// 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;

View file

@ -128,7 +128,6 @@ int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
}
// 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;

View file

@ -26,7 +26,6 @@ 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.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -89,8 +89,6 @@ int MaterialEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags MaterialEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_MATERIAL_URL;

View file

@ -32,7 +32,6 @@ 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.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -144,7 +144,6 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
@ -721,4 +720,4 @@ bool ModelEntityItem::isAnimatingSomething() const {
_animationProperties.getRunning() &&
(_animationProperties.getFPS() != 0.0f);
});
}
}

View file

@ -30,7 +30,6 @@ 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.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -503,8 +503,6 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);

View file

@ -216,8 +216,6 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da
return bytesRead;
}
// 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;

View file

@ -26,7 +26,6 @@ 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.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -183,8 +183,6 @@ int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* dat
return bytesRead;
}
// 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;

View file

@ -26,7 +26,6 @@ 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.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -188,8 +188,6 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
return bytesRead;
}
// 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;

View file

@ -98,8 +98,6 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
return bytesRead;
}
// 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;

View file

@ -30,7 +30,6 @@ 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.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -83,8 +83,6 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
return bytesRead;
}
// 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;

View file

@ -29,7 +29,6 @@ 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.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -191,8 +191,6 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);

View file

@ -33,7 +33,6 @@ public:
virtual bool setProperties(const EntityItemProperties& properties) override;
virtual bool setSubClassProperties(const EntityItemProperties& properties) override;
// 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,

View file

@ -45,7 +45,6 @@
#include "Octree.h"
#include "OctreeConstants.h"
#include "OctreeElementBag.h"
#include "OctreeLogging.h"
#include "OctreeQueryNode.h"
#include "OctreeUtils.h"
@ -57,7 +56,6 @@ Octree::Octree(bool shouldReaverage) :
_rootElement(NULL),
_isDirty(true),
_shouldReaverage(shouldReaverage),
_stopImport(false),
_isViewing(false),
_isServer(false)
{
@ -490,131 +488,6 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, uint64_t buffe
// skip bitstream to new startPoint
bitstreamAt += theseBytesRead;
bytesRead += theseBytesRead;
if (args.wantImportProgress) {
emit importProgress((100 * (bitstreamAt - bitstream)) / bufferSizeBytes);
}
}
}
void Octree::deleteOctreeElementAt(float x, float y, float z, float s) {
unsigned char* octalCode = pointToOctalCode(x,y,z,s);
deleteOctalCodeFromTree(octalCode);
delete[] octalCode; // cleanup memory
}
class DeleteOctalCodeFromTreeArgs {
public:
bool collapseEmptyTrees;
const unsigned char* codeBuffer;
int lengthOfCode;
bool deleteLastChild;
bool pathChanged;
};
// Note: uses the codeColorBuffer format, but the color's are ignored, because
// this only finds and deletes the element from the tree.
void Octree::deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool collapseEmptyTrees) {
// recurse the tree while decoding the codeBuffer, once you find the element in question, recurse
// back and implement color reaveraging, and marking of lastChanged
DeleteOctalCodeFromTreeArgs args;
args.collapseEmptyTrees = collapseEmptyTrees;
args.codeBuffer = codeBuffer;
args.lengthOfCode = numberOfThreeBitSectionsInCode(codeBuffer);
args.deleteLastChild = false;
args.pathChanged = false;
withWriteLock([&] {
deleteOctalCodeFromTreeRecursion(_rootElement, &args);
});
}
void Octree::deleteOctalCodeFromTreeRecursion(const OctreeElementPointer& element, void* extraData) {
DeleteOctalCodeFromTreeArgs* args = (DeleteOctalCodeFromTreeArgs*)extraData;
int lengthOfElementCode = numberOfThreeBitSectionsInCode(element->getOctalCode());
// Since we traverse the tree in code order, we know that if our code
// matches, then we've reached our target element.
if (lengthOfElementCode == args->lengthOfCode) {
// we've reached our target, depending on how we're called we may be able to operate on it
// it here, we need to recurse up, and delete it there. So we handle these cases the same to keep
// the logic consistent.
args->deleteLastChild = true;
return;
}
// Ok, we know we haven't reached our target element yet, so keep looking
int childIndex = branchIndexWithDescendant(element->getOctalCode(), args->codeBuffer);
OctreeElementPointer childElement = element->getChildAtIndex(childIndex);
// If there is no child at the target location, and the current parent element is a colored leaf,
// then it means we were asked to delete a child out of a larger leaf voxel.
// We support this by breaking up the parent voxel into smaller pieces.
if (!childElement && element->requiresSplit()) {
// we need to break up ancestors until we get to the right level
OctreeElementPointer ancestorElement = element;
while (true) {
int index = branchIndexWithDescendant(ancestorElement->getOctalCode(), args->codeBuffer);
// we end up with all the children, even the one we want to delete
ancestorElement->splitChildren();
int lengthOfAncestorElement = numberOfThreeBitSectionsInCode(ancestorElement->getOctalCode());
// If we've reached the parent of the target, then stop breaking up children
if (lengthOfAncestorElement == (args->lengthOfCode - 1)) {
// since we created all the children when we split, we need to delete this target one
ancestorElement->deleteChildAtIndex(index);
break;
}
ancestorElement = ancestorElement->getChildAtIndex(index);
}
_isDirty = true;
args->pathChanged = true;
// ends recursion, unwinds up stack
return;
}
// if we don't have a child and we reach this point, then we actually know that the parent
// isn't a colored leaf, and the child branch doesn't exist, so there's nothing to do below and
// we can safely return, ending the recursion and unwinding
if (!childElement) {
return;
}
// If we got this far then we have a child for the branch we're looking for, but we're not there yet
// recurse till we get there
deleteOctalCodeFromTreeRecursion(childElement, args);
// If the lower level determined it needs to be deleted, then we should delete now.
if (args->deleteLastChild) {
element->deleteChildAtIndex(childIndex); // note: this will track dirtiness and lastChanged for this element
// track our tree dirtiness
_isDirty = true;
// track that path has changed
args->pathChanged = true;
// If we're in collapseEmptyTrees mode, and this was the last child of this element, then we also want
// to delete this element. This will collapse the empty tree above us.
if (args->collapseEmptyTrees && element->getChildCount() == 0) {
// Can't delete the root this way.
if (element == _rootElement) {
args->deleteLastChild = false; // reset so that further up the unwinding chain we don't do anything
}
} else {
args->deleteLastChild = false; // reset so that further up the unwinding chain we don't do anything
}
}
// If the lower level did some work, then we need to let this element know, so it can
// do any bookkeeping it wants to, like color re-averaging, time stamp marking, etc
if (args->pathChanged) {
element->handleSubtreeChanged(shared_from_this());
}
}
@ -883,720 +756,6 @@ OctreeElementPointer Octree::getElementEnclosingPoint(const glm::vec3& point, Oc
return args.element;
}
int Octree::encodeTreeBitstream(const OctreeElementPointer& element,
OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params) {
// How many bytes have we written so far at this level;
int bytesWritten = 0;
// you can't call this without a valid element
if (!element) {
qCDebug(octree, "WARNING! encodeTreeBitstream() called with element=NULL");
params.stopReason = EncodeBitstreamParams::NULL_NODE;
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 (octreeQueryNode->getUsesFrustum() && !params.recurseEverything && !element->isInView(params.viewFrustum)) {
params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
return bytesWritten;
}
// write the octal code
bool roomForOctalCode = false; // assume the worst
int codeLength = 1; // assume root
if (params.chopLevels) {
unsigned char* newCode = chopOctalCode(element->getOctalCode(), params.chopLevels);
roomForOctalCode = packetData->startSubTree(newCode);
if (newCode) {
codeLength = numberOfThreeBitSectionsInCode(newCode);
delete[] newCode;
} else {
codeLength = 1;
}
} else {
roomForOctalCode = packetData->startSubTree(element->getOctalCode());
codeLength = (int)bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(element->getOctalCode()));
}
// If the octalcode couldn't fit, then we can return, because no nodes below us will fit...
if (!roomForOctalCode) {
bag.insert(element);
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
return bytesWritten;
}
bytesWritten += codeLength; // keep track of byte count
int currentEncodeLevel = 0;
// 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
octreeQueryNode->stats.traversed(element);
ViewFrustum::intersection parentLocationThisView = ViewFrustum::INTERSECT; // assume parent is in view, but not fully
int childBytesWritten = encodeTreeBitstreamRecursion(element, packetData, bag, params,
currentEncodeLevel, parentLocationThisView);
// if childBytesWritten == 1 then something went wrong... that's not possible
assert(childBytesWritten != 1);
// if childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some
// reason couldn't be written... so reset them here... This isn't true for the non-color included case
if (suppressEmptySubtrees() && childBytesWritten == 2) {
childBytesWritten = 0;
//params.stopReason = EncodeBitstreamParams::UNKNOWN; // possibly should be DIDNT_FIT...
}
// if we wrote child bytes, then return our result of all bytes written
if (childBytesWritten) {
bytesWritten += childBytesWritten;
} else {
// otherwise... if we didn't write any child bytes, then pretend like we also didn't write our octal code
bytesWritten = 0;
//params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
}
if (bytesWritten == 0) {
packetData->discardSubTree();
} else {
packetData->endSubTree();
}
return bytesWritten;
}
int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel,
const ViewFrustum::intersection& parentLocationThisView) const {
const bool wantDebug = false;
// The append state of this level/element.
OctreeElement::AppendState elementAppendState = OctreeElement::COMPLETED; // assume the best
// How many bytes have we written so far at this level;
int bytesAtThisLevel = 0;
// you can't call this without a valid element
if (!element) {
qCDebug(octree, "WARNING! encodeTreeBitstreamRecursion() called with element=NULL");
params.stopReason = EncodeBitstreamParams::NULL_NODE;
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++;
params.maxLevelReached = std::max(currentEncodeLevel, params.maxLevelReached);
// If we've reached our max Search Level, then stop searching.
if (currentEncodeLevel >= params.maxEncodeLevel) {
params.stopReason = EncodeBitstreamParams::TOO_DEEP;
return bytesAtThisLevel;
}
ViewFrustum::intersection nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside
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) {
octreeQueryNode->stats.skippedDistance(element);
params.stopReason = EncodeBitstreamParams::LOD_SKIP;
return bytesAtThisLevel;
}
// if the parent isn't known to be INSIDE, then it must be INTERSECT, and we should double check to see
// if we are INSIDE, INTERSECT, or OUTSIDE
if (parentLocationThisView != ViewFrustum::INSIDE) {
assert(parentLocationThisView != ViewFrustum::OUTSIDE); // we shouldn't be here if our parent was OUTSIDE!
nodeLocationThisView = element->computeViewIntersection(params.viewFrustum);
}
// If we're at a element that is out of view, then we can return, because no nodes below us will be in view!
// 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) {
octreeQueryNode->stats.skippedOutOfView(element);
params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
return bytesAtThisLevel;
}
// Ok, we are in view, but if we're in delta mode, then we also want to make sure we weren't already in view
// because we don't send nodes from the previously know in view frustum.
bool wasInView = false;
if (params.deltaView) {
ViewFrustum::intersection location = element->computeViewIntersection(params.lastViewFrustum);
// If we're a leaf, then either intersect or inside is considered "formerly in view"
if (element->isLeaf()) {
wasInView = location != ViewFrustum::OUTSIDE;
} else {
wasInView = location == ViewFrustum::INSIDE;
}
// If we were in view, double check that we didn't switch LOD visibility... namely, the was in view doesn't
// tell us if it was so small we wouldn't have rendered it. Which may be the case. And we may have moved closer
// to it, and so therefore it may now be visible from an LOD perspective, in which case we don't consider it
// as "was in view"...
if (wasInView) {
float boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
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;
}
}
}
// 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(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 voxel hasn't changed,
// then we can also bail early and save bits
if (!params.forceSendScene && !params.deltaView &&
!element->hasChangedSince(octreeQueryNode->getLastTimeBagEmpty() - CHANGE_FUDGE)) {
octreeQueryNode->stats.skippedNoChange(element);
params.stopReason = EncodeBitstreamParams::NO_CHANGE;
return bytesAtThisLevel;
}
bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more!
// At any given point in writing the bitstream, the largest minimum we might need to flesh out the current level
// is 1 byte for child colors + 3*NUMBER_OF_CHILDREN bytes for the actual colors + 1 byte for child trees.
// There could be sub trees below this point, which might take many more bytes, but that's ok, because we can
// always mark our subtrees as not existing and stop the packet at this point, then start up with a new packet
// for the remaining sub trees.
unsigned char childrenExistInTreeBits = 0;
unsigned char childrenExistInPacketBits = 0;
unsigned char childrenDataBits = 0;
// Make our local buffer large enough to handle writing at this level in case we need to.
LevelDetails thisLevelKey = packetData->startLevel();
int requiredBytes = sizeof(childrenDataBits) + sizeof(childrenExistInPacketBits);
if (params.includeExistsBits) {
requiredBytes += sizeof(childrenExistInTreeBits);
}
// If this datatype allows root elements to include data, and this is the root, then ask the tree for the
// minimum bytes needed for root data and reserve those also
if (element == _rootElement && rootElementHasData()) {
requiredBytes += minimumRequiredRootDataBytes();
}
bool continueThisLevel = packetData->reserveBytes(requiredBytes);
// If we can't reserve our minimum bytes then we can discard this level and return as if none of this level fits
if (!continueThisLevel) {
packetData->discardLevel(thisLevelKey);
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
bag.insert(element);
return bytesAtThisLevel;
}
int inViewCount = 0;
int inViewNotLeafCount = 0;
int inViewWithColorCount = 0;
OctreeElementPointer sortedChildren[NUMBER_OF_CHILDREN] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
float distancesToChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 };
int indexOfChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
OctreeElementPointer childElement = element->getChildAtIndex(i);
// if the caller wants to include childExistsBits, then include them
if (params.includeExistsBits && childElement) {
childrenExistInTreeBits += (1 << (7 - i));
}
sortedChildren[i] = childElement;
indexOfChildren[i] = i;
distancesToChildren[i] = 0.0f;
// track stats
// must check childElement here, because it could be we got here with no childElement
if (childElement) {
octreeQueryNode->stats.traversed(childElement);
}
}
// for each child element in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
// add them to our distance ordered array of children
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
OctreeElementPointer childElement = sortedChildren[i];
int originalIndex = indexOfChildren[i];
bool childIsInView = (childElement &&
(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
));
if (!childIsInView) {
// must check childElement here, because it could be we got here because there was no 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 || !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
octreeQueryNode->stats.skippedDistance(childElement);
} else {
inViewCount++;
// track children in view as existing and not a leaf, if they're a leaf,
// we don't care about recursing deeper on them, and we don't consider their
// subtree to exist
if (!(childElement && childElement->isLeaf())) {
childrenExistInPacketBits += (1 << (7 - originalIndex));
inViewNotLeafCount++;
}
bool childIsOccluded = false; // assume it's not occluded
bool shouldRender = params.recurseEverything || !octreeQueryNode->getUsesFrustum() ||
childElement->calculateShouldRender(params.viewFrustum,
params.octreeElementSizeScale, params.boundaryLevelAdjust);
// track some stats
// 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!
if (shouldRender && !childIsOccluded) {
bool childWasInView = false;
if (childElement && params.deltaView) {
ViewFrustum::intersection location = childElement->computeViewIntersection(params.lastViewFrustum);
// If we're a leaf, then either intersect or inside is considered "formerly in view"
if (childElement->isLeaf()) {
childWasInView = location != ViewFrustum::OUTSIDE;
} else {
childWasInView = location == ViewFrustum::INSIDE;
}
}
// If our child wasn't in view (or we're ignoring wasInView) then we add it to our sending items.
// Or if we were previously in the view, but this element has changed since it was last sent, then we do
// need to send it.
if (!childWasInView ||
(params.deltaView &&
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 (childWasInView) {
octreeQueryNode->stats.skippedWasInView(childElement);
} else {
octreeQueryNode->stats.skippedNoChange(childElement);
}
}
}
}
}
}
// NOTE: the childrenDataBits indicates that there is an array of child element data included in this packet.
// We will write this bit mask but we may come back later and update the bits that are actually included
packetData->releaseReservedBytes(sizeof(childrenDataBits));
continueThisLevel = packetData->appendBitMask(childrenDataBits);
int childDataBitsPlaceHolder = packetData->getUncompressedByteOffset(sizeof(childrenDataBits));
unsigned char actualChildrenDataBits = 0;
assert(continueThisLevel); // since we used reserved bits, this really shouldn't fail
bytesAtThisLevel += sizeof(childrenDataBits); // keep track of byte count
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);
// write the child element data...
// NOTE: the format of the bitstream is generally this:
// [octalcode]
// [bitmask for existence of child data]
// N x [child data]
// [bitmask for existence of child elements in tree]
// [bitmask for existence of child elements in buffer]
// N x [ ... tree for children ...]
//
// This section of the code, is writing the "N x [child data]" portion of this bitstream
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (oneAtBit(childrenDataBits, i)) {
OctreeElementPointer childElement = element->getChildAtIndex(i);
// the childrenDataBits were set up by the in view/LOD logic, it may contain children that we've already
// processed and sent the data bits for. Let our tree subclass determine if it really wants to send the
// data for this child at this point
if (childElement && element->shouldIncludeChildData(i, params)) {
int bytesBeforeChild = packetData->getUncompressedSize();
// a childElement may "partially" write it's data. for example, the model server where the entire
// contents of the element may be larger than can fit in a single MTU/packetData. In this case,
// we want to allow the appendElementData() to respond that it produced partial data, which should be
// written, but that the childElement needs to be reprocessed in an additional pass or passes
// to be completed.
LevelDetails childDataLevelKey = packetData->startLevel();
OctreeElement::AppendState childAppendState = childElement->appendElementData(packetData, params);
// allow our tree subclass to do any additional bookkeeping it needs to do with encoded data state
element->updateEncodedData(i, childAppendState, params);
// Continue this level so long as some part of this child element was appended.
bool childFit = (childAppendState != OctreeElement::NONE);
// some datatypes (like Voxels) assume that all child data will fit, if it doesn't fit
// the data type wants to bail on this element level completely
if (!childFit && mustIncludeAllChildData()) {
continueThisLevel = false;
break;
}
// If the child was partially or fully appended, then mark the actualChildrenDataBits as including
// this child data
if (childFit) {
actualChildrenDataBits += (1 << (7 - i));
continueThisLevel = packetData->endLevel(childDataLevelKey);
} else {
packetData->discardLevel(childDataLevelKey);
elementAppendState = OctreeElement::PARTIAL;
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
}
// If this child was partially appended, then consider this element to be partially appended
if (childAppendState == OctreeElement::PARTIAL) {
elementAppendState = OctreeElement::PARTIAL;
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
}
int bytesAfterChild = packetData->getUncompressedSize();
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 (childAppendState != OctreeElement::NONE) {
octreeQueryNode->stats.colorSent(childElement);
}
}
}
}
if (!mustIncludeAllChildData() && !continueThisLevel) {
qCDebug(octree) << "WARNING UNEXPECTED CASE: reached end of child element data loop with continueThisLevel=FALSE";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
if (continueThisLevel && actualChildrenDataBits != childrenDataBits) {
// repair the child data mask
continueThisLevel = packetData->updatePriorBitMask(childDataBitsPlaceHolder, actualChildrenDataBits);
if (!continueThisLevel) {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to update childDataBitsPlaceHolder";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
}
// if the caller wants to include childExistsBits, then include them even if not in view, put them before the
// childrenExistInPacketBits, so that the lower code can properly repair the packet exists bits
if (continueThisLevel && params.includeExistsBits) {
packetData->releaseReservedBytes(sizeof(childrenExistInTreeBits));
continueThisLevel = packetData->appendBitMask(childrenExistInTreeBits);
if (continueThisLevel) {
bytesAtThisLevel += sizeof(childrenExistInTreeBits); // keep track of byte count
octreeQueryNode->stats.existsBitsWritten();
} else {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to append childrenExistInTreeBits";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
}
// write the child exist bits
if (continueThisLevel) {
packetData->releaseReservedBytes(sizeof(childrenExistInPacketBits));
continueThisLevel = packetData->appendBitMask(childrenExistInPacketBits);
if (continueThisLevel) {
bytesAtThisLevel += sizeof(childrenExistInPacketBits); // keep track of byte count
octreeQueryNode->stats.existsInPacketBitsWritten();
} else {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to append childrenExistInPacketBits";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
}
// We only need to keep digging, if there is at least one child that is inView, and not a leaf.
keepDiggingDeeper = (inViewNotLeafCount > 0);
//
// NOTE: the format of the bitstream is generally this:
// [octalcode]
// [bitmask for existence of child data]
// N x [child data]
// [bitmask for existence of child elements in tree]
// [bitmask for existence of child elements in buffer]
// N x [ ... tree for children ...]
//
// This section of the code, is writing the "N x [ ... tree for children ...]" portion of this bitstream
//
if (continueThisLevel && keepDiggingDeeper) {
// at this point, we need to iterate the children who are in view, even if not colored
// and we need to determine if there's a deeper tree below them that we care about.
//
// Since this recursive function assumes we're already writing, we know we've already written our
// childrenExistInPacketBits. But... we don't really know how big the child tree will be. And we don't know if
// we'll have room in our buffer to actually write all these child trees. What we kinda would like to do is
// write our childExistsBits as a place holder. Then let each potential tree have a go at it. If they
// write something, we keep them in the bits, if they don't, we take them out.
//
// we know the last thing we wrote to the packet was our childrenExistInPacketBits. Let's remember where that was!
int childExistsPlaceHolder = packetData->getUncompressedByteOffset(sizeof(childrenExistInPacketBits));
// we are also going to recurse these child trees in "distance" sorted order, but we need to pack them in the
// final packet in standard order. So what we're going to do is keep track of how big each subtree was in bytes,
// and then later reshuffle these sections of our output buffer back into normal order. This allows us to make
// a single recursive pass in distance sorted order, but retain standard order in our encoded packet
// for each child element in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
// add them to our distance ordered array of children
for (int indexByDistance = 0; indexByDistance < NUMBER_OF_CHILDREN; indexByDistance++) {
OctreeElementPointer childElement = sortedChildren[indexByDistance];
int originalIndex = indexOfChildren[indexByDistance];
if (oneAtBit(childrenExistInPacketBits, originalIndex)) {
int thisLevel = currentEncodeLevel;
int childTreeBytesOut = 0;
// NOTE: some octree styles (like models and particles) will store content in parent elements, and child
// elements. In this case, if we stop recursion when we include any data (the colorbits should really be
// 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 || !octreeQueryNode->getUsesFrustum()
|| recurseChildrenWithData() || !oneAtBit(childrenDataBits, originalIndex)) {
// Allow the datatype a chance to determine if it really wants to recurse this tree. Usually this
// will be true. But if the tree has already been encoded, we will skip this.
if (element->shouldRecurseChildTree(originalIndex, params)) {
childTreeBytesOut = encodeTreeBitstreamRecursion(childElement, packetData, bag, params,
thisLevel, nodeLocationThisView);
} else {
childTreeBytesOut = 0;
}
}
// if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space,
// basically, the children below don't contain any info.
// if the child tree wrote 1 byte??? something must have gone wrong... because it must have at least the color
// byte and the child exist byte.
//
assert(childTreeBytesOut != 1);
// if the child tree wrote just 2 bytes, then it means: it had no colors and no child nodes, because...
// if it had colors it would write 1 byte for the color mask,
// and at least a color's worth of bytes for the element of colors.
// if it had child trees (with something in them) then it would have the 1 byte for child mask
// and some number of bytes of lower children...
// so, if the child returns 2 bytes out, we can actually consider that an empty tree also!!
//
// we can make this act like no bytes out, by just resetting the bytes out in this case
if (suppressEmptySubtrees() && !params.includeExistsBits && childTreeBytesOut == 2) {
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
}
bytesAtThisLevel += childTreeBytesOut;
// If we had previously started writing, and if the child DIDN'T write any bytes,
// then we want to remove their bit from the childExistsPlaceHolder bitmask
if (childTreeBytesOut == 0) {
// remove this child's bit...
childrenExistInPacketBits -= (1 << (7 - originalIndex));
// repair the child exists mask
continueThisLevel = packetData->updatePriorBitMask(childExistsPlaceHolder, childrenExistInPacketBits);
if (!continueThisLevel) {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to update childExistsPlaceHolder";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
// If this is the last of the child exists bits, then we're actually be rolling out the entire tree
if (childrenExistInPacketBits == 0) {
octreeQueryNode->stats.childBitsRemoved(params.includeExistsBits);
}
if (!continueThisLevel) {
if (wantDebug) {
qCDebug(octree) << " WARNING line:" << __LINE__;
qCDebug(octree) << " breaking the child recursion loop with continueThisLevel=false!!!";
qCDebug(octree) << " AFTER attempting to updatePriorBitMask() for empty sub tree....";
qCDebug(octree) << " IS THIS ACCEPTABLE!!!!";
}
break; // can't continue...
}
// Note: no need to move the pointer, cause we already stored this
} // end if (childTreeBytesOut == 0)
} // end if (oneAtBit(childrenExistInPacketBits, originalIndex))
} // end for
} // end keepDiggingDeeper
// If we made it this far, then we've written all of our child data... if this element is the root
// element, then we also allow the root element to write out it's data...
if (continueThisLevel && element == _rootElement && rootElementHasData()) {
int bytesBeforeChild = packetData->getUncompressedSize();
// release the bytes we reserved...
packetData->releaseReservedBytes(minimumRequiredRootDataBytes());
LevelDetails rootDataLevelKey = packetData->startLevel();
OctreeElement::AppendState rootAppendState = element->appendElementData(packetData, params);
bool partOfRootFit = (rootAppendState != OctreeElement::NONE);
bool allOfRootFit = (rootAppendState == OctreeElement::COMPLETED);
if (partOfRootFit) {
continueThisLevel = packetData->endLevel(rootDataLevelKey);
if (!continueThisLevel) {
qCDebug(octree) << " UNEXPECTED ROOT ELEMENT -- could not packetData->endLevel(rootDataLevelKey) -- line:" << __LINE__;
}
} else {
packetData->discardLevel(rootDataLevelKey);
}
if (!allOfRootFit) {
elementAppendState = OctreeElement::PARTIAL;
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
}
// do we really ever NOT want to continue this level???
//continueThisLevel = (rootAppendState == OctreeElement::COMPLETED);
int bytesAfterChild = packetData->getUncompressedSize();
if (continueThisLevel) {
bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child
octreeQueryNode->stats.colorSent(element);
}
if (!continueThisLevel) {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Something failed in packing ROOT data";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
}
// if we were unable to fit this level in our packet, then rewind and add it to the element bag for
// sending later...
if (continueThisLevel) {
continueThisLevel = packetData->endLevel(thisLevelKey);
} else {
packetData->discardLevel(thisLevelKey);
if (!mustIncludeAllChildData()) {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Something failed in attempting to pack this element";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
}
// This happens if the element could not be written at all. In the case of Octree's that support partial
// element data, continueThisLevel will be true. So this only happens if the full element needs to be
// added back to the element bag.
if (!continueThisLevel) {
if (!mustIncludeAllChildData()) {
qCDebug(octree) << "WARNING UNEXPECTED CASE - Something failed in attempting to pack this element.";
qCDebug(octree) << " If the datatype requires all child data, then this might happen. Otherwise" ;
qCDebug(octree) << " this is an unexpected case and we should research a potential logic error." ;
}
bag.insert(element);
// don't need to check element here, because we can't get here with no element
octreeQueryNode->stats.didntFit(element);
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
bytesAtThisLevel = 0; // didn't fit
} else {
// assuming we made it here with continueThisLevel == true, we STILL might want
// to add our element back to the bag for additional encoding, specifically if
// the appendState is PARTIAL, in this case, we re-add our element to the bag
// and assume that the appendElementData() has stored any required state data
// in the params extraEncodeData
if (elementAppendState == OctreeElement::PARTIAL) {
bag.insert(element);
}
}
// If our element is completed let the element know so it can do any cleanup it of extra wants
if (elementAppendState == OctreeElement::COMPLETED) {
element->elementEncodeComplete(params);
}
return bytesAtThisLevel;
}
bool Octree::readFromFile(const char* fileName) {
QString qFileName = findMostRecentFileExtension(fileName, PERSIST_EXTENSIONS);
@ -1615,14 +774,10 @@ bool Octree::readFromFile(const char* fileName) {
QFileInfo fileInfo(qFileName);
uint64_t fileLength = fileInfo.size();
emit importSize(1.0f, 1.0f, 1.0f);
emit importProgress(0);
qCDebug(octree) << "Loading file" << qFileName << "...";
bool success = readFromStream(fileLength, fileInputStream);
emit importProgress(100);
file.close();
return success;
@ -1863,7 +1018,3 @@ bool Octree::countOctreeElementsOperation(const OctreeElementPointer& element, v
(*(uint64_t*)extraData)++;
return true; // keep going
}
void Octree::cancelImport() {
_stopImport = true;
}

View file

@ -49,126 +49,62 @@ public:
// Callback function, for recuseTreeWithOperation
using RecurseOctreeOperation = std::function<bool(const OctreeElementPointer&, void*)>;
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
typedef QHash<uint, AACube> CubeList;
const bool NO_EXISTS_BITS = false;
const bool WANT_EXISTS_BITS = true;
const bool COLLAPSE_EMPTY_TREE = true;
const bool DONT_COLLAPSE = false;
const int DONT_CHOP = 0;
const int NO_BOUNDARY_ADJUST = 0;
const int LOW_RES_MOVING_ADJUST = 1;
#define IGNORE_COVERAGE_MAP NULL
class EncodeBitstreamParams {
public:
ViewFrustum viewFrustum;
ViewFrustum lastViewFrustum;
int maxEncodeLevel;
int maxLevelReached;
bool includeExistsBits;
int chopLevels;
bool deltaView;
bool recurseEverything { false };
int boundaryLevelAdjust;
float octreeElementSizeScale;
bool forceSendScene;
NodeData* nodeData;
// output hints from the encode process
typedef enum {
UNKNOWN,
DIDNT_FIT,
NULL_NODE,
NULL_NODE_DATA,
TOO_DEEP,
LOD_SKIP,
OUT_OF_VIEW,
WAS_IN_VIEW,
NO_CHANGE,
OCCLUDED,
FINISHED
} reason;
reason stopReason;
EncodeBitstreamParams(
int maxEncodeLevel = INT_MAX,
bool includeExistsBits = WANT_EXISTS_BITS,
int chopLevels = 0,
bool useDeltaView = false,
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
bool forceSendScene = true,
NodeData* nodeData = nullptr) :
maxEncodeLevel(maxEncodeLevel),
maxLevelReached(0),
EncodeBitstreamParams(bool includeExistsBits = WANT_EXISTS_BITS,
NodeData* nodeData = nullptr) :
includeExistsBits(includeExistsBits),
chopLevels(chopLevels),
deltaView(useDeltaView),
boundaryLevelAdjust(boundaryLevelAdjust),
octreeElementSizeScale(octreeElementSizeScale),
forceSendScene(forceSendScene),
nodeData(nodeData),
stopReason(UNKNOWN)
{
lastViewFrustum.invalidate();
}
void displayStopReason() {
printf("StopReason: ");
switch (stopReason) {
default:
case UNKNOWN: qDebug("UNKNOWN"); break;
case DIDNT_FIT: qDebug("DIDNT_FIT"); break;
case NULL_NODE: qDebug("NULL_NODE"); break;
case TOO_DEEP: qDebug("TOO_DEEP"); break;
case LOD_SKIP: qDebug("LOD_SKIP"); break;
case OUT_OF_VIEW: qDebug("OUT_OF_VIEW"); break;
case WAS_IN_VIEW: qDebug("WAS_IN_VIEW"); break;
case NO_CHANGE: qDebug("NO_CHANGE"); break;
case OCCLUDED: qDebug("OCCLUDED"); break;
case FINISHED: qDebug("FINISHED"); break;
}
}
QString getStopReason() {
switch (stopReason) {
default:
case UNKNOWN: return QString("UNKNOWN"); break;
case DIDNT_FIT: return QString("DIDNT_FIT"); break;
case NULL_NODE: return QString("NULL_NODE"); break;
case TOO_DEEP: return QString("TOO_DEEP"); break;
case LOD_SKIP: return QString("LOD_SKIP"); break;
case OUT_OF_VIEW: return QString("OUT_OF_VIEW"); break;
case WAS_IN_VIEW: return QString("WAS_IN_VIEW"); break;
case NO_CHANGE: return QString("NO_CHANGE"); break;
case OCCLUDED: return QString("OCCLUDED"); break;
case FINISHED: return QString("FINISHED"); break;
}
}
std::function<void(const QUuid& dataID, quint64 itemLastEdited)> trackSend { [](const QUuid&, quint64){} };
};
class ReadElementBufferToTreeArgs {
public:
const unsigned char* buffer;
int length;
bool destructive;
bool pathChanged;
};
class ReadBitstreamToTreeParams {
public:
bool includeExistsBits;
OctreeElementPointer destinationElement;
QUuid sourceUUID;
SharedNodePointer sourceNode;
bool wantImportProgress;
PacketVersion bitstreamVersion;
int elementsPerPacket = 0;
int entitiesPerPacket = 0;
@ -176,15 +112,11 @@ public:
bool includeExistsBits = WANT_EXISTS_BITS,
OctreeElementPointer destinationElement = NULL,
QUuid sourceUUID = QUuid(),
SharedNodePointer sourceNode = SharedNodePointer(),
bool wantImportProgress = false,
PacketVersion bitstreamVersion = 0) :
SharedNodePointer sourceNode = SharedNodePointer()) :
includeExistsBits(includeExistsBits),
destinationElement(destinationElement),
sourceUUID(sourceUUID),
sourceNode(sourceNode),
wantImportProgress(wantImportProgress),
bitstreamVersion(bitstreamVersion)
sourceNode(sourceNode)
{}
};
@ -199,7 +131,6 @@ public:
// These methods will allow the OctreeServer to send your tree inbound edit packets of your
// own definition. Implement these to allow your octree based server to support editing
virtual bool getWantSVOfileVersions() const { return false; }
virtual PacketType expectedDataPacketType() const { return PacketType::Unknown; }
virtual PacketVersion expectedVersion() const { return versionForPacketType(expectedDataPacketType()); }
virtual bool handlesEditPacketType(PacketType packetType) const { return false; }
@ -209,12 +140,8 @@ public:
virtual void processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; }
virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; }
virtual bool recurseChildrenWithData() const { return true; }
virtual bool rootElementHasData() const { return false; }
virtual int minimumRequiredRootDataBytes() const { return 0; }
virtual bool suppressEmptySubtrees() const { return true; }
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const { }
virtual bool mustIncludeAllChildData() const { return true; }
virtual void update() { } // nothing to do by default
@ -223,11 +150,8 @@ public:
virtual void eraseAllOctreeElements(bool createNewRoot = true);
virtual void readBitstreamToTree(const unsigned char* bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args);
void deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE);
void reaverageOctreeElements(OctreeElementPointer startElement = OctreeElementPointer());
void deleteOctreeElementAt(float x, float y, float z, float s);
/// Find the voxel at position x,y,z,s
/// \return pointer to the OctreeElement or NULL if none at x,y,z,s.
OctreeElementPointer getOctreeElementAt(float x, float y, float z, float s) const;
@ -250,8 +174,6 @@ public:
void recurseTreeWithOperator(RecurseOctreeOperator* operatorObject);
int encodeTreeBitstream(const OctreeElementPointer& element, OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params) ;
bool isDirty() const { return _isDirty; }
void clearDirtyBit() { _isDirty = false; }
@ -344,22 +266,10 @@ public:
void incrementPersistDataVersion() { _persistDataVersion++; }
signals:
void importSize(float x, float y, float z);
void importProgress(int progress);
public slots:
void cancelImport();
protected:
void deleteOctalCodeFromTreeRecursion(const OctreeElementPointer& element, void* extraData);
int encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel,
const ViewFrustum::intersection& parentLocationThisView) const;
static bool countOctreeElementsOperation(const OctreeElementPointer& element, void* extraData);
OctreeElementPointer nodeForOctalCode(const OctreeElementPointer& ancestorElement, const unsigned char* needleCode, OctreeElementPointer* parentOfFoundElement) const;
@ -374,7 +284,6 @@ protected:
bool _isDirty;
bool _shouldReaverage;
bool _stopImport;
bool _isViewing;
bool _isServer;

View file

@ -461,33 +461,6 @@ ViewFrustum::intersection OctreeElement::computeViewIntersection(const ViewFrust
return viewFrustum.calculateCubeKeyholeIntersection(_cube);
}
// There are two types of nodes for which we want to "render"
// 1) Leaves that are in the LOD
// 2) Non-leaves are more complicated though... usually you don't want to render them, but if their children
// wouldn't be rendered, then you do want to render them. But sometimes they have some children that ARE
// in the LOD, and others that are not. In this case we want to render the parent, and none of the children.
//
// Since, if we know the camera position and orientation, we can know which of the corners is the "furthest"
// corner. We can use we can use this corner as our "voxel position" to do our distance calculations off of.
// By doing this, we don't need to test each child voxel's position vs the LOD boundary
bool OctreeElement::calculateShouldRender(const ViewFrustum& viewFrustum, float voxelScaleSize, int boundaryLevelAdjust) const {
bool shouldRender = false;
if (hasContent()) {
float furthestDistance = furthestDistanceToCamera(viewFrustum);
float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize);
bool inChildBoundary = (furthestDistance <= childBoundary);
if (hasDetailedContent() && inChildBoundary) {
shouldRender = true;
} else {
float boundary = childBoundary * 2.0f; // the boundary is always twice the distance of the child boundary
bool inBoundary = (furthestDistance <= boundary);
shouldRender = inBoundary && !inChildBoundary;
}
}
return shouldRender;
}
// Calculates the distance to the furthest point of the voxel to the camera
// does as much math as possible in voxel scale and then scales up to TREE_SCALE at end
float OctreeElement::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const {

View file

@ -85,16 +85,6 @@ public:
typedef enum { COMPLETED, PARTIAL, NONE } AppendState;
virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const { }
virtual void initializeExtraEncodeData(EncodeBitstreamParams& params) { }
virtual bool shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const { return true; }
virtual bool shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const { return true; }
virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const { }
virtual void elementEncodeComplete(EncodeBitstreamParams& params) const { }
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
virtual AppendState appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const
{ return COMPLETED; }
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
/// from the network.
@ -139,9 +129,6 @@ public:
float distanceToCamera(const ViewFrustum& viewFrustum) const;
float furthestDistanceToCamera(const ViewFrustum& viewFrustum) const;
bool calculateShouldRender(const ViewFrustum& viewFrustum,
float voxelSizeScale = DEFAULT_OCTREE_SIZE_SCALE, int boundaryLevelAdjust = 0) const;
// points are assumed to be in Voxel Coordinates (not TREE_SCALE'd)
float distanceSquareToPoint(const glm::vec3& point) const; // when you don't need the actual distance, use this.
float distanceToPoint(const glm::vec3& point) const;

View file

@ -1,40 +0,0 @@
//
// OctreeElementBag.cpp
// libraries/octree/src
//
// Created by Brad Hefta-Gaub on 4/25/2013.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OctreeElementBag.h"
#include <OctalCode.h>
void OctreeElementBag::deleteAll() {
_bagElements = Bag();
}
/// does the bag contain elements?
/// if all of the contained elements are expired, they will not report as empty, and
/// a single last item will be returned by extract as a null pointer
bool OctreeElementBag::isEmpty() {
return _bagElements.empty();
}
void OctreeElementBag::insert(const OctreeElementPointer& element) {
_bagElements[element.get()] = element;
}
OctreeElementPointer OctreeElementBag::extract() {
OctreeElementPointer result;
// Find the first element still alive
Bag::iterator it = _bagElements.begin();
while (it != _bagElements.end() && !result) {
result = it->second.lock();
it = _bagElements.erase(it);
}
return result;
}

View file

@ -16,30 +16,8 @@
#ifndef hifi_OctreeElementBag_h
#define hifi_OctreeElementBag_h
#include <unordered_map>
#include "OctreeElement.h"
class OctreeElementBag {
using Bag = std::unordered_map<OctreeElement*, OctreeElementWeakPointer>;
public:
void insert(const OctreeElementPointer& element); // put a element into the bag
OctreeElementPointer extract(); /// pull a element out of the bag (could come in any order) and if all of the
/// elements have expired, a single null pointer will be returned
bool isEmpty(); /// does the bag contain elements,
/// if all of the contained elements are expired, they will not report as empty, and
/// a single last item will be returned by extract as a null pointer
void deleteAll();
size_t size() const { return _bagElements.size(); }
private:
Bag _bagElements;
};
class OctreeElementExtraEncodeDataBase {
public:
OctreeElementExtraEncodeDataBase() {}

View file

@ -117,7 +117,7 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
if (sectionLength) {
// ask the VoxelTree to read the bitstream into the tree
ReadBitstreamToTreeParams args(WANT_EXISTS_BITS, NULL,
sourceUUID, sourceNode, false, message.getVersion());
sourceUUID, sourceNode);
quint64 startUncompress, startLock = usecTimestampNow();
quint64 startReadBitsteam, endReadBitsteam;
// FIXME STUTTER - there may be an opportunity to bump this lock outside of the

View file

@ -144,11 +144,6 @@ void OctreeQueryNode::copyCurrentViewFrustum(ViewFrustum& viewOut) const {
viewOut = _currentViewFrustum;
}
void OctreeQueryNode::copyLastKnownViewFrustum(ViewFrustum& viewOut) const {
QMutexLocker viewLocker(&_viewMutex);
viewOut = _lastKnownViewFrustum;
}
bool OctreeQueryNode::updateCurrentViewFrustum() {
// if shutting down, return immediately
if (_isShuttingDown) {
@ -229,70 +224,6 @@ void OctreeQueryNode::setViewSent(bool viewSent) {
}
}
void OctreeQueryNode::updateLastKnownViewFrustum() {
// if shutting down, return immediately
if (_isShuttingDown) {
return;
}
{
QMutexLocker viewLocker(&_viewMutex);
bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum);
if (frustumChanges) {
// save our currentViewFrustum into our lastKnownViewFrustum
_lastKnownViewFrustum = _currentViewFrustum;
}
}
// save that we know the view has been sent.
setLastTimeBagEmpty();
}
bool OctreeQueryNode::moveShouldDump() const {
// if shutting down, return immediately
if (_isShuttingDown) {
return false;
}
QMutexLocker viewLocker(&_viewMutex);
glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition();
glm::vec3 newPosition = _currentViewFrustum.getPosition();
// theoretically we could make this slightly larger but relative to avatar scale.
const float MAXIMUM_MOVE_WITHOUT_DUMP = 0.0f;
return glm::distance(newPosition, oldPosition) > MAXIMUM_MOVE_WITHOUT_DUMP;
}
void OctreeQueryNode::dumpOutOfView() {
// if shutting down, return immediately
if (_isShuttingDown) {
return;
}
int stillInView = 0;
int outOfView = 0;
OctreeElementBag tempBag;
ViewFrustum viewCopy;
copyCurrentViewFrustum(viewCopy);
while (OctreeElementPointer elementToCheck = elementBag.extract()) {
if (elementToCheck->isInView(viewCopy)) {
tempBag.insert(elementToCheck);
stillInView++;
} else {
outOfView++;
}
}
if (stillInView > 0) {
while (OctreeElementPointer elementToKeepInBag = tempBag.extract()) {
if (elementToKeepInBag->isInView(viewCopy)) {
elementBag.insert(elementToKeepInBag);
}
}
}
}
void OctreeQueryNode::packetSent(const NLPacket& packet) {
_sentPacketHistory.packetSent(_sequenceNumber, packet);
_sequenceNumber++;

View file

@ -46,23 +46,14 @@ public:
bool shouldSuppressDuplicatePacket();
unsigned int getAvailable() const { return _octreePacket->bytesAvailableForWrite(); }
int getMaxSearchLevel() const { return _maxSearchLevel; }
void resetMaxSearchLevel() { _maxSearchLevel = 1; }
void incrementMaxSearchLevel() { _maxSearchLevel++; }
int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; }
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
OctreeElementBag elementBag;
OctreeElementExtraEncodeData extraEncodeData;
void copyCurrentViewFrustum(ViewFrustum& viewOut) const;
void copyLastKnownViewFrustum(ViewFrustum& viewOut) const;
// These are not classic setters because they are calculating and maintaining state
// which is set asynchronously through the network receive
bool updateCurrentViewFrustum();
void updateLastKnownViewFrustum();
bool getViewSent() const { return _viewSent; }
void setViewSent(bool viewSent);
@ -70,24 +61,13 @@ public:
bool getViewFrustumChanging() const { return _viewFrustumChanging; }
bool getViewFrustumJustStoppedChanging() const { return _viewFrustumJustStoppedChanging; }
bool moveShouldDump() const;
quint64 getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }
void setLastTimeBagEmpty() { _lastTimeBagEmpty = _sceneSendStartTime; }
bool hasLodChanged() const { return _lodChanged; }
OctreeSceneStats stats;
void dumpOutOfView();
quint64 getLastRootTimestamp() const { return _lastRootTimestamp; }
void setLastRootTimestamp(quint64 timestamp) { _lastRootTimestamp = timestamp; }
unsigned int getlastOctreePacketLength() const { return _lastOctreePacketLength; }
int getDuplicatePacketCount() const { return _duplicatePacketCount; }
void sceneStart(quint64 sceneSendStartTime) { _sceneSendStartTime = sceneSendStartTime; }
void nodeKilled();
bool isShuttingDown() const { return _isShuttingDown; }
@ -118,18 +98,11 @@ private:
int _duplicatePacketCount { 0 };
quint64 _firstSuppressedPacket { usecTimestampNow() };
int _maxSearchLevel { 1 };
int _maxLevelReachedInLastSearch { 1 };
mutable QMutex _viewMutex { QMutex::Recursive };
ViewFrustum _currentViewFrustum;
ViewFrustum _lastKnownViewFrustum;
quint64 _lastTimeBagEmpty { 0 };
bool _viewFrustumChanging { false };
bool _viewFrustumJustStoppedChanging { true };
OctreeSendThread* _octreeSendThread { nullptr };
// watch for LOD changes
int _lastClientBoundaryLevelAdjust { 0 };
float _lastClientOctreeSizeScale { DEFAULT_OCTREE_SIZE_SCALE };
@ -138,16 +111,12 @@ private:
OCTREE_PACKET_SEQUENCE _sequenceNumber { 0 };
quint64 _lastRootTimestamp { 0 };
PacketType _myPacketType { PacketType::Unknown };
bool _isShuttingDown { false };
SentPacketHistory _sentPacketHistory;
QQueue<OCTREE_PACKET_SEQUENCE> _nackedSequenceNumbers;
quint64 _sceneSendStartTime = 0;
std::array<char, udt::MAX_PACKET_SIZE> _lastOctreePayload;
QJsonObject _lastCheckJSONParameters;

View file

@ -284,10 +284,6 @@ void OctreeSceneStats::didntFit(const OctreeElementPointer& element) {
}
}
void OctreeSceneStats::colorBitsWritten() {
_colorBitsWritten++;
}
void OctreeSceneStats::existsBitsWritten() {
_existsBitsWritten++;
}

View file

@ -79,9 +79,6 @@ public:
/// Track that a element was due to be sent, but didn't fit in the packet and was moved to next packet
void didntFit(const OctreeElementPointer& element);
/// Track that the color bitmask was was sent as part of computation of a scene
void colorBitsWritten();
/// Track that the exists in tree bitmask was was sent as part of computation of a scene
void existsBitsWritten();

View file

@ -248,22 +248,6 @@ void setOctalCodeSectionValue(unsigned char* octalCode, int section, char sectio
}
}
unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels) {
int codeLength = numberOfThreeBitSectionsInCode(originalOctalCode);
unsigned char* newCode = NULL;
if (codeLength > chopLevels) {
int newLength = codeLength - chopLevels;
newCode = new unsigned char[newLength+1];
*newCode = newLength; // set the length byte
for (int section = chopLevels; section < codeLength; section++) {
char sectionValue = getOctalCodeSectionValue(originalOctalCode, section);
setOctalCodeSectionValue(newCode, section - chopLevels, sectionValue);
}
}
return newCode;
}
bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent, int descendentsChild) {
if (!possibleAncestor || !possibleDescendent) {
return false;

View file

@ -40,8 +40,6 @@ const int UNKNOWN_OCTCODE_LENGTH = -2;
/// \param int maxBytes number of bytes that octalCode is expected to be, -1 if unknown
int numberOfThreeBitSectionsInCode(const unsigned char* octalCode, int maxBytes = UNKNOWN_OCTCODE_LENGTH);
unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels);
const int CHECK_NODE_ONLY = -1;
bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent,
int descendentsChild = CHECK_NODE_ONLY);

View file

@ -160,7 +160,6 @@ int main(int argc, char** argv) {
QByteArray packet = file.readAll();
EntityItemPointer item = ShapeEntityItem::boxFactory(EntityItemID(), EntityItemProperties());
ReadBitstreamToTreeParams params;
params.bitstreamVersion = 33;
auto start = usecTimestampNow();
for (int i = 0; i < 1000; ++i) {