From e574c5766b566a2d6abc0a2d07df8c11cac88711 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Nov 2014 22:28:27 -0800 Subject: [PATCH 1/9] EntityItem::setSimulationState() now public --- libraries/entities/src/EntityItem.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 3f63c96c4e..c1b1f243ef 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -292,16 +292,10 @@ public: uint32_t getUpdateFlags() const { return _updateFlags; } void clearUpdateFlags() { _updateFlags = 0; } -#ifdef USE_BULLET_PHYSICS - EntityMotionState* getMotionState() const { return _motionState; } - virtual EntityMotionState* createMotionState() { return NULL; } - void destroyMotionState(); -#endif // USE_BULLET_PHYSICS SimulationState getSimulationState() const { return _simulationState; } -protected: - friend class EntityTree; void setSimulationState(SimulationState state) { _simulationState = state; } +protected: virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init virtual void recalculateCollisionShape(); From 0d9855574065f6ad4a392d22a1a7936f11896aa0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Nov 2014 22:29:24 -0800 Subject: [PATCH 2/9] add EntitySimulation and SimpleEntitySimulation --- libraries/entities/src/EntitySimulation.h | 37 +++ .../entities/src/SimpleEntitySimulation.cpp | 213 ++++++++++++++++++ .../entities/src/SimpleEntitySimulation.h | 46 ++++ 3 files changed, 296 insertions(+) create mode 100644 libraries/entities/src/EntitySimulation.h create mode 100644 libraries/entities/src/SimpleEntitySimulation.cpp create mode 100644 libraries/entities/src/SimpleEntitySimulation.h diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h new file mode 100644 index 0000000000..230e744a16 --- /dev/null +++ b/libraries/entities/src/EntitySimulation.h @@ -0,0 +1,37 @@ +// +// EntitySimulation.h +// 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 +// + +#ifndef hifi_EntitySimulation_h +#define hifi_EntitySimulation_h + +#include + +#include "EntityItemID.h" +#include "EntityTree.h" + +class EntitySimulation { +public: + EntitySimulation(EntityTree* tree) : _myTree(tree) { assert(tree); } + virtual ~EntitySimulation() { _myTree = NULL; } + + /// \sideeffect For each EntityItem* that EntitySimulation puts on entitiesToDelete it will automatically + /// removeEntity() on any internal lists -- no need to call removeEntity() for that one later. + virtual void update(QSet& entitiesToDelete) = 0; + + virtual void addEntity(EntityItem* entity) = 0; + virtual void removeEntity(EntityItem* entity) = 0; + virtual void updateEntity(EntityItem* entity) = 0; + +protected: + EntityTree* _myTree; +}; + +#endif // hifi_EntitySimulation_h diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp new file mode 100644 index 0000000000..d8edfb8ba7 --- /dev/null +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -0,0 +1,213 @@ +// +// SimpleEntitySimulation.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 +#include + +#include "EntityItem.h" +#include "MovingEntitiesOperator.h" +#include "SimpleEntitySimulation.h" + +void SimpleEntitySimulation::update(QSet& entitiesToDelete) { + quint64 now = usecTimestampNow(); + updateChangedEntities(now, entitiesToDelete); + updateMovingEntities(now, entitiesToDelete); + updateMortalEntities(now, entitiesToDelete); +} + +void SimpleEntitySimulation::addEntity(EntityItem* entity) { + assert(entity && entity->getSimulationState() == EntityItem::Static); + EntityItem::SimulationState state = entity->computeSimulationState(); + switch(state) { + case EntityItem::Moving: + _movingEntities.push_back(entity); + entity->setSimulationState(state); + break; + case EntityItem::Mortal: + _mortalEntities.push_back(entity); + entity->setSimulationState(state); + break; + case EntityItem::Static: + default: + break; + } +} + +void SimpleEntitySimulation::removeEntity(EntityItem* entity) { + assert(entity); + // make sure to remove it from any of our simulation lists + EntityItem::SimulationState state = entity->getSimulationState(); + switch (state) { + case EntityItem::Moving: + _movingEntities.removeAll(entity); + break; + case EntityItem::Mortal: + _mortalEntities.removeAll(entity); + break; + + default: + break; + } + _changedEntities.remove(entity); +} + +void SimpleEntitySimulation::updateEntity(EntityItem* entity) { + assert(entity); + // we'll deal with thsi change later + _changedEntities.insert(entity); +} + +void SimpleEntitySimulation::updateChangedEntities(quint64 now, QSet& entitiesToDelete) { + foreach (EntityItem* entity, _changedEntities) { + // check to see if the lifetime has expired, for immortal entities this is always false + if (entity->lifetimeHasExpired()) { + qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID(); + entitiesToDelete << entity->getEntityItemID(); + clearEntityState(entity); + } else { + updateEntityState(entity); + } + entity->clearUpdateFlags(); + } + _changedEntities.clear(); +} + +void SimpleEntitySimulation::updateMovingEntities(quint64 now, QSet& entitiesToDelete) { + if (_movingEntities.size() > 0) { + PerformanceTimer perfTimer("_movingEntities"); + MovingEntitiesOperator moveOperator(_myTree); + QList::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; + } + } + } + } + if (moveOperator.hasMovingEntities()) { + PerformanceTimer perfTimer("recurseTreeWithOperator"); + _myTree->recurseTreeWithOperator(&moveOperator); + } + } +} + +void SimpleEntitySimulation::updateMortalEntities(quint64 now, QSet& entitiesToDelete) { + QList::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; + } + } + } +} + +void SimpleEntitySimulation::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 SimpleEntitySimulation::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); +} + + diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h new file mode 100644 index 0000000000..bc79bf9958 --- /dev/null +++ b/libraries/entities/src/SimpleEntitySimulation.h @@ -0,0 +1,46 @@ +// +// SimpleEntitySimulation.h +// 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 +// + +#ifndef hifi_SimpleEntitySimulation_h +#define hifi_SimpleEntitySimulation_h + +#include "EntitySimulation.h" + +/// provides simple velocity + gravity extrapolation of EntityItem's + +class SimpleEntitySimulation : public EntitySimulation { +public: + SimpleEntitySimulation(EntityTree* tree) : EntitySimulation(tree) { } + virtual ~SimpleEntitySimulation() { } + + virtual void update(QSet& entitiesToDelete); + + virtual void addEntity(EntityItem* entity); + virtual void removeEntity(EntityItem* entity); + virtual void updateEntity(EntityItem* entity); + +private: + void updateEntityState(EntityItem* entity); + void clearEntityState(EntityItem* entity); + + QList& getMovingEntities() { return _movingEntities; } + + void updateChangedEntities(quint64 now, QSet& entitiesToDelete); + void updateMovingEntities(quint64 now, QSet& entitiesToDelete); + void updateMortalEntities(quint64 now, QSet& entitiesToDelete); + +private: + QList _movingEntities; // entities that need to be updated + QList _mortalEntities; // non-moving entities that need to be checked for expiry + QSet _changedEntities; // entities that have changed in the last frame +}; + +#endif // hifi_SimpleEntitySimulation_h From dd3a7b9b9dc20c396a482973b973aaa741cf4d7d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 26 Nov 2014 12:12:43 -0800 Subject: [PATCH 3/9] EntitySimulation takes EntityItem* rather than ID --- .../src/entities}/EntityCollisionSystem.cpp | 0 .../src/entities}/EntityCollisionSystem.h | 0 .../entities/src/DeleteEntityOperator.cpp | 15 +- libraries/entities/src/EntitySimulation.cpp | 20 ++ libraries/entities/src/EntitySimulation.h | 28 +- libraries/entities/src/EntityTree.cpp | 258 ++++-------------- libraries/entities/src/EntityTree.h | 22 +- libraries/entities/src/EntityTreeElement.cpp | 1 - .../entities/src/SimpleEntitySimulation.cpp | 89 +++--- .../entities/src/SimpleEntitySimulation.h | 21 +- 10 files changed, 162 insertions(+), 292 deletions(-) rename {libraries/entities/src => interface/src/entities}/EntityCollisionSystem.cpp (100%) rename {libraries/entities/src => interface/src/entities}/EntityCollisionSystem.h (100%) create mode 100644 libraries/entities/src/EntitySimulation.cpp diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/interface/src/entities/EntityCollisionSystem.cpp similarity index 100% rename from libraries/entities/src/EntityCollisionSystem.cpp rename to interface/src/entities/EntityCollisionSystem.cpp diff --git a/libraries/entities/src/EntityCollisionSystem.h b/interface/src/entities/EntityCollisionSystem.h similarity index 100% rename from libraries/entities/src/EntityCollisionSystem.h rename to interface/src/entities/EntityCollisionSystem.h diff --git a/libraries/entities/src/DeleteEntityOperator.cpp b/libraries/entities/src/DeleteEntityOperator.cpp index 5b0ada4ec1..12441e5427 100644 --- a/libraries/entities/src/DeleteEntityOperator.cpp +++ b/libraries/entities/src/DeleteEntityOperator.cpp @@ -48,9 +48,6 @@ void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEnt details.cube = details.containingElement->getAACube(); _entitiesToDelete << details; _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(element); // 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 - // and of the following are true: - // * We have not yet found the old entity, and this branch contains our old entity - // * 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. + // path of the tree. For this operation, we want to recurse the branch of the tree if: + // * We have not yet found the all entities, and + // * this branch contains our some of the entities we're looking for. 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) { EntityItemID entityItemID = details.entity->getEntityItemID(); EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID); // find the actual entity + assert(theEntity); + _tree->trackDeletedEntity(theEntity); entityTreeElement->removeEntityItem(theEntity); // remove it from the element _tree->setContainingElement(entityItemID, NULL); // update or id to element lookup delete theEntity; // now actually delete the entity! diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp new file mode 100644 index 0000000000..8058c2f24e --- /dev/null +++ b/libraries/entities/src/EntitySimulation.cpp @@ -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; +} + diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index 230e744a16..770d6ebdb0 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -14,24 +14,36 @@ #include -#include "EntityItemID.h" #include "EntityTree.h" class EntitySimulation { public: - EntitySimulation(EntityTree* tree) : _myTree(tree) { assert(tree); } - virtual ~EntitySimulation() { _myTree = NULL; } + EntitySimulation() : _entityTree(NULL) { } + virtual ~EntitySimulation() {} - /// \sideeffect For each EntityItem* that EntitySimulation puts on entitiesToDelete it will automatically - /// removeEntity() on any internal lists -- no need to call removeEntity() for that one later. - virtual void update(QSet& entitiesToDelete) = 0; + /// \param tree pointer to EntityTree which is stored internally + virtual void setEntityTree(EntityTree* tree); + /// \param[out] entitiesToDelete list of entities removed from simulation and should be deleted. + virtual void update(QSet& 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; + + /// \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 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: - EntityTree* _myTree; + EntityTree* _entityTree; }; #endif // hifi_EntitySimulation_h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index bb201b6f86..5d4c5b0fc4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -12,13 +12,14 @@ #include #include "EntityTree.h" +#include "EntitySimulation.h" #include "AddEntityOperator.h" #include "DeleteEntityOperator.h" #include "MovingEntitiesOperator.h" #include "UpdateEntityOperator.h" -EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage) { +EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), _simulation(NULL) { _rootElement = createNewElement(); } @@ -34,14 +35,14 @@ EntityTreeElement* EntityTree::createNewElement(unsigned char * octalCode) { void EntityTree::eraseAllOctreeElements(bool createNewRoot) { // this would be a good place to clean up our entities... + if (_simulation) { + _simulation->clearEntities(); + } foreach (EntityTreeElement* element, _entityToElementMap) { element->cleanupEntities(); } _entityToElementMap.clear(); Octree::eraseAllOctreeElements(createNewRoot); - _movingEntities.clear(); - _mortalEntities.clear(); - _changedEntities.clear(); } bool EntityTree::handlesEditPacketType(PacketType packetType) const { @@ -89,13 +90,14 @@ void EntityTree::addEntityItem(EntityItem* entityItem) { recurseTreeWithOperator(&theOperator); // check to see if we need to simulate this entity.. - updateEntityState(entityItem); + if (_simulation) { + _simulation->addEntity(entityItem); + } _isDirty = true; } 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); if (!containingElement) { 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); recurseTreeWithOperator(&theOperator); _isDirty = true; + if (_simulation && existingEntity->getUpdateFlags() != 0) { + _simulation->entityChanged(existingEntity); + } } } } else { @@ -129,13 +134,14 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp recurseTreeWithOperator(&theOperator); _isDirty = true; - updateEntityState(existingEntity); + if (_simulation && existingEntity->getUpdateFlags() != 0) { + _simulation->entityChanged(existingEntity); + } QString entityScriptAfter = existingEntity->getScript(); if (entityScriptBefore != entityScriptAfter) { emitEntityScriptChanging(entityID); // the entity script has changed } - } 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 if (getIsServer()) { // set up the deleted entities ID quint64 deletedAt = usecTimestampNow(); _recentlyDeletedEntitiesLock.lockForWrite(); - _recentlyDeletedEntityItemIDs.insert(deletedAt, entityID.id); + _recentlyDeletedEntityItemIDs.insert(deletedAt, entity->getEntityItemID().id); _recentlyDeletedEntitiesLock.unlock(); } } @@ -198,6 +207,19 @@ void EntityTree::emitEntityScriptChanging(const EntityItemID& 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) { emit deletingEntity(entityID); @@ -220,29 +242,6 @@ void EntityTree::deleteEntities(QSet entityIDs) { _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 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 @@ -575,183 +574,29 @@ void EntityTree::releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncod 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) { - _changedEntities.insert(entity); + if (_simulation) { + _simulation->entityChanged(entity); + } } void EntityTree::update() { - // our new strategy should be to segregate entities into three classes: - // 1) stationary things that are not changing - most models - // 2) mortal things - these are stationary but have a lifetime - then need to be checked, - // can be touched linearly, and won't change the tree - // 2) changing things - like things animating they can be touched linearly and they don't change the tree - // 3) moving things - these need to scan the tree and update accordingly - // finally - all things that need to be deleted, can be handled on a single delete pass. - // - // TODO: theoretically we could combine the move and delete tree passes... - lockForWrite(); - quint64 now = usecTimestampNow(); - QSet entitiesToDelete; - updateChangedEntities(now, entitiesToDelete); - updateMovingEntities(now, entitiesToDelete); - updateMortalEntities(now, entitiesToDelete); - - if (entitiesToDelete.size() > 0) { - deleteEntities(entitiesToDelete); - } - unlock(); -} - -void EntityTree::updateChangedEntities(quint64 now, QSet& 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& entitiesToDelete) { - PerformanceTimer perfTimer("updateMovingEntities"); - if (_movingEntities.size() > 0) { - MovingEntitiesOperator moveOperator(this); - { - PerformanceTimer perfTimer("_movingEntities"); - - QList::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; - } - } - } + if (_simulation) { + lockForWrite(); + QSet entitiesToDelete; + _simulation->update(entitiesToDelete); + if (entitiesToDelete.size() > 0) { + // translate into list of ID's + QSet idsToDelete; + foreach (EntityItem* entity, entitiesToDelete) { + idsToDelete.insert(entity->getEntityItemID()); } + deleteEntities(idsToDelete); } - if (moveOperator.hasMovingEntities()) { - PerformanceTimer perfTimer("recurseTreeWithOperator"); - recurseTreeWithOperator(&moveOperator); - } + unlock(); } } -void EntityTree::updateMortalEntities(quint64 now, QSet& entitiesToDelete) { - QList::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) { // we can probably leverage the ordered nature of QMultiMap to do this quickly... bool hasSomethingNewer = false; @@ -966,19 +811,16 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID) /*const*/ { // TODO: do we need to make this thread safe? Or is it acceptable as is - if (_entityToElementMap.contains(entityItemID)) { - return _entityToElementMap.value(entityItemID); - } else if (entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN){ + EntityTreeElement* element = _entityToElementMap.value(entityItemID); + if (!element && entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN){ // check the creator token version too... EntityItemID creatorTokenOnly; creatorTokenOnly.id = UNKNOWN_ENTITY_ID; creatorTokenOnly.creatorTokenID = entityItemID.creatorTokenID; creatorTokenOnly.isKnownID = false; - if (_entityToElementMap.contains(creatorTokenOnly)) { - return _entityToElementMap.value(creatorTokenOnly); - } + element = _entityToElementMap.value(creatorTokenOnly); } - return NULL; + return element; } // TODO: do we need to make this thread safe? Or is it acceptable as is diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 7370ebadc6..db128e4563 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -19,6 +19,7 @@ class Model; +class EntitySimulation; class NewlyCreatedEntityHook { public: @@ -84,7 +85,7 @@ public: bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties); void deleteEntity(const EntityItemID& entityID); void deleteEntities(QSet entityIDs); - void removeEntityFromSimulationLists(const EntityItemID& entityID); + void removeEntityFromSimulation(EntityItem* entity); const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius); EntityItem* findEntityByID(const QUuid& id); @@ -137,18 +138,15 @@ public: 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 trackDeletedEntity(const EntityItemID& entityID); + void trackDeletedEntity(EntityItem* entity); void emitAddingEntity(const EntityItemID& entityItemID); void emitEntityScriptChanging(const EntityItemID& entityItemID); - QList& getMovingEntities() { return _movingEntities; } - + void setSimulation(EntitySimulation* simulation); + signals: void deletingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID); @@ -157,10 +155,6 @@ signals: private: - void updateChangedEntities(quint64 now, QSet& entitiesToDelete); - void updateMovingEntities(quint64 now, QSet& entitiesToDelete); - void updateMortalEntities(quint64 now, QSet& entitiesToDelete); - static bool findNearPointOperation(OctreeElement* element, void* extraData); static bool findInSphereOperation(OctreeElement* element, void* extraData); static bool findInCubeOperation(OctreeElement* element, void* extraData); @@ -176,11 +170,7 @@ private: EntityItemFBXService* _fbxService; QHash _entityToElementMap; - - QList _movingEntities; // entities that need to be updated - QList _mortalEntities; // entities that need to be checked for expiry - - QSet _changedEntities; // entities that have changed in the last frame + EntitySimulation* _simulation; }; #endif // hifi_EntityTree_h diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 2646cc0dfd..17f7456670 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -768,7 +768,6 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int addEntityItem(entityItem); // add this new entity to this elements entities entityItemID = entityItem->getEntityItemID(); _myTree->setContainingElement(entityItemID, this); - _myTree->updateEntityState(entityItem); _myTree->emitAddingEntity(entityItemID); // we just added an entity } } diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index d8edfb8ba7..b3316978a9 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -16,7 +16,7 @@ #include "MovingEntitiesOperator.h" #include "SimpleEntitySimulation.h" -void SimpleEntitySimulation::update(QSet& entitiesToDelete) { +void SimpleEntitySimulation::update(QSet& entitiesToDelete) { quint64 now = usecTimestampNow(); updateChangedEntities(now, entitiesToDelete); updateMovingEntities(now, entitiesToDelete); @@ -56,21 +56,32 @@ void SimpleEntitySimulation::removeEntity(EntityItem* entity) { default: break; } + entity->setSimulationState(EntityItem::Static); _changedEntities.remove(entity); } -void SimpleEntitySimulation::updateEntity(EntityItem* entity) { +void SimpleEntitySimulation::entityChanged(EntityItem* entity) { assert(entity); - // we'll deal with thsi change later + // we batch all changes and deal with them in updateChangedEntities() _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& entitiesToDelete) { +void SimpleEntitySimulation::updateChangedEntities(quint64 now, QSet& entitiesToDelete) { foreach (EntityItem* entity, _changedEntities) { // check to see if the lifetime has expired, for immortal entities this is always false if (entity->lifetimeHasExpired()) { qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID(); - entitiesToDelete << entity->getEntityItemID(); + entitiesToDelete.insert(entity); clearEntityState(entity); } else { updateEntityState(entity); @@ -80,44 +91,44 @@ void SimpleEntitySimulation::updateChangedEntities(quint64 now, QSet& entitiesToDelete) { - if (_movingEntities.size() > 0) { +void SimpleEntitySimulation::updateMovingEntities(quint64 now, QSet& entitiesToDelete) { + if (_entityTree && _movingEntities.size() > 0) { PerformanceTimer perfTimer("_movingEntities"); - MovingEntitiesOperator moveOperator(_myTree); + MovingEntitiesOperator moveOperator(_entityTree); QList::iterator item_itr = _movingEntities.begin(); 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 - if (thisEntity->lifetimeHasExpired()) { - qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID(); - entitiesToDelete << thisEntity->getEntityItemID(); - // remove thisEntity from the list + if (entity->lifetimeHasExpired()) { + qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID(); + entitiesToDelete.insert(entity); + // remove entity from the list item_itr = _movingEntities.erase(item_itr); - thisEntity->setSimulationState(EntityItem::Static); + entity->setSimulationState(EntityItem::Static); } else { - AACube oldCube = thisEntity->getMaximumAACube(); - thisEntity->update(now); - AACube newCube = thisEntity->getMaximumAACube(); + AACube oldCube = entity->getMaximumAACube(); + entity->update(now); + AACube newCube = entity->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 + qDebug() << "Entity " << entity->getEntityItemID() << " moved out of domain bounds."; + entitiesToDelete.insert(entity); + // remove entity from the list item_itr = _movingEntities.erase(item_itr); - thisEntity->setSimulationState(EntityItem::Static); + entity->setSimulationState(EntityItem::Static); } else { - moveOperator.addEntityToMoveList(thisEntity, oldCube, newCube); - EntityItem::SimulationState newState = thisEntity->computeSimulationState(); + moveOperator.addEntityToMoveList(entity, oldCube, newCube); + EntityItem::SimulationState newState = entity->computeSimulationState(); if (newState != EntityItem::Moving) { 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); - thisEntity->setSimulationState(newState); + entity->setSimulationState(newState); } else { ++item_itr; } @@ -126,33 +137,33 @@ void SimpleEntitySimulation::updateMovingEntities(quint64 now, QSetrecurseTreeWithOperator(&moveOperator); + _entityTree->recurseTreeWithOperator(&moveOperator); } } } -void SimpleEntitySimulation::updateMortalEntities(quint64 now, QSet& entitiesToDelete) { +void SimpleEntitySimulation::updateMortalEntities(quint64 now, QSet& entitiesToDelete) { QList::iterator item_itr = _mortalEntities.begin(); while (item_itr != _mortalEntities.end()) { - EntityItem* thisEntity = *item_itr; - thisEntity->update(now); + EntityItem* entity = *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 + if (entity->lifetimeHasExpired()) { + qDebug() << "Lifetime has expired for entity:" << entity->getEntityItemID(); + entitiesToDelete.insert(entity); + // remove entity from the list item_itr = _mortalEntities.erase(item_itr); - thisEntity->setSimulationState(EntityItem::Static); + entity->setSimulationState(EntityItem::Static); } else { // 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::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); - thisEntity->setSimulationState(newState); + entity->setSimulationState(newState); } else { ++item_itr; } diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h index bc79bf9958..7d0e8f0113 100644 --- a/libraries/entities/src/SimpleEntitySimulation.h +++ b/libraries/entities/src/SimpleEntitySimulation.h @@ -18,29 +18,30 @@ class SimpleEntitySimulation : public EntitySimulation { public: - SimpleEntitySimulation(EntityTree* tree) : EntitySimulation(tree) { } - virtual ~SimpleEntitySimulation() { } + SimpleEntitySimulation() : EntitySimulation() { } + virtual ~SimpleEntitySimulation() { setEntityTree(NULL); } - virtual void update(QSet& entitiesToDelete); + virtual void update(QSet& entitiesToDelete); virtual void addEntity(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 clearEntityState(EntityItem* entity); QList& getMovingEntities() { return _movingEntities; } - void updateChangedEntities(quint64 now, QSet& entitiesToDelete); - void updateMovingEntities(quint64 now, QSet& entitiesToDelete); - void updateMortalEntities(quint64 now, QSet& entitiesToDelete); + void updateChangedEntities(quint64 now, QSet& entitiesToDelete); + void updateMovingEntities(quint64 now, QSet& entitiesToDelete); + void updateMortalEntities(quint64 now, QSet& entitiesToDelete); -private: + QSet _changedEntities; // entities that have changed in the last frame QList _movingEntities; // entities that need to be updated QList _mortalEntities; // non-moving entities that need to be checked for expiry - QSet _changedEntities; // entities that have changed in the last frame }; #endif // hifi_SimpleEntitySimulation_h From cb6d9a4312fcad37625c4bce7ed66e9632c42215 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 26 Nov 2014 12:14:17 -0800 Subject: [PATCH 4/9] EntityServer gets a simuation --- assignment-client/src/entities/EntityServer.cpp | 10 +++++++++- assignment-client/src/entities/EntityServer.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 2b7d6873cc..f3c52f5895 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "EntityServer.h" #include "EntityServerConsts.h" @@ -20,7 +21,8 @@ const char* MODEL_SERVER_NAME = "Entity"; const char* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server"; const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo"; -EntityServer::EntityServer(const QByteArray& packet) : OctreeServer(packet) { +EntityServer::EntityServer(const QByteArray& packet) + : OctreeServer(packet), _entitySimulation(NULL) { // nothing special to do here... } @@ -36,6 +38,12 @@ OctreeQueryNode* EntityServer::createOctreeQueryNode() { Octree* EntityServer::createTree() { EntityTree* tree = new EntityTree(true); tree->addNewlyCreatedHook(this); + if (!_entitySimulation) { + SimpleEntitySimulation* simpleSimulation = new SimpleEntitySimulation(); + simpleSimulation->setEntityTree(tree); + tree->setSimulation(simpleSimulation); + _entitySimulation = simpleSimulation; + } return tree; } diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 563efed288..14fd26b775 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -47,6 +47,7 @@ public slots: void pruneDeletedEntities(); private: + EntitySimulation* _entitySimulation; }; #endif // hifi_EntityServer_h From cda8ac85c108e9fed76a359cebf8c512386e2d42 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 26 Nov 2014 12:15:24 -0800 Subject: [PATCH 5/9] EntityCollisionSystem moves to interface and interface connects EntityCollision system to EntityTree --- interface/src/Application.cpp | 14 ++++-- interface/src/Application.h | 2 +- .../src/entities/EntityCollisionSystem.cpp | 48 +++++++++++-------- .../src/entities/EntityCollisionSystem.h | 17 +++---- 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 58b28fcef9..f2663829e4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -441,6 +441,7 @@ void Application::aboutToQuit() { } Application::~Application() { + _entities.getTree()->setSimulation(NULL); qInstallMessageHandler(NULL); saveSettings(); @@ -1968,7 +1969,9 @@ void Application::init() { _entities.init(); _entities.setViewFrustum(getViewFrustum()); - _entityCollisionSystem.init(&_entityEditSender, _entities.getTree(), _voxels.getTree(), &_audio, &_avatarManager); + EntityTree* entityTree = _entities.getTree(); + _entityCollisionSystem.init(&_entityEditSender, entityTree, _voxels.getTree(), &_audio, &_avatarManager); + entityTree->setSimulation(&_entityCollisionSystem); // connect the _entityCollisionSystem to our script engine's EntityScriptingInterface connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithVoxel, @@ -2319,11 +2322,12 @@ void Application::update(float deltaTime) { if (!_aboutToQuit) { PerformanceTimer perfTimer("entities"); + // NOTE: the _entities.update() call below will wait for lock + // and will simulate entity motion (the EntityTree has been given an EntitySimulation). _entities.update(); // update the models... - { - PerformanceTimer perfTimer("collisions"); - _entityCollisionSystem.update(); // collide the entities... - } + // The _entityCollisionSystem.updateCollisions() call below merely tries for lock, + // and on failure it skips collision detection. + _entityCollisionSystem.updateCollisions(); // collide the entities... } { diff --git a/interface/src/Application.h b/interface/src/Application.h index c75202d96f..1c49e871e7 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -31,7 +31,6 @@ #include #include -#include #include #include #include @@ -60,6 +59,7 @@ #include "devices/SixenseManager.h" #include "devices/Visage.h" #include "devices/DdeFaceTracker.h" +#include "entities/EntityCollisionSystem.h" #include "entities/EntityTreeRenderer.h" #include "renderer/AmbientOcclusionEffect.h" #include "renderer/DeferredLightingEffect.h" diff --git a/interface/src/entities/EntityCollisionSystem.cpp b/interface/src/entities/EntityCollisionSystem.cpp index b080212479..1760d63157 100644 --- a/interface/src/entities/EntityCollisionSystem.cpp +++ b/interface/src/entities/EntityCollisionSystem.cpp @@ -16,27 +16,33 @@ #include #include #include +#include #include -#include "EntityItem.h" +#include +#include +#include +#include + #include "EntityCollisionSystem.h" -#include "EntityEditPacketSender.h" -#include "EntityTree.h" -#include "EntityTreeElement.h" const int MAX_COLLISIONS_PER_Entity = 16; -EntityCollisionSystem::EntityCollisionSystem(EntityEditPacketSender* packetSender, - EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio, - AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_Entity) { - init(packetSender, Entities, voxels, audio, avatars); +EntityCollisionSystem::EntityCollisionSystem() + : SimpleEntitySimulation(), + _packetSender(NULL), + _voxels(NULL), + _audio(NULL), + _avatars(NULL), + _collisions(MAX_COLLISIONS_PER_Entity) { } void EntityCollisionSystem::init(EntityEditPacketSender* packetSender, - EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio, - AvatarHashMap* avatars) { + EntityTree* entities, VoxelTree* voxels, AbstractAudioInterface* audio, + AvatarHashMap* avatars) { + assert(entities); + setEntityTree(entities); _packetSender = packetSender; - _entities = Entities; _voxels = voxels; _audio = audio; _avatars = avatars; @@ -45,14 +51,15 @@ void EntityCollisionSystem::init(EntityEditPacketSender* packetSender, EntityCollisionSystem::~EntityCollisionSystem() { } -void EntityCollisionSystem::update() { +void EntityCollisionSystem::updateCollisions() { + PerformanceTimer perfTimer("collisions"); + assert(_entityTree); // update all Entities - if (_entities->tryLockForRead()) { - QList& movingEntities = _entities->getMovingEntities(); - foreach (EntityItem* entity, movingEntities) { + if (_entityTree->tryLockForRead()) { + foreach (EntityItem* entity, _movingEntities) { checkEntity(entity); } - _entities->unlock(); + _entityTree->unlock(); } } @@ -127,9 +134,8 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { CollisionList collisions(MAX_COLLISIONS_PER_ENTITY); bool shapeCollisionsAccurate = false; - bool shapeCollisions = _entities->findShapeCollisions(&entityA->getCollisionShapeInMeters(), + bool shapeCollisions = _entityTree->findShapeCollisions(&entityA->getCollisionShapeInMeters(), collisions, Octree::NoLock, &shapeCollisionsAccurate); - if (shapeCollisions) { for(int i = 0; i < collisions.size(); i++) { @@ -203,7 +209,7 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { propertiesA.setPosition(newPositionA * (float)TREE_SCALE); propertiesA.setLastEdited(now); - _entities->updateEntity(idA, propertiesA); + _entityTree->updateEntity(idA, propertiesA); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA); } @@ -220,7 +226,7 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { propertiesB.setPosition(newPositionB * (float)TREE_SCALE); propertiesB.setLastEdited(now); - _entities->updateEntity(idB, propertiesB); + _entityTree->updateEntity(idB, propertiesB); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB); } } @@ -326,6 +332,6 @@ void EntityCollisionSystem::applyHardCollision(EntityItem* entity, const Collisi properties.setVelocity(velocity * (float)TREE_SCALE); properties.setLastEdited(usecTimestampNow()); - _entities->updateEntity(entityItemID, properties); + _entityTree->updateEntity(entityItemID, properties); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties); } diff --git a/interface/src/entities/EntityCollisionSystem.h b/interface/src/entities/EntityCollisionSystem.h index 4b483122fe..a1eb174756 100644 --- a/interface/src/entities/EntityCollisionSystem.h +++ b/interface/src/entities/EntityCollisionSystem.h @@ -20,31 +20,29 @@ #include #include -#include +#include #include +#include +#include #include -#include "EntityItem.h" - class AbstractAudioInterface; class AvatarData; class EntityEditPacketSender; class EntityTree; class VoxelTree; -class EntityCollisionSystem : public QObject { +class EntityCollisionSystem : public QObject, public SimpleEntitySimulation { Q_OBJECT public: - EntityCollisionSystem(EntityEditPacketSender* packetSender = NULL, EntityTree* Entitys = NULL, - VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL, - AvatarHashMap* avatars = NULL); + EntityCollisionSystem(); - void init(EntityEditPacketSender* packetSender, EntityTree* Entitys, VoxelTree* voxels, + void init(EntityEditPacketSender* packetSender, EntityTree* entities, VoxelTree* voxels, AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL); ~EntityCollisionSystem(); - void update(); + void updateCollisions(); void checkEntity(EntityItem* Entity); void updateCollisionWithVoxels(EntityItem* Entity); @@ -65,7 +63,6 @@ private: void emitGlobalEntityCollisionWithEntity(EntityItem* entityA, EntityItem* entityB, const CollisionInfo& penetration); EntityEditPacketSender* _packetSender; - EntityTree* _entities; VoxelTree* _voxels; AbstractAudioInterface* _audio; AvatarHashMap* _avatars; From b543434a1754e21fdceba7578ea60dc490a1c1dc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 26 Nov 2014 12:16:52 -0800 Subject: [PATCH 6/9] EntityTreeHeadlessViewer gets a simulation --- libraries/entities/src/EntityTreeHeadlessViewer.cpp | 13 ++++++++++--- libraries/entities/src/EntityTreeHeadlessViewer.h | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityTreeHeadlessViewer.cpp b/libraries/entities/src/EntityTreeHeadlessViewer.cpp index df00d302e3..45d21c0987 100644 --- a/libraries/entities/src/EntityTreeHeadlessViewer.cpp +++ b/libraries/entities/src/EntityTreeHeadlessViewer.cpp @@ -10,9 +10,10 @@ // #include "EntityTreeHeadlessViewer.h" +#include "SimpleEntitySimulation.h" -EntityTreeHeadlessViewer::EntityTreeHeadlessViewer() : - OctreeHeadlessViewer() { +EntityTreeHeadlessViewer::EntityTreeHeadlessViewer() + : OctreeHeadlessViewer(), _simulation(NULL) { } EntityTreeHeadlessViewer::~EntityTreeHeadlessViewer() { @@ -20,9 +21,15 @@ EntityTreeHeadlessViewer::~EntityTreeHeadlessViewer() { void EntityTreeHeadlessViewer::init() { OctreeHeadlessViewer::init(); + if (!_simulation) { + SimpleEntitySimulation* simpleSimulation = new SimpleEntitySimulation(); + EntityTree* entityTree = static_cast(_tree); + simpleSimulation->setEntityTree(entityTree); + entityTree->setSimulation(simpleSimulation); + _simulation = simpleSimulation; + } } - void EntityTreeHeadlessViewer::update() { if (_tree) { EntityTree* tree = static_cast(_tree); diff --git a/libraries/entities/src/EntityTreeHeadlessViewer.h b/libraries/entities/src/EntityTreeHeadlessViewer.h index b6cc04fb12..16839f9951 100644 --- a/libraries/entities/src/EntityTreeHeadlessViewer.h +++ b/libraries/entities/src/EntityTreeHeadlessViewer.h @@ -21,6 +21,8 @@ #include "EntityTree.h" +class EntitySimulation; + // Generic client side Octree renderer class. class EntityTreeHeadlessViewer : public OctreeHeadlessViewer { Q_OBJECT @@ -40,6 +42,8 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode); virtual void init(); +protected: + EntitySimulation* _simulation; }; #endif // hifi_EntityTreeHeadlessViewer_h From 3781c23480816671f47769b55a5b8184fab2fd1d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 26 Nov 2014 17:09:53 -0800 Subject: [PATCH 7/9] add entities to simulation after unpacking --- libraries/entities/src/EntityTree.cpp | 31 ++++++-------------- libraries/entities/src/EntityTree.h | 3 +- libraries/entities/src/EntityTreeElement.cpp | 2 +- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5d4c5b0fc4..6ac3c31939 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -76,25 +76,14 @@ EntityItem* EntityTree::getOrCreateEntityItem(const EntityItemID& entityID, cons } /// Adds a new entity item to the tree -void EntityTree::addEntityItem(EntityItem* entityItem) { - // You should not call this on existing entities that are already part of the tree! Call updateEntity() - EntityItemID entityID = entityItem->getEntityItemID(); - EntityTreeElement* containingElement = getContainingElement(entityID); - if (containingElement) { - qDebug() << "UNEXPECTED!!!! don't call addEntityItem() on existing EntityItems. entityID=" << entityID; - return; - } - - // Recurse the tree and store the entity in the correct tree element - AddEntityOperator theOperator(this, entityItem); - recurseTreeWithOperator(&theOperator); - +void EntityTree::postAddEntity(EntityItem* entity) { + assert(entity); // check to see if we need to simulate this entity.. if (_simulation) { - _simulation->addEntity(entityItem); + _simulation->addEntity(entity); } - _isDirty = true; + emit addingEntity(entity->getEntityItemID()); } bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -177,9 +166,11 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem result = EntityTypes::constructEntityItem(type, entityID, properties); if (result) { - // this does the actual adding of the entity - addEntityItem(result); - emitAddingEntity(entityID); + // Recurse the tree and store the entity in the correct tree element + AddEntityOperator theOperator(this, result); + recurseTreeWithOperator(&theOperator); + + postAddEntity(result); } return result; } @@ -199,10 +190,6 @@ void EntityTree::trackDeletedEntity(EntityItem* entity) { } } -void EntityTree::emitAddingEntity(const EntityItemID& entityItemID) { - emit addingEntity(entityItemID); -} - void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) { emit entityScriptChanging(entityItemID); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index db128e4563..5dccfd7709 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -79,7 +79,7 @@ public: // The newer API... EntityItem* getOrCreateEntityItem(const EntityItemID& entityID, const EntityItemProperties& properties); - void addEntityItem(EntityItem* entityItem); + void postAddEntity(EntityItem* entityItem); EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties); bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -142,7 +142,6 @@ public: void trackDeletedEntity(EntityItem* entity); - void emitAddingEntity(const EntityItemID& entityItemID); void emitEntityScriptChanging(const EntityItemID& entityItemID); void setSimulation(EntitySimulation* simulation); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 17f7456670..f0eeb40ede 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -768,7 +768,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int addEntityItem(entityItem); // add this new entity to this elements entities entityItemID = entityItem->getEntityItemID(); _myTree->setContainingElement(entityItemID, this); - _myTree->emitAddingEntity(entityItemID); // we just added an entity + _myTree->postAddEntity(entityItem); } } // Move the buffer forward to read more entities From 0814949e4cd9ec65c43657fa3daad7dc8325370f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Dec 2014 11:05:17 -0800 Subject: [PATCH 8/9] moving EntityCollisionSystem back to entities lib --- interface/src/Application.h | 2 +- .../entities/src}/EntityCollisionSystem.cpp | 9 ++++----- .../entities/src}/EntityCollisionSystem.h | 5 +++-- 3 files changed, 8 insertions(+), 8 deletions(-) rename {interface/src/entities => libraries/entities/src}/EntityCollisionSystem.cpp (99%) rename {interface/src/entities => libraries/entities/src}/EntityCollisionSystem.h (97%) diff --git a/interface/src/Application.h b/interface/src/Application.h index 1c49e871e7..6980ac7a48 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -59,7 +60,6 @@ #include "devices/SixenseManager.h" #include "devices/Visage.h" #include "devices/DdeFaceTracker.h" -#include "entities/EntityCollisionSystem.h" #include "entities/EntityTreeRenderer.h" #include "renderer/AmbientOcclusionEffect.h" #include "renderer/DeferredLightingEffect.h" diff --git a/interface/src/entities/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp similarity index 99% rename from interface/src/entities/EntityCollisionSystem.cpp rename to libraries/entities/src/EntityCollisionSystem.cpp index 1760d63157..2ac8ea596d 100644 --- a/interface/src/entities/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -19,12 +19,11 @@ #include #include -#include -#include -#include -#include - #include "EntityCollisionSystem.h" +#include "EntityEditPacketSender.h" +#include "EntityItem.h" +#include "EntityTreeElement.h" +#include "EntityTree.h" const int MAX_COLLISIONS_PER_Entity = 16; diff --git a/interface/src/entities/EntityCollisionSystem.h b/libraries/entities/src/EntityCollisionSystem.h similarity index 97% rename from interface/src/entities/EntityCollisionSystem.h rename to libraries/entities/src/EntityCollisionSystem.h index a1eb174756..b4421ffc72 100644 --- a/interface/src/entities/EntityCollisionSystem.h +++ b/libraries/entities/src/EntityCollisionSystem.h @@ -20,12 +20,13 @@ #include #include -#include #include #include -#include #include +#include "EntityItem.h" +#include "SimpleEntitySimulation.h" + class AbstractAudioInterface; class AvatarData; class EntityEditPacketSender; From b9944edf66d33a08f857325737d342684fd7550b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Dec 2014 11:13:37 -0800 Subject: [PATCH 9/9] OctreeServer::createTree() is now protected --- assignment-client/src/entities/EntityServer.h | 4 +++- assignment-client/src/octree/OctreeServer.h | 2 +- assignment-client/src/voxels/VoxelServer.h | 2 +- interface/src/entities/EntityTreeRenderer.h | 4 +++- libraries/entities/src/EntityTreeHeadlessViewer.h | 4 +++- libraries/octree/src/OctreeRenderer.h | 3 ++- libraries/voxels/src/VoxelTreeHeadlessViewer.h | 4 +++- 7 files changed, 16 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 14fd26b775..d072d18cdf 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -27,7 +27,6 @@ public: // Subclasses must implement these methods virtual OctreeQueryNode* createOctreeQueryNode(); - virtual Octree* createTree(); virtual char getMyNodeType() const { return NodeType::EntityServer; } virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; } virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; } @@ -46,6 +45,9 @@ public: public slots: void pruneDeletedEntities(); +protected: + virtual Octree* createTree(); + private: EntitySimulation* _entitySimulation; }; diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 27365c1e9d..4999594a98 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -62,7 +62,6 @@ public: // Subclasses must implement these methods virtual OctreeQueryNode* createOctreeQueryNode() = 0; - virtual Octree* createTree() = 0; virtual char getMyNodeType() const = 0; virtual PacketType getMyQueryMessageType() const = 0; virtual const char* getMyServerName() const = 0; @@ -132,6 +131,7 @@ public slots: void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); protected: + virtual Octree* createTree() = 0; bool readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result); bool readOptionInt(const QString& optionName, const QJsonObject& settingsSectionObject, int& result); bool readOptionString(const QString& optionName, const QJsonObject& settingsSectionObject, QString& result); diff --git a/assignment-client/src/voxels/VoxelServer.h b/assignment-client/src/voxels/VoxelServer.h index f4b6bd3a42..4d21695f33 100644 --- a/assignment-client/src/voxels/VoxelServer.h +++ b/assignment-client/src/voxels/VoxelServer.h @@ -36,7 +36,6 @@ public: // Subclasses must implement these methods virtual OctreeQueryNode* createOctreeQueryNode(); - virtual Octree* createTree(); virtual char getMyNodeType() const { return NodeType::VoxelServer; } virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; } virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; } @@ -50,6 +49,7 @@ public: virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); protected: + virtual Octree* createTree(); virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject); private: diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 9f06011d30..0042dd495f 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -40,7 +40,6 @@ public: EntityTreeRenderer(bool wantScripts); virtual ~EntityTreeRenderer(); - virtual Octree* createTree() { return new EntityTree(true); } virtual char getMyNodeType() const { return NodeType::EntityServer; } virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; } virtual PacketType getExpectedPacketType() const { return PacketTypeEntityData; } @@ -108,6 +107,9 @@ public slots: void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID); void entitySciptChanging(const EntityItemID& entityID); +protected: + virtual Octree* createTree() { return new EntityTree(true); } + private: void checkAndCallPreload(const EntityItemID& entityID); void checkAndCallUnload(const EntityItemID& entityID); diff --git a/libraries/entities/src/EntityTreeHeadlessViewer.h b/libraries/entities/src/EntityTreeHeadlessViewer.h index 16839f9951..3989582c2b 100644 --- a/libraries/entities/src/EntityTreeHeadlessViewer.h +++ b/libraries/entities/src/EntityTreeHeadlessViewer.h @@ -30,7 +30,6 @@ public: EntityTreeHeadlessViewer(); virtual ~EntityTreeHeadlessViewer(); - virtual Octree* createTree() { return new EntityTree(true); } virtual char getMyNodeType() const { return NodeType::EntityServer; } virtual PacketType getMyQueryMessageType() const { return PacketTypeEntityQuery; } virtual PacketType getExpectedPacketType() const { return PacketTypeEntityData; } @@ -42,7 +41,10 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode); virtual void init(); + protected: + virtual Octree* createTree() { return new EntityTree(true); } + EntitySimulation* _simulation; }; diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index 2999f34fb6..4ee0865243 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -35,7 +35,6 @@ public: OctreeRenderer(); virtual ~OctreeRenderer(); - virtual Octree* createTree() = 0; virtual char getMyNodeType() const = 0; virtual PacketType getMyQueryMessageType() const = 0; virtual PacketType getExpectedPacketType() const = 0; @@ -81,6 +80,8 @@ public: int getOpaqueMeshPartsRendered() const { return _opaqueMeshPartsRendered; } protected: + virtual Octree* createTree() = 0; + Octree* _tree; bool _managedTree; ViewFrustum* _viewFrustum; diff --git a/libraries/voxels/src/VoxelTreeHeadlessViewer.h b/libraries/voxels/src/VoxelTreeHeadlessViewer.h index 4acd5aa52d..291ad7f813 100644 --- a/libraries/voxels/src/VoxelTreeHeadlessViewer.h +++ b/libraries/voxels/src/VoxelTreeHeadlessViewer.h @@ -27,7 +27,6 @@ public: VoxelTreeHeadlessViewer(); virtual ~VoxelTreeHeadlessViewer(); - virtual Octree* createTree() { return new VoxelTree(true); } virtual char getMyNodeType() const { return NodeType::VoxelServer; } virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; } virtual PacketType getExpectedPacketType() const { return PacketTypeVoxelData; } @@ -35,6 +34,9 @@ public: VoxelTree* getTree() { return (VoxelTree*)_tree; } virtual void init(); + +protected: + virtual Octree* createTree() { return new VoxelTree(true); } }; #endif // hifi_VoxelTreeHeadlessViewer_h