EntitySimulation takes EntityItem* rather than ID

This commit is contained in:
Andrew Meadows 2014-11-26 12:12:43 -08:00
parent 0d98555740
commit dd3a7b9b9d
10 changed files with 162 additions and 292 deletions

View file

@ -48,9 +48,6 @@ void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEnt
details.cube = details.containingElement->getAACube(); details.cube = details.containingElement->getAACube();
_entitiesToDelete << details; _entitiesToDelete << details;
_lookingCount++; _lookingCount++;
_tree->trackDeletedEntity(searchEntityID);
// before deleting any entity make sure to remove it from our Mortal, Changing, and Moving lists
_tree->removeEntityFromSimulationLists(searchEntityID);
} }
} }
} }
@ -78,13 +75,9 @@ bool DeleteEntityOperator::preRecursion(OctreeElement* element) {
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element); EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
// In Pre-recursion, we're generally deciding whether or not we want to recurse this // In Pre-recursion, we're generally deciding whether or not we want to recurse this
// path of the tree. For this operation, we want to recurse the branch of the tree if // path of the tree. For this operation, we want to recurse the branch of the tree if:
// and of the following are true: // * We have not yet found the all entities, and
// * We have not yet found the old entity, and this branch contains our old entity // * this branch contains our some of the entities we're looking for.
// * We have not yet found the new entity, and this branch contains our new entity
//
// Note: it's often the case that the branch in question contains both the old entity
// and the new entity.
bool keepSearching = false; // assume we don't need to search any more bool keepSearching = false; // assume we don't need to search any more
@ -100,6 +93,8 @@ bool DeleteEntityOperator::preRecursion(OctreeElement* element) {
if (entityTreeElement == details.containingElement) { if (entityTreeElement == details.containingElement) {
EntityItemID entityItemID = details.entity->getEntityItemID(); EntityItemID entityItemID = details.entity->getEntityItemID();
EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID); // find the actual entity EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID); // find the actual entity
assert(theEntity);
_tree->trackDeletedEntity(theEntity);
entityTreeElement->removeEntityItem(theEntity); // remove it from the element entityTreeElement->removeEntityItem(theEntity); // remove it from the element
_tree->setContainingElement(entityItemID, NULL); // update or id to element lookup _tree->setContainingElement(entityItemID, NULL); // update or id to element lookup
delete theEntity; // now actually delete the entity! delete theEntity; // now actually delete the entity!

View file

@ -0,0 +1,20 @@
//
// EntitySimulation.cpp
// libraries/entities/src
//
// Created by Andrew Meadows on 2014.11.24
// Copyright 2014 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 "EntitySimulation.h"
void EntitySimulation::setEntityTree(EntityTree* tree) {
if (_entityTree && _entityTree != tree) {
clearEntities();
}
_entityTree = tree;
}

View file

@ -14,24 +14,36 @@
#include <QSet> #include <QSet>
#include "EntityItemID.h"
#include "EntityTree.h" #include "EntityTree.h"
class EntitySimulation { class EntitySimulation {
public: public:
EntitySimulation(EntityTree* tree) : _myTree(tree) { assert(tree); } EntitySimulation() : _entityTree(NULL) { }
virtual ~EntitySimulation() { _myTree = NULL; } virtual ~EntitySimulation() {}
/// \sideeffect For each EntityItem* that EntitySimulation puts on entitiesToDelete it will automatically /// \param tree pointer to EntityTree which is stored internally
/// removeEntity() on any internal lists -- no need to call removeEntity() for that one later. virtual void setEntityTree(EntityTree* tree);
virtual void update(QSet<EntityItemID>& entitiesToDelete) = 0;
/// \param[out] entitiesToDelete list of entities removed from simulation and should be deleted.
virtual void update(QSet<EntityItem*>& entitiesToDelete) = 0;
/// \param entity pointer to EntityItem to add to the simulation
/// \sideeffect the EntityItem::_simulationState member may be updated to indicate membership to internal list
virtual void addEntity(EntityItem* entity) = 0; virtual void addEntity(EntityItem* entity) = 0;
/// \param entity pointer to EntityItem to removed from the simulation
/// \sideeffect the EntityItem::_simulationState member may be updated to indicate non-membership to internal list
virtual void removeEntity(EntityItem* entity) = 0; virtual void removeEntity(EntityItem* entity) = 0;
virtual void updateEntity(EntityItem* entity) = 0;
/// \param entity pointer to EntityItem to that may have changed in a way that would affect its simulation
virtual void entityChanged(EntityItem* entity) = 0;
virtual void clearEntities() = 0;
EntityTree* getEntityTree() { return _entityTree; }
protected: protected:
EntityTree* _myTree; EntityTree* _entityTree;
}; };
#endif // hifi_EntitySimulation_h #endif // hifi_EntitySimulation_h

View file

@ -12,13 +12,14 @@
#include <PerfStat.h> #include <PerfStat.h>
#include "EntityTree.h" #include "EntityTree.h"
#include "EntitySimulation.h"
#include "AddEntityOperator.h" #include "AddEntityOperator.h"
#include "DeleteEntityOperator.h" #include "DeleteEntityOperator.h"
#include "MovingEntitiesOperator.h" #include "MovingEntitiesOperator.h"
#include "UpdateEntityOperator.h" #include "UpdateEntityOperator.h"
EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage) { EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), _simulation(NULL) {
_rootElement = createNewElement(); _rootElement = createNewElement();
} }
@ -34,14 +35,14 @@ EntityTreeElement* EntityTree::createNewElement(unsigned char * octalCode) {
void EntityTree::eraseAllOctreeElements(bool createNewRoot) { void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
// this would be a good place to clean up our entities... // this would be a good place to clean up our entities...
if (_simulation) {
_simulation->clearEntities();
}
foreach (EntityTreeElement* element, _entityToElementMap) { foreach (EntityTreeElement* element, _entityToElementMap) {
element->cleanupEntities(); element->cleanupEntities();
} }
_entityToElementMap.clear(); _entityToElementMap.clear();
Octree::eraseAllOctreeElements(createNewRoot); Octree::eraseAllOctreeElements(createNewRoot);
_movingEntities.clear();
_mortalEntities.clear();
_changedEntities.clear();
} }
bool EntityTree::handlesEditPacketType(PacketType packetType) const { bool EntityTree::handlesEditPacketType(PacketType packetType) const {
@ -89,13 +90,14 @@ void EntityTree::addEntityItem(EntityItem* entityItem) {
recurseTreeWithOperator(&theOperator); recurseTreeWithOperator(&theOperator);
// check to see if we need to simulate this entity.. // check to see if we need to simulate this entity..
updateEntityState(entityItem); if (_simulation) {
_simulation->addEntity(entityItem);
}
_isDirty = true; _isDirty = true;
} }
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
// You should not call this on existing entities that are already part of the tree! Call updateEntity()
EntityTreeElement* containingElement = getContainingElement(entityID); EntityTreeElement* containingElement = getContainingElement(entityID);
if (!containingElement) { if (!containingElement) {
qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID; qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
@ -119,6 +121,9 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
UpdateEntityOperator theOperator(this, containingElement, existingEntity, tempProperties); UpdateEntityOperator theOperator(this, containingElement, existingEntity, tempProperties);
recurseTreeWithOperator(&theOperator); recurseTreeWithOperator(&theOperator);
_isDirty = true; _isDirty = true;
if (_simulation && existingEntity->getUpdateFlags() != 0) {
_simulation->entityChanged(existingEntity);
}
} }
} }
} else { } else {
@ -129,13 +134,14 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
recurseTreeWithOperator(&theOperator); recurseTreeWithOperator(&theOperator);
_isDirty = true; _isDirty = true;
updateEntityState(existingEntity); if (_simulation && existingEntity->getUpdateFlags() != 0) {
_simulation->entityChanged(existingEntity);
}
QString entityScriptAfter = existingEntity->getScript(); QString entityScriptAfter = existingEntity->getScript();
if (entityScriptBefore != entityScriptAfter) { if (entityScriptBefore != entityScriptAfter) {
emitEntityScriptChanging(entityID); // the entity script has changed emitEntityScriptChanging(entityID); // the entity script has changed
} }
} }
containingElement = getContainingElement(entityID); containingElement = getContainingElement(entityID);
@ -179,13 +185,16 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem
} }
void EntityTree::trackDeletedEntity(const EntityItemID& entityID) { void EntityTree::trackDeletedEntity(EntityItem* entity) {
if (_simulation) {
_simulation->removeEntity(entity);
}
// this is only needed on the server to send delete messages for recently deleted entities to the viewers // this is only needed on the server to send delete messages for recently deleted entities to the viewers
if (getIsServer()) { if (getIsServer()) {
// set up the deleted entities ID // set up the deleted entities ID
quint64 deletedAt = usecTimestampNow(); quint64 deletedAt = usecTimestampNow();
_recentlyDeletedEntitiesLock.lockForWrite(); _recentlyDeletedEntitiesLock.lockForWrite();
_recentlyDeletedEntityItemIDs.insert(deletedAt, entityID.id); _recentlyDeletedEntityItemIDs.insert(deletedAt, entity->getEntityItemID().id);
_recentlyDeletedEntitiesLock.unlock(); _recentlyDeletedEntitiesLock.unlock();
} }
} }
@ -198,6 +207,19 @@ void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) {
emit entityScriptChanging(entityItemID); emit entityScriptChanging(entityItemID);
} }
void EntityTree::setSimulation(EntitySimulation* simulation) {
if (simulation) {
// assert that the simulation's backpointer has already been properly connected
assert(simulation->getEntityTree() == this);
}
if (_simulation && _simulation != simulation) {
// It's important to clearEntities() on the simulation since taht will update each
// EntityItem::_simulationState correctly so as to not confuse the next _simulation.
_simulation->clearEntities();
}
_simulation = simulation;
}
void EntityTree::deleteEntity(const EntityItemID& entityID) { void EntityTree::deleteEntity(const EntityItemID& entityID) {
emit deletingEntity(entityID); emit deletingEntity(entityID);
@ -220,29 +242,6 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs) {
_isDirty = true; _isDirty = true;
} }
void EntityTree::removeEntityFromSimulationLists(const EntityItemID& entityID) {
EntityItem* theEntity = findEntityByEntityItemID(entityID);
if (theEntity) {
// make sure to remove it from any of our simulation lists
EntityItem::SimulationState theState = theEntity->getSimulationState();
switch (theState) {
case EntityItem::Moving:
_movingEntities.removeAll(theEntity);
break;
case EntityItem::Mortal:
_mortalEntities.removeAll(theEntity);
break;
default:
break;
}
_changedEntities.remove(theEntity);
}
}
/// This method is used to find and fix entity IDs that are shifting from creator token based to known ID based entity IDs. /// This method is used to find and fix entity IDs that are shifting from creator token based to known ID based entity IDs.
/// This should only be used on a client side (viewing) tree. The typical usage is that a local editor has been creating /// This should only be used on a client side (viewing) tree. The typical usage is that a local editor has been creating
/// entities in the local tree, those entities have creatorToken based entity IDs. But those entity edits are also sent up to /// entities in the local tree, those entities have creatorToken based entity IDs. But those entity edits are also sent up to
@ -575,183 +574,29 @@ void EntityTree::releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncod
extraEncodeData->clear(); extraEncodeData->clear();
} }
void EntityTree::updateEntityState(EntityItem* entity) {
EntityItem::SimulationState oldState = entity->getSimulationState();
EntityItem::SimulationState newState = entity->computeSimulationState();
if (newState != oldState) {
switch (oldState) {
case EntityItem::Moving:
_movingEntities.removeAll(entity);
break;
case EntityItem::Mortal:
_mortalEntities.removeAll(entity);
break;
default:
break;
}
switch (newState) {
case EntityItem::Moving:
_movingEntities.push_back(entity);
break;
case EntityItem::Mortal:
_mortalEntities.push_back(entity);
break;
default:
break;
}
entity->setSimulationState(newState);
}
}
void EntityTree::clearEntityState(EntityItem* entity) {
EntityItem::SimulationState oldState = entity->getSimulationState();
switch (oldState) {
case EntityItem::Moving:
_movingEntities.removeAll(entity);
break;
case EntityItem::Mortal:
_mortalEntities.removeAll(entity);
break;
default:
break;
}
entity->setSimulationState(EntityItem::Static);
}
void EntityTree::entityChanged(EntityItem* entity) { void EntityTree::entityChanged(EntityItem* entity) {
_changedEntities.insert(entity); if (_simulation) {
_simulation->entityChanged(entity);
}
} }
void EntityTree::update() { void EntityTree::update() {
// our new strategy should be to segregate entities into three classes: if (_simulation) {
// 1) stationary things that are not changing - most models lockForWrite();
// 2) mortal things - these are stationary but have a lifetime - then need to be checked, QSet<EntityItem*> entitiesToDelete;
// can be touched linearly, and won't change the tree _simulation->update(entitiesToDelete);
// 2) changing things - like things animating they can be touched linearly and they don't change the tree if (entitiesToDelete.size() > 0) {
// 3) moving things - these need to scan the tree and update accordingly // translate into list of ID's
// finally - all things that need to be deleted, can be handled on a single delete pass. QSet<EntityItemID> idsToDelete;
// foreach (EntityItem* entity, entitiesToDelete) {
// TODO: theoretically we could combine the move and delete tree passes... idsToDelete.insert(entity->getEntityItemID());
lockForWrite();
quint64 now = usecTimestampNow();
QSet<EntityItemID> entitiesToDelete;
updateChangedEntities(now, entitiesToDelete);
updateMovingEntities(now, entitiesToDelete);
updateMortalEntities(now, entitiesToDelete);
if (entitiesToDelete.size() > 0) {
deleteEntities(entitiesToDelete);
}
unlock();
}
void EntityTree::updateChangedEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
foreach (EntityItem* thisEntity, _changedEntities) {
// check to see if the lifetime has expired, for immortal entities this is always false
if (thisEntity->lifetimeHasExpired()) {
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
entitiesToDelete << thisEntity->getEntityItemID();
clearEntityState(thisEntity);
} else {
updateEntityState(thisEntity);
}
thisEntity->clearUpdateFlags();
}
_changedEntities.clear();
}
void EntityTree::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
PerformanceTimer perfTimer("updateMovingEntities");
if (_movingEntities.size() > 0) {
MovingEntitiesOperator moveOperator(this);
{
PerformanceTimer perfTimer("_movingEntities");
QList<EntityItem*>::iterator item_itr = _movingEntities.begin();
while (item_itr != _movingEntities.end()) {
EntityItem* thisEntity = *item_itr;
// always check to see if the lifetime has expired, for immortal entities this is always false
if (thisEntity->lifetimeHasExpired()) {
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
entitiesToDelete << thisEntity->getEntityItemID();
// remove thisEntity from the list
item_itr = _movingEntities.erase(item_itr);
thisEntity->setSimulationState(EntityItem::Static);
} else {
AACube oldCube = thisEntity->getMaximumAACube();
thisEntity->update(now);
AACube newCube = thisEntity->getMaximumAACube();
// check to see if this movement has sent the entity outside of the domain.
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
if (!domainBounds.touches(newCube)) {
qDebug() << "Entity " << thisEntity->getEntityItemID() << " moved out of domain bounds.";
entitiesToDelete << thisEntity->getEntityItemID();
// remove thisEntity from the list
item_itr = _movingEntities.erase(item_itr);
thisEntity->setSimulationState(EntityItem::Static);
} else {
moveOperator.addEntityToMoveList(thisEntity, oldCube, newCube);
EntityItem::SimulationState newState = thisEntity->computeSimulationState();
if (newState != EntityItem::Moving) {
if (newState == EntityItem::Mortal) {
_mortalEntities.push_back(thisEntity);
}
// remove thisEntity from the list
item_itr = _movingEntities.erase(item_itr);
thisEntity->setSimulationState(newState);
} else {
++item_itr;
}
}
}
} }
deleteEntities(idsToDelete);
} }
if (moveOperator.hasMovingEntities()) { unlock();
PerformanceTimer perfTimer("recurseTreeWithOperator");
recurseTreeWithOperator(&moveOperator);
}
} }
} }
void EntityTree::updateMortalEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
QList<EntityItem*>::iterator item_itr = _mortalEntities.begin();
while (item_itr != _mortalEntities.end()) {
EntityItem* thisEntity = *item_itr;
thisEntity->update(now);
// always check to see if the lifetime has expired, for immortal entities this is always false
if (thisEntity->lifetimeHasExpired()) {
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
entitiesToDelete << thisEntity->getEntityItemID();
// remove thisEntity from the list
item_itr = _mortalEntities.erase(item_itr);
thisEntity->setSimulationState(EntityItem::Static);
} else {
// check to see if this entity is no longer moving
EntityItem::SimulationState newState = thisEntity->computeSimulationState();
if (newState != EntityItem::Mortal) {
if (newState == EntityItem::Moving) {
_movingEntities.push_back(thisEntity);
}
// remove thisEntity from the list
item_itr = _mortalEntities.erase(item_itr);
thisEntity->setSimulationState(newState);
} else {
++item_itr;
}
}
}
}
bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) {
// we can probably leverage the ordered nature of QMultiMap to do this quickly... // we can probably leverage the ordered nature of QMultiMap to do this quickly...
bool hasSomethingNewer = false; bool hasSomethingNewer = false;
@ -966,19 +811,16 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID) /*const*/ { EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID) /*const*/ {
// TODO: do we need to make this thread safe? Or is it acceptable as is // TODO: do we need to make this thread safe? Or is it acceptable as is
if (_entityToElementMap.contains(entityItemID)) { EntityTreeElement* element = _entityToElementMap.value(entityItemID);
return _entityToElementMap.value(entityItemID); if (!element && entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN){
} else if (entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN){
// check the creator token version too... // check the creator token version too...
EntityItemID creatorTokenOnly; EntityItemID creatorTokenOnly;
creatorTokenOnly.id = UNKNOWN_ENTITY_ID; creatorTokenOnly.id = UNKNOWN_ENTITY_ID;
creatorTokenOnly.creatorTokenID = entityItemID.creatorTokenID; creatorTokenOnly.creatorTokenID = entityItemID.creatorTokenID;
creatorTokenOnly.isKnownID = false; creatorTokenOnly.isKnownID = false;
if (_entityToElementMap.contains(creatorTokenOnly)) { element = _entityToElementMap.value(creatorTokenOnly);
return _entityToElementMap.value(creatorTokenOnly);
}
} }
return NULL; return element;
} }
// TODO: do we need to make this thread safe? Or is it acceptable as is // TODO: do we need to make this thread safe? Or is it acceptable as is

View file

@ -19,6 +19,7 @@
class Model; class Model;
class EntitySimulation;
class NewlyCreatedEntityHook { class NewlyCreatedEntityHook {
public: public:
@ -84,7 +85,7 @@ public:
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties); bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
void deleteEntity(const EntityItemID& entityID); void deleteEntity(const EntityItemID& entityID);
void deleteEntities(QSet<EntityItemID> entityIDs); void deleteEntities(QSet<EntityItemID> entityIDs);
void removeEntityFromSimulationLists(const EntityItemID& entityID); void removeEntityFromSimulation(EntityItem* entity);
const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius); const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius);
EntityItem* findEntityByID(const QUuid& id); EntityItem* findEntityByID(const QUuid& id);
@ -137,18 +138,15 @@ public:
void sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z); void sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z);
void updateEntityState(EntityItem* entity);
void clearEntityState(EntityItem* entity);
void entityChanged(EntityItem* entity); void entityChanged(EntityItem* entity);
void trackDeletedEntity(const EntityItemID& entityID); void trackDeletedEntity(EntityItem* entity);
void emitAddingEntity(const EntityItemID& entityItemID); void emitAddingEntity(const EntityItemID& entityItemID);
void emitEntityScriptChanging(const EntityItemID& entityItemID); void emitEntityScriptChanging(const EntityItemID& entityItemID);
QList<EntityItem*>& getMovingEntities() { return _movingEntities; } void setSimulation(EntitySimulation* simulation);
signals: signals:
void deletingEntity(const EntityItemID& entityID); void deletingEntity(const EntityItemID& entityID);
void addingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID);
@ -157,10 +155,6 @@ signals:
private: private:
void updateChangedEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete);
void updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete);
void updateMortalEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete);
static bool findNearPointOperation(OctreeElement* element, void* extraData); static bool findNearPointOperation(OctreeElement* element, void* extraData);
static bool findInSphereOperation(OctreeElement* element, void* extraData); static bool findInSphereOperation(OctreeElement* element, void* extraData);
static bool findInCubeOperation(OctreeElement* element, void* extraData); static bool findInCubeOperation(OctreeElement* element, void* extraData);
@ -176,11 +170,7 @@ private:
EntityItemFBXService* _fbxService; EntityItemFBXService* _fbxService;
QHash<EntityItemID, EntityTreeElement*> _entityToElementMap; QHash<EntityItemID, EntityTreeElement*> _entityToElementMap;
EntitySimulation* _simulation;
QList<EntityItem*> _movingEntities; // entities that need to be updated
QList<EntityItem*> _mortalEntities; // entities that need to be checked for expiry
QSet<EntityItem*> _changedEntities; // entities that have changed in the last frame
}; };
#endif // hifi_EntityTree_h #endif // hifi_EntityTree_h

View file

@ -768,7 +768,6 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
addEntityItem(entityItem); // add this new entity to this elements entities addEntityItem(entityItem); // add this new entity to this elements entities
entityItemID = entityItem->getEntityItemID(); entityItemID = entityItem->getEntityItemID();
_myTree->setContainingElement(entityItemID, this); _myTree->setContainingElement(entityItemID, this);
_myTree->updateEntityState(entityItem);
_myTree->emitAddingEntity(entityItemID); // we just added an entity _myTree->emitAddingEntity(entityItemID); // we just added an entity
} }
} }

View file

@ -16,7 +16,7 @@
#include "MovingEntitiesOperator.h" #include "MovingEntitiesOperator.h"
#include "SimpleEntitySimulation.h" #include "SimpleEntitySimulation.h"
void SimpleEntitySimulation::update(QSet<EntityItemID>& entitiesToDelete) { void SimpleEntitySimulation::update(QSet<EntityItem*>& entitiesToDelete) {
quint64 now = usecTimestampNow(); quint64 now = usecTimestampNow();
updateChangedEntities(now, entitiesToDelete); updateChangedEntities(now, entitiesToDelete);
updateMovingEntities(now, entitiesToDelete); updateMovingEntities(now, entitiesToDelete);
@ -56,21 +56,32 @@ void SimpleEntitySimulation::removeEntity(EntityItem* entity) {
default: default:
break; break;
} }
entity->setSimulationState(EntityItem::Static);
_changedEntities.remove(entity); _changedEntities.remove(entity);
} }
void SimpleEntitySimulation::updateEntity(EntityItem* entity) { void SimpleEntitySimulation::entityChanged(EntityItem* entity) {
assert(entity); assert(entity);
// we'll deal with thsi change later // we batch all changes and deal with them in updateChangedEntities()
_changedEntities.insert(entity); _changedEntities.insert(entity);
} }
void SimpleEntitySimulation::clearEntities() {
foreach (EntityItem* entity, _changedEntities) {
entity->clearUpdateFlags();
entity->setSimulationState(EntityItem::Static);
}
_changedEntities.clear();
_movingEntities.clear();
_mortalEntities.clear();
}
void SimpleEntitySimulation::updateChangedEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) { void SimpleEntitySimulation::updateChangedEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete) {
foreach (EntityItem* entity, _changedEntities) { foreach (EntityItem* entity, _changedEntities) {
// check to see if the lifetime has expired, for immortal entities this is always false // check to see if the lifetime has expired, for immortal entities this is always false
if (entity->lifetimeHasExpired()) { if (entity->lifetimeHasExpired()) {
qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID(); qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID();
entitiesToDelete << entity->getEntityItemID(); entitiesToDelete.insert(entity);
clearEntityState(entity); clearEntityState(entity);
} else { } else {
updateEntityState(entity); updateEntityState(entity);
@ -80,44 +91,44 @@ void SimpleEntitySimulation::updateChangedEntities(quint64 now, QSet<EntityItemI
_changedEntities.clear(); _changedEntities.clear();
} }
void SimpleEntitySimulation::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) { void SimpleEntitySimulation::updateMovingEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete) {
if (_movingEntities.size() > 0) { if (_entityTree && _movingEntities.size() > 0) {
PerformanceTimer perfTimer("_movingEntities"); PerformanceTimer perfTimer("_movingEntities");
MovingEntitiesOperator moveOperator(_myTree); MovingEntitiesOperator moveOperator(_entityTree);
QList<EntityItem*>::iterator item_itr = _movingEntities.begin(); QList<EntityItem*>::iterator item_itr = _movingEntities.begin();
while (item_itr != _movingEntities.end()) { while (item_itr != _movingEntities.end()) {
EntityItem* thisEntity = *item_itr; EntityItem* entity = *item_itr;
// always check to see if the lifetime has expired, for immortal entities this is always false // always check to see if the lifetime has expired, for immortal entities this is always false
if (thisEntity->lifetimeHasExpired()) { if (entity->lifetimeHasExpired()) {
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID(); qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID();
entitiesToDelete << thisEntity->getEntityItemID(); entitiesToDelete.insert(entity);
// remove thisEntity from the list // remove entity from the list
item_itr = _movingEntities.erase(item_itr); item_itr = _movingEntities.erase(item_itr);
thisEntity->setSimulationState(EntityItem::Static); entity->setSimulationState(EntityItem::Static);
} else { } else {
AACube oldCube = thisEntity->getMaximumAACube(); AACube oldCube = entity->getMaximumAACube();
thisEntity->update(now); entity->update(now);
AACube newCube = thisEntity->getMaximumAACube(); AACube newCube = entity->getMaximumAACube();
// check to see if this movement has sent the entity outside of the domain. // check to see if this movement has sent the entity outside of the domain.
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f); AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
if (!domainBounds.touches(newCube)) { if (!domainBounds.touches(newCube)) {
qDebug() << "Entity " << thisEntity->getEntityItemID() << " moved out of domain bounds."; qDebug() << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
entitiesToDelete << thisEntity->getEntityItemID(); entitiesToDelete.insert(entity);
// remove thisEntity from the list // remove entity from the list
item_itr = _movingEntities.erase(item_itr); item_itr = _movingEntities.erase(item_itr);
thisEntity->setSimulationState(EntityItem::Static); entity->setSimulationState(EntityItem::Static);
} else { } else {
moveOperator.addEntityToMoveList(thisEntity, oldCube, newCube); moveOperator.addEntityToMoveList(entity, oldCube, newCube);
EntityItem::SimulationState newState = thisEntity->computeSimulationState(); EntityItem::SimulationState newState = entity->computeSimulationState();
if (newState != EntityItem::Moving) { if (newState != EntityItem::Moving) {
if (newState == EntityItem::Mortal) { if (newState == EntityItem::Mortal) {
_mortalEntities.push_back(thisEntity); _mortalEntities.push_back(entity);
} }
// remove thisEntity from the list // remove entity from the list
item_itr = _movingEntities.erase(item_itr); item_itr = _movingEntities.erase(item_itr);
thisEntity->setSimulationState(newState); entity->setSimulationState(newState);
} else { } else {
++item_itr; ++item_itr;
} }
@ -126,33 +137,33 @@ void SimpleEntitySimulation::updateMovingEntities(quint64 now, QSet<EntityItemID
} }
if (moveOperator.hasMovingEntities()) { if (moveOperator.hasMovingEntities()) {
PerformanceTimer perfTimer("recurseTreeWithOperator"); PerformanceTimer perfTimer("recurseTreeWithOperator");
_myTree->recurseTreeWithOperator(&moveOperator); _entityTree->recurseTreeWithOperator(&moveOperator);
} }
} }
} }
void SimpleEntitySimulation::updateMortalEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) { void SimpleEntitySimulation::updateMortalEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete) {
QList<EntityItem*>::iterator item_itr = _mortalEntities.begin(); QList<EntityItem*>::iterator item_itr = _mortalEntities.begin();
while (item_itr != _mortalEntities.end()) { while (item_itr != _mortalEntities.end()) {
EntityItem* thisEntity = *item_itr; EntityItem* entity = *item_itr;
thisEntity->update(now);
// always check to see if the lifetime has expired, for immortal entities this is always false // always check to see if the lifetime has expired, for immortal entities this is always false
if (thisEntity->lifetimeHasExpired()) { if (entity->lifetimeHasExpired()) {
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID(); qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID();
entitiesToDelete << thisEntity->getEntityItemID(); entitiesToDelete.insert(entity);
// remove thisEntity from the list // remove entity from the list
item_itr = _mortalEntities.erase(item_itr); item_itr = _mortalEntities.erase(item_itr);
thisEntity->setSimulationState(EntityItem::Static); entity->setSimulationState(EntityItem::Static);
} else { } else {
// check to see if this entity is no longer moving // check to see if this entity is no longer moving
EntityItem::SimulationState newState = thisEntity->computeSimulationState(); EntityItem::SimulationState newState = entity->computeSimulationState();
if (newState != EntityItem::Mortal) { if (newState != EntityItem::Mortal) {
if (newState == EntityItem::Moving) { if (newState == EntityItem::Moving) {
_movingEntities.push_back(thisEntity); entity->update(now);
_movingEntities.push_back(entity);
} }
// remove thisEntity from the list // remove entity from the list
item_itr = _mortalEntities.erase(item_itr); item_itr = _mortalEntities.erase(item_itr);
thisEntity->setSimulationState(newState); entity->setSimulationState(newState);
} else { } else {
++item_itr; ++item_itr;
} }

View file

@ -18,29 +18,30 @@
class SimpleEntitySimulation : public EntitySimulation { class SimpleEntitySimulation : public EntitySimulation {
public: public:
SimpleEntitySimulation(EntityTree* tree) : EntitySimulation(tree) { } SimpleEntitySimulation() : EntitySimulation() { }
virtual ~SimpleEntitySimulation() { } virtual ~SimpleEntitySimulation() { setEntityTree(NULL); }
virtual void update(QSet<EntityItemID>& entitiesToDelete); virtual void update(QSet<EntityItem*>& entitiesToDelete);
virtual void addEntity(EntityItem* entity); virtual void addEntity(EntityItem* entity);
virtual void removeEntity(EntityItem* entity); virtual void removeEntity(EntityItem* entity);
virtual void updateEntity(EntityItem* entity); virtual void entityChanged(EntityItem* entity);
private: virtual void clearEntities();
protected:
void updateEntityState(EntityItem* entity); void updateEntityState(EntityItem* entity);
void clearEntityState(EntityItem* entity); void clearEntityState(EntityItem* entity);
QList<EntityItem*>& getMovingEntities() { return _movingEntities; } QList<EntityItem*>& getMovingEntities() { return _movingEntities; }
void updateChangedEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete); void updateChangedEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete);
void updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete); void updateMovingEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete);
void updateMortalEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete); void updateMortalEntities(quint64 now, QSet<EntityItem*>& entitiesToDelete);
private: QSet<EntityItem*> _changedEntities; // entities that have changed in the last frame
QList<EntityItem*> _movingEntities; // entities that need to be updated QList<EntityItem*> _movingEntities; // entities that need to be updated
QList<EntityItem*> _mortalEntities; // non-moving entities that need to be checked for expiry QList<EntityItem*> _mortalEntities; // non-moving entities that need to be checked for expiry
QSet<EntityItem*> _changedEntities; // entities that have changed in the last frame
}; };
#endif // hifi_SimpleEntitySimulation_h #endif // hifi_SimpleEntitySimulation_h