diff --git a/libraries/entities/src/BoxEntityItem.cpp b/libraries/entities/src/BoxEntityItem.cpp index 39fe1e480b..eaa737e37d 100644 --- a/libraries/entities/src/BoxEntityItem.cpp +++ b/libraries/entities/src/BoxEntityItem.cpp @@ -61,9 +61,9 @@ bool BoxEntityItem::setProperties(const EntityItemProperties& properties, bool f bool wantDebug = false; if (wantDebug) { uint64_t now = usecTimestampNow(); - int elapsed = now - _lastEdited; + int elapsed = now - getLastEdited(); qDebug() << "BoxEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << - "now=" << now << " _lastEdited=" << _lastEdited; + "now=" << now << " getLastEdited()=" << getLastEdited(); } setLastEdited(properties._lastEdited); } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 77672b6c6f..bbea772ed1 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -42,7 +42,9 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { // init values with defaults before calling setProperties //uint64_t now = usecTimestampNow(); - _lastEdited = 0; + _lastEditedRemote = 0; + _lastEditedLocal = 0; + //_lastEdited = 0; _lastUpdated = 0; _created = 0; // TODO: when do we actually want to make this "now" @@ -60,7 +62,9 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) { _type = EntityTypes::Unknown; - _lastEdited = 0; + _lastEditedRemote = 0; + _lastEditedLocal = 0; + //_lastEdited = 0; _lastUpdated = 0; _created = properties.getCreated(); initFromEntityItemID(entityItemID); @@ -84,7 +88,16 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param } OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData) const { + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData) const { + + + const bool wantDebug = false; + + if (wantDebug) { + qDebug() << "EntityItem::appendEntityData()...."; + qDebug() << " entity=" << this; + qDebug() << " entityItemID=" << getEntityItemID(); + } // ALL this fits... // object ID [16 bytes] @@ -111,9 +124,42 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet EntityPropertyFlags propertiesDidntFit = requestedProperties; // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, - // then our modelTreeElementExtraEncodeData should include data about which properties we need to append. - if (modelTreeElementExtraEncodeData && modelTreeElementExtraEncodeData->includedItems.contains(getEntityItemID())) { - requestedProperties = modelTreeElementExtraEncodeData->includedItems.value(getEntityItemID()); + // then our entityTreeElementExtraEncodeData should include data about which properties we need to append. + if (entityTreeElementExtraEncodeData && wantDebug) { + qDebug() << " entityTreeElementExtraEncodeData INCLUDED"; + } + + + if (entityTreeElementExtraEncodeData && entityTreeElementExtraEncodeData->entities.contains(getEntityItemID())) { + requestedProperties = entityTreeElementExtraEncodeData->entities.value(getEntityItemID()); + + if (wantDebug) { + qDebug() << " entityTreeElementExtraEncodeData INCLUDED -AND- this entity is included"; + qDebug() << " --- requestedProperties ---"; + qDebug() << " PROP_MODEL_URL: " << requestedProperties.getHasProperty(PROP_MODEL_URL)<< " entity ID:" << getEntityItemID(); + qDebug() << " --- all requestedProperties ---"; + + #define DEBUG_REQUESTED_PROPERTY(x) if (requestedProperties.getHasProperty(x)) { qDebug() << " " #x; } + + DEBUG_REQUESTED_PROPERTY(PROP_MODEL_URL); + DEBUG_REQUESTED_PROPERTY(PROP_PAGED_PROPERTY) + DEBUG_REQUESTED_PROPERTY(PROP_CUSTOM_PROPERTIES_INCLUDED); + DEBUG_REQUESTED_PROPERTY(PROP_POSITION); + DEBUG_REQUESTED_PROPERTY(PROP_RADIUS); + DEBUG_REQUESTED_PROPERTY(PROP_ROTATION); + DEBUG_REQUESTED_PROPERTY(PROP_MASS); + DEBUG_REQUESTED_PROPERTY(PROP_VELOCITY); + DEBUG_REQUESTED_PROPERTY(PROP_GRAVITY); + DEBUG_REQUESTED_PROPERTY(PROP_DAMPING); + DEBUG_REQUESTED_PROPERTY(PROP_LIFETIME); + DEBUG_REQUESTED_PROPERTY(PROP_SCRIPT); + DEBUG_REQUESTED_PROPERTY(PROP_COLOR); + DEBUG_REQUESTED_PROPERTY(PROP_ANIMATION_URL); + DEBUG_REQUESTED_PROPERTY(PROP_ANIMATION_FPS); + DEBUG_REQUESTED_PROPERTY(PROP_ANIMATION_FRAME_INDEX); + DEBUG_REQUESTED_PROPERTY(PROP_ANIMATION_PLAYING); + } + } LevelDetails modelLevel = packetData->startLevel(); @@ -320,16 +366,40 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet propertiesDidntFit -= PROP_SCRIPT; } - appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, + appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); } - + +#define DEBUG_PROPERTY(y, x) if (y.getHasProperty(x)) { qDebug() << " " #x; } + if (propertyCount > 0) { int endOfEntityItemData = packetData->getUncompressedByteOffset(); + + qDebug() << "Entity Properties THIS ROUND... entityID:" << getEntityItemID(); + + DEBUG_PROPERTY(propertyFlags, PROP_MODEL_URL); + DEBUG_PROPERTY(propertyFlags, PROP_PAGED_PROPERTY) + DEBUG_PROPERTY(propertyFlags, PROP_CUSTOM_PROPERTIES_INCLUDED); + DEBUG_PROPERTY(propertyFlags, PROP_POSITION); + DEBUG_PROPERTY(propertyFlags, PROP_RADIUS); + DEBUG_PROPERTY(propertyFlags, PROP_ROTATION); + DEBUG_PROPERTY(propertyFlags, PROP_MASS); + DEBUG_PROPERTY(propertyFlags, PROP_VELOCITY); + DEBUG_PROPERTY(propertyFlags, PROP_GRAVITY); + DEBUG_PROPERTY(propertyFlags, PROP_DAMPING); + DEBUG_PROPERTY(propertyFlags, PROP_LIFETIME); + DEBUG_PROPERTY(propertyFlags, PROP_SCRIPT); + DEBUG_PROPERTY(propertyFlags, PROP_COLOR); + DEBUG_PROPERTY(propertyFlags, PROP_ANIMATION_URL); + DEBUG_PROPERTY(propertyFlags, PROP_ANIMATION_FPS); + DEBUG_PROPERTY(propertyFlags, PROP_ANIMATION_FRAME_INDEX); + DEBUG_PROPERTY(propertyFlags, PROP_ANIMATION_PLAYING); + + encodedPropertyFlags = propertyFlags; int newPropertyFlagsLength = encodedPropertyFlags.length(); @@ -338,6 +408,9 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet // if the size of the PropertyFlags shrunk, we need to shift everything down to front of packet. if (newPropertyFlagsLength < oldPropertyFlagsLength) { + +qDebug() << "PACKET SHIFTING!!! <<<<<<<<<<<<<<<< "; + int oldSize = packetData->getUncompressedSize(); const unsigned char* modelItemData = packetData->getUncompressedData(propertyFlagsOffset + oldPropertyFlagsLength); int modelItemDataLength = endOfEntityItemData - startOfEntityItemData; @@ -359,7 +432,32 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet // If any part of the model items didn't fit, then the element is considered partial if (appendState != OctreeElement::COMPLETED) { // add this item into our list for the next appendElementData() pass - modelTreeElementExtraEncodeData->includedItems.insert(getEntityItemID(), propertiesDidntFit); + + qDebug() << "Entity Partially encoded... entityID:" << getEntityItemID(); + + #define DEBUG_PROPERTY(y, x) if (y.getHasProperty(x)) { qDebug() << " " #x; } + + DEBUG_PROPERTY(propertiesDidntFit, PROP_MODEL_URL); + DEBUG_PROPERTY(propertiesDidntFit, PROP_PAGED_PROPERTY) + DEBUG_PROPERTY(propertiesDidntFit, PROP_CUSTOM_PROPERTIES_INCLUDED); + DEBUG_PROPERTY(propertiesDidntFit, PROP_POSITION); + DEBUG_PROPERTY(propertiesDidntFit, PROP_RADIUS); + DEBUG_PROPERTY(propertiesDidntFit, PROP_ROTATION); + DEBUG_PROPERTY(propertiesDidntFit, PROP_MASS); + DEBUG_PROPERTY(propertiesDidntFit, PROP_VELOCITY); + DEBUG_PROPERTY(propertiesDidntFit, PROP_GRAVITY); + DEBUG_PROPERTY(propertiesDidntFit, PROP_DAMPING); + DEBUG_PROPERTY(propertiesDidntFit, PROP_LIFETIME); + DEBUG_PROPERTY(propertiesDidntFit, PROP_SCRIPT); + DEBUG_PROPERTY(propertiesDidntFit, PROP_COLOR); + DEBUG_PROPERTY(propertiesDidntFit, PROP_ANIMATION_URL); + DEBUG_PROPERTY(propertiesDidntFit, PROP_ANIMATION_FPS); + DEBUG_PROPERTY(propertiesDidntFit, PROP_ANIMATION_FRAME_INDEX); + DEBUG_PROPERTY(propertiesDidntFit, PROP_ANIMATION_PLAYING); + + entityTreeElementExtraEncodeData->entities.insert(getEntityItemID(), propertiesDidntFit); + } else { + qDebug() << "Entity COMPLETED... entityID:" << getEntityItemID(); } return appendState; @@ -448,34 +546,42 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } quint64 lastEditedFromBuffer = 0; + quint64 lastEditedFromBufferAdjusted = 0; // TODO: we could make this encoded as a delta from _created // _lastEdited memcpy(&lastEditedFromBuffer, dataAt, sizeof(lastEditedFromBuffer)); dataAt += sizeof(lastEditedFromBuffer); bytesRead += sizeof(lastEditedFromBuffer); - lastEditedFromBuffer -= clockSkew; + + qDebug() << "data from server **************** "; + qDebug() << " entityItemID=" << getEntityItemID(); + qDebug() << " now=" << usecTimestampNow(); + qDebug() << " getLastEdited();=" << getLastEdited(); + qDebug() << " _lastEditedRemote=" << _lastEditedRemote; + qDebug() << " _lastEditedLocal=" << _lastEditedLocal; + qDebug() << " lastEditedFromBuffer=" << lastEditedFromBuffer << " (BEFORE clockskew adjust)"; + qDebug() << " clockSkew=" << clockSkew; + lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew; + qDebug() << " lastEditedFromBufferAdjusted=" << lastEditedFromBufferAdjusted << " (AFTER clockskew adjust)"; + // If we've changed our local tree more recently than the new data from this packet // then we will not be changing our values, instead we just read and skip the data - if (_lastEdited > lastEditedFromBuffer) { + if (_lastEditedLocal > lastEditedFromBufferAdjusted) { overwriteLocalData = false; - - if (wantDebug) { - qDebug() << "IGNORING old data from server!!! **************** _lastEdited=" << _lastEdited - << "lastEditedFromBuffer=" << lastEditedFromBuffer << "now=" << usecTimestampNow(); + if (true || wantDebug) { + qDebug() << "IGNORING old data from server!!! ****************"; } } else { - if (wantDebug) { - qDebug() << "USING NEW data from server!!! **************** OLD _lastEdited=" << _lastEdited - << "lastEditedFromBuffer=" << lastEditedFromBuffer << "now=" << usecTimestampNow(); + if (true || wantDebug) { + qDebug() << "USING NEW data from server!!! ****************"; } - _lastEdited = lastEditedFromBuffer; - + _lastEditedRemote = lastEditedFromBuffer; + _lastEditedRemoteClockSkew = clockSkew; somethingChangedNotification(); // notify derived classes that something has changed - } // last updated is stored as ByteCountCoded delta from lastEdited @@ -483,7 +589,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef ByteCountCoded updateDeltaCoder = encodedUpdateDelta; quint64 updateDelta = updateDeltaCoder; if (overwriteLocalData) { - _lastUpdated = _lastEdited + updateDelta; // don't adjust for clock skew since we already did that for _lastEdited + _lastUpdated = lastEditedFromBufferAdjusted + updateDelta; // don't adjust for clock skew since we already did that for _lastEdited } encodedUpdateDelta = updateDeltaCoder; // determine true length dataAt += encodedUpdateDelta.size(); @@ -865,9 +971,9 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc bool wantDebug = false; if (wantDebug) { uint64_t now = usecTimestampNow(); - int elapsed = now - _lastEdited; + int elapsed = now - getLastEdited(); qDebug() << "EntityItem::setProperties() AFTER update... edited AGO=" << elapsed << - "now=" << now << " _lastEdited=" << _lastEdited; + "now=" << now << " getLastEdited()=" << getLastEdited(); } setLastEdited(properties._lastEdited); } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 70871f3583..4f9faca192 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -62,19 +62,21 @@ public: virtual void somethingChangedNotification() { } quint64 getLastUpdated() const { return _lastUpdated; } /// Last simulated time of this entity universal usecs - quint64 getLastEdited() const { return _lastEdited; } /// Last edited time of this entity universal usecs - void setLastEdited(quint64 lastEdited) { _lastEdited = lastEdited; _lastUpdated = lastEdited; } + + /// Last edited time of this entity universal usecs + quint64 getLastEdited() const { return std::max((_lastEditedRemote - _lastEditedRemoteClockSkew), _lastEditedLocal); } + void setLastEdited(quint64 lastEdited) { _lastEditedLocal = lastEdited; _lastEditedLocal = lastEdited; } float getEditedAgo() const /// Elapsed seconds since this entity was last edited - { return (float)(usecTimestampNow() - _lastEdited) / (float)USECS_PER_SECOND; } + { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } // TODO: eventually only include properties changed since the params.lastViewFrustumSent time virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData) const; + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData) const; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, @@ -188,7 +190,10 @@ protected: uint32_t _creatorTokenID; bool _newlyCreated; quint64 _lastUpdated; - quint64 _lastEdited; + //quint64 _lastEdited; + quint64 _lastEditedLocal; + quint64 _lastEditedRemote; + quint64 _lastEditedRemoteClockSkew; quint64 _created; glm::vec3 _position; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 6a819ca5f5..5ded476ec0 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -62,6 +62,11 @@ public: const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode); virtual bool rootElementHasData() const { return true; } + + // the root at least needs to store the number of entities in the packet/buffer + virtual int minimumRequiredRootDataBytes() const { return sizeof(uint16_t); } + virtual bool suppressEmptySubtrees() const { return false; } + virtual bool versionHasSVOfileBreaks(PacketVersion thisVersion) const { return thisVersion >= VERSION_ENTITIES_HAS_FILE_BREAKS; } diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 570861cbbe..b4bd11c4d6 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -50,6 +50,246 @@ EntityTreeElement* EntityTreeElement::addChildAtIndex(int index) { return newElement; } +void EntityTreeElement::debugExtraEncodeData(EncodeBitstreamParams& params) const { + qDebug() << "EntityTreeElement::debugExtraEncodeData()... "; + qDebug() << " element:" << getAACube(); + + OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData; + assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes + + if (extraEncodeData->contains(this)) { + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData + = static_cast(extraEncodeData->value(this)); + qDebug() << " encode data:" << entityTreeElementExtraEncodeData; + } else { + qDebug() << " encode data: MISSING!!"; + } +} + +void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params) const { + OctreeElementExtraEncodeData* extraEncodeData = params.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)) { + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData = new EntityTreeElementExtraEncodeData(); + + qDebug() << "EntityTreeElement::initializeExtraEncodeData()... "; + qDebug() << " element:" << getAACube(); + entityTreeElementExtraEncodeData->elementCompleted = (_entityItems->size() == 0); + qDebug() << " elementCompleted:" << entityTreeElementExtraEncodeData->elementCompleted << "[ _entityItems->size()=" << _entityItems->size() <<" ]"; + qDebug() << " --- initialize this element's child element state ---"; + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + EntityTreeElement* child = getChildAtIndex(i); + if (!child) { + entityTreeElementExtraEncodeData->childCompleted[i] = true; // if no child exists, it is completed + qDebug() << " childCompleted[" << i <<"]= true -- completed"; + } else { + if (child->hasEntities()) { + qDebug() << " childCompleted[" << i <<"] " << child->getAACube() << "= false -- HAS ENTITIES NEEDS ENCODING"; + } else { + entityTreeElementExtraEncodeData->childCompleted[i] = true; // if the child doesn't have enities, it is completed + qDebug() << " childCompleted[" << i <<"] " << child->getAACube() << "= true -- doesn't have entities"; + } + } + } + qDebug() << " --- initialize this element's entities state ---"; + for (uint16_t i = 0; i < _entityItems->size(); i++) { + EntityItem* entity = (*_entityItems)[i]; + entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params)); + } + + // TODO: some of these inserts might be redundant!!! + qDebug() << " ADDING encode data (" << __LINE__ << ") for element " << getAACube() << " data=" << entityTreeElementExtraEncodeData; + extraEncodeData->insert(this, entityTreeElementExtraEncodeData); + } +} + +bool EntityTreeElement::shouldIncludeChild(int childIndex, EncodeBitstreamParams& params) const { + OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData; + assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes + + if (extraEncodeData->contains(this)) { + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData + = static_cast(extraEncodeData->value(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::shouldRecurseSubtree(OctreeElement* parent, EncodeBitstreamParams& params, OctreeElementBag* bag) const { + qDebug() << "EntityTreeElement::shouldRecurseSubtree()... ????????????????????????"; + qDebug() << " element:" << getAACube(); + + OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData; + assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes + + if (extraEncodeData->contains(this)) { + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData + = static_cast(extraEncodeData->value(this)); + qDebug() << " this element encode data:" << entityTreeElementExtraEncodeData; + } else { + qDebug() << " this element encode data: MISSING!!!"; + } + + qDebug() << " parent:" << parent->getAACube(); + if (extraEncodeData->contains(parent)) { + EntityTreeElementExtraEncodeData* parentExtraEncodeData + = static_cast(extraEncodeData->value(parent)); + qDebug() << " parent encode data:" << parentExtraEncodeData; + } else { + qDebug() << " parent encode data: MISSING!!!"; + } + + + return true; +} + + +void EntityTreeElement::updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const { + OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData; + assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes + + qDebug() << "EntityTreeElement::updateEncodedData()... "; + qDebug() << " element:" << getAACube(); + qDebug() << " child:" << childIndex << getChildAtIndex(childIndex)->getAACube(); + switch(childAppendState) { + case OctreeElement::NONE: + qDebug() << " childAppendState: NONE"; + break; + case OctreeElement::PARTIAL: + qDebug() << " childAppendState: PARTIAL"; + break; + case OctreeElement::COMPLETED: + qDebug() << " childAppendState: COMPLETED"; + break; + } + + if (extraEncodeData->contains(this)) { + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData + = static_cast(extraEncodeData->value(this)); + + + if (childAppendState == OctreeElement::COMPLETED) { + entityTreeElementExtraEncodeData->childCompleted[childIndex] = true; + qDebug() << " SETTING childCompleted[" << childIndex << "] = true - " << getChildAtIndex(childIndex)->getAACube(); + } + + qDebug() << " encode data:" << entityTreeElementExtraEncodeData; + } else { + assert(false); // this shouldn't happen! + } +} + + + + +void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params, OctreeElementBag* bag) const { + const bool wantDebug = true; + OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData; + assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes + assert(extraEncodeData->contains(this)); + + EntityTreeElementExtraEncodeData* thisExtraEncodeData + = static_cast(extraEncodeData->value(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) out 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 + + if (wantDebug) { + qDebug() << "------------------------------------------------------------------------------------"; + qDebug() << "EntityTreeElement::elementEncodeComplete()..."; + qDebug() << " element=" << getAACube(); + qDebug() << " encode data (this):" << thisExtraEncodeData; + if (!thisExtraEncodeData->elementCompleted) { + qDebug() << " ******* PROBABLY OK ---- WARNING ********* this element was not complete!!"; + qDebug() << " we really only care that our data is complete when we attempt to check our parent..."; + } + } + + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + EntityTreeElement* childElement = getChildAtIndex(i); + if (childElement) { + bool isThisChildReallyComplete = thisExtraEncodeData->childCompleted[i]; + + // why would this ever fail??? + // If we've encoding this element before... but we're coming back a second time in an attempt to + // encoud our parent... this might happen. + if (extraEncodeData->contains(childElement)) { + EntityTreeElementExtraEncodeData* childExtraEncodeData + = static_cast(extraEncodeData->value(childElement)); + + // If the child element is not complete, then it should be in the bag for re-encoding + if (!thisExtraEncodeData->childCompleted[i]) { + if (bag->contains(childElement)) { + qDebug() << " GOOD this element's child["<< i << "] " << childElement->getAACube() << " was not complete, but it's in the bag!!"; + } else { + qDebug() << " ******* WARNING ********* this element's child["<< i << "] " << childElement->getAACube() << " was not complete, AND IT'S NOT IN THE BAG!!"; + } + } + + if (wantDebug) { + qDebug() << " child[" << i <<"] has extra data"; + qDebug() << " child:" << childElement->getAACube(); + qDebug() << " encode data (child):" << childExtraEncodeData; + // If we're completing THIS element then ALL of our child elements must have been able to add their element data + if (childExtraEncodeData->elementCompleted) { + qDebug() << " GOOD this element's child["<< i << "] " << childElement->getAACube() << " element data was complete!!"; + } else { + qDebug() << " ******* WARNING ********* this element's child["<< i << "] " << childElement->getAACube() << " element data was NOT COMPLETE!!"; + } + + } + + for (int ii = 0; ii < NUMBER_OF_CHILDREN; ii++) { + if (!childExtraEncodeData->childCompleted[ii]) { + OctreeElement* grandChild = childElement->getChildAtIndex(ii); + if (bag->contains(childElement)) { + qDebug() << " GOOD this element's child["<< i << "]'s child["<< ii << "] " << grandChild->getAACube() + << " was not complete, but the child " << childElement->getAACube() << " is in the bag!!"; + } else { + qDebug() << " ******* WARNING ********* this element's child["<< i << "]'s child["<< ii << "] " << grandChild->getAACube() + << " was not complete, AND THE CHILD " << childElement->getAACube() << " IS NOT IN THE BAG!!"; + } + + isThisChildReallyComplete = false; + } + } + + if (isThisChildReallyComplete) { + /* + qDebug() << " REMOVE CHILD EXTRA DATA...."; + qDebug() << " DELETING -- CHILD EXTRA DATA...."; + qDebug() << " REMOVING encode data (" << __LINE__ << ") for element " << childElement->getAACube() << " data=" << childExtraEncodeData; + extraEncodeData->remove(childElement); + delete childExtraEncodeData; + */ + } + } else { + qDebug() << " ******* WARNING ********* this element's child["<< i << "] " << childElement->getAACube() << " didn't have extra encode data ------ UNEXPECTED!!!!!"; + } + } + } + + qDebug() << "------------------------------------------------------------------------------------"; +} OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const { @@ -61,6 +301,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData qDebug() << " START OF ELEMENT packetData->uncompressed size:" << packetData->getUncompressedSize(); qDebug() << " params.lastViewFrustumSent=" << params.lastViewFrustumSent; } + OctreeElement::AppendState appendElementState = OctreeElement::COMPLETED; // assume the best... // first, check the params.extraEncodeData to see if there's any partial re-encode data for this element @@ -73,9 +314,42 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData } else { // if there wasn't one already, then create one entityTreeElementExtraEncodeData = new EntityTreeElementExtraEncodeData(); + + qDebug() << "EntityTreeElement::appendElementData()... ENCODE DATA MISSING, SETTING IT UP NOW "; + qDebug() << " element:" << getAACube(); + entityTreeElementExtraEncodeData->elementCompleted = (_entityItems->size() == 0); + qDebug() << " elementCompleted:" << entityTreeElementExtraEncodeData->elementCompleted << "[ _entityItems->size()=" << _entityItems->size() <<" ]"; + qDebug() << " --- initialize child elements state ---"; + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + EntityTreeElement* child = getChildAtIndex(i); + if (!child) { + entityTreeElementExtraEncodeData->childCompleted[i] = true; // if no child exists, it is completed + qDebug() << " childCompleted[" << i <<"]= true -- completed"; + } else { + if (child->hasEntities()) { + qDebug() << " childCompleted[" << i <<"] " << child->getAACube() << "= false -- HAS ENTITIES NEEDS ENCODING"; + } else { + entityTreeElementExtraEncodeData->childCompleted[i] = true; // if the child doesn't have enities, it is completed + qDebug() << " childCompleted[" << i <<"] " << child->getAACube() << "= true -- doesn't have entities"; + } + } + } + qDebug() << " --- initialize this element's entities state ---"; + for (uint16_t i = 0; i < _entityItems->size(); i++) { + EntityItem* entity = (*_entityItems)[i]; + entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params)); + } } + //assert(extraEncodeData); + //assert(extraEncodeData->contains(this)); + //entityTreeElementExtraEncodeData = static_cast(extraEncodeData->value(this)); + + LevelDetails elementLevel = packetData->startLevel(); + if (wantDebug) { + qDebug() << "------------- elementLevel = packetData->startLevel() -------------"; + } // write our entities out... first determine which of the entities are in view based on our params uint16_t numberOfEntities = 0; @@ -86,50 +360,56 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData qDebug() << "EntityTreeElement::appendElementData() _entityItems->size()=" << _entityItems->size(); } - for (uint16_t i = 0; i < _entityItems->size(); i++) { - EntityItem* entity = (*_entityItems)[i]; - bool includeThisEntity = true; + // 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) { + for (uint16_t i = 0; i < _entityItems->size(); i++) { + EntityItem* entity = (*_entityItems)[i]; + bool includeThisEntity = true; - if (wantDebug) { - qDebug() << "params.forceSendScene=" << params.forceSendScene; - qDebug() << "entity->getLastEdited()=" << entity->getLastEdited(); - qDebug() << "entity->getLastEdited() > params.lastViewFrustumSent=" - << (entity->getLastEdited() > params.lastViewFrustumSent); - } - - if (!params.forceSendScene && entity->getLastEdited() < params.lastViewFrustumSent) { - if (wantDebug) { - qDebug() << "NOT forceSendScene, and not changed since last sent SUPPRESSING this ENTITY" - << entity->getEntityItemID(); + if (false && wantDebug) { + qDebug() << "params.forceSendScene=" << params.forceSendScene; + qDebug() << "entity->getLastEdited()=" << entity->getLastEdited(); + qDebug() << "entity->getLastEdited() > params.lastViewFrustumSent=" + << (entity->getLastEdited() > params.lastViewFrustumSent); } - includeThisEntity = false; - } - if (hadElementExtraData) { - includeThisEntity = includeThisEntity && - entityTreeElementExtraEncodeData->includedItems.contains(entity->getEntityItemID()); - if (wantDebug) { - qDebug() << " hadElementExtraData=" << hadElementExtraData; - qDebug() << " entity[" << i <<"].entityItemID=" << entity->getEntityItemID(); - qDebug() << " entity[" << i <<"].includeThisEntity=" << includeThisEntity; + if (!params.forceSendScene && entity->getLastEdited() < params.lastViewFrustumSent) { + if (false && wantDebug) { + qDebug() << "NOT forceSendScene, and not changed since last sent SUPPRESSING this ENTITY" + << entity->getEntityItemID(); + } + includeThisEntity = false; } - } - if (includeThisEntity && params.viewFrustum) { - AACube entityCube = entity->getAACube(); - entityCube.scale(TREE_SCALE); - if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) { - includeThisEntity = false; // out of view, don't include it + if (hadElementExtraData) { + includeThisEntity = includeThisEntity && + entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID()); if (wantDebug) { - qDebug() << " entity[" << i <<"] cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE " - "includeThisEntity=" << includeThisEntity; + if (includeThisEntity) { + qDebug() << " entity[" << i <<"].entityItemID=" << entity->getEntityItemID(); + qDebug() << " entity[" << i <<"].includeThisEntity=" << includeThisEntity; + } } } - } - if (includeThisEntity) { - indexesOfEntitiesToInclude << i; - numberOfEntities++; + if (includeThisEntity && params.viewFrustum) { + AACube entityCube = entity->getAACube(); + entityCube.scale(TREE_SCALE); + if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) { + includeThisEntity = false; // out of view, don't include it + if (wantDebug) { + qDebug() << " entity[" << i <<"] cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE " + "includeThisEntity=" << includeThisEntity; + } + } + } + + if (includeThisEntity) { + indexesOfEntitiesToInclude << i; + numberOfEntities++; + } } } @@ -173,13 +453,25 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData qDebug() << "--- AFTER entity ---"; qDebug() << " packetData->getUncompressedSize=" << packetData->getUncompressedSize() << "line:" << __LINE__; qDebug() << " packetData->getReservedBytes=" << packetData->getReservedBytes(); + + switch(appendEntityState) { + case OctreeElement::NONE: + qDebug() << " indexesOfEntitiesToInclude.... entity[" << i <<"] DIDN'T FIT!!!"; + break; + case OctreeElement::PARTIAL: + qDebug() << " indexesOfEntitiesToInclude.... entity[" << i <<"] PARTIAL FIT!!!"; + break; + case OctreeElement::COMPLETED: + qDebug() << " indexesOfEntitiesToInclude.... entity[" << i <<"] IT ALL FIT!!!"; + break; + } } - + // 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) { if (wantDebug) { - qDebug() << " indexesOfEntitiesToInclude.... entity[" << i <<"] DIDN'T FIT!!!"; + qDebug() << " calling discardLevel(entityLevel)..."; } packetData->discardLevel(entityLevel); } else { @@ -188,33 +480,31 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData packetData->endLevel(entityLevel); actualNumberOfEntities++; if (wantDebug) { - qDebug() << " indexesOfEntitiesToInclude.... entity[" << i <<"] ALL OR SOME FIT!!!"; + qDebug() << " calling endLevel(entityLevel)..."; } } // If the entity item got completely appended, then we can remove it from the extra encode data if (appendEntityState == OctreeElement::COMPLETED) { - entityTreeElementExtraEncodeData->includedItems.remove(entity->getEntityItemID()); if (wantDebug) { - qDebug() << " indexesOfEntitiesToInclude.... entity[" << i <<"] IT ALL FIT!!!"; - } - } else { - if (wantDebug) { - if (appendEntityState == OctreeElement::NONE) { - qDebug() << " indexesOfEntitiesToInclude.... entity[" << i <<"] DIDN'T FIT!!!"; - } else { - qDebug() << " indexesOfEntitiesToInclude.... entity[" << i <<"] PARTIAL FIT!!!"; - } + qDebug() << " since entity fit, removing it from entities..."; } + 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) { + if (wantDebug) { + qDebug() << " entity not complete, element must be partial!!!!"; + } 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 (wantDebug) { @@ -222,30 +512,121 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData qDebug() << " actualNumberOfEntities=" << actualNumberOfEntities; qDebug() << " numberOfEntities=" << numberOfEntities; qDebug() << " appendElementState=" << appendElementState; + switch(appendElementState) { + case OctreeElement::NONE: + qDebug() << " OctreeElement::NONE"; + break; + case OctreeElement::PARTIAL: + qDebug() << " OctreeElement::PARTIAL"; + break; + case OctreeElement::COMPLETED: + qDebug() << " OctreeElement::COMPLETED"; + break; + } } // 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) { - if (wantDebug) { qDebug() << " handling extra encode data...."; } - // If after processing we have some includedItems left in it, then make sure we re-add it back to our map - if (entityTreeElementExtraEncodeData->includedItems.size()) { + // After processing, if we are PARTIAL or COMPLETED then we need to re-include our extra data. + // Only our patent can remove our extra data in these cases and only after it knows that all of it's + // children have been encoded. + // 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) { + /* + if (wantDebug) { + qDebug() << " REMOVING OUR EXTRA DATA BECAUSE NOTHING FIT AND WE HAD NO PREVIOUS DATA...."; + qDebug() << " for element:" << getAACube(); + qDebug() << " elementCompleted=" << entityTreeElementExtraEncodeData->elementCompleted; + qDebug() << " entities.size()=" << entityTreeElementExtraEncodeData->entities.size(); + qDebug() << " appendElementState="; + switch(appendElementState) { + case OctreeElement::NONE: + qDebug() << " OctreeElement::NONE"; + break; + case OctreeElement::PARTIAL: + qDebug() << " OctreeElement::PARTIAL"; + break; + case OctreeElement::COMPLETED: + qDebug() << " OctreeElement::COMPLETED"; + break; + } + } + + qDebug() << " --------- DO WE REALLY WANT TO DO THIS?????????????? --------------------"; + qDebug() << " REMOVING encode data (" << __LINE__ << ") for element " << getAACube() << " data=" << entityTreeElementExtraEncodeData; + extraEncodeData->remove(this); + delete entityTreeElementExtraEncodeData; + */ + + + } else { + // TODO: some of these inserts might be redundant!!! + qDebug() << " ADDING encode data (" << __LINE__ << ") for element " << getAACube() << " data=" << entityTreeElementExtraEncodeData; + extraEncodeData->insert(this, entityTreeElementExtraEncodeData); + + if (wantDebug) { + qDebug() << " RE INSERT OUR EXTRA DATA.... NOTHING FIT... BUT WE PREVIOUSLY STORED SOMETHING..."; + qDebug() << " for element:" << getAACube(); + qDebug() << " elementCompleted=" << entityTreeElementExtraEncodeData->elementCompleted; + qDebug() << " entities.size()=" << entityTreeElementExtraEncodeData->entities.size(); + qDebug() << " appendElementState="; + switch(appendElementState) { + case OctreeElement::NONE: + qDebug() << " OctreeElement::NONE"; + break; + case OctreeElement::PARTIAL: + qDebug() << " OctreeElement::PARTIAL"; + break; + case OctreeElement::COMPLETED: + qDebug() << " OctreeElement::COMPLETED"; + break; + } + } + } + } 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) { + if (wantDebug) { + qDebug() << " since our entities.size() is 0 --- we are assuming we're done <<<<<<<<<<<<<<<<<<"; + } + entityTreeElementExtraEncodeData->elementCompleted = true; + } + } + + // TODO: some of these inserts might be redundant!!! + qDebug() << " ADDING encode data (" << __LINE__ << ") for element " << getAACube() << " data=" << entityTreeElementExtraEncodeData; extraEncodeData->insert(this, entityTreeElementExtraEncodeData); if (wantDebug) { qDebug() << " RE INSERT OUR EXTRA DATA...."; + qDebug() << " for element:" << getAACube(); + qDebug() << " elementCompleted=" << entityTreeElementExtraEncodeData->elementCompleted; + qDebug() << " entities.size()=" << entityTreeElementExtraEncodeData->entities.size(); + qDebug() << " appendElementState="; + switch(appendElementState) { + case OctreeElement::NONE: + qDebug() << " OctreeElement::NONE"; + break; + case OctreeElement::PARTIAL: + qDebug() << " OctreeElement::PARTIAL"; + break; + case OctreeElement::COMPLETED: + qDebug() << " OctreeElement::COMPLETED"; + break; + } } - } else { - // otherwise, clean things up... - extraEncodeData->remove(this); - delete entityTreeElementExtraEncodeData; - if (wantDebug) { - qDebug() << " REMOVE OUR EXTRA DATA...."; - } + } } @@ -288,6 +669,18 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData if (wantDebug) { qDebug() << "END OF ELEMENT packetData->uncompressed size:" << packetData->getUncompressedSize(); + qDebug() << "RETURNING appendElementState="; + switch(appendElementState) { + case OctreeElement::NONE: + qDebug() << " OctreeElement::NONE"; + break; + case OctreeElement::PARTIAL: + qDebug() << " OctreeElement::PARTIAL"; + break; + case OctreeElement::COMPLETED: + qDebug() << " OctreeElement::COMPLETED"; + break; + } } return appendElementState; diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 2e9697abc7..fff2927bb3 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -38,9 +38,28 @@ public: class EntityTreeElementExtraEncodeData { public: - QMap includedItems; + EntityTreeElementExtraEncodeData() : + elementCompleted(false), + entities() { + memset(childCompleted, 0, sizeof(childCompleted)); + } + bool elementCompleted; + bool childCompleted[NUMBER_OF_CHILDREN]; + QMap entities; }; +inline QDebug operator<<(QDebug debug, const EntityTreeElementExtraEncodeData* data) { + debug << "{"; + debug << " elementCompleted: " << data->elementCompleted << ", "; + debug << " childCompleted[]: "; + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + debug << " " << i << ":" << data->childCompleted[i] << ", "; + } + debug << " entities.size: " << data->entities.size() << "}"; + return debug; +} + + class SendModelsOperationArgs { public: glm::vec3 root; @@ -59,7 +78,7 @@ public: virtual ~EntityTreeElement(); // type safe versions of OctreeElement methods - EntityTreeElement* getChildAtIndex(int index) { return (EntityTreeElement*)OctreeElement::getChildAtIndex(index); } + EntityTreeElement* getChildAtIndex(int index) const { return (EntityTreeElement*)OctreeElement::getChildAtIndex(index); } // methods you can and should override to implement your tree functionality @@ -88,6 +107,14 @@ public: /// Override to indicate that this element requires a split before editing lower elements in the octree virtual bool requiresSplit() const { return false; } + virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const; + virtual void initializeExtraEncodeData(EncodeBitstreamParams& params) const; + virtual bool shouldIncludeChild(int childIndex, EncodeBitstreamParams& params) const; + virtual bool shouldRecurseSubtree(OctreeElement* parent, EncodeBitstreamParams& params, OctreeElementBag* bag) const; + virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const; + virtual void elementEncodeComplete(EncodeBitstreamParams& params, OctreeElementBag* bag) 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; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 0879fac10f..07546b5e69 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -93,9 +93,9 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties, bool bool wantDebug = false; if (wantDebug) { uint64_t now = usecTimestampNow(); - int elapsed = now - _lastEdited; + int elapsed = now - getLastEdited(); qDebug() << "ModelEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << - "now=" << now << " _lastEdited=" << _lastEdited; + "now=" << now << " getLastEdited()=" << getLastEdited(); } setLastEdited(properties._lastEdited); } @@ -141,8 +141,14 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, QString modelURLString((const char*)dataAt); dataAt += modelURLLength; bytesRead += modelURLLength; + +qDebug() << "ModelEntityItem::readEntitySubclassDataFromBuffer().... EntityID: " << getEntityItemID() << " --- PROP_MODEL_URL:" << modelURLString; + if (overwriteLocalData) { setModelURL(modelURLString); +qDebug() << " setModelURL(modelURLString)=" << getModelURL(); + } else { +qDebug() << " WARNING >>>>>>>>>>> IGNORING NEW DATA!!!! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"; } } @@ -223,11 +229,11 @@ int ModelEntityItem::oldVersionReadEntityDataFromBuffer(const unsigned char* dat _lastUpdated -= clockSkew; // _lastEdited - memcpy(&_lastEdited, dataAt, sizeof(_lastEdited)); - dataAt += sizeof(_lastEdited); - bytesRead += sizeof(_lastEdited); - _lastEdited -= clockSkew; - _created = _lastEdited; // NOTE: old models didn't have age or created time, assume their last edit was a create + memcpy(&_lastEditedRemote, dataAt, sizeof(_lastEditedRemote)); + dataAt += sizeof(_lastEditedRemote); + bytesRead += sizeof(_lastEditedRemote); + _lastEditedRemote -= clockSkew; + _created = _lastEditedRemote; // NOTE: old models didn't have age or created time, assume their last edit was a create QString ageAsString = formatSecondsElapsed(getAge()); qDebug() << "Loading old model file, _created = _lastEdited =" << _created @@ -320,7 +326,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, @@ -350,6 +356,9 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit LevelDetails propertyLevel = packetData->startLevel(); successPropertyFits = packetData->appendValue(getModelURL()); if (successPropertyFits) { + +qDebug() << "ModelEntityItem::appendSubclassData().... EntityID: " << getEntityItemID() << " --- PROP_MODEL_URL:" << getModelURL(); + propertyFlags |= PROP_MODEL_URL; propertiesDidntFit -= PROP_MODEL_URL; propertyCount++; diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 00a3e01293..c8d2abf29a 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -30,7 +30,7 @@ public: virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 69f883c41f..bf5f159a99 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -58,9 +58,9 @@ bool SphereEntityItem::setProperties(const EntityItemProperties& properties, boo bool wantDebug = false; if (wantDebug) { uint64_t now = usecTimestampNow(); - int elapsed = now - _lastEdited; + int elapsed = now - getLastEdited(); qDebug() << "SphereEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << - "now=" << now << " _lastEdited=" << _lastEdited; + "now=" << now << " getLastEdited()=" << getLastEdited(); } setLastEdited(properties.getLastEdited()); } diff --git a/libraries/entities/src/todo.txt b/libraries/entities/src/todo.txt index 8bb7d412b3..7f344270aa 100644 --- a/libraries/entities/src/todo.txt +++ b/libraries/entities/src/todo.txt @@ -1,7 +1,41 @@ // REQUIRED: - 2) Test file save load for case where two siblings have more than MTU amount of data. I wonder if the fact that file save - doesn't include the extra exists bits will break something. + +http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX#1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 +http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/newInvader16x16-large-purple.svo#1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 +-123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 + + + 1) clock skew sometimes causes properties to get reject when sent from the server... + + -- this appears to be related to clock skew!!! + -- need to determine "when to deleted extra data" -- + + + + When encoding ---- + if the parent node gets pulled from the bag first... things go ok + if the child node gets pulled first -- + and it encodes the grandchildren --- + THEN -- it seems like things break: + 1) the parent node will encode the children -- OK + 2) then will attempt to recurse into the children.. + + + + + + + + + + + + + + + + 7) some jutter with moving entities -- I think this might only happen with lots of models in an element or in view @@ -14,20 +48,27 @@ - does the same pruning/reallocating issue as the old UpdateEntityOperator - doesn't used clamped boxes for best fit tests... so could be problematic - -- crash on shutdown while animating... - Thread 0 Crashed:: Dispatch queue: com.apple.main-thread - 0 io.highfidelity.Interface 0x000000010cf99a6a EntityTree::updateChangingEntities(unsigned long long, QSet&) + 202 - 1 io.highfidelity.Interface 0x000000010cf998a6 EntityTree::update() + 70 - 2 io.highfidelity.Interface 0x000000010cb64a3e EntityTreeRenderer::update() + 62 - 3 io.highfidelity.Interface 0x000000010c9eb6ec Application::update(float) + 796 - 4 io.highfidelity.Interface 0x000000010c9f5e26 Application::idle() + 550 - 5 io.highfidelity.Interface 0x000000010c99f567 Application::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) + 1655 - 6 QtCore 0x0000000112474a30 QMetaObject::activate(QObject*, int, int, void**) + 2640 - 7 QtCore 0x000000011246d5c1 QObject::event(QEvent*) + 49 + // NICE TO HAVE: + + 1) clean up Octree::encodeTreeBitstreamRecursion() -- + It would be nice to clean up this function to be shorter... + 1015-1158 - helper functions "exit early helpers" + 1200-1245 - helper function "sorted children" + 1248-1376 - helper function "visible children" - + this handles LOD, occlusion, in view/was in view for child elements + NOTE: this information could be cached for secondary passes of the element + in the same scene encode since they shouldn't change in same scene encode + + + 11) quickly do some edits... then change domains... watch the entities continue to exist in new domain and move around. + -- verify this happens in old code, if so... move to "nice to have" + -- maybe this relates to having incoming packets waiting in the processing queue... they should be discarded on switching domains? + + Z) Consider using client side "on the fly" billboard of entites to handle LOD -- have routine to shoot views of an entity for 6 faces -- store those images in the entity on the client @@ -102,9 +143,17 @@ 10 libsystem_pthread.dylib 0x00007fff8bb9afc9 thread_start + 13 - 11) quickly do some edits... then change domains... watch the entities continue to exist in new domain and move around. - -- verify this happens in old code, if so... move to "nice to have" + -- crash on shutdown while animating...??? + Thread 0 Crashed:: Dispatch queue: com.apple.main-thread + 0 io.highfidelity.Interface 0x000000010cf99a6a EntityTree::updateChangingEntities(unsigned long long, QSet&) + 202 + 1 io.highfidelity.Interface 0x000000010cf998a6 EntityTree::update() + 70 + 2 io.highfidelity.Interface 0x000000010cb64a3e EntityTreeRenderer::update() + 62 + 3 io.highfidelity.Interface 0x000000010c9eb6ec Application::update(float) + 796 + 4 io.highfidelity.Interface 0x000000010c9f5e26 Application::idle() + 550 + 5 io.highfidelity.Interface 0x000000010c99f567 Application::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) + 1655 + 6 QtCore 0x0000000112474a30 QMetaObject::activate(QObject*, int, int, void**) + 2640 + 7 QtCore 0x000000011246d5c1 QObject::event(QEvent*) + 49 =============== @@ -237,6 +286,7 @@ // SOLVED -- 52) Look into why non-changed octree cells are being resent when editing an entity -- // this is probably because we're marking the trees as dirty -- but we probably can not send entitys that haven't changed // Solution -- suppress sending of entities that haven't been edited since the last view frustum sent +// SOLVED -- 53) Test sibling cells diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 05539ef767..22c4363e22 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -952,6 +952,7 @@ int Octree::encodeTreeBitstream(OctreeElement* element, // If the octalcode couldn't fit, then we can return, because no nodes below us will fit... if (!roomForOctalCode) { bag.insert(element); +qDebug() << "params.stopReason = EncodeBitstreamParams::DIDNT_FIT --- line:" << __LINE__; params.stopReason = EncodeBitstreamParams::DIDNT_FIT; return bytesWritten; } @@ -977,9 +978,11 @@ int Octree::encodeTreeBitstream(OctreeElement* element, // if includeColor and 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 (params.includeColor && childBytesWritten == 2) { - childBytesWritten = 0; - //params.stopReason = EncodeBitstreamParams::UNKNOWN; // possibly should be DIDNT_FIT... + if (suppressEmptySubtrees()) { + if (params.includeColor && 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 @@ -1010,6 +1013,15 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // The append state of this level/element. OctreeElement::AppendState elementAppendState = OctreeElement::COMPLETED; // assume the best + if (element != _rootElement) { + qDebug() << "TOP OF Octree::encodeTreeBitstreamRecursion().... elementAppendState = OctreeElement::COMPLETED ----"; + if (element) { + qDebug() << " element=" << element->getAACube(); + element->debugExtraEncodeData(params); + } else { + qDebug() << " element=NULL"; + } + } // How many bytes have we written so far at this level; int bytesAtThisLevel = 0; @@ -1018,6 +1030,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, if (!element) { qDebug("WARNING! encodeTreeBitstreamRecursion() called with element=NULL"); params.stopReason = EncodeBitstreamParams::NULL_NODE; + qDebug() << "encodeTreeBitstreamRecursion() --- returning from line: " << __LINE__; return bytesAtThisLevel; } @@ -1029,6 +1042,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // If we've reached our max Search Level, then stop searching. if (currentEncodeLevel >= params.maxEncodeLevel) { params.stopReason = EncodeBitstreamParams::TOO_DEEP; + qDebug() << "encodeTreeBitstreamRecursion() --- returning from line: " << __LINE__; return bytesAtThisLevel; } @@ -1038,6 +1052,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // but once we're in our own jurisdiction, then we need to make sure we're not below it. if (JurisdictionMap::BELOW == params.jurisdictionMap->isMyJurisdiction(element->getOctalCode(), CHECK_NODE_ONLY)) { params.stopReason = EncodeBitstreamParams::OUT_OF_JURISDICTION; + qDebug() << "encodeTreeBitstreamRecursion() --- returning from line: " << __LINE__; return bytesAtThisLevel; } } @@ -1056,6 +1071,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, params.stats->skippedDistance(element); } params.stopReason = EncodeBitstreamParams::LOD_SKIP; + qDebug() << "encodeTreeBitstreamRecursion() --- returning from line: " << __LINE__; return bytesAtThisLevel; } @@ -1074,6 +1090,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, params.stats->skippedOutOfView(element); } params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW; + qDebug() << "encodeTreeBitstreamRecursion() --- returning from line: " << __LINE__; return bytesAtThisLevel; } @@ -1114,6 +1131,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, params.stats->skippedWasInView(element); } params.stopReason = EncodeBitstreamParams::WAS_IN_VIEW; + qDebug() << "encodeTreeBitstreamRecursion() --- returning from line: " << __LINE__; return bytesAtThisLevel; } @@ -1125,6 +1143,9 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, params.stats->skippedNoChange(element); } params.stopReason = EncodeBitstreamParams::NO_CHANGE; + if (element != _rootElement) { + qDebug() << "encodeTreeBitstreamRecursion() --- returning from line: " << __LINE__; + } return bytesAtThisLevel; } @@ -1145,6 +1166,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, params.stats->skippedOccluded(element); } params.stopReason = EncodeBitstreamParams::OCCLUDED; + qDebug() << "encodeTreeBitstreamRecursion() --- returning from line: " << __LINE__; return bytesAtThisLevel; } } else { @@ -1171,14 +1193,30 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, 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(); + if (wantDebug) { + qDebug() << "Reserving at least " << minimumRequiredRootDataBytes() << " additional bytes for root data <<<<<<<<<<"; + } + } + if (wantDebug) { + qDebug() << "Reserving total of " << requiredBytes <<" bytes for this elements data <<<<<<<<<<<<<<"; + } + 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) { + qDebug() << " .....COULDN'T RESERVE MINIMUM BYTES....."; packetData->discardLevel(thisLevelKey); +qDebug() << "params.stopReason = EncodeBitstreamParams::DIDNT_FIT --- line:" << __LINE__; params.stopReason = EncodeBitstreamParams::DIDNT_FIT; bag.insert(element); + qDebug() << "encodeTreeBitstreamRecursion() --- returning from line: " << __LINE__; return bytesAtThisLevel; } @@ -1361,29 +1399,41 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, } } - // NOTE: the childrenDataBits is really more generically the childDataBits and it indicates - // that there is an array of child element data included in this packet. We wil write this bit mask - // but we may come back later and update the bits that are actually included + // NOTE: the childrenDataBits indicates that there is an array of child element data included in this packet. + // We wil 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); - - // we know the last thing we wrote to the packet was our childrenDataBits. Let's remember where that was! int childDataBitsPlaceHolder = packetData->getUncompressedByteOffset(sizeof(childrenDataBits)); unsigned char actualChildrenDataBits = 0; - if (continueThisLevel) { - bytesAtThisLevel += sizeof(childrenDataBits); // keep track of byte count - if (params.stats) { - params.stats->colorBitsWritten(); - } + assert(continueThisLevel); // since we used reserved bits, this really shouldn't fail + bytesAtThisLevel += sizeof(childrenDataBits); // keep track of byte count + if (params.stats) { + params.stats->colorBitsWritten(); // really data bits not just color bits } - // write the child element data... - if (continueThisLevel && params.includeColor) { + // 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: includeColor means include 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 + if (params.includeColor) { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { if (oneAtBit(childrenDataBits, i)) { OctreeElement* childElement = element->getChildAtIndex(i); - if (childElement) { + + // 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->shouldIncludeChild(i, params)) { int bytesBeforeChild = packetData->getUncompressedSize(); @@ -1394,7 +1444,11 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // to be completed. LevelDetails childDataLevelKey = packetData->startLevel(); +qDebug() << "Octree::encodeTreeBitstreamRecursion().... calling childElement->appendElementData()... child:" << childElement->getAACube(); 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); @@ -1405,15 +1459,27 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, actualChildrenDataBits += (1 << (7 - i)); continueThisLevel = packetData->endLevel(childDataLevelKey); } else { + if (wantDebug) { + qDebug() << "(childAppendState == OctreeElement::NONE) ... consider this element PARTIAL *************************************************"; + qDebug() << " childElement=" << childElement->getAACube(); + } packetData->discardLevel(childDataLevelKey); elementAppendState = OctreeElement::PARTIAL; params.stopReason = EncodeBitstreamParams::DIDNT_FIT; +qDebug() << "Octree::encodeTreeBitstreamRecursion().... at least one child didn't fit elementAppendState = OctreeElement::PARTIAL ----"; +qDebug() << "params.stopReason = EncodeBitstreamParams::DIDNT_FIT --- line:" << __LINE__; } // If this child was partially appended, then consider this element to be partially appended if (childAppendState == OctreeElement::PARTIAL) { + if (wantDebug) { + qDebug() << "(childAppendState == OctreeElement::PARTIAL) ... consider this element PARTIAL"; + qDebug() << " childElement=" << childElement->getAACube(); + } elementAppendState = OctreeElement::PARTIAL; params.stopReason = EncodeBitstreamParams::DIDNT_FIT; +qDebug() << "Octree::encodeTreeBitstreamRecursion().... at least one child WAS PARTIAL elementAppendState = OctreeElement::PARTIAL ----"; +qDebug() << "params.stopReason = EncodeBitstreamParams::DIDNT_FIT --- line:" << __LINE__; } int bytesAfterChild = packetData->getUncompressedSize(); @@ -1424,14 +1490,35 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, if (params.stats && (childAppendState != OctreeElement::NONE)) { params.stats->colorSent(childElement); } + } else { + qDebug() << "Octree::encodeTreeBitstreamRecursion().... DIDN'T ATTEMPT TO appendElementData() for child[" << i << "]"; + if (childElement) { + qDebug() << " childElement=" << childElement->getAACube(); + } else { + qDebug() << " childElement=NULL"; + } } } } } + + if (!continueThisLevel) { + qDebug() << "******* WARNING UNEXPECTED CASE ***********************************************************"; + qDebug() << "reached end of child element data loop with continueThisLevel=FALSE...."; + qDebug() << "This is not expected!!!!"; + qDebug() << "*******************************************************************************************"; + } if (actualChildrenDataBits != childrenDataBits) { // repair the child data mask continueThisLevel = packetData->updatePriorBitMask(childDataBitsPlaceHolder, actualChildrenDataBits); + + if (!continueThisLevel) { + qDebug() << "******* WARNING UNEXPECTED CASE ***********************************************************"; + qDebug() << "Failed to update childDataBitsPlaceHolder -- continueThisLevel=FALSE...."; + qDebug() << "This is not expected!!!!"; + qDebug() << "*******************************************************************************************"; + } } // if the caller wants to include childExistsBits, then include them even if not in view, put them before the @@ -1444,6 +1531,11 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, if (params.stats) { params.stats->existsBitsWritten(); } + } else { + qDebug() << "******* WARNING UNEXPECTED CASE ***********************************************************"; + qDebug() << "Failed to append childrenExistInTreeBits -- continueThisLevel=FALSE...."; + qDebug() << "This is not expected!!!!"; + qDebug() << "*******************************************************************************************"; } } @@ -1456,12 +1548,28 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, if (params.stats) { params.stats->existsInPacketBitsWritten(); } + } else { + qDebug() << "******* WARNING UNEXPECTED CASE ***********************************************************"; + qDebug() << "Failed to append childrenExistInPacketBits -- continueThisLevel=FALSE...."; + qDebug() << "This is not expected!!!!"; + qDebug() << "*******************************************************************************************"; } } // 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 @@ -1505,8 +1613,10 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // recursing, by returning TRUE in recurseChildrenWithData(). if (recurseChildrenWithData() || !params.viewFrustum || !oneAtBit(childrenDataBits, originalIndex)) { - childTreeBytesOut = encodeTreeBitstreamRecursion(childElement, packetData, bag, params, - thisLevel, nodeLocationThisView); + if (childElement->shouldRecurseSubtree(element, params, &bag)) { + childTreeBytesOut = encodeTreeBitstreamRecursion(childElement, packetData, bag, params, + thisLevel, nodeLocationThisView); + } } // remember this for reshuffling @@ -1552,6 +1662,12 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // repair the child exists mask continueThisLevel = packetData->updatePriorBitMask(childExistsPlaceHolder, childrenExistInPacketBits); + if (!continueThisLevel) { + qDebug() << "******* WARNING UNEXPECTED CASE ***********************************************************"; + qDebug() << "Failed to update childExistsPlaceHolder -- continueThisLevel=FALSE...."; + qDebug() << "This is not expected!!!!"; + qDebug() << "*******************************************************************************************"; + } // If this is the last of the child exists bits, then we're actually be rolling out the entire tree if (params.stats && childrenExistInPacketBits == 0) { @@ -1559,7 +1675,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, } if (!continueThisLevel) { - if (wantDebug) { + if (true || wantDebug) { qDebug() << " WARNING line:" << __LINE__; qDebug() << " breaking the child recursion loop with continueThisLevel=false!!!"; qDebug() << " AFTER attempting to updatePriorBitMask() for empty sub tree...."; @@ -1594,6 +1710,12 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // now that all slices are back in the correct order, copy them to the correct output buffer continueThisLevel = packetData->updatePriorBytes(firstRecursiveSliceOffset, &tempReshuffleBuffer[0], allSlicesSize); + if (!continueThisLevel) { + qDebug() << "******* WARNING UNEXPECTED CASE ***********************************************************"; + qDebug() << "Failed to update recursive slice!!! -- continueThisLevel=FALSE...."; + qDebug() << "This is not expected!!!!"; + qDebug() << "*******************************************************************************************"; + } } } // end keepDiggingDeeper @@ -1603,6 +1725,10 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, int bytesBeforeChild = packetData->getUncompressedSize(); + // release the bytes we reserved... + qDebug() << "RELEASING previously reserved " << minimumRequiredRootDataBytes() << " bytes for root -- line:" << __LINE__; + packetData->releaseReservedBytes(minimumRequiredRootDataBytes()); + LevelDetails rootDataLevelKey = packetData->startLevel(); OctreeElement::AppendState rootAppendState = element->appendElementData(packetData, params); bool partOfRootFit = (rootAppendState != OctreeElement::NONE); @@ -1620,6 +1746,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, if (!allOfRootFit) { elementAppendState = OctreeElement::PARTIAL; params.stopReason = EncodeBitstreamParams::DIDNT_FIT; +qDebug() << "Octree::encodeTreeBitstreamRecursion().... ROOT DATA WAS PARTIAL OR DIDN'T FIT elementAppendState = OctreeElement::PARTIAL ----"; +qDebug() << "params.stopReason = EncodeBitstreamParams::DIDNT_FIT --- line:" << __LINE__; } // do we really ever NOT want to continue this level??? @@ -1634,6 +1762,14 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, params.stats->colorSent(element); } } + + if (!continueThisLevel) { + qDebug() << "******* WARNING UNEXPECTED CASE ***********************************************************"; + qDebug() << "Something failed in packing ROOT data -- continueThisLevel=FALSE...."; + qDebug() << "This is not expected!!!!"; + qDebug() << "*******************************************************************************************"; + } + } // if we were unable to fit this level in our packet, then rewind and add it to the element bag for @@ -1642,12 +1778,22 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, continueThisLevel = packetData->endLevel(thisLevelKey); } else { packetData->discardLevel(thisLevelKey); + qDebug() << "******* WARNING UNEXPECTED CASE ***********************************************************"; + qDebug() << "Something failed in attempting to pack this element -- continueThisLevel=FALSE...."; + qDebug() << "This is not expected!!!!"; + qDebug() << "*******************************************************************************************"; } // 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) { + qDebug() << "******* WARNING UNEXPECTED CASE ***********************************************************"; + qDebug() << "Something failed in attempting to pack this element -- continueThisLevel=FALSE...."; + qDebug() << "IS THIS EVER EXPECTED????"; + qDebug() << " calling bag.insert(element);....."; + qDebug() << "*******************************************************************************************"; + bag.insert(element); // don't need to check element here, because we can't get here with no element @@ -1656,6 +1802,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, } params.stopReason = EncodeBitstreamParams::DIDNT_FIT; +qDebug() << "params.stopReason = EncodeBitstreamParams::DIDNT_FIT --- line:" << __LINE__; bytesAtThisLevel = 0; // didn't fit } else { @@ -1665,10 +1812,31 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // and assume that the appendElementData() has stored any required state data // in the params extraEncodeData if (elementAppendState == OctreeElement::PARTIAL) { + if (true || wantDebug) { + qDebug() << "(elementAppendState == OctreeElement::PARTIAL) ..."; + qDebug() << " RE INSERT THIS(parent) element into bag"; + qDebug() << " element:" << element->getAACube(); + } + 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) { + if (true || wantDebug) { + qDebug() << "*********************************************************************************************************"; + qDebug() << "(elementAppendState == OctreeElement::COMPLETED)"; + qDebug() << " calling element->elementEncodeComplete(params)"; + qDebug() << " element=" << element->getAACube(); + } + element->elementEncodeComplete(params, &bag); + if (true || wantDebug) { + qDebug() << "*********************************************************************************************************"; + } + } + qDebug() << "encodeTreeBitstreamRecursion() --- returning from line: " << __LINE__; return bytesAtThisLevel; } @@ -1855,11 +2023,18 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { params.extraEncodeData = &extraEncodeData; while (!elementBag.isEmpty()) { + qDebug() << "WRITING SVO ---- START LOOP ---------------"; OctreeElement* subTree = elementBag.extract(); + qDebug() << "WRITING SVO subTree=" << subTree->getAACube(); + lockForRead(); // do tree locking down here so that we have shorter slices and less thread contention bytesWritten = encodeTreeBitstream(subTree, &packetData, elementBag, params); unlock(); + qDebug() << "WRITING SVO subTree=" << subTree->getAACube() << " bytesWritten=" << bytesWritten; + qDebug() << "WRITING SVO subTree=" << subTree->getAACube() << " params.stopReason=" << params.getStopReason(); + qDebug() << "WRITING SVO subTree=" << subTree->getAACube() << " packetData.hasContent()=" << packetData.hasContent(); + // if the subTree couldn't fit, and so we should reset the packet and reinsert the element in our bag and try again if (bytesWritten == 0 && (params.stopReason == EncodeBitstreamParams::DIDNT_FIT)) { if (packetData.hasContent()) { @@ -1870,13 +2045,16 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { file.write((const char*)&bufferSize, sizeof(bufferSize)); } file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize()); + qDebug() << "WRITING SVO actually writing to the file bufferSize:" << packetData.getFinalizedSize(); lastPacketWritten = true; } packetData.reset(); // is there a better way to do this? could we fit more? + qDebug() << "WRITING SVO INSERT SUBTREE FOR ANOTHER GO ... subTree=" << subTree->getAACube(); elementBag.insert(subTree); } else { lastPacketWritten = false; } + qDebug() << "WRITING SVO ---- END LOOP ---------------"; } if (!lastPacketWritten) { @@ -1887,8 +2065,10 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { file.write((const char*)&bufferSize, sizeof(bufferSize)); } file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize()); + qDebug() << "WRITING SVO actually writing to the file bufferSize:" << packetData.getFinalizedSize(); } } + qDebug() << "WRITING SVO CLOSING FILE"; file.close(); } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 5901896edb..7629c4a05d 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -233,6 +233,8 @@ public: virtual bool recurseChildrenWithData() const { return true; } virtual bool rootElementHasData() const { return false; } + virtual int minimumRequiredRootDataBytes() const { return 0; } + virtual bool suppressEmptySubtrees() const { return true; } /// some versions of the SVO file will include breaks with buffer lengths between each buffer chunk in the SVO /// file. If the Octree subclass expects this for this particular version of the file, it should override this diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 861eb6767e..e5c4196880 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -28,6 +28,7 @@ class EncodeBitstreamParams; class Octree; class OctreeElement; +class OctreeElementBag; class OctreeElementDeleteHook; class OctreePacketData; class ReadBitstreamToTreeParams; @@ -90,6 +91,14 @@ public: /// The state of the call to appendElementData typedef enum { COMPLETED, PARTIAL, NONE } AppendState; + virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const { } + virtual void initializeExtraEncodeData(EncodeBitstreamParams& params) const { } + virtual bool shouldIncludeChild(int childIndex, EncodeBitstreamParams& params) const { return true; } + virtual bool shouldRecurseSubtree(OctreeElement* parent, EncodeBitstreamParams& params, OctreeElementBag* bag) const { return true; } + + virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const { } + virtual void elementEncodeComplete(EncodeBitstreamParams& params, OctreeElementBag* bag) 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; }