From e574c5766b566a2d6abc0a2d07df8c11cac88711 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Nov 2014 22:28:27 -0800 Subject: [PATCH 01/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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/57] 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 8449575fa231cfe504cbfcecc395d2f447f59f16 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 1 Dec 2014 09:25:21 -0800 Subject: [PATCH 08/57] fix for ice-server heartbeat without an access token --- domain-server/src/DomainServer.cpp | 45 ++++++++++++++++-------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index ddc06c8015..80777ce529 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -337,9 +337,31 @@ bool DomainServer::optionallySetupAssignmentPayment() { void DomainServer::setupAutomaticNetworking() { + LimitedNodeList* nodeList = LimitedNodeList::getInstance(); + + const int STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS = 10 * 1000; + const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000; + + // setup our timer to check our IP via stun every X seconds + QTimer* dynamicIPTimer = new QTimer(this); + connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN); + + if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { + dynamicIPTimer->start(STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS); + + // setup a timer to heartbeat with the ice-server every so often + QTimer* iceHeartbeatTimer = new QTimer(this); + connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates); + iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS); + + // call our sendHeartbeaToIceServer immediately anytime a local or public socket changes + connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); + connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); + } + if (!didSetupAccountManagerWithAccessToken()) { - qDebug() << "Cannot setup domain-server automatic networking without an access token."; - qDebug() << "Please add an access token to your config file or via the web interface."; + qDebug() << "Cannot send heartbeat to data server without an access token."; + qDebug() << "Add an access token to your config file or via the web interface."; return; } @@ -350,37 +372,18 @@ void DomainServer::setupAutomaticNetworking() { if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE || _automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { - LimitedNodeList* nodeList = LimitedNodeList::getInstance(); const QUuid& domainID = nodeList->getSessionUUID(); if (!domainID.isNull()) { qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID" << uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString(); - const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000; - const int STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS = 10 * 1000; - - // setup our timer to check our IP via stun every X seconds - QTimer* dynamicIPTimer = new QTimer(this); - connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN); - if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) { dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS); // send public socket changes to the data server so nodes can find us at our new IP connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::performIPAddressUpdate); } else { - dynamicIPTimer->start(STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS); - - // setup a timer to heartbeat with the ice-server every so often - QTimer* iceHeartbeatTimer = new QTimer(this); - connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates); - iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS); - - // call our sendHeartbeaToIceServer immediately anytime a local or public socket changes - connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); - connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); - // send our heartbeat to data server so it knows what our network settings are sendHeartbeatToDataServer(); } From 40db44e4bd708831694cc283cccb03e5174c23e3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 1 Dec 2014 09:59:51 -0800 Subject: [PATCH 09/57] fix for custom hifi URL lookup --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 20ae07b0c9..31b73274fc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -842,7 +842,7 @@ bool Application::event(QEvent* event) { QFileOpenEvent* fileEvent = static_cast(event); if (!fileEvent->url().isEmpty()) { - AddressManager::getInstance().handleLookupString(fileEvent->url().toLocalFile()); + AddressManager::getInstance().handleLookupString(fileEvent->url().toString()); } return false; From 6d7f799dbe072a857d9ffdf1a9be66c6578f764f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 1 Dec 2014 10:32:35 -0800 Subject: [PATCH 10/57] oculus mirror lean hack --- interface/src/avatar/MyAvatar.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3cbe2ac8ae..3301cf0347 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -367,6 +367,15 @@ void MyAvatar::updateFromTrackers(float deltaTime) { const float TORSO_LENGTH = 0.5f; glm::vec3 relativePosition = estimatedPosition - glm::vec3(0.0f, -TORSO_LENGTH, 0.0f); const float MAX_LEAN = 45.0f; + + // Invert left/right lean when in mirror mode + // NOTE: this is kinda a hack, it's the same hack we use to make the head tilt. But it's not really a mirror + // it just makes you feel like you're looking in a mirror because the body movements of the avatar appear to + // match your body movements. + if (OculusManager::isConnected() && Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { + relativePosition.x = -relativePosition.x; + } + head->setLeanSideways(glm::clamp(glm::degrees(atanf(relativePosition.x * _leanScale / TORSO_LENGTH)), -MAX_LEAN, MAX_LEAN)); head->setLeanForward(glm::clamp(glm::degrees(atanf(relativePosition.z * _leanScale / TORSO_LENGTH)), From 7f36a11f3539ec07ca807ea097d813d48acd2627 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 1 Dec 2014 10:40:37 -0800 Subject: [PATCH 11/57] Find Oculus libraries when using Visual Studio 2013 --- cmake/modules/FindLibOVR.cmake | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmake/modules/FindLibOVR.cmake b/cmake/modules/FindLibOVR.cmake index a1d75add3f..6ffb3ed309 100644 --- a/cmake/modules/FindLibOVR.cmake +++ b/cmake/modules/FindLibOVR.cmake @@ -48,8 +48,13 @@ elseif (UNIX) select_library_configurations(XINERAMA) elseif (WIN32) - find_library(LIBOVR_LIBRARY_DEBUG NAMES libovrd PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${LIBOVR_SEARCH_DIRS}) - find_library(LIBOVR_LIBRARY_RELEASE NAMES libovr PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${LIBOVR_SEARCH_DIRS}) + if (MSVC10) + find_library(LIBOVR_LIBRARY_DEBUG NAMES libovrd PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE NAMES libovr PATH_SUFFIXES Lib/Win32/VS2010 HINTS ${LIBOVR_SEARCH_DIRS}) + elseif (MSVC12) + find_library(LIBOVR_LIBRARY_DEBUG NAMES libovrd PATH_SUFFIXES Lib/Win32/VS2013 HINTS ${LIBOVR_SEARCH_DIRS}) + find_library(LIBOVR_LIBRARY_RELEASE NAMES libovr PATH_SUFFIXES Lib/Win32/VS2013 HINTS ${LIBOVR_SEARCH_DIRS}) + endif () find_package(ATL) endif () From 09eb150edc9adb0e06ac9931633ce00115e99240 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 1 Dec 2014 11:40:35 -0800 Subject: [PATCH 12/57] Update build instructions for VS2013 Community and Professional --- BUILD.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/BUILD.md b/BUILD.md index d6e1603f37..e1d920b8df 100644 --- a/BUILD.md +++ b/BUILD.md @@ -96,12 +96,13 @@ Currently building on Windows has been tested using the following compilers: #####Windows SDK 7.1 -Whichever version of Visual Studio you use, you will need [Microsoft Windows SDK for Windows 7 and .NET Framework 4](http://www.microsoft.com/en-us/download/details.aspx?id=8279). +If using Visual Studio 2010, or using Visual Studio 2013 but building as a Visual Studio 2010 project, you need [Microsoft Windows SDK for Windows 7 and .NET Framework 4](http://www.microsoft.com/en-us/download/details.aspx?id=8279). NOTE: If using Visual Studio C++ 2010 Express, you need to follow a specific install order. See below before installing the Windows SDK. -######Windows 8.1 -You may have already downloaded the Windows 8 SDK (e.g. if you have previously installed Visual Studio 2013). If so, change CMAKE_PREFIX_PATH in %HIFI_DIR%\CMakeLists.txt to point to the Windows 8 SDK binaries. The default path is `C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86` +######Windows SDK 8.1 + +If using Visual Studio 2013 and building as a Visual Studio 2013 project you need the Windows 8 SDK which you should already have as part of installing Visual Studio 2013. You should be able to see it at `C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86`. #####Visual Studio C++ 2010 Express @@ -123,9 +124,11 @@ Some of the build instructions will ask you to start a Visual Studio Command Pro #####Visual Studio 2013 -This product must be purchased separately. +You can use the Community or Professional editions of Visual Studio 2013. -Visual Studio 2013 doesn't have a shortcut to start a Visual Studio Command Prompt. Instead, start a regular command prompt and then run: +You can start a Visual Studio 2013 command prompt using the shortcut provided in the Visual Studio Tools folder installed as part of Visual Studio 2013. + +Or you can start a regular command prompt and then run: "%VS120COMNTOOLS%\vsvars32.bat" @@ -146,6 +149,8 @@ Once Qt is installed, you need to manually configure the following: * Make sure the Qt runtime DLLs are loadable. You must do this before you attempt to build because some tools for the build depend on Qt. E.g., add to the PATH: `Qt\5.2.0\msvc2010_opengl\bin\`. * Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.2.0\msvc2010_opengl` directory. +If building as a Visual Studio 2013 project, download and configure the msvc2013 version of Qt instead. + ####External Libraries CMake will need to know where the headers and libraries for required external dependencies are. From 845e687fc579add8016dd593d41f94f9a956dad2 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 1 Dec 2014 11:45:07 -0800 Subject: [PATCH 13/57] Adding a Hack for the apaartment model to boost the lightmaps --- interface/src/renderer/GeometryCache.cpp | 5 ++++- libraries/fbx/src/FBXReader.cpp | 7 ++++--- libraries/fbx/src/FBXReader.h | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 04b59e7ea1..e2517613df 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -835,11 +835,14 @@ void GeometryReader::run() { fbxgeo = readSVO(_reply->readAll()); } else { bool grabLightmaps = true; + float lightmapLevel = 1.0f; // HACK: For monday 12/01/2014 we need to kill lighmaps loading in starchamber... if (_url.path().toLower().endsWith("loungev4_11-18.fbx")) { grabLightmaps = false; + } else if (_url.path().toLower().endsWith("apt8_reboot.fbx")) { + lightmapLevel = 4.0f; } - fbxgeo = readFBX(_reply->readAll(), _mapping, grabLightmaps); + fbxgeo = readFBX(_reply->readAll(), _mapping, grabLightmaps, lightmapLevel); } QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index d77cbbd0a1..5f215ac4d0 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1194,7 +1194,7 @@ int matchTextureUVSetToAttributeChannel(const std::string& texUVSetName, const Q } } -FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, bool loadLightmaps) { +FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) { QHash meshes; QHash modelIDsToNames; QHash meshIDsToMeshIndices; @@ -1954,6 +1954,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, FBXTexture emissiveTexture; glm::vec2 emissiveParams(0.f, 1.f); + emissiveParams.y = lightmapLevel; QString emissiveTextureID = emissiveTextures.value(childID); QString ambientTextureID = ambientTextures.value(childID); if (!emissiveTextureID.isNull() || !ambientTextureID.isNull()) { @@ -2372,10 +2373,10 @@ QByteArray writeMapping(const QVariantHash& mapping) { return buffer.data(); } -FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps) { +FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); - return extractFBXGeometry(parseFBX(&buffer), mapping, loadLightmaps); + return extractFBXGeometry(parseFBX(&buffer), mapping, loadLightmaps, lightmapLevel); } bool addMeshVoxelsOperation(OctreeElement* element, void* extraData) { diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 3adf5b5ffb..a5df7ccc0c 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -253,7 +253,7 @@ QByteArray writeMapping(const QVariantHash& mapping); /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps = true); +FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps = true, float lightmapLevel = 1.0f); /// Reads SVO geometry from the supplied model data. FBXGeometry readSVO(const QByteArray& model); From 1950ce5a4b9d1d9d023205300a8a51e3a820d497 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Nov 2014 14:09:32 -0800 Subject: [PATCH 14/57] Add option for white grid color --- examples/html/gridControls.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/html/gridControls.html b/examples/html/gridControls.html index 241fa2406c..d95c9545e4 100644 --- a/examples/html/gridControls.html +++ b/examples/html/gridControls.html @@ -6,7 +6,7 @@ var gridColor = { red: 0, green: 0, blue: 0 }; var gridColors = [ { red: 0, green: 0, blue: 0 }, - { red: 128, green: 128, blue: 128 }, + { red: 255, green: 255, blue: 255 }, { red: 255, green: 0, blue: 0 }, { red: 0, green: 255, blue: 0}, { red: 0, green: 0, blue: 255 }, From 2c524cd70ce3835982092e9b4a40d7563d70b77d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Nov 2014 14:10:39 -0800 Subject: [PATCH 15/57] Darken colors of selection/highlight 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 813bf015d1..f1e311246e 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -306,7 +306,7 @@ SelectionDisplay = (function () { var highlightBox = Overlays.addOverlay("cube", { position: { x:0, y: 0, z: 0}, size: 1, - color: { red: 180, green: 180, blue: 180}, + color: { red: 90, green: 90, blue: 90}, alpha: 1, solid: false, visible: false, @@ -318,7 +318,7 @@ SelectionDisplay = (function () { var selectionBox = Overlays.addOverlay("cube", { position: { x:0, y: 0, z: 0}, size: 1, - color: { red: 180, green: 180, blue: 180}, + color: { red: 60, green: 60, blue: 60}, alpha: 1, solid: false, visible: false, From fc5adef501a326300fb3ef42cf29a14188ea3a08 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Nov 2014 14:11:04 -0800 Subject: [PATCH 16/57] Update the tool window to use tabbed windows --- interface/src/ui/ToolWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index de88d75b3d..799402a486 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -21,6 +21,7 @@ ToolWindow::ToolWindow(QWidget* parent) : _hasShown(false), _lastGeometry() { + setDockOptions(QMainWindow::ForceTabbedDocks); Application::getInstance()->installEventFilter(this); } From da568c96cea7d7a5bacb43b5f0f1de63f5c70b30 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 Dec 2014 12:04:00 -0800 Subject: [PATCH 17/57] Add rotation degrees overlay --- examples/libraries/entitySelectionTool.js | 131 +++++++++++++++++++--- 1 file changed, 113 insertions(+), 18 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index f1e311246e..2b3819aff1 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -200,6 +200,12 @@ SelectionManager = (function() { return that; })(); +function normalizeDegrees(degrees) { + while (degrees > 180) degrees -= 360; + while (degrees < -180) degrees += 360; + return degrees; +} + SelectionDisplay = (function () { var that = {}; @@ -207,6 +213,8 @@ SelectionDisplay = (function () { var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.0075; + var ROTATION_DISPLAY_DISTANCE_MULTIPLIER = 1.2; + var showExtendedStretchHandles = false; var spaceMode = SPACE_LOCAL; @@ -223,6 +231,9 @@ SelectionDisplay = (function () { var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool var innerRadius; var outerRadius; + var yawOffset = 0; + var pitchOffset = 0; + var rollOffset = 0; var yawHandleRotation; var pitchHandleRotation; var rollHandleRotation; @@ -326,6 +337,23 @@ SelectionDisplay = (function () { lineWidth: 1.0, }); + var rotationDegreesDisplay = Overlays.addOverlay("text3d", { + position: { x:0, y: 0, z: 0}, + text: "", + color: { red: 0, green: 0, blue: 0}, + backgroundColor: { red: 255, green: 255, blue: 255 }, + alpha: 0.7, + visible: false, + isFacingAvatar: true, + drawInFront: true, + dimensions: { x: 0, y: 0 }, + lineHeight: 0.0, + topMargin: 0, + rightMargin: 0, + bottomMargin: 0, + leftMargin: 0, + }); + var grabberMoveUp = Overlays.addOverlay("billboard", { url: HIFI_PUBLIC_BUCKET + "images/up-arrow.png", position: { x:0, y: 0, z: 0}, @@ -585,6 +613,7 @@ SelectionDisplay = (function () { rotateOverlayCurrent, rotateZeroOverlay, rotateCurrentOverlay, + rotationDegreesDisplay, xRailOverlay, yRailOverlay, zRailOverlay, @@ -757,6 +786,10 @@ SelectionDisplay = (function () { pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 }); + yawOffset = 90; + pitchOffset = 0; + rollOffset = 0; + yawNormal = { x: 0, y: 1, z: 0 }; pitchNormal = { x: 1, y: 0, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; @@ -788,6 +821,10 @@ SelectionDisplay = (function () { pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 90 }); + yawOffset = 0; + pitchOffset = 180; + rollOffset = 90; + yawNormal = { x: 0, y: 1, z: 0 }; pitchNormal = { x: 1, y: 0, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; @@ -822,6 +859,10 @@ SelectionDisplay = (function () { pitchHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 0, z: 90 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); + yawOffset = 180; + pitchOffset = 90; + rollOffset = 180; + yawNormal = { x: 0, y: 1, z: 0 }; pitchNormal = { x: 1, y: 0, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; @@ -848,8 +889,12 @@ SelectionDisplay = (function () { } else { yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 270, z: 0 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); + rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); + + yawOffset = 270; + pitchOffset = 180; + rollOffset = 180; yawNormal = { x: 0, y: 1, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; @@ -1570,11 +1615,17 @@ SelectionDisplay = (function () { endAt: 0, innerRadius: 0.9, }); + + Overlays.editOverlay(rotationDegreesDisplay, { + visible: true, + ignoreRayIntersection: true, + }); }, onEnd: function(event, reason) { Overlays.editOverlay(rotateOverlayInner, { visible: false }); Overlays.editOverlay(rotateOverlayOuter, { visible: false }); Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); pushCommandForSelections(); }, @@ -1605,12 +1656,9 @@ SelectionDisplay = (function () { var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); var distanceFromCenter = Vec3.distance(center, result.intersection); - var snapToInner = false; - // var innerRadius = (Vec3.length(selectionManager.worldDimensions) / 2) * 1.1; - if (distanceFromCenter < innerRadius) { - angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; - snapToInner = true; - } + var snapToInner = distanceFromCenter < innerRadius; + var snapAngle = snapToInner ? innerSnapAngle : 1.0; + angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; // for debugging if (debug) { @@ -1632,7 +1680,20 @@ SelectionDisplay = (function () { rotation: Quat.multiply(yawChange, initialProperties.rotation), }); } - + + var angle = (yawOffset + angleFromZero + 180) * (Math.PI / 180); + var position = Vec3.sum( selectionManager.worldPosition, { + x: -Math.cos(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, + y: -selectionManager.worldDimensions.y / 2, + z: Math.sin(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, + }); + Overlays.editOverlay(rotationDegreesDisplay, { + position: position, + dimensions: { x: innerRadius / 2, y: innerRadius / 5.6 }, + lineHeight: innerRadius / 6, + text: normalizeDegrees(angleFromZero), + }); + // update the rotation display accordingly... var startAtCurrent = 0; var endAtCurrent = angleFromZero; @@ -1701,11 +1762,17 @@ SelectionDisplay = (function () { endAt: 0, innerRadius: 0.9, }); + + Overlays.editOverlay(rotationDegreesDisplay, { + visible: true, + ignoreRayIntersection: true, + }); }, onEnd: function(event, reason) { Overlays.editOverlay(rotateOverlayInner, { visible: false }); Overlays.editOverlay(rotateOverlayOuter, { visible: false }); Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); pushCommandForSelections(); }, @@ -1736,11 +1803,9 @@ SelectionDisplay = (function () { var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); var distanceFromCenter = Vec3.distance(center, result.intersection); - var snapToInner = false; - if (distanceFromCenter < innerRadius) { - angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; - snapToInner = true; - } + var snapToInner = distanceFromCenter < innerRadius; + var snapAngle = snapToInner ? innerSnapAngle : 1.0; + angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; // for debugging if (debug) { @@ -1764,6 +1829,19 @@ SelectionDisplay = (function () { }); } + var angle = (rollOffset + angleFromZero - 90) * (Math.PI / 180); + var position = Vec3.sum( selectionManager.worldPosition, { + x: selectionManager.worldDimensions.x / 2, + y: Math.cos(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, + z: Math.sin(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, + }); + Overlays.editOverlay(rotationDegreesDisplay, { + position: position, + dimensions: { x: innerRadius / 2, y: innerRadius / 5.6 }, + lineHeight: innerRadius / 6, + text: normalizeDegrees(angleFromZero), + }); + // update the rotation display accordingly... var startAtCurrent = 0; var endAtCurrent = angleFromZero; @@ -1831,11 +1909,17 @@ SelectionDisplay = (function () { endAt: 0, innerRadius: 0.9, }); + + Overlays.editOverlay(rotationDegreesDisplay, { + visible: true, + ignoreRayIntersection: true, + }); }, onEnd: function(event, reason) { Overlays.editOverlay(rotateOverlayInner, { visible: false }); Overlays.editOverlay(rotateOverlayOuter, { visible: false }); Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + Overlays.editOverlay(rotationDegreesDisplay, { visible: false }); pushCommandForSelections(); }, @@ -1866,11 +1950,9 @@ SelectionDisplay = (function () { var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); var distanceFromCenter = Vec3.distance(center, result.intersection); - var snapToInner = false; - if (distanceFromCenter < innerRadius) { - angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; - snapToInner = true; - } + var snapToInner = distanceFromCenter < innerRadius; + var snapAngle = snapToInner ? innerSnapAngle : 1.0; + angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; // for debugging if (debug) { @@ -1893,6 +1975,19 @@ SelectionDisplay = (function () { }); } + var angle = (rollOffset + angleFromZero + 90) * (Math.PI / 180); + var position = Vec3.sum( selectionManager.worldPosition, { + x: Math.sin(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, + y: -Math.cos(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, + z: -selectionManager.worldDimensions.z / 2, + }); + Overlays.editOverlay(rotationDegreesDisplay, { + position: position, + dimensions: { x: innerRadius / 2, y: innerRadius / 5.6 }, + lineHeight: innerRadius / 6, + text: normalizeDegrees(angleFromZero), + }); + // update the rotation display accordingly... var startAtCurrent = 0; var endAtCurrent = angleFromZero; From 4f0683dc5ee9f45d02275e52c0540b19462e511c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 Dec 2014 15:07:03 -0800 Subject: [PATCH 18/57] Fix stretch tools not following mouse correctly --- examples/libraries/entitySelectionTool.js | 76 ++++++++++++++--------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 2b3819aff1..ead424522a 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1337,7 +1337,11 @@ SelectionDisplay = (function () { var vec3Mult = function(v1, v2) { return { x: v1.x * v2.x, y: v1.y * v2.y, z: v1.z * v2.z }; } - var makeStretchTool = function(stretchMode, direction, pivot) { + // stretchMode - name of mode + // direction - direction to stretch in + // pivot - point to use as a pivot + // offset - the position of the overlay tool relative to the selections center position + var makeStretchTool = function(stretchMode, direction, pivot, offset) { var signs = { x: direction.x < 0 ? -1 : (direction.x > 0 ? 1 : 0), y: direction.y < 0 ? -1 : (direction.y > 0 ? 1 : 0), @@ -1358,6 +1362,7 @@ SelectionDisplay = (function () { var initialDimensions = null; var initialIntersection = null; var initialProperties = null; + var pickRayPosition = null; var rotation = null; var onBegin = function(event) { @@ -1366,13 +1371,22 @@ SelectionDisplay = (function () { rotation = spaceMode == SPACE_LOCAL ? properties.rotation : Quat.fromPitchYawRollDegrees(0, 0, 0); if (spaceMode == SPACE_LOCAL) { + rotation = SelectionManager.localRotation; initialPosition = SelectionManager.localPosition; initialDimensions = SelectionManager.localDimensions; } else { + rotation = SelectionManager.worldRotation; initialPosition = SelectionManager.worldPosition; initialDimensions = SelectionManager.worldDimensions; } + var scaledOffset = { + x: initialDimensions.x * offset.x * 0.5, + y: initialDimensions.y * offset.y * 0.5, + z: initialDimensions.z * offset.z * 0.5, + }; + pickRayPosition = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffset)); + if (numDimensions == 1 && mask.x) { var start = Vec3.multiplyQbyV(rotation, { x: -10000, y: 0, z: 0 }); start = Vec3.sum(start, properties.position); @@ -1426,7 +1440,7 @@ SelectionDisplay = (function () { planeNormal = Vec3.multiplyQbyV(rotation, planeNormal); var pickRay = Camera.computePickRay(event.x, event.y); lastPick = rayPlaneIntersection(pickRay, - initialPosition, + pickRayPosition, planeNormal); // Overlays.editOverlay(normalLine, { @@ -1461,7 +1475,7 @@ SelectionDisplay = (function () { var pickRay = Camera.computePickRay(event.x, event.y); newPick = rayPlaneIntersection(pickRay, - initialPosition, + pickRayPosition, planeNormal); var vector = Vec3.subtract(newPick, lastPick); @@ -1536,44 +1550,44 @@ SelectionDisplay = (function () { }; }; - function addStretchTool(overlay, mode, pivot, direction) { + function addStretchTool(overlay, mode, pivot, direction, offset) { if (!pivot) { pivot = Vec3.multiply(-1, direction); pivot.y = direction.y; } - var tool = makeStretchTool(mode, direction, pivot); + var tool = makeStretchTool(mode, direction, pivot, offset); addGrabberTool(overlay, tool); } - addStretchTool(grabberNEAR, "STRETCH_NEAR", { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }); - addStretchTool(grabberFAR, "STRETCH_FAR", { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); - addStretchTool(grabberTOP, "STRETCH_TOP", { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }); - addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }); - addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }); - addStretchTool(grabberLEFT, "STRETCH_LEFT", { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }); + addStretchTool(grabberNEAR, "STRETCH_NEAR", { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); + addStretchTool(grabberFAR, "STRETCH_FAR", { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }); + addStretchTool(grabberTOP, "STRETCH_TOP", { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }); + addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }); + addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }); + addStretchTool(grabberLEFT, "STRETCH_LEFT", { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }); - addStretchTool(grabberLBN, "STRETCH_LBN", null, {x: 1, y: 0, z: 1}); - addStretchTool(grabberRBN, "STRETCH_RBN", null, {x: -1, y: 0, z: 1}); - addStretchTool(grabberLBF, "STRETCH_LBF", null, {x: 1, y: 0, z: -1}); - addStretchTool(grabberRBF, "STRETCH_RBF", null, {x: -1, y: 0, z: -1}); - addStretchTool(grabberLTN, "STRETCH_LTN", null, {x: 1, y: 0, z: 1}); - addStretchTool(grabberRTN, "STRETCH_RTN", null, {x: -1, y: 0, z: 1}); - addStretchTool(grabberLTF, "STRETCH_LTF", null, {x: 1, y: 0, z: -1}); - addStretchTool(grabberRTF, "STRETCH_RTF", null, {x: -1, y: 0, z: -1}); + addStretchTool(grabberLBN, "STRETCH_LBN", null, {x: 1, y: 0, z: 1}, { x: -1, y: -1, z: -1 }); + addStretchTool(grabberRBN, "STRETCH_RBN", null, {x: -1, y: 0, z: 1}, { x: 1, y: -1, z: -1 }); + addStretchTool(grabberLBF, "STRETCH_LBF", null, {x: 1, y: 0, z: -1}, { x: -1, y: -1, z: 1 }); + addStretchTool(grabberRBF, "STRETCH_RBF", null, {x: -1, y: 0, z: -1}, { x: 1, y: -1, z: 1 }); + addStretchTool(grabberLTN, "STRETCH_LTN", null, {x: 1, y: 0, z: 1}, { x: -1, y: 1, z: -1 }); + addStretchTool(grabberRTN, "STRETCH_RTN", null, {x: -1, y: 0, z: 1}, { x: 1, y: 1, z: -1 }); + addStretchTool(grabberLTF, "STRETCH_LTF", null, {x: 1, y: 0, z: -1}, { x: -1, y: 1, z: 1 }); + addStretchTool(grabberRTF, "STRETCH_RTF", null, {x: -1, y: 0, z: -1}, { x: 1, y: 1, z: 1 }); - addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, {x: 1, y: 1, z: 0}); - addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, {x: -1, y: 1, z: 0}); - addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, {x: 0, y: 1, z: -1}); - addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, {x: 0, y: 1, z: 1}); - addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, {x: -1, y: 0, z: 0}); - addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, {x: 1, y: 0, z: 0}); - addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, {x: 0, y: 0, z: -1}); - addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, {x: 0, y: 0, z: 1}); - addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, {x: -1, y: 0, z: 1}); - addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, {x: 1, y: 0, z: 1}); - addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, {x: -1, y: 0, z: -1}); - addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, {x: 1, y: 0, z: -1}); + addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, {x: 1, y: 1, z: 0}, { x: 1, y: 1, z: 0 }); + addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, {x: -1, y: 1, z: 0}, { x: -1, y: 1, z: 0 }); + addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, {x: 0, y: 1, z: -1}, { x: 0, y: 1, z: -1 }); + addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, {x: 0, y: 1, z: 1}, { x: 0, y: 1, z: 1 }); + addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, {x: -1, y: 0, z: 0}, { x: 1, y: -1, z: 0 }); + addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, {x: 1, y: 0, z: 0}, { x: -1, y: -1, z: 0 }); + addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, {x: 0, y: 0, z: -1}, { x: 0, y: -1, z: -1 }); + addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, {x: 0, y: 0, z: 1}, { x: 0, y: -1, z: 1 }); + addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, {x: -1, y: 0, z: 1}, { x: 1, y: 0, z: -1 }); + addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, {x: 1, y: 0, z: 1}, { x: -1, y: 0, z: -1 }); + addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, {x: -1, y: 0, z: -1}, { x: 1, y: 0, z: 1 }); + addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, {x: 1, y: 0, z: -1}, { x: -1, y: 0, z: 1 }); var initialPosition = SelectionManager.worldPosition; addGrabberTool(yawHandle, { From 06ab17987bbd12e58c8457a8a5e4aedd8a12d33f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 Dec 2014 16:08:50 -0800 Subject: [PATCH 19/57] Fix position of rotation degrees display The position of the display was not always correct depending on the camera orientation relative to the selection. --- examples/libraries/entitySelectionTool.js | 69 +++++++++++------------ 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index ead424522a..e37171c92a 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -213,7 +213,11 @@ SelectionDisplay = (function () { var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.0075; + // These are multipliers for sizing the rotation degrees display while rotating an entity var ROTATION_DISPLAY_DISTANCE_MULTIPLIER = 1.2; + var ROTATION_DISPLAY_SIZE_X_MULTIPLIER = 0.5; + var ROTATION_DISPLAY_SIZE_Y_MULTIPLIER = 0.18; + var ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER = 0.17; var showExtendedStretchHandles = false; @@ -1589,6 +1593,26 @@ SelectionDisplay = (function () { addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, {x: -1, y: 0, z: -1}, { x: 1, y: 0, z: 1 }); addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, {x: 1, y: 0, z: -1}, { x: -1, y: 0, z: 1 }); + function updateRotationDegreesOverlay(angleFromZero, handleRotation, centerPosition) { + var angle = angleFromZero * (Math.PI / 180); + var position = { + x: Math.cos(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, + y: Math.sin(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, + z: 0, + }; + position = Vec3.multiplyQbyV(handleRotation, position); + position = Vec3.sum(centerPosition, position); + Overlays.editOverlay(rotationDegreesDisplay, { + position: position, + dimensions: { + x: innerRadius * ROTATION_DISPLAY_SIZE_X_MULTIPLIER, + y: innerRadius * ROTATION_DISPLAY_SIZE_Y_MULTIPLIER + }, + lineHeight: innerRadius * ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER, + text: normalizeDegrees(angleFromZero), + }); + } + var initialPosition = SelectionManager.worldPosition; addGrabberTool(yawHandle, { mode: "ROTATE_YAW", @@ -1634,6 +1658,8 @@ SelectionDisplay = (function () { visible: true, ignoreRayIntersection: true, }); + + updateRotationDegreesOverlay(0, yawHandleRotation, yawCenter); }, onEnd: function(event, reason) { Overlays.editOverlay(rotateOverlayInner, { visible: false }); @@ -1695,18 +1721,7 @@ SelectionDisplay = (function () { }); } - var angle = (yawOffset + angleFromZero + 180) * (Math.PI / 180); - var position = Vec3.sum( selectionManager.worldPosition, { - x: -Math.cos(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - y: -selectionManager.worldDimensions.y / 2, - z: Math.sin(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - }); - Overlays.editOverlay(rotationDegreesDisplay, { - position: position, - dimensions: { x: innerRadius / 2, y: innerRadius / 5.6 }, - lineHeight: innerRadius / 6, - text: normalizeDegrees(angleFromZero), - }); + updateRotationDegreesOverlay(angleFromZero, yawHandleRotation, yawCenter); // update the rotation display accordingly... var startAtCurrent = 0; @@ -1781,6 +1796,8 @@ SelectionDisplay = (function () { visible: true, ignoreRayIntersection: true, }); + + updateRotationDegreesOverlay(0, pitchHandleRotation, pitchCenter); }, onEnd: function(event, reason) { Overlays.editOverlay(rotateOverlayInner, { visible: false }); @@ -1843,18 +1860,7 @@ SelectionDisplay = (function () { }); } - var angle = (rollOffset + angleFromZero - 90) * (Math.PI / 180); - var position = Vec3.sum( selectionManager.worldPosition, { - x: selectionManager.worldDimensions.x / 2, - y: Math.cos(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - z: Math.sin(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - }); - Overlays.editOverlay(rotationDegreesDisplay, { - position: position, - dimensions: { x: innerRadius / 2, y: innerRadius / 5.6 }, - lineHeight: innerRadius / 6, - text: normalizeDegrees(angleFromZero), - }); + updateRotationDegreesOverlay(angleFromZero, pitchHandleRotation, pitchCenter); // update the rotation display accordingly... var startAtCurrent = 0; @@ -1928,6 +1934,8 @@ SelectionDisplay = (function () { visible: true, ignoreRayIntersection: true, }); + + updateRotationDegreesOverlay(0, rollHandleRotation, rollCenter); }, onEnd: function(event, reason) { Overlays.editOverlay(rotateOverlayInner, { visible: false }); @@ -1989,18 +1997,7 @@ SelectionDisplay = (function () { }); } - var angle = (rollOffset + angleFromZero + 90) * (Math.PI / 180); - var position = Vec3.sum( selectionManager.worldPosition, { - x: Math.sin(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - y: -Math.cos(angle) * outerRadius * ROTATION_DISPLAY_DISTANCE_MULTIPLIER, - z: -selectionManager.worldDimensions.z / 2, - }); - Overlays.editOverlay(rotationDegreesDisplay, { - position: position, - dimensions: { x: innerRadius / 2, y: innerRadius / 5.6 }, - lineHeight: innerRadius / 6, - text: normalizeDegrees(angleFromZero), - }); + updateRotationDegreesOverlay(angleFromZero, rollHandleRotation, rollCenter); // update the rotation display accordingly... var startAtCurrent = 0; From ce4d4073789da913e19eefe32f4e1d3ea2b2e027 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 Dec 2014 16:12:18 -0800 Subject: [PATCH 20/57] Update normalizeDegrees to be exclusive on the -180 end --- examples/libraries/entitySelectionTool.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index e37171c92a..478782c6ec 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -200,9 +200,10 @@ SelectionManager = (function() { return that; })(); +// Normalize degrees to be in the range (-180, 180] function normalizeDegrees(degrees) { while (degrees > 180) degrees -= 360; - while (degrees < -180) degrees += 360; + while (degrees <= -180) degrees += 360; return degrees; } From 0b7ddb009dca37a3433def60898fbb9347cbc73a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 Dec 2014 16:33:51 -0800 Subject: [PATCH 21/57] Fix tabification of tool windows --- interface/src/ui/ToolWindow.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index 799402a486..da1d2c68f4 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -100,8 +100,17 @@ void ToolWindow::onChildVisibilityUpdated(bool visible) { } void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget) { + QList dockWidgets = findChildren(); + QMainWindow::addDockWidget(area, dockWidget); + // We want to force tabbing, so retabify all of our widgets. + QDockWidget* lastDockWidget = dockWidget; + foreach (QDockWidget* nextDockWidget, dockWidgets) { + tabifyDockWidget(lastDockWidget, nextDockWidget); + lastDockWidget = nextDockWidget; + } + connect(dockWidget, &QDockWidget::visibilityChanged, this, &ToolWindow::onChildVisibilityUpdated); } From a8eb49a8226c5ae6a4acf550a5fe37818620369d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 Dec 2014 16:38:40 -0800 Subject: [PATCH 22/57] Remove now-unused *Offset variables --- examples/libraries/entitySelectionTool.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 478782c6ec..5544fec84d 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -236,9 +236,6 @@ SelectionDisplay = (function () { var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool var innerRadius; var outerRadius; - var yawOffset = 0; - var pitchOffset = 0; - var rollOffset = 0; var yawHandleRotation; var pitchHandleRotation; var rollHandleRotation; @@ -791,10 +788,6 @@ SelectionDisplay = (function () { pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 }); - yawOffset = 90; - pitchOffset = 0; - rollOffset = 0; - yawNormal = { x: 0, y: 1, z: 0 }; pitchNormal = { x: 1, y: 0, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; @@ -826,10 +819,6 @@ SelectionDisplay = (function () { pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 90 }); - yawOffset = 0; - pitchOffset = 180; - rollOffset = 90; - yawNormal = { x: 0, y: 1, z: 0 }; pitchNormal = { x: 1, y: 0, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; @@ -864,10 +853,6 @@ SelectionDisplay = (function () { pitchHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 0, z: 90 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); - yawOffset = 180; - pitchOffset = 90; - rollOffset = 180; - yawNormal = { x: 0, y: 1, z: 0 }; pitchNormal = { x: 1, y: 0, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; @@ -897,10 +882,6 @@ SelectionDisplay = (function () { pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); - yawOffset = 270; - pitchOffset = 180; - rollOffset = 180; - yawNormal = { x: 0, y: 1, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; pitchNormal = { x: 1, y: 0, z: 0 }; From 9d9a7ead5b8f73dcfe6e306aac220bbb7bfb2a6a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 Dec 2014 16:39:36 -0800 Subject: [PATCH 23/57] Remove unnecessary overlay property updates --- examples/libraries/entitySelectionTool.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 5544fec84d..1a43231f6f 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -348,6 +348,7 @@ SelectionDisplay = (function () { visible: false, isFacingAvatar: true, drawInFront: true, + ignoreRayIntersection: true, dimensions: { x: 0, y: 0 }, lineHeight: 0.0, topMargin: 0, @@ -1638,7 +1639,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotationDegreesDisplay, { visible: true, - ignoreRayIntersection: true, }); updateRotationDegreesOverlay(0, yawHandleRotation, yawCenter); @@ -1776,7 +1776,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotationDegreesDisplay, { visible: true, - ignoreRayIntersection: true, }); updateRotationDegreesOverlay(0, pitchHandleRotation, pitchCenter); @@ -1914,7 +1913,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotationDegreesDisplay, { visible: true, - ignoreRayIntersection: true, }); updateRotationDegreesOverlay(0, rollHandleRotation, rollCenter); From 36f716cd6165e846a4bb475a08c217de20a37001 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Dec 2014 10:13:43 -0800 Subject: [PATCH 24/57] have script loading be separate from ScriptEngine constructor --- libraries/script-engine/src/ScriptEngine.cpp | 114 ++++++++----------- libraries/script-engine/src/ScriptEngine.h | 8 +- 2 files changed, 54 insertions(+), 68 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e6002d7c10..fefe8e2103 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -100,71 +100,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam { } -ScriptEngine::ScriptEngine(const QUrl& scriptURL, - AbstractControllerScriptingInterface* controllerScriptingInterface) : - _scriptContents(), - _isFinished(false), - _isRunning(false), - _isInitialized(false), - _isAvatar(false), - _avatarIdentityTimer(NULL), - _avatarBillboardTimer(NULL), - _timerFunctionMap(), - _isListeningToAudioStream(false), - _avatarSound(NULL), - _numAvatarSoundSentBytes(0), - _controllerScriptingInterface(controllerScriptingInterface), - _avatarData(NULL), - _scriptName(), - _fileNameString(), - _quatLibrary(), - _vec3Library(), - _uuidLibrary(), - _animationCache(this), - _isUserLoaded(false), - _arrayBufferClass(new ArrayBufferClass(this)) -{ - QString scriptURLString = scriptURL.toString(); - _fileNameString = scriptURLString; - - QUrl url(scriptURL); - - // if the scheme length is one or lower, maybe they typed in a file, let's try - const int WINDOWS_DRIVE_LETTER_SIZE = 1; - if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { - url = QUrl::fromLocalFile(scriptURLString); - } - - // ok, let's see if it's valid... and if so, load it - if (url.isValid()) { - if (url.scheme() == "file") { - QString fileName = url.toLocalFile(); - QFile scriptFile(fileName); - if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { - qDebug() << "Loading file:" << fileName; - QTextStream in(&scriptFile); - _scriptContents = in.readAll(); - } else { - qDebug() << "ERROR Loading file:" << fileName; - emit errorMessage("ERROR Loading file:" + fileName); - } - } else { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); - qDebug() << "Downloading script at" << url; - QEventLoop loop; - QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - loop.exec(); - if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) { - _scriptContents = reply->readAll(); - } else { - qDebug() << "ERROR Loading file:" << url.toString(); - emit errorMessage("ERROR Loading file:" + url.toString()); - } - } - } -} - void ScriptEngine::setIsAvatar(bool isAvatar) { _isAvatar = isAvatar; @@ -217,6 +152,55 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents, const QStrin return true; } +void ScriptEngine::loadURL(const QUrl& scriptURL) { + if (_isRunning) { + return; + } + + QString scriptURLString = scriptURL.toString(); + _fileNameString = scriptURLString; + + QUrl url(scriptURL); + + // if the scheme length is one or lower, maybe they typed in a file, let's try + const int WINDOWS_DRIVE_LETTER_SIZE = 1; + if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { + url = QUrl::fromLocalFile(scriptURLString); + } + + // ok, let's see if it's valid... and if so, load it + if (url.isValid()) { + if (url.scheme() == "file") { + QString fileName = url.toLocalFile(); + QFile scriptFile(fileName); + if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { + qDebug() << "Loading file:" << fileName; + QTextStream in(&scriptFile); + _scriptContents = in.readAll(); + emit scriptLoaded(); + } else { + qDebug() << "ERROR Loading file:" << fileName; + emit errorLoadingScript(); + } + } else { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); + connect(reply, &QNetworkReply::finished, this, &ScriptEngine::handleScriptDownload); + } + } +} + +void ScriptEngine::handleScriptDownload() { + QNetworkReply* reply = qobject_cast(sender()); + + if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) { + _scriptContents = reply->readAll(); + } else { + qDebug() << "ERROR Loading file:" << reply->url().toString(); + emit errorLoadingScript(); + } +} + Q_SCRIPT_DECLARE_QMETAOBJECT(LocalVoxels, QString) void ScriptEngine::init() { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index bb279b8887..ee952d9d35 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -40,9 +40,6 @@ const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 10 class ScriptEngine : public QScriptEngine { Q_OBJECT public: - ScriptEngine(const QUrl& scriptURL, - AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); - ScriptEngine(const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString(""), AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); @@ -94,6 +91,7 @@ public: bool isUserLoaded() const { return _isUserLoaded; } public slots: + void loadURL(const QUrl& scriptURL); void stop(); QScriptValue evaluate(const QString& program, const QString& fileName = QString(), int lineNumber = 1); @@ -109,6 +107,8 @@ public slots: void nodeKilled(SharedNodePointer node); signals: + void scriptLoaded(); + void errorLoadingScript(); void update(float deltaTime); void scriptEnding(); void finished(const QString& fileNameString); @@ -155,6 +155,8 @@ private: ArrayBufferClass* _arrayBufferClass; QHash _outgoingScriptAudioSequenceNumbers; +private slots: + void handleScriptDownload(); }; #endif // hifi_ScriptEngine_h From d7f168999d1de69951942d02fa4fa6a39a3d65b1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Dec 2014 10:30:23 -0800 Subject: [PATCH 25/57] don't block main thread for a script load --- interface/src/Application.cpp | 49 +++++++++++++------- interface/src/Application.h | 3 ++ libraries/script-engine/src/ScriptEngine.cpp | 7 +-- libraries/script-engine/src/ScriptEngine.h | 4 +- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 20ae07b0c9..35b13cbec7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4013,26 +4013,23 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser return _scriptEnginesHash[scriptURLString]; } - ScriptEngine* scriptEngine; - if (scriptFilename.isNull()) { - scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); - } else { - // start the script on a new thread... - scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - - if (!scriptEngine->hasScript()) { - qDebug() << "Application::loadScript(), script failed to load..."; - QMessageBox::warning(getWindow(), "Error Loading Script", scriptURLString + " failed to load."); - return NULL; - } - - _scriptEnginesHash.insertMulti(scriptURLString, scriptEngine); - _runningScriptsWidget->setRunningScripts(getRunningScripts()); - UserActivityLogger::getInstance().loadedScript(scriptURLString); - } + ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); scriptEngine->setUserLoaded(isUserLoaded); - registerScriptEngineWithApplicationServices(scriptEngine); + if (scriptFilename.isNull()) { + // this had better be the script editor (we should de-couple so somebody who thinks they are loading a script + // doesn't just get an empty script engine) + + // we can complete setup now since there isn't a script we have to load + registerScriptEngineWithApplicationServices(scriptEngine); + } else { + // connect to the appropriate signals of this script engine + connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &Application::handleScriptEngineLoaded); + connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &Application::handleScriptLoadError); + + // get the script engine object to load the script at the designated script URL + scriptEngine->loadURL(scriptUrl); + } // restore the main window's active state if (activateMainWindow && !loadScriptFromEditor) { @@ -4043,6 +4040,22 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser return scriptEngine; } +void Application::handleScriptEngineLoaded(const QUrl& scriptURL) { + ScriptEngine* scriptEngine = qobject_cast(sender()); + + _scriptEnginesHash.insertMulti(scriptURL.toString(), scriptEngine); + _runningScriptsWidget->setRunningScripts(getRunningScripts()); + UserActivityLogger::getInstance().loadedScript(scriptURL.toString()); + + // register our application services and set it off on its own thread + registerScriptEngineWithApplicationServices(scriptEngine); +} + +void Application::handleScriptLoadError(const QUrl& scriptURL) { + qDebug() << "Application::loadScript(), script failed to load..."; + QMessageBox::warning(getWindow(), "Error Loading Script", scriptURL.toString() + " failed to load."); +} + void Application::scriptFinished(const QString& scriptName) { const QString& scriptURLString = QUrl(scriptName).toString(); QHash::iterator it = _scriptEnginesHash.find(scriptURLString); diff --git a/interface/src/Application.h b/interface/src/Application.h index 9f2cdbd520..caaebea876 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -393,6 +393,9 @@ private slots: void timer(); void idle(); void aboutToQuit(); + + void handleScriptEngineLoaded(const QUrl& scriptURL); + void handleScriptLoadError(const QUrl& scriptURL); void connectedToDomain(const QString& hostname); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fefe8e2103..6cef69d23f 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -177,10 +177,10 @@ void ScriptEngine::loadURL(const QUrl& scriptURL) { qDebug() << "Loading file:" << fileName; QTextStream in(&scriptFile); _scriptContents = in.readAll(); - emit scriptLoaded(); + emit scriptLoaded(url); } else { qDebug() << "ERROR Loading file:" << fileName; - emit errorLoadingScript(); + emit errorLoadingScript(url); } } else { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); @@ -195,9 +195,10 @@ void ScriptEngine::handleScriptDownload() { if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) { _scriptContents = reply->readAll(); + emit scriptLoaded(reply->url()); } else { qDebug() << "ERROR Loading file:" << reply->url().toString(); - emit errorLoadingScript(); + emit errorLoadingScript(reply->url()); } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index ee952d9d35..4b6b3e48ab 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -107,8 +107,8 @@ public slots: void nodeKilled(SharedNodePointer node); signals: - void scriptLoaded(); - void errorLoadingScript(); + void scriptLoaded(const QUrl& scriptURL); + void errorLoadingScript(const QUrl& scriptURL); void update(float deltaTime); void scriptEnding(); void finished(const QString& fileNameString); From 0a92374ca6df9172e031999f2f58600ed5d1aec9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Dec 2014 10:39:20 -0800 Subject: [PATCH 26/57] fix an incorrect connection to script load error --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 35b13cbec7..f499d04cfa 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4025,7 +4025,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser } else { // connect to the appropriate signals of this script engine connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &Application::handleScriptEngineLoaded); - connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &Application::handleScriptLoadError); + connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError); // get the script engine object to load the script at the designated script URL scriptEngine->loadURL(scriptUrl); From 0814949e4cd9ec65c43657fa3daad7dc8325370f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Dec 2014 11:05:17 -0800 Subject: [PATCH 27/57] 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 28/57] 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 29/57] 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 4d233c09a36003a968bed277dabfad83dfd3559e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 2 Dec 2014 13:19:39 -0800 Subject: [PATCH 30/57] At least on Linux, fixes an annoying issue where closing the application window causes it to disappear and reappear briefly before exiting. --- interface/src/Application.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 22a221c3a8..9561115478 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1574,7 +1574,9 @@ void Application::setFullscreen(bool fullscreen) { } _window->setWindowState(fullscreen ? (_window->windowState() | Qt::WindowFullScreen) : (_window->windowState() & ~Qt::WindowFullScreen)); - _window->show(); + if (!_aboutToQuit) { + _window->show(); + } } void Application::setEnable3DTVMode(bool enable3DTVMode) { From 40df4973745655ff5d2af1c42d6705300cd89cc9 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 2 Dec 2014 13:48:59 -0800 Subject: [PATCH 31/57] Hack for the Viking Palace scene before demo for good looking lightmaps --- interface/src/renderer/GeometryCache.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index e2517613df..e523d7e608 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -841,6 +841,8 @@ void GeometryReader::run() { grabLightmaps = false; } else if (_url.path().toLower().endsWith("apt8_reboot.fbx")) { lightmapLevel = 4.0f; + } else if (_url.path().toLower().endsWith("palaceoforinthilian4.fbx")) { + lightmapLevel = 3.5f; } fbxgeo = readFBX(_reply->readAll(), _mapping, grabLightmaps, lightmapLevel); } From 2f11501e5b8f3fa5ff8049e8fbd8948f51fcb3c3 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 2 Dec 2014 14:28:46 -0800 Subject: [PATCH 32/57] Fix constant movement with hydra by adding null zone for thrust --- examples/hydraMove.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/hydraMove.js b/examples/hydraMove.js index 236cecab18..f469fbbe74 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -245,10 +245,15 @@ function handleGrabBehavior(deltaTime) { } // Update for joysticks and move button +var THRUST_DEAD_ZONE = 0.1; function flyWithHydra(deltaTime) { var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER); - if (thrustJoystickPosition.x != 0 || thrustJoystickPosition.y != 0) { + if (debug) { + print("thrust X: " + thrustJoystickPosition.x + " Y: " + thrustJoystickPosition.y); + } + + if (Math.abs(thrustJoystickPosition.x) > THRUST_DEAD_ZONE || Math.abs(thrustJoystickPosition.y) > THRUST_DEAD_ZONE) { if (thrustMultiplier < MAX_THRUST_MULTIPLIER) { thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE); } From 1ce201c04f9b93a17dc43ee6464dbbac809997a8 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 2 Dec 2014 14:33:19 -0800 Subject: [PATCH 33/57] add same for rotation --- examples/hydraMove.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/hydraMove.js b/examples/hydraMove.js index f469fbbe74..46715839c3 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -246,13 +246,10 @@ function handleGrabBehavior(deltaTime) { // Update for joysticks and move button var THRUST_DEAD_ZONE = 0.1; +var ROTATE_DEAD_ZONE = 0.1; function flyWithHydra(deltaTime) { var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER); - if (debug) { - print("thrust X: " + thrustJoystickPosition.x + " Y: " + thrustJoystickPosition.y); - } - if (Math.abs(thrustJoystickPosition.x) > THRUST_DEAD_ZONE || Math.abs(thrustJoystickPosition.y) > THRUST_DEAD_ZONE) { if (thrustMultiplier < MAX_THRUST_MULTIPLIER) { thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE); @@ -275,7 +272,7 @@ function flyWithHydra(deltaTime) { // View Controller var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER); - if (viewJoystickPosition.x != 0 || viewJoystickPosition.y != 0) { + if (Math.abs(viewJoystickPosition.x) > ROTATE_DEAD_ZONE || Math.abs(viewJoystickPosition.y) > ROTATE_DEAD_ZONE) { // change the body yaw based on our x controller var orientation = MyAvatar.orientation; From 06b22cf6057c54aa326c88ac474780c766c85445 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 2 Dec 2014 14:50:25 -0800 Subject: [PATCH 34/57] ...and don't turn on/off UI from hydra --- interface/src/ui/ApplicationOverlay.cpp | 26 ------------------------- 1 file changed, 26 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 1307672ad1..305119a019 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -571,8 +571,6 @@ void ApplicationOverlay::renderControllerPointers() { static quint64 pressedTime[NUMBER_OF_MAGNIFIERS] = { 0ULL, 0ULL, 0ULL }; static bool isPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false }; static bool stateWhenPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false }; - static bool triggerPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false }; - static bool bumperPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false }; const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); @@ -613,30 +611,6 @@ void ApplicationOverlay::renderControllerPointers() { } } - //Check for UI active toggle - if (palmData->getTrigger() == 1.0f) { - if (!triggerPressed[index]) { - if (bumperPressed[index]) { - Menu::getInstance()->setIsOptionChecked(MenuOption::UserInterface, - !Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)); - } - triggerPressed[index] = true; - } - } else { - triggerPressed[index] = false; - } - if ((controllerButtons & BUTTON_FWD)) { - if (!bumperPressed[index]) { - if (triggerPressed[index]) { - Menu::getInstance()->setIsOptionChecked(MenuOption::UserInterface, - !Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)); - } - bumperPressed[index] = true; - } - } else { - bumperPressed[index] = false; - } - //if we have the oculus, we should make the cursor smaller since it will be //magnified if (OculusManager::isConnected()) { From 167ab05de4bf6a0fbf4ac104de6b0ab8086e3a8a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Dec 2014 15:19:51 -0800 Subject: [PATCH 35/57] 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 36/57] 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 37/57] 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 38/57] 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 39/57] 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 40/57] 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 41/57] 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 42/57] 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 43/57] 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 44/57] 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 3ca20a7eeef7f44bbb8698e84970fc72f7519a89 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 2 Dec 2014 23:08:34 -0800 Subject: [PATCH 45/57] Add jump/warp turning when in HMD --- interface/src/avatar/MyAvatar.cpp | 24 ++++++++++++++++++++++-- interface/src/avatar/MyAvatar.h | 1 + 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3301cf0347..a07a3d9b28 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -68,6 +68,7 @@ const int SCRIPTED_MOTOR_WORLD_FRAME = 2; MyAvatar::MyAvatar() : Avatar(), _mousePressed(false), + _turningKeyPressTime(0.0f), _bodyPitchDelta(0.0f), _bodyRollDelta(0.0f), _gravity(0.0f, 0.0f, 0.0f), @@ -1169,8 +1170,27 @@ bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode rend void MyAvatar::updateOrientation(float deltaTime) { // Gather rotation information from keyboard - _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; - _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; + const float TIME_BETWEEN_HMD_TURNS = 0.5f; + const float HMD_TURN_DEGREES = 22.5f; + if (!OculusManager::isConnected()) { + // Smoothly rotate body with arrow keys if not in HMD + _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; + _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; + } else { + // Jump turns if in HMD + if (_driveKeys[ROT_RIGHT] || _driveKeys[ROT_LEFT]) { + if (_turningKeyPressTime == 0.0f) { + setOrientation(getOrientation() * + glm::quat(glm::radians(glm::vec3(0.f, _driveKeys[ROT_LEFT] ? HMD_TURN_DEGREES : -HMD_TURN_DEGREES, 0.0f)))); + } + _turningKeyPressTime += deltaTime; + if (_turningKeyPressTime > TIME_BETWEEN_HMD_TURNS) { + _turningKeyPressTime = 0.0f; + } + } else { + _turningKeyPressTime = 0.0f; + } + } getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); // update body yaw by body yaw delta diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index dd1178c7b5..6926ac9505 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -204,6 +204,7 @@ protected: private: bool _mousePressed; + float _turningKeyPressTime; float _bodyPitchDelta; // degrees float _bodyRollDelta; // degrees glm::vec3 _gravity; From 57e2f71bb7fd7701e299acbb1038c1da1942aa19 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 2 Dec 2014 23:24:47 -0800 Subject: [PATCH 46/57] remove unused body delta pitch, roll --- interface/src/avatar/MyAvatar.cpp | 14 +++----------- interface/src/avatar/MyAvatar.h | 2 -- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a07a3d9b28..a4fc696bc9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -69,8 +69,6 @@ MyAvatar::MyAvatar() : Avatar(), _mousePressed(false), _turningKeyPressTime(0.0f), - _bodyPitchDelta(0.0f), - _bodyRollDelta(0.0f), _gravity(0.0f, 0.0f, 0.0f), _distanceToNearestAvatar(std::numeric_limits::max()), _shouldJump(false), @@ -1193,22 +1191,18 @@ void MyAvatar::updateOrientation(float deltaTime) { } getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); - // update body yaw by body yaw delta - glm::quat orientation = getOrientation() * glm::quat(glm::radians( - glm::vec3(_bodyPitchDelta, _bodyYawDelta, _bodyRollDelta) * deltaTime)); + // update body orientation by movement inputs + setOrientation(getOrientation() * + glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta, 0.0f) * deltaTime))); // decay body rotation momentum const float BODY_SPIN_FRICTION = 7.5f; float bodySpinMomentum = 1.0f - BODY_SPIN_FRICTION * deltaTime; if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; } - _bodyPitchDelta *= bodySpinMomentum; _bodyYawDelta *= bodySpinMomentum; - _bodyRollDelta *= bodySpinMomentum; float MINIMUM_ROTATION_RATE = 2.0f; if (fabs(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { _bodyYawDelta = 0.0f; } - if (fabs(_bodyRollDelta) < MINIMUM_ROTATION_RATE) { _bodyRollDelta = 0.0f; } - if (fabs(_bodyPitchDelta) < MINIMUM_ROTATION_RATE) { _bodyPitchDelta = 0.0f; } if (OculusManager::isConnected()) { // these angles will be in radians @@ -1237,8 +1231,6 @@ void MyAvatar::updateOrientation(float deltaTime) { } - // update the euler angles - setOrientation(orientation); } glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVelocity, bool hasFloor) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 6926ac9505..37b93e2f04 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -205,8 +205,6 @@ protected: private: bool _mousePressed; float _turningKeyPressTime; - float _bodyPitchDelta; // degrees - float _bodyRollDelta; // degrees glm::vec3 _gravity; float _distanceToNearestAvatar; // How close is the nearest avatar? From d63ace9f6108a8fcef1a752d65189ab59b2c7dfd Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 2 Dec 2014 23:53:46 -0800 Subject: [PATCH 47/57] fix angular velocity from oculus, tweak hair --- interface/src/Hair.cpp | 4 ++-- interface/src/avatar/MyAvatar.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index e14fc519d3..cb664f39ed 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -18,8 +18,8 @@ const float HAIR_DAMPING = 0.99f; const float CONSTRAINT_RELAXATION = 10.0f; const float HAIR_ACCELERATION_COUPLING = 0.045f; -const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.020f; -const float HAIR_ANGULAR_ACCELERATION_COUPLING = 0.003f; +const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.001f; +const float HAIR_ANGULAR_ACCELERATION_COUPLING = 0.001f; const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; const float HAIR_STIFFNESS = 0.00f; const glm::vec3 HAIR_COLOR1(0.98f, 0.76f, 0.075f); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a4fc696bc9..9f3309ece2 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1215,8 +1215,11 @@ void MyAvatar::updateOrientation(float deltaTime) { // Record the angular velocity Head* head = getHead(); - glm::vec3 angularVelocity(yaw - head->getBaseYaw(), pitch - head->getBasePitch(), roll - head->getBaseRoll()); - head->setAngularVelocity(angularVelocity); + if (deltaTime > 0.0f) { + glm::vec3 angularVelocity(pitch - head->getBasePitch(), yaw - head->getBaseYaw(), roll - head->getBaseRoll()); + angularVelocity *= 1.0f / deltaTime; + head->setAngularVelocity(angularVelocity); + } //Invert yaw and roll when in mirror mode if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { From 49a087018f0a8d48c14f5634f91fc8f62407ca17 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 3 Dec 2014 08:22:50 -0800 Subject: [PATCH 48/57] fix for delayed heartbeats, late socket lookup --- domain-server/src/DomainServer.cpp | 31 ++++++++++++++-------- domain-server/src/DomainServer.h | 2 ++ libraries/networking/src/HifiSockAddr.cpp | 2 ++ libraries/networking/src/HifiSockAddr.h | 3 +++ libraries/networking/src/LimitedNodeList.h | 1 + 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 80777ce529..75db0b8022 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -37,6 +37,8 @@ int const DomainServer::EXIT_CODE_REBOOT = 234923; +const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io"; + DomainServer::DomainServer(int argc, char* argv[]) : QCoreApplication(argc, argv), _shutdownEventListener(this), @@ -52,7 +54,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : _webAuthenticationStateSet(), _cookieSessionHash(), _automaticNetworkingSetting(), - _settingsManager() + _settingsManager(), + _iceServerSocket(ICE_SERVER_DEFAULT_HOSTNAME, ICE_SERVER_DEFAULT_PORT) { LogUtils::init(); @@ -346,6 +349,9 @@ void DomainServer::setupAutomaticNetworking() { QTimer* dynamicIPTimer = new QTimer(this); connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN); + _automaticNetworkingSetting = + _settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString(); + if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { dynamicIPTimer->start(STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS); @@ -354,9 +360,17 @@ void DomainServer::setupAutomaticNetworking() { connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates); iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS); - // call our sendHeartbeaToIceServer immediately anytime a local or public socket changes + // call our sendHeartbeatToIceServer immediately anytime a local or public socket changes connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); + + // attempt to update our public socket now, this will send a heartbeat once we get public socket + requestCurrentPublicSocketViaSTUN(); + + // in case the STUN lookup is still happening we should re-request a public socket once we get that address + connect(&nodeList->getSTUNSockAddr(), &HifiSockAddr::lookupCompleted, + this, &DomainServer::requestCurrentPublicSocketViaSTUN); + } if (!didSetupAccountManagerWithAccessToken()) { @@ -366,9 +380,6 @@ void DomainServer::setupAutomaticNetworking() { return; } - _automaticNetworkingSetting = - _settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString(); - if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE || _automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { @@ -383,14 +394,13 @@ void DomainServer::setupAutomaticNetworking() { // send public socket changes to the data server so nodes can find us at our new IP connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::performIPAddressUpdate); + + // attempt to update our sockets now + requestCurrentPublicSocketViaSTUN(); } else { // send our heartbeat to data server so it knows what our network settings are sendHeartbeatToDataServer(); } - - // attempt to update our sockets now - requestCurrentPublicSocketViaSTUN(); - } else { qDebug() << "Cannot enable domain-server automatic networking without a domain ID." << "Please add an ID to your config file or via the web interface."; @@ -1167,8 +1177,7 @@ void DomainServer::performICEUpdates() { } void DomainServer::sendHeartbeatToIceServer() { - static HifiSockAddr ICE_SERVER_SOCK_ADDR = HifiSockAddr("ice.highfidelity.io", ICE_SERVER_DEFAULT_PORT); - LimitedNodeList::getInstance()->sendHeartbeatToIceServer(ICE_SERVER_SOCK_ADDR); + LimitedNodeList::getInstance()->sendHeartbeatToIceServer(_iceServerSocket); } void DomainServer::sendICEPingPackets() { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index de485da5e7..2a92c63923 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -154,6 +154,8 @@ private: QString _automaticNetworkingSetting; DomainServerSettingsManager _settingsManager; + + HifiSockAddr _iceServerSocket; }; diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index f2419f5124..425dffefe9 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -86,6 +86,7 @@ bool HifiSockAddr::operator==(const HifiSockAddr& rhsSockAddr) const { void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) { if (hostInfo.error() != QHostInfo::NoError) { qDebug() << "Lookup failed for" << hostInfo.lookupId() << ":" << hostInfo.errorString(); + emit lookupFailed(); } foreach(const QHostAddress& address, hostInfo.addresses()) { @@ -94,6 +95,7 @@ void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) { _address = address; qDebug() << "QHostInfo lookup result for" << hostInfo.hostName() << "with lookup ID" << hostInfo.lookupId() << "is" << address.toString(); + emit lookupCompleted(); break; } } diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 064f8032ca..4d3944012e 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -54,6 +54,9 @@ public: friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr); private slots: void handleLookupResult(const QHostInfo& hostInfo); +signals: + void lookupCompleted(); + void lookupFailed(); private: QHostAddress _address; quint16 _port; diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index ad8c1688bb..2fc8751e3f 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -105,6 +105,7 @@ public: const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; } + const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; } void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet); void processKillNode(const QByteArray& datagram); From 98f56aaa0c81955a6db55243a3b3d09b3e4a9aef Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 3 Dec 2014 09:55:55 -0800 Subject: [PATCH 49/57] fix URL/filename discrepancy for local scripts --- interface/src/Application.cpp | 10 ++++----- interface/src/Application.h | 4 ++-- libraries/script-engine/src/ScriptEngine.cpp | 23 ++++++++++---------- libraries/script-engine/src/ScriptEngine.h | 4 ++-- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9561115478..9c3eb48993 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4042,20 +4042,20 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser return scriptEngine; } -void Application::handleScriptEngineLoaded(const QUrl& scriptURL) { +void Application::handleScriptEngineLoaded(const QString& scriptFilename) { ScriptEngine* scriptEngine = qobject_cast(sender()); - _scriptEnginesHash.insertMulti(scriptURL.toString(), scriptEngine); + _scriptEnginesHash.insertMulti(scriptFilename, scriptEngine); _runningScriptsWidget->setRunningScripts(getRunningScripts()); - UserActivityLogger::getInstance().loadedScript(scriptURL.toString()); + UserActivityLogger::getInstance().loadedScript(scriptFilename); // register our application services and set it off on its own thread registerScriptEngineWithApplicationServices(scriptEngine); } -void Application::handleScriptLoadError(const QUrl& scriptURL) { +void Application::handleScriptLoadError(const QString& scriptFilename) { qDebug() << "Application::loadScript(), script failed to load..."; - QMessageBox::warning(getWindow(), "Error Loading Script", scriptURL.toString() + " failed to load."); + QMessageBox::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load."); } void Application::scriptFinished(const QString& scriptName) { diff --git a/interface/src/Application.h b/interface/src/Application.h index caaebea876..92ef00e8a5 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -394,8 +394,8 @@ private slots: void idle(); void aboutToQuit(); - void handleScriptEngineLoaded(const QUrl& scriptURL); - void handleScriptLoadError(const QUrl& scriptURL); + void handleScriptEngineLoaded(const QString& scriptFilename); + void handleScriptLoadError(const QString& scriptFilename); void connectedToDomain(const QString& hostname); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 6cef69d23f..70c536e116 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -156,31 +156,30 @@ void ScriptEngine::loadURL(const QUrl& scriptURL) { if (_isRunning) { return; } - - QString scriptURLString = scriptURL.toString(); - _fileNameString = scriptURLString; + + _fileNameString = scriptURL.toString(); QUrl url(scriptURL); // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { - url = QUrl::fromLocalFile(scriptURLString); + url = QUrl::fromLocalFile(_fileNameString); } // ok, let's see if it's valid... and if so, load it if (url.isValid()) { if (url.scheme() == "file") { - QString fileName = url.toLocalFile(); - QFile scriptFile(fileName); + _fileNameString = url.toLocalFile(); + QFile scriptFile(_fileNameString); if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { - qDebug() << "Loading file:" << fileName; + qDebug() << "ScriptEngine loading file:" << _fileNameString; QTextStream in(&scriptFile); _scriptContents = in.readAll(); - emit scriptLoaded(url); + emit scriptLoaded(_fileNameString); } else { - qDebug() << "ERROR Loading file:" << fileName; - emit errorLoadingScript(url); + qDebug() << "ERROR Loading file:" << _fileNameString; + emit errorLoadingScript(_fileNameString); } } else { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); @@ -195,10 +194,10 @@ void ScriptEngine::handleScriptDownload() { if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) { _scriptContents = reply->readAll(); - emit scriptLoaded(reply->url()); + emit scriptLoaded(_fileNameString); } else { qDebug() << "ERROR Loading file:" << reply->url().toString(); - emit errorLoadingScript(reply->url()); + emit errorLoadingScript(_fileNameString); } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 4b6b3e48ab..fd28e98cab 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -107,8 +107,8 @@ public slots: void nodeKilled(SharedNodePointer node); signals: - void scriptLoaded(const QUrl& scriptURL); - void errorLoadingScript(const QUrl& scriptURL); + void scriptLoaded(const QString& scriptFilename); + void errorLoadingScript(const QString& scriptFilename); void update(float deltaTime); void scriptEnding(); void finished(const QString& fileNameString); From f0b0c5fe0b546912581468f2c277df7aefed9fe6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 3 Dec 2014 11:08:41 -0800 Subject: [PATCH 50/57] change warp button to button face right --- examples/gamepad.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gamepad.js b/examples/gamepad.js index b4c2758edd..a2f1034e26 100644 --- a/examples/gamepad.js +++ b/examples/gamepad.js @@ -20,7 +20,7 @@ var BUTTON_TURN_AROUND = Joysticks.BUTTON_RIGHT_STICK; var BUTTON_FLY_UP = Joysticks.BUTTON_RIGHT_SHOULDER; var BUTTON_FLY_DOWN = Joysticks.BUTTON_LEFT_SHOULDER; -var BUTTON_WARP = Joysticks.BUTTON_FACE_BOTTOM; +var BUTTON_WARP = Joysticks.BUTTON_FACE_RIGHT; var BUTTON_WARP_FORWARD = Joysticks.BUTTON_DPAD_UP; var BUTTON_WARP_BACKWARD = Joysticks.BUTTON_DPAD_DOWN; From 1e93137e6b5195a2219c12c68bffb5503debad7e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Dec 2014 12:24:58 -0800 Subject: [PATCH 51/57] Fix for overflow when computing metavoxel stats. --- interface/src/ui/Stats.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index f041fb8569..d0a25f4f4a 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -461,10 +461,10 @@ void Stats::display( if (_metavoxelSendTotal > 0 || _metavoxelReceiveTotal > 0) { stringstream reliableStats; if (_metavoxelSendTotal > 0) { - reliableStats << "Upload: " << (_metavoxelSendProgress * 100 / _metavoxelSendTotal) << "% "; + reliableStats << "Upload: " << (_metavoxelSendProgress * 100LL / _metavoxelSendTotal) << "% "; } if (_metavoxelReceiveTotal > 0) { - reliableStats << "Download: " << (_metavoxelReceiveProgress * 100 / _metavoxelReceiveTotal) << "%"; + reliableStats << "Download: " << (_metavoxelReceiveProgress * 100LL / _metavoxelReceiveTotal) << "%"; } verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, reliableStats.str().c_str(), color); From d975e3f0ce8b3e6afd4c9cd58aae99dc826df414 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Dec 2014 12:38:10 -0800 Subject: [PATCH 52/57] Fixed signed/unsigned warnign. --- interface/src/Audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 167c44111e..2214bb54cf 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -1438,7 +1438,7 @@ void Audio::renderToolBox(int x, int y, bool boxed) { static const float PULSE_MAX = 1.0f; static const float PULSE_FREQUENCY = 1.0f; // in Hz qint64 now = usecTimestampNow(); - if (now - _iconPulseTimeReference > USECS_PER_SECOND) { + if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { // Prevents t from getting too big, which would diminish glm::cos precision _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); } From 88048e8599a83beea1f77a02c4eed5ddf0c605c9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Dec 2014 12:47:32 -0800 Subject: [PATCH 53/57] Make sure the position of the heightfield/voxel brush is valid before acting. --- interface/src/ui/MetavoxelEditor.cpp | 14 ++++++++++---- interface/src/ui/MetavoxelEditor.h | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index b6b43c4baf..349a73b9c7 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -884,7 +884,8 @@ void ImportHeightfieldTool::updateSpanner() { } HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) : - MetavoxelTool(editor, name, false) { + MetavoxelTool(editor, name, false), + _positionValid(false) { QWidget* widget = new QWidget(); widget->setLayout(_form = new QFormLayout()); @@ -911,8 +912,10 @@ void HeightfieldBrushTool::render() { float distance; if (!Application::getInstance()->getMetavoxels()->findFirstRayHeightfieldIntersection(origin, direction, distance)) { + _positionValid = false; return; } + _positionValid = true; Application::getInstance()->getMetavoxels()->renderHeightfieldCursor( _position = origin + distance * direction, _radius->value()); } @@ -924,7 +927,7 @@ bool HeightfieldBrushTool::eventFilter(QObject* watched, QEvent* event) { _radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE)); return true; - } else if (event->type() == QEvent::MouseButtonPress) { + } else if (event->type() == QEvent::MouseButtonPress && _positionValid) { MetavoxelEditMessage message = { createEdit(static_cast(event)->button() == Qt::RightButton) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); return true; @@ -1103,7 +1106,8 @@ void VoxelMaterialSpannerTool::applyEdit(const AttributePointer& attribute, cons } VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) : - MetavoxelTool(editor, name, false, true) { + MetavoxelTool(editor, name, false, true), + _positionValid(false) { QWidget* widget = new QWidget(); widget->setLayout(_form = new QFormLayout()); @@ -1132,8 +1136,10 @@ void VoxelBrushTool::render() { if (!(Application::getInstance()->getMetavoxels()->findFirstRayHeightfieldIntersection( origin, direction, heightfieldDistance) | Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, voxelDistance))) { + _positionValid = false; return; } + _positionValid = true; Application::getInstance()->getMetavoxels()->renderVoxelCursor( _position = origin + qMin(heightfieldDistance, voxelDistance) * direction, _radius->value()); } @@ -1145,7 +1151,7 @@ bool VoxelBrushTool::eventFilter(QObject* watched, QEvent* event) { _radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE)); return true; - } else if (event->type() == QEvent::MouseButtonPress) { + } else if (event->type() == QEvent::MouseButtonPress && _positionValid) { MetavoxelEditMessage message = { createEdit(static_cast(event)->button() == Qt::RightButton) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); return true; diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h index cccb41ecfc..15e731e2ee 100644 --- a/interface/src/ui/MetavoxelEditor.h +++ b/interface/src/ui/MetavoxelEditor.h @@ -326,6 +326,7 @@ protected: QDoubleSpinBox* _radius; glm::vec3 _position; + bool _positionValid; }; /// Allows raising or lowering parts of the heightfield. @@ -456,6 +457,7 @@ protected: QDoubleSpinBox* _radius; glm::vec3 _position; + bool _positionValid; }; /// Allows texturing parts of the voxel field. From fecb225a0653e5f7fabc4cd3b19035a749757621 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Dec 2014 12:55:58 -0800 Subject: [PATCH 54/57] Prevent erroneous resource download percentages; Qt reports the total bytes as -1 before the actual size is available. --- libraries/networking/src/ResourceCache.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 496839fdcd..e1b6327652 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -109,11 +109,11 @@ public: /// For loading resources, returns the number of bytes received. qint64 getBytesReceived() const { return _bytesReceived; } - /// For loading resources, returns the number of total bytes (or zero if unknown). + /// For loading resources, returns the number of total bytes (<= zero if unknown). qint64 getBytesTotal() const { return _bytesTotal; } /// For loading resources, returns the load progress. - float getProgress() const { return (_bytesTotal == 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } + float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } /// Refreshes the resource. void refresh(); From d08337326ce9117eef95c8451354ab6f427475a5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 Dec 2014 12:59:51 -0800 Subject: [PATCH 55/57] 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 = []; From df23a84cc27c916a4a594cc3e8b2842739b4ba02 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Dec 2014 14:18:47 -0800 Subject: [PATCH 56/57] Bump up the glyph texture size so that we don't spill over into another texture. --- interface/src/ui/TextRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index edc14dabf5..fce4c76c0d 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -29,7 +29,7 @@ // the width/height of the cached glyph textures -const int IMAGE_SIZE = 256; +const int IMAGE_SIZE = 512; static uint qHash(const TextRenderer::Properties& key, uint seed = 0) { // can be switched to qHash(key.font, seed) when we require Qt 5.3+ From ab011d5b30409aca9c0ab52dcd1a65f08ad16d0a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Dec 2014 16:49:49 -0800 Subject: [PATCH 57/57] fix for non-animating models --- libraries/entities/src/ModelEntityItem.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 63fe3daa03..0de2035dec 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -374,17 +374,9 @@ bool ModelEntityItem::isAnimatingSomething() const { } EntityItem::SimulationState ModelEntityItem::computeSimulationState() const { - EntityItem::SimulationState baseClassState = EntityItem::computeSimulationState(); - - // if the base class is static, then consider our animation state, and upgrade to changing if - // we are animating. If the base class has a higher simulation state than static, then - // use the base class state. - if (baseClassState == EntityItem::Static) { - if (isAnimatingSomething()) { - return EntityItem::Moving; - } - } - return baseClassState; + // if we're animating then we need to have update() periodically called on this entity + // which means we need to categorized as Moving + return isAnimatingSomething() ? EntityItem::Moving : EntityItem::computeSimulationState(); } void ModelEntityItem::update(const quint64& updateTime) {