EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID entityID) {
    EntityItemProperties results;
    EntityItemID identity = identifyEntity(entityID);
    if (_entityTree) {
        _entityTree->lockForRead();
        EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(identity));
        
        if (entity) {
            results = entity->getProperties();

            // TODO: improve sitting points and naturalDimensions in the future, 
            //       for now we've included the old sitting points model behavior for entity types that are models
            //        we've also added this hack for setting natural dimensions of models
            if (entity->getType() == EntityTypes::Model) {
                const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
                if (geometry) {
                    results.setSittingPoints(geometry->sittingPoints);
                    Extents meshExtents = geometry->getUnscaledMeshExtents();
                    results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
                }
            }

        } else {
            results.setIsUnknownID();
        }
        _entityTree->unlock();
    }
    
    return results;
}
Beispiel #2
0
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();
        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; // HAS ENTITIES NEEDS ENCODING
                } else {
                    entityTreeElementExtraEncodeData->childCompleted[i] = true; // 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));
        }
        
        // TODO: some of these inserts might be redundant!!!
        extraEncodeData->insert(this, entityTreeElementExtraEncodeData);
    }
}
Beispiel #3
0
// private
void EntitySimulation::sortEntitiesThatMoved() {
    // NOTE: this is only for entities that have been moved by THIS EntitySimulation.
    // External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
    PerformanceTimer perfTimer("sortingEntities");
    MovingEntitiesOperator moveOperator(_entityTree);
    AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), (float)TREE_SCALE);
    QSet<EntityItem*>::iterator itemItr = _entitiesToBeSorted.begin();
    while (itemItr != _entitiesToBeSorted.end()) {
        EntityItem* entity = *itemItr;
        // check to see if this movement has sent the entity outside of the domain.
        AACube newCube = entity->getMaximumAACube();
        if (!domainBounds.touches(newCube)) {
            qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
            _entitiesToDelete.insert(entity);
            _mortalEntities.remove(entity);
            _updateableEntities.remove(entity);
            removeEntityInternal(entity);
            itemItr = _entitiesToBeSorted.erase(itemItr);
        } else {
            moveOperator.addEntityToMoveList(entity, newCube);
            ++itemItr;
        }
    }
    if (moveOperator.hasMovingEntities()) {
        PerformanceTimer perfTimer("recurseTreeWithOperator");
        _entityTree->recurseTreeWithOperator(&moveOperator);
    }

    sortEntitiesThatMovedInternal();
    _entitiesToBeSorted.clear();
}
Beispiel #4
0
void Map::DestroyBlock(int i, int j)
{
	if (GetBlock(i, j) == 0)
		return;
	int id = blocks[i][j].Id;

	SetBlock(i, j, 0);

	EntityItem* item = new EntityItem(Block::GetBlockById(id)->GetDrop());
	item->SetPosition(glm::vec2(i + rand() % 500 / 1000.0, j + rand() % 500 / 1000.0));
	drops.push_back(item);
	entities.push_back(item);
}
Beispiel #5
0
// private
void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
    PerformanceTimer perfTimer("updatingEntities");
    QSet<EntityItem*>::iterator itemItr = _updateableEntities.begin();
    while (itemItr != _updateableEntities.end()) {
        EntityItem* entity = *itemItr;
        // TODO: catch transition from needing update to not as a "change" 
        // so we don't have to scan for it here.
        if (!entity->needsToCallUpdate()) {
            itemItr = _updateableEntities.erase(itemItr);
        } else {
            entity->update(now);
            ++itemItr;
        }
    }
}
EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const EntityItemProperties& properties) {
    EntityItemID actualID = entityID;
    // if the entity is unknown, attempt to look it up
    if (!entityID.isKnownID) {
        actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
        if (actualID.id != UNKNOWN_ENTITY_ID) {
            entityID.id = actualID.id;
            entityID.isKnownID = true;
        }
    }
    
    // If we have a local entity tree set, then also update it. We can do this even if we don't know
    // the actual id, because we can edit out local entities just with creatorTokenID
    if (_entityTree) {
        _entityTree->lockForWrite();
        _entityTree->updateEntity(entityID, properties);
        _entityTree->unlock();
    }

    // if at this point, we know the id, send the update to the entity server
    if (entityID.isKnownID) {
        // make sure the properties has a type, so that the encode can know which properties to include
        if (properties.getType() == EntityTypes::Unknown) {
            EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
            if (entity) {
                EntityItemProperties tempProperties = properties;
                tempProperties.setType(entity->getType());
                queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, tempProperties);
                return entityID;
            }
        }
        
        // if the properties already includes the type, then use it as is
        queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties);
    }
    
    return entityID;
}
void EntityScriptingInterface::deleteEntity(EntityItemID entityID) {

    EntityItemID actualID = entityID;
    
    // if the entity is unknown, attempt to look it up
    if (!entityID.isKnownID) {
        actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
        if (actualID.id != UNKNOWN_ENTITY_ID) {
            entityID.id = actualID.id;
            entityID.isKnownID = true;
        }
    }

    bool shouldDelete = true;

    // If we have a local entity tree set, then also update it.
    if (_entityTree) {
        _entityTree->lockForWrite();

        EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(actualID));
        if (entity) {
            if (entity->getLocked()) {
                shouldDelete = false;
            } else {
                _entityTree->deleteEntity(entityID);
            }
        }

        _entityTree->unlock();
    }

    // if at this point, we know the id, and we should still delete the entity, send the update to the entity server
    if (shouldDelete && entityID.isKnownID) {
        getEntityPacketSender()->queueEraseEntityMessage(entityID);
    }
}
Beispiel #8
0
// private
void EntitySimulation::expireMortalEntities(const quint64& now) {
    if (now > _nextExpiry) {
        // only search for expired entities if we expect to find one
        _nextExpiry = quint64(-1);
        QSet<EntityItem*>::iterator itemItr = _mortalEntities.begin();
        while (itemItr != _mortalEntities.end()) {
            EntityItem* entity = *itemItr;
            quint64 expiry = entity->getExpiry();
            if (expiry < now) {
                _entitiesToDelete.insert(entity);
                itemItr = _mortalEntities.erase(itemItr);
                _updateableEntities.remove(entity);
                _entitiesToBeSorted.remove(entity);
                removeEntityInternal(entity);
            } else {
                if (expiry < _nextExpiry) {
                    // remeber the smallest _nextExpiry so we know when to start the next search
                    _nextExpiry = expiry;
                }
                ++itemItr;
            }
        }
    }
}
Beispiel #9
0
void OctreeTests::modelItemTests() {
#if 0 // TODO - repair/replace these
    bool verbose = true;

    //verbose = true;
    EntityTreeElementExtraEncodeData modelTreeElementExtraEncodeData;
    int testsTaken = 0;
    int testsPassed = 0;
    int testsFailed = 0;

    if (verbose) {
        qDebug() << "******************************************************************************************";
    }
    
    qDebug() << "OctreeTests::modelItemTests()";

    EncodeBitstreamParams params;
    OctreePacketData packetData;
    EntityItem entityItem;
    
    entityItem.setID(1042);
    entityItem.setModelURL("http://foo.com/foo.fbx");

    bool appendResult = entityItem.appendEntityData(&packetData, params, &modelTreeElementExtraEncodeData);
    int bytesWritten = packetData.getUncompressedSize();
    if (verbose) {
        qDebug() << "Test 1: bytesRead == bytesWritten ...";
        qDebug() << "appendResult=" << appendResult;
        qDebug() << "bytesWritten=" << bytesWritten;
    }

    {
        ReadBitstreamToTreeParams args;
        EntityItem modelItemFromBuffer;
        const unsigned char* data = packetData.getUncompressedData();
        int bytesLeftToRead = packetData.getUncompressedSize();
    
        int bytesRead =  modelItemFromBuffer.readEntityDataFromBuffer(data, bytesLeftToRead, args);
        if (verbose) {
            qDebug() << "bytesRead=" << bytesRead;
            qDebug() << "modelItemFromBuffer.getID()=" << modelItemFromBuffer.getID();
            qDebug() << "modelItemFromBuffer.getModelURL()=" << modelItemFromBuffer.getModelURL();
        }

        QCOMPARE(bytesRead, bytesWritten);

        if (verbose) {
            qDebug() << "Test 2: modelItemFromBuffer.getModelURL() == 'http://foo.com/foo.fbx'";
        }
    
        QCOMPARE(modelItemFromBuffer.getModelURL(), "http://foo.com/foo.fbx");
    }
    
    // TEST 3:
    // Reset the packet, fill it with data so that EntityItem header won't fit, and verify that we don't let it fit
    {
        packetData.reset();
        int remainingSpace = 10;
        int almostFullOfData = MAX_OCTREE_UNCOMRESSED_PACKET_SIZE - remainingSpace;
        QByteArray garbageData(almostFullOfData, 0);
        packetData.appendValue(garbageData);

        appendResult = entityItem.appendEntityData(&packetData, params, &modelTreeElementExtraEncodeData);
        bytesWritten = packetData.getUncompressedSize() - almostFullOfData;
        if (verbose) {
            qDebug() << "Test 3: attempt to appendEntityData in nearly full packetData ...";
            qDebug() << "appendResult=" << appendResult;
            qDebug() << "bytesWritten=" << bytesWritten;
        }
        QCOMPARE(appendResult, false);
        QCOMPARE(bytesWritten, 0);
    }
    
    // TEST 4:
    // Reset the packet, fill it with data so that some of EntityItem won't fit, and verify that we write what can fit
    {
        packetData.reset();
        int remainingSpace = 50;
        int almostFullOfData = MAX_OCTREE_UNCOMRESSED_PACKET_SIZE - remainingSpace;
        QByteArray garbageData(almostFullOfData, 0);
        packetData.appendValue(garbageData);

        appendResult = entityItem.appendEntityData(&packetData, params, &modelTreeElementExtraEncodeData);
        bytesWritten = packetData.getUncompressedSize() - almostFullOfData;
        if (verbose) {
            qDebug() << "Test 4: attempt to appendEntityData in nearly full packetData which some should fit ...";
            qDebug() << "appendResult=" << appendResult;
            qDebug() << "bytesWritten=" << bytesWritten;
        }
        
        QCOMPARE(appendResult, true);

        ReadBitstreamToTreeParams args;
        EntityItem modelItemFromBuffer;
        const unsigned char* data = packetData.getUncompressedData() + almostFullOfData;
        int bytesLeftToRead = packetData.getUncompressedSize() - almostFullOfData;
    
        int bytesRead =  modelItemFromBuffer.readEntityDataFromBuffer(data, bytesLeftToRead, args);
        if (verbose) {
            qDebug() << "Test 5: partial EntityItem written ... bytesRead == bytesWritten...";
            qDebug() << "bytesRead=" << bytesRead;
            qDebug() << "modelItemFromBuffer.getID()=" << modelItemFromBuffer.getID();
            qDebug() << "modelItemFromBuffer.getModelURL()=" << modelItemFromBuffer.getModelURL();
        }

        QCOMPARE(bytesRead, bytesWritten);

        if (verbose) {
            qDebug() << "Test 6: partial EntityItem written ... getModelURL() NOT SET ...";
        }
    
        QCOMPARE(modelItemFromBuffer.getModelURL(), "");
    }
    
    if (verbose) {
        qDebug() << "******************************************************************************************";
    }
    
    QCOMPARE(testsPassed, testsTaken);
    if (verbose) {
        qDebug() << "******************************************************************************************";
    }
    
#endif 
}
void TreeWidgetController::reinitialize()
{
    QProgressDialog dlg(tr("Reinitializing view..."), QString(), 0, 3, this);

    // Perform a BFS and remember the expanded entity items.
    map<DatabaseEntity::EntityType, set<int> > expandedEntities;
    queue<QTreeWidgetItem *> q;
    q.push(invisibleRootItem());
    while (!q.empty()) {
        QTreeWidgetItem *currentItem = q.front();
        q.pop();
        
        // Don't bother with non-expanded items.
        if (currentItem != invisibleRootItem() && !currentItem->isExpanded())
            continue;
        
        // Enqueue the current item's children.
        for (int i = 0; i < currentItem->childCount(); i++)
            q.push(currentItem->child(i));
        
        // Don't bother with non-entity types.
        if (currentItem->type() != EntityItem::EntityType)
            continue;
        
        EntityItem *entityItem = static_cast<EntityItem *>(currentItem);
        if (entityItem->type() != DatabaseEntity::Feature) {
            const int id = getEntityID(entityItem->entityPtr());
            set<int> &s = expandedEntities[entityItem->entityPtr()->entityType()];
            s.insert(id);
        }
    }
    
    // Backup the expansion-states of the root items.
    const bool expandResponses = _rootResponses->isExpanded();
    const bool expandLabels = _rootLabels->isExpanded();
    const bool expandProcesses = _rootProcesses->isExpanded();
    const bool expandClassificationObjects = 
        _rootClassificationObjects->isExpanded();
    
    // Clear the model.
    if (_rootResponses)
        invisibleRootItem()->removeChild(_rootResponses);
    if (_rootLabels)
        invisibleRootItem()->removeChild(_rootLabels);
    if (_rootProcesses)
        invisibleRootItem()->removeChild(_rootProcesses);
    if (_rootClassificationObjects)
        invisibleRootItem()->removeChild(_rootClassificationObjects);
    dlg.setValue(1);
    
    // Reload.
    initializeView();
    dlg.setValue(2);
    
    // Perform a BFS and reopen the formerly expanded items.
    q.push(invisibleRootItem());
    while (!q.empty()) {
        QTreeWidgetItem *currentItem = q.front();
        q.pop();
        
        // Enqueue the current item's children.
        for (int i = 0; i < currentItem->childCount(); i++)
            q.push(currentItem->child(i));
        
        // Don't bother with non-entity types.
        if (currentItem->type() != EntityItem::EntityType)
            continue;
        
        // Determine the entity's id and reexpand this item if this id is stored
        // in the entity type's corresponding set of expanded ids.
        EntityItem *entityItem = static_cast<EntityItem *>(currentItem);
        const int id = getEntityID(entityItem->entityPtr());
        if (id) {
            set<int> &s = expandedEntities[entityItem->entityPtr()->entityType()];
            if (s.find(id) != s.end()) {
                QTreeWidgetItem *item = entityItem;
                while (item && item != invisibleRootItem()) {
                    item->setExpanded(true);
                    item = item->parent();
                }
            }
        }
    }
    dlg.setValue(3);
    
    // Restore the expansion-states of the root items.
    if (expandResponses && !_rootResponses->isExpanded())
        _rootResponses->setExpanded(true);
    if (expandLabels && !_rootLabels->isExpanded())
        _rootLabels->setExpanded(true);
    if (expandProcesses && !_rootProcesses->isExpanded())
        _rootProcesses->setExpanded(true);
    if (expandClassificationObjects && !_rootClassificationObjects->isExpanded())
        _rootClassificationObjects->setExpanded(true);
}
Beispiel #11
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 {