1054 lines · 48.3 KB · C++
RawDownload
////  EntityTreeElement.cpp//  libraries/entities/src////  Created by Brad Hefta-Gaub on 12/4/13.//  Copyright 2013 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include <glm/gtx/transform.hpp> #include <FBXReader.h>#include <GeometryUtil.h>#include <OctreeUtils.h> #include "EntitiesLogging.h"#include "EntityItemProperties.h"#include "EntityTree.h"#include "EntityTreeElement.h"#include "EntityTypes.h" EntityTreeElement::EntityTreeElement(unsigned char* octalCode) : OctreeElement() {    init(octalCode);}; EntityTreeElement::~EntityTreeElement() {    _octreeMemoryUsage -= sizeof(EntityTreeElement);} OctreeElementPointer EntityTreeElement::createNewElement(unsigned char* octalCode) {    auto newChild = EntityTreeElementPointer(new EntityTreeElement(octalCode));    newChild->setTree(_myTree);    return newChild;} void EntityTreeElement::init(unsigned char* octalCode) {    OctreeElement::init(octalCode);    _octreeMemoryUsage += sizeof(EntityTreeElement);} OctreeElementPointer EntityTreeElement::addChildAtIndex(int index) {    OctreeElementPointer newElement = OctreeElement::addChildAtIndex(index);    std::static_pointer_cast<EntityTreeElement>(newElement)->setTree(_myTree);    return newElement;} void EntityTreeElement::debugExtraEncodeData(EncodeBitstreamParams& params) const {    qCDebug(entities) << "EntityTreeElement::debugExtraEncodeData()... ";    qCDebug(entities) << "    element:" << _cube;     OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;    assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes     if (extraEncodeData->contains(this)) {        EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData            = static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(this));        qCDebug(entities) << "    encode data:" << entityTreeElementExtraEncodeData;    } else {        qCDebug(entities) << "    encode data: MISSING!!";    }} void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params) {     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();        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 {    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;    assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes     if (extraEncodeData->contains(this)) {        EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData                        = static_cast<EntityTreeElementExtraEncodeData*>(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::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 {    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;    assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes     if (extraEncodeData->contains(this)) {        EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData                        = static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(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 {    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;    assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes    if (extraEncodeData->contains(this)) {        EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData                        = static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(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;    }     OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;    assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes    assert(extraEncodeData->contains(this));     EntityTreeElementExtraEncodeData* thisExtraEncodeData                = static_cast<EntityTreeElementExtraEncodeData*>(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) 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            // encoud our parent... this might happen.            if (extraEncodeData->contains(childElement.get())) {                EntityTreeElementExtraEncodeData* childExtraEncodeData                    = static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(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...     // first, check the params.extraEncodeData to see if there's any partial re-encode data for this element    OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData;    EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData = NULL;    bool hadElementExtraData = false;    if (extraEncodeData && extraEncodeData->contains(this)) {        entityTreeElementExtraEncodeData =            static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(this));        hadElementExtraData = true;    } else {        // if there wasn't one already, then create one        entityTreeElementExtraEncodeData = 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 = static_cast<EntityTreeElementExtraEncodeData*>(extraEncodeData->value(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) {            for (uint16_t i = 0; i < _entityItems.size(); i++) {                EntityItemPointer entity = _entityItems[i];                bool includeThisEntity = true;                 if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastViewFrustumSent) {                    includeThisEntity = false;                }                 if (hadElementExtraData) {                    includeThisEntity = includeThisEntity &&                        entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID());                }                 if (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                            qDebug() << "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                    qDebug() << "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);    if (!success) {        return false;    }    return containsBounds(queryCube);} bool EntityTreeElement::bestFitEntityBounds(EntityItemPointer entity) const {    bool success;    auto queryCube = entity->getQueryAACube(success);    if (!success) {        qDebug() << "EntityTreeElement::bestFitEntityBounds couldn't get queryCube for" << entity->getName() << entity->getID();        return false;    }    return bestFitBounds(queryCube);} bool EntityTreeElement::containsBounds(const EntityItemProperties& properties) const {    return containsBounds(properties.getQueryAACube());} bool EntityTreeElement::bestFitBounds(const EntityItemProperties& properties) const {    return bestFitBounds(properties.getQueryAACube());} bool EntityTreeElement::containsBounds(const AACube& bounds) const {    return containsBounds(bounds.getMinimumPoint(), bounds.getMaximumPoint());} bool EntityTreeElement::bestFitBounds(const AACube& bounds) const {    return bestFitBounds(bounds.getMinimumPoint(), bounds.getMaximumPoint());} bool EntityTreeElement::containsBounds(const AABox& bounds) const {    return containsBounds(bounds.getMinimumPoint(), bounds.getMaximumPoint());} 

Lines 1–500 of 1054.