From e574c5766b566a2d6abc0a2d07df8c11cac88711 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Nov 2014 22:28:27 -0800 Subject: [PATCH 01/21] 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 02/21] 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 03/21] 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 04/21] 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 05/21] 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 06/21] 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 07/21] 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 08/21] 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 09/21] 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 From 89b3a4d9872c4864af6501f7a56de959bca2bc35 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 12:17:10 -0800 Subject: [PATCH 10/21] Fix selection shadow not updating when grid moves --- examples/libraries/gridTool.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/libraries/gridTool.js b/examples/libraries/gridTool.js index de4fd7b8d4..aa412b1a76 100644 --- a/examples/libraries/gridTool.js +++ b/examples/libraries/gridTool.js @@ -177,6 +177,8 @@ Grid = function(opts) { color: gridColor, alpha: gridAlpha, }); + + that.emitUpdate(); } function cleanup() { @@ -207,6 +209,7 @@ GridTool = function(opts) { horizontalGrid.addListener(function(data) { webView.eventBridge.emitScriptEvent(JSON.stringify(data)); + selectionDisplay.updateHandles(); }); webView.eventBridge.webEventReceived.connect(function(data) { From 167ab05de4bf6a0fbf4ac104de6b0ab8086e3a8a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 15:19:51 -0800 Subject: [PATCH 11/21] Default 'Allow Select Large/Small Models' to true --- examples/newEditEntities.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index bc2d87259e..b013b40bce 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -51,8 +51,8 @@ var toolWidth = 50; var MIN_ANGULAR_SIZE = 2; var MAX_ANGULAR_SIZE = 45; -var allowLargeModels = false; -var allowSmallModels = false; +var allowLargeModels = true; +var allowSmallModels = true; var wantEntityGlow = false; var SPAWN_DISTANCE = 1; @@ -644,9 +644,9 @@ function setupModelMenus() { Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L", - afterItem: "Paste Models", isCheckable: true }); + afterItem: "Paste Models", isCheckable: true, isChecked: true }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S", - afterItem: "Allow Select Large Models", isCheckable: true }); + afterItem: "Allow Select Large Models", isCheckable: true, isChecked: true }); Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" }); From 77365ec2829150819debd02c1acf212708126440 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 15:49:45 -0800 Subject: [PATCH 12/21] Move entity selection to click event and add ability to deselect --- examples/libraries/entitySelectionTool.js | 12 +- examples/newEditEntities.js | 129 ++++++++++++---------- 2 files changed, 80 insertions(+), 61 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 1a43231f6f..8fffcf276f 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -89,11 +89,19 @@ SelectionManager = (function() { } } - that.addEntity = function(entityID) { + that.addEntity = function(entityID, toggleSelection) { if (entityID.isKnownID) { - var idx = that.selections.indexOf(entityID); + var idx = -1; + for (var i = 0; i < that.selections.length; i++) { + if (entityID.id == that.selections[i].id) { + idx = i; + break; + } + } if (idx == -1) { that.selections.push(entityID); + } else if (toggleSelection) { + that.selections.splice(idx, 1); } } else { var idx = that.pendingSelections.indexOf(entityID); diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index b013b40bce..db59944d57 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -485,74 +485,18 @@ function findClickedEntity(event) { return { pickRay: pickRay, entityID: foundEntity }; } +var mouseHasMovedSincePress = false; function mousePressEvent(event) { + mouseHasMovedSincePress = false; + if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) { return; } if (isActive) { - var entitySelected = false; if (cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) { // Event handled; do nothing. return; - } else { - var result = findClickedEntity(event); - if (result === null) { - selectionManager.clearSelections(); - return; - } - var pickRay = result.pickRay; - var foundEntity = result.entityID; - - var properties = Entities.getEntityProperties(foundEntity); - if (isLocked(properties)) { - print("Model locked " + properties.id); - } else { - var halfDiagonal = Vec3.length(properties.dimensions) / 2.0; - - print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal); - // P P - Model - // /| A - Palm - // / | d B - unit vector toward tip - // / | X - base of the perpendicular line - // A---X----->B d - distance fom axis - // x x - distance from A - // - // |X-A| = (P-A).B - // X == A + ((P-A).B)B - // d = |P-X| - - var A = pickRay.origin; - var B = Vec3.normalize(pickRay.direction); - var P = properties.position; - - var x = Vec3.dot(Vec3.subtract(P, A), B); - var X = Vec3.sum(A, Vec3.multiply(B, x)); - var d = Vec3.length(Vec3.subtract(P, X)); - var halfDiagonal = Vec3.length(properties.dimensions) / 2.0; - - var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; - - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) - && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); - - if (0 < x && sizeOK) { - entitySelected = true; - selectedEntityID = foundEntity; - orientation = MyAvatar.orientation; - intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); - - if (!event.isShifted) { - selectionManager.clearSelections(); - } - selectionManager.addEntity(foundEntity); - - print("Model selected: " + foundEntity.id); - } - } - } - if (entitySelected) { - selectionDisplay.select(selectedEntityID, event); } } else if (Menu.isOptionChecked(MENU_INSPECT_TOOL_ENABLED)) { var result = findClickedEntity(event); @@ -572,6 +516,7 @@ function mousePressEvent(event) { var highlightedEntityID = { isKnownID: false }; function mouseMoveEvent(event) { + mouseHasMovedSincePress = true; if (isActive) { // allow the selectionDisplay and cameraManager to handle the event first, if it doesn't handle it, then do our own thing if (selectionDisplay.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) { @@ -615,6 +560,72 @@ function mouseReleaseEvent(event) { } cameraManager.mouseReleaseEvent(event); + + if (!mouseHasMovedSincePress) { + mouseClickEvent(event); + } +} + +function mouseClickEvent(event) { + var result = findClickedEntity(event); + if (result === null) { + if (!event.isShifted) { + selectionManager.clearSelections(); + } + return; + } + var pickRay = result.pickRay; + var foundEntity = result.entityID; + + var properties = Entities.getEntityProperties(foundEntity); + if (isLocked(properties)) { + print("Model locked " + properties.id); + } else { + var halfDiagonal = Vec3.length(properties.dimensions) / 2.0; + + print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal); + // P P - Model + // /| A - Palm + // / | d B - unit vector toward tip + // / | X - base of the perpendicular line + // A---X----->B d - distance fom axis + // x x - distance from A + // + // |X-A| = (P-A).B + // X == A + ((P-A).B)B + // d = |P-X| + + var A = pickRay.origin; + var B = Vec3.normalize(pickRay.direction); + var P = properties.position; + + var x = Vec3.dot(Vec3.subtract(P, A), B); + var X = Vec3.sum(A, Vec3.multiply(B, x)); + var d = Vec3.length(Vec3.subtract(P, X)); + var halfDiagonal = Vec3.length(properties.dimensions) / 2.0; + + var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; + + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) + && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); + + if (0 < x && sizeOK) { + entitySelected = true; + selectedEntityID = foundEntity; + orientation = MyAvatar.orientation; + intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); + + if (!event.isShifted) { + selectionManager.clearSelections(); + } + + var toggle = event.isShifted; + selectionManager.addEntity(foundEntity, toggle); + + print("Model selected: " + foundEntity.id); + selectionDisplay.select(selectedEntityID, event); + } + } } Controller.mousePressEvent.connect(mousePressEvent); From 2b5a4e4563249c20584ded8295613df882176fe0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 15:56:43 -0800 Subject: [PATCH 13/21] Add selection boxes in multi-selection mode --- examples/libraries/entitySelectionTool.js | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 8fffcf276f..302c612f15 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -347,6 +347,8 @@ SelectionDisplay = (function () { lineWidth: 1.0, }); + var selectionBoxes = []; + var rotationDegreesDisplay = Overlays.addOverlay("text3d", { position: { x:0, y: 0, z: 0}, text: "", @@ -692,6 +694,9 @@ SelectionDisplay = (function () { for (var i = 0; i < allOverlays.length; i++) { Overlays.deleteOverlay(allOverlays[i]); } + for (var i = 0; i < selectionBoxes.length; i++) { + Overlays.deleteOverlay(selectionBoxes[i]); + } }; that.highlightSelectable = function(entityID) { @@ -1134,6 +1139,33 @@ SelectionDisplay = (function () { visible: !(mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL"), }); + // Create more selection box overlays if we don't have enough + var overlaysNeeded = selectionManager.selections.length - selectionBoxes.length; + for (var i = 0; i < overlaysNeeded; i++) { + selectionBoxes.push( + Overlays.addOverlay("cube", { + position: { x: 0, y: 0, z: 0 }, + size: 1, + color: { red: 255, green: 153, blue: 0 }, + alpha: 1, + solid: false, + visible: false, + dashed: false, + lineWidth: 1.0, + ignoreRayIntersection: true, + })); + } + + for (var i = 0; i < selectionManager.selections.length; i++) { + var properties = Entities.getEntityProperties(selectionManager.selections[i]); + Overlays.editOverlay(selectionBoxes[i], { + position: properties.position, + rotation: properties.rotation, + dimensions: properties.dimensions, + visible: true, + }); + } + Overlays.editOverlay(grabberEdgeTR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTR }); Overlays.editOverlay(grabberEdgeTL, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTL }); Overlays.editOverlay(grabberEdgeTF, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTF }); @@ -1157,6 +1189,10 @@ SelectionDisplay = (function () { for (var i = 0; i < length; i++) { Overlays.editOverlay(allOverlays[i], { visible: isVisible }); } + length = selectionBoxes.length; + for (var i = 0; i < length; i++) { + Overlays.editOverlay(selectionBoxes[i], { visible: isVisible }); + } }; that.unselect = function (entityID) { From 2f1ceb922a5a0a2d4ba92dd732b0a91e4d77842e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 15:59:56 -0800 Subject: [PATCH 14/21] Increase size of selection boxes --- examples/libraries/entitySelectionTool.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 302c612f15..92d432df1e 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -332,7 +332,7 @@ SelectionDisplay = (function () { solid: false, visible: false, dashed: true, - lineWidth: 1.0, + lineWidth: 2.0, ignoreRayIntersection: true // this never ray intersects }); @@ -344,7 +344,7 @@ SelectionDisplay = (function () { solid: false, visible: false, dashed: true, - lineWidth: 1.0, + lineWidth: 2.0, }); var selectionBoxes = []; From 0fb0169ff26266487935783e01f6ae5d8c8cd62c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 16:00:33 -0800 Subject: [PATCH 15/21] Move baseOfEntityProjectionOverlay update to updateHandles() --- examples/libraries/entitySelectionTool.js | 35 +++++++++++------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 92d432df1e..babf658f95 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -946,23 +946,6 @@ SelectionDisplay = (function () { var dimensions = selectionManager.worldDimensions; var position = selectionManager.worldPosition; - Overlays.editOverlay(baseOfEntityProjectionOverlay, - { - visible: mode != "ROTATE_YAW" && mode != "ROTATE_PITCH" && mode != "ROTATE_ROLL", - solid: true, - // lineWidth: 2.0, - position: { - x: position.x, - y: grid.getOrigin().y, - z: position.z - }, - dimensions: { - x: dimensions.x, - y: dimensions.z - }, - rotation: rotation, - }); - Overlays.editOverlay(rotateOverlayTarget, { visible: rotationOverlaysVisible }); Overlays.editOverlay(rotateZeroOverlay, { visible: rotationOverlaysVisible }); @@ -999,7 +982,6 @@ SelectionDisplay = (function () { return; } - that.updateRotationHandles(); that.highlightSelectable(); @@ -1182,6 +1164,23 @@ SelectionDisplay = (function () { var grabberMoveUpOffset = 0.1; grabberMoveUpPosition = { x: position.x, y: position.y + worldTop + grabberMoveUpOffset, z: position.z } Overlays.editOverlay(grabberMoveUp, { visible: activeTool == null || mode == "TRANSLATE_UP_DOWN" }); + + Overlays.editOverlay(baseOfEntityProjectionOverlay, { + visible: mode != "ROTATE_YAW" && mode != "ROTATE_PITCH" && mode != "ROTATE_ROLL", + solid: true, + position: { + x: selectionManager.worldPosition.x, + y: grid.getOrigin().y, + z: selectionManager.worldPosition.z + }, + dimensions: { + x: selectionManager.worldDimensions.x, + y: selectionManager.worldDimensions.z + }, + rotation: Quat.fromPitchYawRollDegrees(0, 0, 0), + }); + + }; that.setOverlaysVisible = function(isVisible) { From 30a7b609586192b3a137003cefbe1f00bd2cfa6f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 16:01:09 -0800 Subject: [PATCH 16/21] Update checkMove to only update rotation handles instead of all handles --- examples/libraries/entitySelectionTool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index babf658f95..f88a5c576f 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -2053,7 +2053,7 @@ SelectionDisplay = (function () { that.checkMove = function() { if (SelectionManager.hasSelection() && (!Vec3.equal(Camera.getPosition(), lastCameraPosition) || !Quat.equal(Camera.getOrientation(), lastCameraOrientation))){ - that.select(selectionManager.selections[0], false, false); + that.updateRotationHandles(); } }; From c4ca7e1b18df38381539da8bbf32b4eb6344bf05 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 16:01:40 -0800 Subject: [PATCH 17/21] Remove clearSelections from findClickedEntity - it shouldn't handle it --- examples/newEditEntities.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index db59944d57..92db9daab5 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -476,7 +476,6 @@ function findClickedEntity(event) { var identify = Entities.identifyEntity(foundEntity); if (!identify.isKnownID) { print("Unknown ID " + identify.id + " (update loop " + foundEntity.id + ")"); - selectionManager.clearSelections(); return null; } foundEntity = identify; From aa067bc15389c492baf51d279115e9222b3a707c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 17:49:25 -0800 Subject: [PATCH 18/21] Update UpDown translation to follow the mouse as other tools do --- examples/libraries/entitySelectionTool.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index f88a5c576f..5f2549a85c 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1297,10 +1297,17 @@ SelectionDisplay = (function () { }; var lastXYPick = null + var upDownPickNormal = null; addGrabberTool(grabberMoveUp, { mode: "TRANSLATE_UP_DOWN", onBegin: function(event) { - lastXYPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, Quat.getFront(lastCameraOrientation)); + pickRay = Camera.computePickRay(event.x, event.y); + + upDownPickNormal = Quat.getFront(lastCameraOrientation); + // Remove y component so the y-axis lies along the plane we picking on - this will + // give movements that follow the mouse. + upDownPickNormal.y = 0; + lastXYPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, upDownPickNormal); SelectionManager.saveProperties(); @@ -1328,11 +1335,9 @@ SelectionDisplay = (function () { pickRay = Camera.computePickRay(event.x, event.y); // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - SelectionManager.worldPosition, - Quat.getFront(lastCameraOrientation)); + var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, upDownPickNormal); - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); + var vector = Vec3.subtract(newIntersection, lastXYPick); vector = grid.snapToGrid(vector); // we only care about the Y axis From f25d509363e0daf52fe83c4901dce70e9455ffef Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 17:50:34 -0800 Subject: [PATCH 19/21] Remove unused lastPlaneIntersection --- examples/libraries/entitySelectionTool.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 5f2549a85c..077c77f999 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -235,7 +235,6 @@ SelectionDisplay = (function () { var overlayNames = new Array(); var lastCameraPosition = Camera.getPosition(); var lastCameraOrientation = Camera.getOrientation(); - var lastPlaneIntersection; var handleHoverColor = { red: 224, green: 67, blue: 36 }; var handleHoverAlpha = 1.0; @@ -721,13 +720,11 @@ SelectionDisplay = (function () { if (event !== false) { pickRay = Camera.computePickRay(event.x, event.y); - lastPlaneIntersection = rayPlaneIntersection(pickRay, properties.position, Quat.getFront(lastCameraOrientation)); var wantDebug = false; if (wantDebug) { print("select() with EVENT...... "); print(" event.y:" + event.y); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); Vec3.print(" current position:", properties.position); } @@ -1285,7 +1282,6 @@ SelectionDisplay = (function () { if (wantDebug) { print("translateXZ... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); Vec3.print(" vector:", vector); Vec3.print(" newPosition:", properties.position); Vec3.print(" newPosition:", newPosition); @@ -1348,7 +1344,6 @@ SelectionDisplay = (function () { if (wantDebug) { print("translateUpDown... "); print(" event.y:" + event.y); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); Vec3.print(" newIntersection:", newIntersection); Vec3.print(" vector:", vector); Vec3.print(" newPosition:", newPosition); @@ -1561,7 +1556,6 @@ SelectionDisplay = (function () { var wantDebug = false; if (wantDebug) { print(stretchMode); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); Vec3.print(" newIntersection:", newIntersection); Vec3.print(" vector:", vector); Vec3.print(" oldPOS:", oldPOS); @@ -2310,11 +2304,8 @@ SelectionDisplay = (function () { if (somethingClicked) { pickRay = Camera.computePickRay(event.x, event.y); - lastPlaneIntersection = rayPlaneIntersection(pickRay, selectionManager.worldPosition, - Quat.getFront(lastCameraOrientation)); if (wantDebug) { - print("mousePressEvent()...... " + overlayNames[result.overlayID]); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); + print("mousePressEvent()...... " + overlayNames[result.overlayID]); } } From 4e78003520504187bcb68b435a08f59b43a9a7ff Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 18:03:00 -0800 Subject: [PATCH 20/21] Fix issue with selection boxes not being hidden --- examples/libraries/entitySelectionTool.js | 24 +++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 077c77f999..fff1a54abd 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1135,14 +1135,22 @@ SelectionDisplay = (function () { })); } - for (var i = 0; i < selectionManager.selections.length; i++) { - var properties = Entities.getEntityProperties(selectionManager.selections[i]); - Overlays.editOverlay(selectionBoxes[i], { - position: properties.position, - rotation: properties.rotation, - dimensions: properties.dimensions, - visible: true, - }); + var i = 0; + // Only show individual selections boxes if there is more than 1 selection + if (selectionManager.selections.length > 1) { + for (; i < selectionManager.selections.length; i++) { + var properties = Entities.getEntityProperties(selectionManager.selections[i]); + Overlays.editOverlay(selectionBoxes[i], { + position: properties.position, + rotation: properties.rotation, + dimensions: properties.dimensions, + visible: true, + }); + } + } + // Hide any remaining selection boxes + for (; i < selectionBoxes.length; i++) { + Overlays.editOverlay(selectionBoxes[i], { visible: false }); } Overlays.editOverlay(grabberEdgeTR, { visible: extendedStretchHandlesVisible, rotation: rotation, position: EdgeTR }); From d08337326ce9117eef95c8451354ab6f427475a5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 Dec 2014 12:59:51 -0800 Subject: [PATCH 21/21] Update selection box color to red --- examples/libraries/entitySelectionTool.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index fff1a54abd..40b5b78a31 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -338,12 +338,12 @@ SelectionDisplay = (function () { var selectionBox = Overlays.addOverlay("cube", { position: { x:0, y: 0, z: 0}, size: 1, - color: { red: 60, green: 60, blue: 60}, + color: { red: 255, green: 0, blue: 0}, alpha: 1, solid: false, visible: false, - dashed: true, - lineWidth: 2.0, + dashed: false, + lineWidth: 1.0, }); var selectionBoxes = [];