示例#1
0
// A box is inside a sphere if all of its corners are inside the sphere
// A box intersects a sphere if any of its edges (as rays) interesect the sphere
// A box is outside a sphere if none of its edges (as rays) interesect the sphere
ViewFrustum::location ViewFrustum::cubeInKeyhole(const AACube& cube) const {

    // First check to see if the cube is in the bounding cube for the sphere, if it's not, then we can short circuit
    // this and not check with sphere penetration which is more expensive
    if (!_keyholeBoundingCube.contains(cube)) {
        return OUTSIDE;
    }

    glm::vec3 penetration;
    bool intersects = cube.findSpherePenetration(_position, _keyholeRadius, penetration);

    ViewFrustum::location result = OUTSIDE;

    // if the cube intersects the sphere, then it may also be inside... calculate further
    if (intersects) {
        result = INTERSECT;

        // test all the corners, if they are all inside the sphere, the entire cube is in the sphere
        bool allPointsInside = true; // assume the best
        for (int v = BOTTOM_LEFT_NEAR; v < TOP_LEFT_FAR; v++) {
            glm::vec3 vertex = cube.getVertex((BoxVertex)v);
            if (!pointInKeyhole(vertex)) {
                allPointsInside = false;
                break;
            }
        }

        if (allPointsInside) {
            result = INSIDE;
        }
    }

    return result;
}
示例#2
0
ViewFrustum::location ViewFrustum::cubeInFrustum(const AACube& cube) const {

    ViewFrustum::location regularResult = INSIDE;
    ViewFrustum::location keyholeResult = OUTSIDE;

    // If we have a keyholeRadius, check that first, since it's cheaper
    if (_keyholeRadius >= 0.0f) {
        keyholeResult = cubeInKeyhole(cube);
    }
    if (keyholeResult == INSIDE) {
        return keyholeResult;
    }

    // TODO: These calculations are expensive, taking up 80% of our time in this function.
    // This appears to be expensive because we have to test the distance to each plane.
    // One suggested optimization is to first check against the approximated cone. We might
    // also be able to test against the cone to the bounding sphere of the box.
    for(int i=0; i < 6; i++) {
        const glm::vec3& normal = _planes[i].getNormal();
        const glm::vec3& boxVertexP = cube.getVertexP(normal);
        float planeToBoxVertexPDistance = _planes[i].distance(boxVertexP);

        const glm::vec3& boxVertexN = cube.getVertexN(normal);
        float planeToBoxVertexNDistance = _planes[i].distance(boxVertexN);

        if (planeToBoxVertexPDistance < 0) {
            // This is outside the regular frustum, so just return the value from checking the keyhole
            return keyholeResult;
        } else if (planeToBoxVertexNDistance < 0) {
            regularResult =  INTERSECT;
        }
    }
    return regularResult;
}
示例#3
0
// Similar strategy to getProjectedPolygon() we use the knowledge of camera position relative to the
// axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for
// squares and square-roots. Just compares.
void ViewFrustum::getFurthestPointFromCamera(const AACube& box, glm::vec3& furthestPoint) const {
    const glm::vec3& bottomNearRight = box.getCorner();
    float scale = box.getScale();
    float halfScale = scale * 0.5f;

    if (_position.x < bottomNearRight.x + halfScale) {
        // we are to the right of the center, so the left edge is furthest
        furthestPoint.x = bottomNearRight.x + scale;
    } else {
        furthestPoint.x = bottomNearRight.x;
    }

    if (_position.y < bottomNearRight.y + halfScale) {
        // we are below of the center, so the top edge is furthest
        furthestPoint.y = bottomNearRight.y + scale;
    } else {
        furthestPoint.y = bottomNearRight.y;
    }

    if (_position.z < bottomNearRight.z + halfScale) {
        // we are to the near side of the center, so the far side edge is furthest
        furthestPoint.z = bottomNearRight.z + scale;
    } else {
        furthestPoint.z = bottomNearRight.z;
    }
}
示例#4
0
float ConicalViewFrustum::getAngularSize(const AACube& cube) const {
    auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
    auto position = cube.calcCenter() - _position; // position of bounding sphere in view-frame
    float distance = glm::length(position);

    return getAngularSize(distance, radius);
}
示例#5
0
int OctreeElement::getMyChildContaining(const AACube& cube) const {
    float ourScale = getScale();
    float cubeScale = cube.getScale();

    // TODO: consider changing this to assert()
    if (cubeScale > ourScale) {
        qCDebug(octree) << "UNEXPECTED -- OctreeElement::getMyChildContaining() -- (cubeScale > ourScale)";
        qCDebug(octree) << "    cube=" << cube;
        qCDebug(octree) << "    elements AACube=" << _cube;
        qCDebug(octree) << "    cubeScale=" << cubeScale;
        qCDebug(octree) << "    ourScale=" << ourScale;
        assert(false);
    }

    // Determine which of our children the minimum and maximum corners of the cube live in...
    glm::vec3 cubeCornerMinimum = glm::clamp(cube.getCorner(), (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE);
    glm::vec3 cubeCornerMaximum = glm::clamp(cube.calcTopFarLeft(), (float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE);

    if (_cube.contains(cubeCornerMinimum) && _cube.contains(cubeCornerMaximum)) {
        int childIndexCubeMinimum = getMyChildContainingPoint(cubeCornerMinimum);
        int childIndexCubeMaximum = getMyChildContainingPoint(cubeCornerMaximum);

        // If the minimum and maximum corners of the cube are in two different children's cubes, then we are the containing element
        if (childIndexCubeMinimum != childIndexCubeMaximum) {
            return CHILD_UNKNOWN;
        }

        return childIndexCubeMinimum; // either would do, they are the same
    }
    return CHILD_UNKNOWN; // since cube is not contained in our element, it can't be in one of our children
}
示例#6
0
void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
        const DiffTraversal::View& view, const DiffTraversal::View& lastView) {
    if (_nextIndex == -1) {
        // root case is special
        ++_nextIndex;
        EntityTreeElementPointer element = _weakElement.lock();
        next.element = element;
        next.intersection = ViewFrustum::INTERSECT;
        return;
    } else if (_nextIndex < NUMBER_OF_CHILDREN) {
        EntityTreeElementPointer element = _weakElement.lock();
        if (element) {
            while (_nextIndex < NUMBER_OF_CHILDREN) {
                EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
                ++_nextIndex;
                if (nextElement) {
                    AACube cube = nextElement->getAACube();
                    // check for LOD truncation
                    float distance = glm::distance(view.viewFrustum.getPosition(), cube.calcCenter()) + MIN_VISIBLE_DISTANCE;
                    float angularDiameter = cube.getScale() / distance;
                    if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
                        if (view.viewFrustum.calculateCubeKeyholeIntersection(cube) != ViewFrustum::OUTSIDE) {
                            next.element = nextElement;
                            next.intersection = ViewFrustum::OUTSIDE;
                            return;
                        }
                    }
                }
            }
        }
    }
    next.element.reset();
    next.intersection = ViewFrustum::OUTSIDE;
}
示例#7
0
文件: AABox.cpp 项目: JamesLinus/hifi
bool AABox::touches(const AACube& otherCube) const {
    glm::vec3 relativeCenter = _corner - otherCube.getCorner() + ((_scale - otherCube.getDimensions()) * 0.5f);

    glm::vec3 totalHalfScale = (_scale + otherCube.getDimensions()) * 0.5f;

    return fabsf(relativeCenter.x) <= totalHalfScale.x &&
        fabsf(relativeCenter.y) <= totalHalfScale.y &&
        fabsf(relativeCenter.z) <= totalHalfScale.z;
}
示例#8
0
QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube) {
    QScriptValue obj = engine->newObject();
    const glm::vec3& corner = aaCube.getCorner();
    obj.setProperty("x", corner.x);
    obj.setProperty("y", corner.y);
    obj.setProperty("z", corner.z);
    obj.setProperty("scale", aaCube.getScale());
    return obj;
}
示例#9
0
bool OctreePacketData::appendValue(const AACube& aaCube) {
    aaCubeData cube { aaCube.getCorner(), aaCube.getScale() };
    const unsigned char* data = (const unsigned char*)&cube;
    int length = sizeof(aaCubeData);
    bool success = append(data, length);
    if (success) {
        _bytesOfValues += length;
        _totalBytesOfValues += length;
    }
    return success;
}
示例#10
0
// does this entity tree element contain the old entity
bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElement* element) {
    bool containsEntity = false;

    // If we don't have an old entity, then we don't contain the entity, otherwise
    // check the bounds
    if (_entitiesToDelete.size() > 0) {
        AACube elementCube = element->getAACube();
        foreach(const EntityToDeleteDetails& details, _entitiesToDelete) {
            if (elementCube.contains(details.cube)) {
                containsEntity = true;
                break; // if it contains at least one, we're good to go
            }
        }
    }
示例#11
0
void ParticleTreeElement::getParticles(const AACube& box, QVector<Particle*>& foundParticles) {
    QList<Particle>::iterator particleItr = _particles->begin();
    QList<Particle>::iterator particleEnd = _particles->end();
    AACube particleCube;
    while(particleItr != particleEnd) {
        Particle* particle = &(*particleItr);
        float radius = particle->getRadius();
        // NOTE: we actually do box-box collision queries here, which is sloppy but good enough for now
        // TODO: decide whether to replace particleBox-box query with sphere-box (requires a square root
        // but will be slightly more accurate).
        particleCube.setBox(particle->getPosition() - glm::vec3(radius), 2.f * radius);
        if (particleCube.touches(_cube)) {
            foundParticles.push_back(particle);
        }
        ++particleItr;
    }
}
示例#12
0
文件: AACube.cpp 项目: Ian-Mills/hifi
bool AACube::contains(const AACube& otherCube) const {
    for (int v = BOTTOM_LEFT_NEAR; v < TOP_LEFT_FAR; v++) {
        glm::vec3 vertex = otherCube.getVertex((BoxVertex)v);
        if (!contains(vertex)) {
            return false;
        }
    }
    return true;
}
示例#13
0
void aaCubeFromScriptValue(const QScriptValue &object, AACube& aaCube) {
    glm::vec3 corner;
    corner.x = object.property("x").toVariant().toFloat();
    corner.y = object.property("y").toVariant().toFloat();
    corner.z = object.property("z").toVariant().toFloat();
    float scale = object.property("scale").toVariant().toFloat();

    aaCube.setBox(corner, scale);
}
void MovingEntitiesOperator::addEntityToMoveList(EntityItemPointer entity, const AACube& newCube) {
    EntityTreeElementPointer oldContainingElement = entity->getElement();
    AABox newCubeClamped = newCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE);

    if (_wantDebug) {
        qCDebug(entities) << "MovingEntitiesOperator::addEntityToMoveList() -----------------------------";
        qCDebug(entities) << "    newCube:" << newCube;
        qCDebug(entities) << "    newCubeClamped:" << newCubeClamped;
        if (oldContainingElement) {
            qCDebug(entities) << "    oldContainingElement:" << oldContainingElement->getAACube();
            qCDebug(entities) << "    oldContainingElement->bestFitBounds(newCubeClamped):" 
                            << oldContainingElement->bestFitBounds(newCubeClamped);
        } else {
            qCDebug(entities) << "    WARNING NO OLD CONTAINING ELEMENT for entity" << entity->getEntityItemID();
        }
    }

    if (!oldContainingElement) {
        return; // bail without adding.
    }

    // If the original containing element is the best fit for the requested newCube locations then
    // we don't actually need to add the entity for moving and we can short circuit all this work
    if (!oldContainingElement->bestFitBounds(newCubeClamped)) {
        // check our tree, to determine if this entity is known
        EntityToMoveDetails details;
        details.oldContainingElement = oldContainingElement;
        details.oldContainingElementCube = oldContainingElement->getAACube();
        details.entity = entity;
        details.oldFound = false;
        details.newFound = false;
        details.newCube = newCube;
        details.newCubeClamped = newCubeClamped;
        _entitiesToMove << details;
        _lookingCount++;

        if (_wantDebug) {
            qCDebug(entities) << "MovingEntitiesOperator::addEntityToMoveList() -----------------------------";
            qCDebug(entities) << "    details.entity:" << details.entity->getEntityItemID();
            qCDebug(entities) << "    details.oldContainingElementCube:" << details.oldContainingElementCube;
            qCDebug(entities) << "    details.newCube:" << details.newCube;
            qCDebug(entities) << "    details.newCubeClamped:" << details.newCubeClamped;
            qCDebug(entities) << "    _lookingCount:" << _lookingCount;
            qCDebug(entities) << "--------------------------------------------------------------------------";
        }
    } else {
        if (_wantDebug) {
            qCDebug(entities) << "    oldContainingElement->bestFitBounds(newCubeClamped) IS BEST FIT... NOTHING TO DO";
        }
    }

    if (_wantDebug) {
        qCDebug(entities) << "--------------------------------------------------------------------------";
    }
}
示例#15
0
void VoxelShapeManager::updateVoxels(const quint64& now, CubeList& cubes) {
    const quint64 VOXEL_UPDATE_PERIOD = 100000; // usec
    _updateExpiry = now + VOXEL_UPDATE_PERIOD;
    PhysicsSimulation* simulation = getSimulation();
    if (!simulation) {
        return;
    }

    int numChanges = 0;
    VoxelPool::iterator voxelItr = _voxels.begin();
    while (voxelItr != _voxels.end()) {
        // look for this voxel in cubes
        CubeList::iterator cubeItr = cubes.find(voxelItr.key());
        if (cubeItr == cubes.end()) {
            // did not find it --> remove the voxel
            simulation->removeShape(voxelItr.value()._shape);
            voxelItr = _voxels.erase(voxelItr);
            ++numChanges;
        } else {
            // found it --> remove the cube
            cubes.erase(cubeItr);
            voxelItr++;
        }
    }

    // add remaining cubes to _voxels
    glm::vec3 simulationOrigin = simulation->getTranslation();
    CubeList::const_iterator cubeItr = cubes.constBegin();
    while (cubeItr != cubes.constEnd()) {
        AACube cube = cubeItr.value();
        AACubeShape* shape = new AACubeShape(cube.getScale(), cube.calcCenter() - simulationOrigin);
        shape->setEntity(this);
        VoxelInfo voxel = {cube, shape };
        _voxels.insert(cubeItr.key(), voxel);
        ++numChanges;
        ++cubeItr;
    }

    if (numChanges > 0) {
        buildShapes();
    }
}
示例#16
0
文件: AABox.cpp 项目: JamesLinus/hifi
AABox::AABox(const AACube& other) :
    _corner(other.getCorner()), _scale(other.getScale(), other.getScale(), other.getScale()) {
}
示例#17
0
AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const {
    AACube maxCube = getMaximumAACubeInMeters();
    maxCube.scale(1.0f / (float)TREE_SCALE);
    return maxCube;
}
示例#18
0
void EntityTests::entityTreeTests(bool verbose) {

    bool extraVerbose = false;
    int testsTaken = 0;
    int testsPassed = 0;
    int testsFailed = 0;

    if (verbose) {
        qDebug() << "******************************************************************************************";
    }
    
    qDebug() << "EntityTests::entityTreeTests()";

    // Tree, id, and entity properties used in many tests below...
    EntityTree tree;
    QUuid id = QUuid::createUuid();
    EntityItemID entityID(id);
    entityID.isKnownID = false; // this is a temporary workaround to allow local tree entities to be added with known IDs
    EntityItemProperties properties;
    float oneMeter = 1.0f;
    //float halfMeter = oneMeter / 2.0f;
    float halfOfDomain = TREE_SCALE * 0.5f;
    glm::vec3 positionNearOriginInMeters(oneMeter, oneMeter, oneMeter); // when using properties, these are in meter not tree units
    glm::vec3 positionAtCenterInMeters(halfOfDomain, halfOfDomain, halfOfDomain);
    glm::vec3 positionNearOriginInTreeUnits = positionNearOriginInMeters / (float)TREE_SCALE;
    glm::vec3 positionAtCenterInTreeUnits = positionAtCenterInMeters / (float)TREE_SCALE;

    {
        testsTaken++;
        QString testName = "add entity to tree and search";
        if (verbose) {
            qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
        }
        
        properties.setPosition(positionAtCenterInMeters);
        // TODO: Fix these unit tests.
        //properties.setRadius(halfMeter);
        //properties.setModelURL("http://s3.amazonaws.com/hifi-public/ozan/theater.fbx");

        tree.addEntity(entityID, properties);
        
        float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
        const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenterInTreeUnits, targetRadius);
        const EntityItem* foundEntityByID = tree.findEntityByEntityItemID(entityID);
        EntityTreeElement* containingElement = tree.getContainingElement(entityID);
        AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
        
        if (verbose) {
            qDebug() << "foundEntityByRadius=" << foundEntityByRadius;
            qDebug() << "foundEntityByID=" << foundEntityByID;
            qDebug() << "containingElement=" << containingElement;
            qDebug() << "containingElement.box=" 
                << elementCube.getCorner().x * TREE_SCALE << "," 
                << elementCube.getCorner().y * TREE_SCALE << ","
                << elementCube.getCorner().z * TREE_SCALE << ":" 
                << elementCube.getScale() * TREE_SCALE;
            qDebug() << "elementCube.getScale()=" << elementCube.getScale();
            //containingElement->printDebugDetails("containingElement");
        }

        bool passed = foundEntityByRadius && foundEntityByID && (foundEntityByRadius == foundEntityByID);
        if (passed) {
            testsPassed++;
        } else {
            testsFailed++;
            qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName);
        }
    }

    entityID.isKnownID = true; // this is a temporary workaround to allow local tree entities to be added with known IDs

    {
        testsTaken++;
        QString testName = "change position of entity in tree";
        if (verbose) {
            qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
        }
        
        glm::vec3 newPosition = positionNearOriginInMeters;

        properties.setPosition(newPosition);

        tree.updateEntity(entityID, properties);
        
        float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
        const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionNearOriginInTreeUnits, targetRadius);
        const EntityItem* foundEntityByID = tree.findEntityByEntityItemID(entityID);
        EntityTreeElement* containingElement = tree.getContainingElement(entityID);
        AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
        
        if (verbose) {
            qDebug() << "foundEntityByRadius=" << foundEntityByRadius;
            qDebug() << "foundEntityByID=" << foundEntityByID;
            qDebug() << "containingElement=" << containingElement;
            qDebug() << "containingElement.box=" 
                << elementCube.getCorner().x * TREE_SCALE << "," 
                << elementCube.getCorner().y * TREE_SCALE << ","
                << elementCube.getCorner().z * TREE_SCALE << ":" 
                << elementCube.getScale() * TREE_SCALE;
            //containingElement->printDebugDetails("containingElement");
        }

        bool passed = foundEntityByRadius && foundEntityByID && (foundEntityByRadius == foundEntityByID);
        if (passed) {
            testsPassed++;
        } else {
            testsFailed++;
            qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName);
        }
    }

    {
        testsTaken++;
        QString testName = "change position of entity in tree back to center";
        if (verbose) {
            qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
        }
        
        glm::vec3 newPosition = positionAtCenterInMeters;

        properties.setPosition(newPosition);

        tree.updateEntity(entityID, properties);
        
        float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
        const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenterInTreeUnits, targetRadius);
        const EntityItem* foundEntityByID = tree.findEntityByEntityItemID(entityID);
        EntityTreeElement* containingElement = tree.getContainingElement(entityID);
        AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
        
        if (verbose) {
            qDebug() << "foundEntityByRadius=" << foundEntityByRadius;
            qDebug() << "foundEntityByID=" << foundEntityByID;
            qDebug() << "containingElement=" << containingElement;
            qDebug() << "containingElement.box=" 
                << elementCube.getCorner().x * TREE_SCALE << "," 
                << elementCube.getCorner().y * TREE_SCALE << ","
                << elementCube.getCorner().z * TREE_SCALE << ":" 
                << elementCube.getScale() * TREE_SCALE;
            //containingElement->printDebugDetails("containingElement");
        }

        bool passed = foundEntityByRadius && foundEntityByID && (foundEntityByRadius == foundEntityByID);
        if (passed) {
            testsPassed++;
        } else {
            testsFailed++;
            qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName);
        }
    }

    {
        testsTaken++;
        const int TEST_ITERATIONS = 1000;
        QString testName = "Performance - findClosestEntity() "+ QString::number(TEST_ITERATIONS) + " times";
        if (verbose) {
            qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
        }

        float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
        quint64 start = usecTimestampNow();
        const EntityItem* foundEntityByRadius = NULL;
        for (int i = 0; i < TEST_ITERATIONS; i++) {        
            foundEntityByRadius = tree.findClosestEntity(positionAtCenterInTreeUnits, targetRadius);
        }
        quint64 end = usecTimestampNow();
        
        if (verbose) {
            qDebug() << "foundEntityByRadius=" << foundEntityByRadius;
        }

        bool passed = foundEntityByRadius;
        if (passed) {
            testsPassed++;
        } else {
            testsFailed++;
            qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName);
        }
        float USECS_PER_MSECS = 1000.0f;
        float elapsedInMSecs = (float)(end - start) / USECS_PER_MSECS;
        qDebug() << "TIME - Test" << testsTaken <<":" << qPrintable(testName) << "elapsed=" << elapsedInMSecs << "msecs";
    }

    {
        testsTaken++;
        const int TEST_ITERATIONS = 1000;
        QString testName = "Performance - findEntityByID() "+ QString::number(TEST_ITERATIONS) + " times";
        if (verbose) {
            qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
        }

        quint64 start = usecTimestampNow();
        const EntityItem* foundEntityByID = NULL;
        for (int i = 0; i < TEST_ITERATIONS; i++) {
            // TODO: does this need to be updated??
            foundEntityByID = tree.findEntityByEntityItemID(entityID);
        }
        quint64 end = usecTimestampNow();
        
        if (verbose) {
            qDebug() << "foundEntityByID=" << foundEntityByID;
        }

        bool passed = foundEntityByID;
        if (passed) {
            testsPassed++;
        } else {
            testsFailed++;
            qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName);
        }
        float USECS_PER_MSECS = 1000.0f;
        float elapsedInMSecs = (float)(end - start) / USECS_PER_MSECS;
        qDebug() << "TIME - Test" << testsTaken <<":" << qPrintable(testName) << "elapsed=" << elapsedInMSecs << "msecs";
    }

    {
        // seed the random number generator so that our tests are reproducible
        srand(0xFEEDBEEF);
    
        testsTaken++;
        const int TEST_ITERATIONS = 1000;
        QString testName = "Performance - add entity to tree " + QString::number(TEST_ITERATIONS) + " times";
        if (verbose) {
            qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
        }

        int iterationsPassed = 0;
        quint64 totalElapsedAdd = 0;
        quint64 totalElapsedFind = 0;
        for (int i = 0; i < TEST_ITERATIONS; i++) {        
            QUuid id = QUuid::createUuid();// make sure it doesn't collide with previous entity ids
            EntityItemID entityID(id);
            entityID.isKnownID = false; // this is a temporary workaround to allow local tree entities to be added with known IDs

            float randomX = randFloatInRange(1.0f ,(float)TREE_SCALE - 1.0f);
            float randomY = randFloatInRange(1.0f ,(float)TREE_SCALE - 1.0f);
            float randomZ = randFloatInRange(1.0f ,(float)TREE_SCALE - 1.0f);
            glm::vec3 randomPositionInMeters(randomX,randomY,randomZ);
            glm::vec3 randomPositionInTreeUnits = randomPositionInMeters / (float)TREE_SCALE;

            properties.setPosition(randomPositionInMeters);
            
            // TODO: fix these unit tests
            //properties.setRadius(halfMeter);
            //properties.setModelURL("http://s3.amazonaws.com/hifi-public/ozan/theater.fbx");

            if (extraVerbose) {
                qDebug() << "iteration:" << i
                      << "ading entity at x/y/z=" << randomX << "," << randomY << "," << randomZ;
                qDebug() << "before:" << i << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
            }

            quint64 startAdd = usecTimestampNow();
            tree.addEntity(entityID, properties);
            quint64 endAdd = usecTimestampNow();
            totalElapsedAdd += (endAdd - startAdd);

            if (extraVerbose) {
                qDebug() << "after:" << i << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
            }

            quint64 startFind = usecTimestampNow();
            float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
            const EntityItem* foundEntityByRadius = tree.findClosestEntity(randomPositionInTreeUnits, targetRadius);
            const EntityItem* foundEntityByID = tree.findEntityByEntityItemID(entityID);
            quint64 endFind = usecTimestampNow();
            totalElapsedFind += (endFind - startFind);

            EntityTreeElement* containingElement = tree.getContainingElement(entityID);
            AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
            
            bool elementIsBestFit = containingElement->bestFitEntityBounds(foundEntityByID);
            
            if (extraVerbose) {
                qDebug() << "foundEntityByRadius=" << foundEntityByRadius;
                qDebug() << "foundEntityByID=" << foundEntityByID;
                qDebug() << "containingElement=" << containingElement;
                qDebug() << "containingElement.box=" 
                    << elementCube.getCorner().x * TREE_SCALE << "," 
                    << elementCube.getCorner().y * TREE_SCALE << ","
                    << elementCube.getCorner().z * TREE_SCALE << ":" 
                    << elementCube.getScale() * TREE_SCALE;
                qDebug() << "elementCube.getScale()=" << elementCube.getScale();
                //containingElement->printDebugDetails("containingElement");
                qDebug() << "elementIsBestFit=" << elementIsBestFit;
            }
            
            // Every 1000th test, show the size of the tree...
            if (extraVerbose && (i % 1000 == 0)) {
                qDebug() << "after test:" << i << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
            }

            bool passed = foundEntityByRadius && foundEntityByID && (foundEntityByRadius == foundEntityByID) && elementIsBestFit;
            if (passed) {
              iterationsPassed++;
            } else {
                if (extraVerbose) {
                    qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName) << "iteration:" << i
                          << "foundEntityByRadius=" << foundEntityByRadius << "foundEntityByID=" << foundEntityByID
                          << "x/y/z=" << randomX << "," << randomY << "," << randomZ
                          << "elementIsBestFit=" << elementIsBestFit;
                }
            }
        }

        if (extraVerbose) {
            qDebug() << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
        }
        
        bool passed = iterationsPassed == TEST_ITERATIONS;
        if (passed) {
            testsPassed++;
        } else {
            testsFailed++;
            qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName);
        }
        float USECS_PER_MSECS = 1000.0f;
        float elapsedInMSecsAdd = (float)(totalElapsedAdd) / USECS_PER_MSECS;
        float elapsedInMSecsFind = (float)(totalElapsedFind) / USECS_PER_MSECS;
        qDebug() << "TIME - Test" << testsTaken <<":" << qPrintable(testName) 
                        << "elapsed Add=" << elapsedInMSecsAdd << "msecs"
                        << "elapsed Find=" << elapsedInMSecsFind << "msecs";
    }

    {
        testsTaken++;
        const int TEST_ITERATIONS = 1000;
        QString testName = "Performance - delete entity from tree " + QString::number(TEST_ITERATIONS) + " times";
        if (verbose) {
            qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
        }

        int iterationsPassed = 0;
        quint64 totalElapsedDelete = 0;
        quint64 totalElapsedFind = 0;
        for (int i = 0; i < TEST_ITERATIONS; i++) {        
            QUuid id = QUuid::createUuid();// make sure it doesn't collide with previous entity ids
            EntityItemID entityID(id);
            entityID.isKnownID = true; // this is a temporary workaround to allow local tree entities to be added with known IDs

            if (extraVerbose) {
                qDebug() << "before:" << i << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
            }

            quint64 startDelete = usecTimestampNow();
            tree.deleteEntity(entityID);
            quint64 endDelete = usecTimestampNow();
            totalElapsedDelete += (endDelete - startDelete);

            if (extraVerbose) {
                qDebug() << "after:" << i << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
            }

            quint64 startFind = usecTimestampNow();
            const EntityItem* foundEntityByID = tree.findEntityByEntityItemID(entityID);
            quint64 endFind = usecTimestampNow();
            totalElapsedFind += (endFind - startFind);

            EntityTreeElement* containingElement = tree.getContainingElement(entityID);
            
            if (extraVerbose) {
                qDebug() << "foundEntityByID=" << foundEntityByID;
                qDebug() << "containingElement=" << containingElement;
            }
            
            // Every 1000th test, show the size of the tree...
            if (extraVerbose && (i % 1000 == 0)) {
                qDebug() << "after test:" << i << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
            }

            bool passed = foundEntityByID == NULL && containingElement == NULL;
            if (passed) {
              iterationsPassed++;
            } else {
                if (extraVerbose) {
                    qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName) << "iteration:" << i
                          << "foundEntityByID=" << foundEntityByID
                          << "containingElement=" << containingElement;
                }
            }
        }

        if (extraVerbose) {
            qDebug() << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
        }
        
        bool passed = iterationsPassed == TEST_ITERATIONS;
        if (passed) {
            testsPassed++;
        } else {
            testsFailed++;
            qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName);
        }
        float USECS_PER_MSECS = 1000.0f;
        float elapsedInMSecsDelete = (float)(totalElapsedDelete) / USECS_PER_MSECS;
        float elapsedInMSecsFind = (float)(totalElapsedFind) / USECS_PER_MSECS;
        qDebug() << "TIME - Test" << testsTaken <<":" << qPrintable(testName) 
                        << "elapsed Delete=" << elapsedInMSecsDelete << "msecs"
                        << "elapsed Find=" << elapsedInMSecsFind << "msecs";
    }


    {
        testsTaken++;
        const int TEST_ITERATIONS = 100;
        const int ENTITIES_PER_ITERATION = 10;
        QString testName = "Performance - delete " + QString::number(ENTITIES_PER_ITERATION) 
                            + " entities from tree " + QString::number(TEST_ITERATIONS) + " times";
        if (verbose) {
            qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
        }

        int iterationsPassed = 0;
        quint64 totalElapsedDelete = 0;
        quint64 totalElapsedFind = 0;
        for (int i = 0; i < TEST_ITERATIONS; i++) {        

            QSet<EntityItemID> entitiesToDelete;
            for (int j = 0; j < ENTITIES_PER_ITERATION; j++) {        
                //uint32_t id = 2 + (i * ENTITIES_PER_ITERATION) + j; // These are the entities we added above
                QUuid id = QUuid::createUuid();// make sure it doesn't collide with previous entity ids
                EntityItemID entityID(id);
                entitiesToDelete << entityID;
            }

            if (extraVerbose) {
                qDebug() << "before:" << i << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
            }

            quint64 startDelete = usecTimestampNow();
            tree.deleteEntities(entitiesToDelete);
            quint64 endDelete = usecTimestampNow();
            totalElapsedDelete += (endDelete - startDelete);

            if (extraVerbose) {
                qDebug() << "after:" << i << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
            }

            quint64 startFind = usecTimestampNow();
            for (int j = 0; j < ENTITIES_PER_ITERATION; j++) {        
                //uint32_t id = 2 + (i * ENTITIES_PER_ITERATION) + j; // These are the entities we added above
                QUuid id = QUuid::createUuid();// make sure it doesn't collide with previous entity ids
                EntityItemID entityID(id);
                const EntityItem* foundEntityByID = tree.findEntityByEntityItemID(entityID);
                EntityTreeElement* containingElement = tree.getContainingElement(entityID);

                if (extraVerbose) {
                    qDebug() << "foundEntityByID=" << foundEntityByID;
                    qDebug() << "containingElement=" << containingElement;
                }
                bool passed = foundEntityByID == NULL && containingElement == NULL;
                if (passed) {
                  iterationsPassed++;
                } else {
                    if (extraVerbose) {
                        qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName) << "iteration:" << i
                              << "foundEntityByID=" << foundEntityByID
                              << "containingElement=" << containingElement;
                    }
                }

            }

            quint64 endFind = usecTimestampNow();
            totalElapsedFind += (endFind - startFind);
        }

        if (extraVerbose) {
            qDebug() << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
        }
        
        bool passed = iterationsPassed == (TEST_ITERATIONS * ENTITIES_PER_ITERATION);
        if (passed) {
            testsPassed++;
        } else {
            testsFailed++;
            qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName);
        }
        float USECS_PER_MSECS = 1000.0f;
        float elapsedInMSecsDelete = (float)(totalElapsedDelete) / USECS_PER_MSECS;
        float elapsedInMSecsFind = (float)(totalElapsedFind) / USECS_PER_MSECS;
        qDebug() << "TIME - Test" << testsTaken <<":" << qPrintable(testName) 
                        << "elapsed Delete=" << elapsedInMSecsDelete << "msecs"
                        << "elapsed Find=" << elapsedInMSecsFind << "msecs";
    }

    qDebug() << "   tests passed:" << testsPassed << "out of" << testsTaken;
    if (verbose) {
        qDebug() << "******************************************************************************************";
    }
}
	AACube< Point >::AACube(unsigned int box, const AACube< Point > &cube)
	{
		cube.SubCube(box, this);
	}
示例#20
0
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 = (_entityItems->size() == 0);

        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
            } else {
                if (child->hasEntities()) {
                    entityTreeElementExtraEncodeData->childCompleted[i] = false;
                } else {
                    entityTreeElementExtraEncodeData->childCompleted[i] = true; // if the child doesn't have enities, it is completed
                }
            }
        }
        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<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;
    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++) {
            EntityItem* 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.viewFrustum) {
            
                // 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.
                AACube entityCube = entity->getMaximumAACube();
                entityCube.scale(TREE_SCALE);
                if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) {
                    includeThisEntity = false; // out of view, don't include it
                }
            }
        
            if (includeThisEntity) {
                indexesOfEntitiesToInclude << i;
                numberOfEntities++;
            }
        }
    }

    int numberOfEntitiesOffset = packetData->getUncompressedByteOffset();
    bool successAppendEntityCount = packetData->appendValue(numberOfEntities);

    if (successAppendEntityCount) {
        foreach (uint16_t i, indexesOfEntitiesToInclude) {
            EntityItem* 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 {
示例#21
0
OctreeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const {
    const glm::vec3& bottomNearRight = box.getCorner();
    glm::vec3 topFarLeft = box.calcTopFarLeft();

    int lookUp = ((_position.x < bottomNearRight.x)     )   //  1 = right      |   compute 6-bit
               + ((_position.x > topFarLeft.x     ) << 1)   //  2 = left       |         code to
               + ((_position.y < bottomNearRight.y) << 2)   //  4 = bottom     | classify camera
               + ((_position.y > topFarLeft.y     ) << 3)   //  8 = top        | with respect to
               + ((_position.z < bottomNearRight.z) << 4)   // 16 = front/near |  the 6 defining
               + ((_position.z > topFarLeft.z     ) << 5);  // 32 = back/far   |          planes

    int vertexCount = hullVertexLookup[lookUp][0];  //look up number of vertices

    OctreeProjectedPolygon projectedPolygon(vertexCount);

    bool pointInView = true;
    bool allPointsInView = false; // assume the best, but wait till we know we have a vertex
    bool anyPointsInView = false; // assume the worst!
    if (vertexCount) {
        allPointsInView = true; // assume the best!
        for(int i = 0; i < vertexCount; i++) {
            int vertexNum = hullVertexLookup[lookUp][i+1];
            glm::vec3 point = box.getVertex((BoxVertex)vertexNum);
            glm::vec2 projectedPoint = projectPoint(point, pointInView);
            allPointsInView = allPointsInView && pointInView;
            anyPointsInView = anyPointsInView || pointInView;
            projectedPolygon.setVertex(i, projectedPoint);
        }

        /***
        // Now that we've got the polygon, if it extends beyond the clipping window, then let's clip it
        // NOTE: This clipping does not improve our overall performance. It basically causes more polygons to
        // end up in the same quad/half and so the polygon lists get longer, and that's more calls to polygon.occludes()
        if ( (projectedPolygon.getMaxX() > PolygonClip::RIGHT_OF_CLIPPING_WINDOW ) ||
             (projectedPolygon.getMaxY() > PolygonClip::TOP_OF_CLIPPING_WINDOW   ) ||
             (projectedPolygon.getMaxX() < PolygonClip::LEFT_OF_CLIPPING_WINDOW  ) ||
             (projectedPolygon.getMaxY() < PolygonClip::BOTTOM_OF_CLIPPING_WINDOW) ) {

            CoverageRegion::_clippedPolygons++;

            glm::vec2* clippedVertices;
            int        clippedVertexCount;
            PolygonClip::clipToScreen(projectedPolygon.getVertices(), vertexCount, clippedVertices, clippedVertexCount);

            // Now reset the vertices of our projectedPolygon object
            projectedPolygon.setVertexCount(clippedVertexCount);
            for(int i = 0; i < clippedVertexCount; i++) {
                projectedPolygon.setVertex(i, clippedVertices[i]);
            }
            delete[] clippedVertices;

            lookUp += PROJECTION_CLIPPED;
        }
        ***/
    }
    // set the distance from our camera position, to the closest vertex
    float distance = glm::distance(getPosition(), box.calcCenter());
    projectedPolygon.setDistance(distance);
    projectedPolygon.setAnyInView(anyPointsInView);
    projectedPolygon.setAllInView(allPointsInView);
    projectedPolygon.setProjectionType(lookUp); // remember the projection type
    return projectedPolygon;
}
示例#22
0
void EntityItem::recalculateCollisionShape() {
    AACube entityAACube = getMinimumAACube();
    entityAACube.scale(TREE_SCALE); // scale to meters
    _collisionShape.setTranslation(entityAACube.calcCenter());
    _collisionShape.setScale(entityAACube.getScale());
}