From c6347eb92a5950f4b8663993ef69cd92b62d4e91 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 28 Mar 2016 13:39:43 -0700 Subject: [PATCH 01/10] checkpoint for debugging/comparing edit.js --- .../src/entities/AssignmentParentFinder.cpp | 8 ++- .../src/entities/AssignmentParentFinder.h | 2 +- interface/src/Application.cpp | 62 +++++++++++++++---- interface/src/InterfaceParentFinder.cpp | 12 ++-- interface/src/InterfaceParentFinder.h | 2 +- libraries/entities/src/EntityTree.cpp | 11 +++- libraries/entities/src/EntityTree.h | 4 +- libraries/shared/src/SpatialParentFinder.h | 6 +- libraries/shared/src/SpatiallyNestable.cpp | 4 +- libraries/shared/src/SpatiallyNestable.h | 4 +- 10 files changed, 86 insertions(+), 29 deletions(-) diff --git a/assignment-client/src/entities/AssignmentParentFinder.cpp b/assignment-client/src/entities/AssignmentParentFinder.cpp index 294556383e..a0232daff4 100644 --- a/assignment-client/src/entities/AssignmentParentFinder.cpp +++ b/assignment-client/src/entities/AssignmentParentFinder.cpp @@ -11,7 +11,7 @@ #include "AssignmentParentFinder.h" -SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& success) const { +SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& success, SpatialParentTree* entityTree) const { SpatiallyNestableWeakPointer parent; if (parentID.isNull()) { @@ -20,7 +20,11 @@ SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& } // search entities - parent = _tree->findEntityByEntityItemID(parentID); + if (entityTree) { + parent = entityTree->findByID(parentID); + } else { + parent = _tree->findEntityByEntityItemID(parentID); + } if (parent.expired()) { success = false; } else { diff --git a/assignment-client/src/entities/AssignmentParentFinder.h b/assignment-client/src/entities/AssignmentParentFinder.h index 9a776bc7dd..0c16143143 100644 --- a/assignment-client/src/entities/AssignmentParentFinder.h +++ b/assignment-client/src/entities/AssignmentParentFinder.h @@ -25,7 +25,7 @@ class AssignmentParentFinder : public SpatialParentFinder { public: AssignmentParentFinder(EntityTreePointer tree) : _tree(tree) { } virtual ~AssignmentParentFinder() { } - virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const; + virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const; protected: EntityTreePointer _tree; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9bef698167..df08afcf64 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -270,7 +270,7 @@ public: void run() override { while (!_quit) { QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); - +/* fixme uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us uint64_t now = usecTimestampNow(); auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0; @@ -319,6 +319,7 @@ public: deadlockDetectionCrash(); } #endif + */ } } @@ -2790,43 +2791,78 @@ void Application::calibrateEyeTracker5Points() { } #endif +class EntityDatum { // For parent-first sorting and mapping. +public: + EntityItemPointer item; + EntityItemProperties properties; + EntityItemID originalParentID; + EntityItemID mappedID; + EntityDatum() {}; + EntityDatum(EntityItemPointer itemArg, EntityItemProperties propertiesArg, EntityItemID parentID) : + item(itemArg), properties(propertiesArg), originalParentID(parentID) { + }; +}; bool Application::exportEntities(const QString& filename, const QVector& entityIDs) { - QVector entities; + QHash entities; auto entityTree = getEntities()->getTree(); auto exportTree = std::make_shared(); exportTree->createRootElement(); glm::vec3 root(TREE_SCALE, TREE_SCALE, TREE_SCALE); - for (auto entityID : entityIDs) { + for (auto entityID : entityIDs) { // Gather entities and properties. auto entityItem = entityTree->findEntityByEntityItemID(entityID); if (!entityItem) { + qCDebug(interfaceapp) << "Skipping export of" << entityID << "that is not in scene."; continue; } auto properties = entityItem->getProperties(); - auto position = properties.getPosition(); - + auto position = properties.getPosition(); // see setPosition, below. root.x = glm::min(root.x, position.x); root.y = glm::min(root.y, position.y); root.z = glm::min(root.z, position.z); - entities << entityItem; + qCDebug(interfaceapp) << "Exporting" << entityItem->getEntityItemID() << entityItem->getName(); + entities[entityID] = EntityDatum(entityItem, properties, properties.getParentID()); } if (entities.size() == 0) { return false; } - for (auto entityItem : entities) { - auto properties = entityItem->getProperties(); + for (EntityDatum& entityDatum : entities) { + // Recursively add the parents of entities to the exportTree, mapping their new identifiers as we go. + std::function getMapped = [&](EntityDatum& datum) { + auto originalID = datum.item->getEntityItemID(); + if (!datum.mappedID.isInvalidID()) { + qCDebug(interfaceapp) << "already mapped" << datum.properties.getName() << originalID << "=>" << datum.mappedID; + return datum.mappedID; // We are a parent that has already been added/mapped. + } + auto properties = datum.properties; + auto parentID = datum.originalParentID; + if (!datum.originalParentID.isInvalidID()) { // Recurse over ancestors, updating properties. + qCDebug(interfaceapp) << "FIXME recursing" << datum.originalParentID << "parent of" << datum.item->getEntityItemID(); + // Warning: this is not a tail-call, so exporting a REALLY deep parent hierarchy will blow the call stack. + parentID = getMapped(entities[parentID]); + properties.setParentID(parentID); + } + // The so-called root offset (which isn't) is confusing and not what content developers want. And why would queryAACube not then be offset? + // But leaving it in for bug-compatibility right now. -HRS + properties.setPosition(properties.getPosition() - root); + datum.mappedID = originalID; //EntityItemID(QUuid::createUuid()); + auto newEntity = exportTree->addEntity(datum.mappedID, properties); + qCDebug(interfaceapp) << "mapped" << properties.getName(); + qCDebug(interfaceapp) << " " << originalID << "p:" << datum.originalParentID; + qCDebug(interfaceapp) << " =>" << datum.mappedID << "p:" << parentID; - properties.setPosition(properties.getPosition() - root); - exportTree->addEntity(entityItem->getEntityItemID(), properties); + return datum.mappedID; + }; + + getMapped(entityDatum); } - // remap IDs on export so that we aren't publishing the IDs of entities in our domain - exportTree->remapIDs(); + //exportTree->remapIDs(); exportTree->writeToJSONFile(filename.toLocal8Bit().constData()); @@ -2902,7 +2938,7 @@ bool Application::importEntities(const QString& urlOrFilename) { bool success = _entityClipboard->readFromURL(url.toString()); if (success) { - _entityClipboard->remapIDs(); + // FIXME _entityClipboard->remapIDs(); _entityClipboard->reaverageOctreeElements(); } return success; diff --git a/interface/src/InterfaceParentFinder.cpp b/interface/src/InterfaceParentFinder.cpp index de4b0ce38c..b1db63debd 100644 --- a/interface/src/InterfaceParentFinder.cpp +++ b/interface/src/InterfaceParentFinder.cpp @@ -16,7 +16,7 @@ #include "InterfaceParentFinder.h" -SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& success) const { +SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& success, SpatialParentTree* entityTree) const { SpatiallyNestableWeakPointer parent; if (parentID.isNull()) { @@ -25,9 +25,13 @@ SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& s } // search entities - EntityTreeRenderer* treeRenderer = qApp->getEntities(); - EntityTreePointer tree = treeRenderer ? treeRenderer->getTree() : nullptr; - parent = tree ? tree->findEntityByEntityItemID(parentID) : nullptr; + if (entityTree) { + parent = entityTree->findByID(parentID); + } else { + EntityTreeRenderer* treeRenderer = qApp->getEntities(); + EntityTreePointer tree = treeRenderer ? treeRenderer->getTree() : nullptr; + parent = tree ? tree->findEntityByEntityItemID(parentID) : nullptr; + } if (!parent.expired()) { success = true; return parent; diff --git a/interface/src/InterfaceParentFinder.h b/interface/src/InterfaceParentFinder.h index db579c2144..a2e9fb50e4 100644 --- a/interface/src/InterfaceParentFinder.h +++ b/interface/src/InterfaceParentFinder.h @@ -21,7 +21,7 @@ class InterfaceParentFinder : public SpatialParentFinder { public: InterfaceParentFinder() { } virtual ~InterfaceParentFinder() { } - virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const; + virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const; }; #endif // hifi_InterfaceParentFinder_h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index af1c2e71aa..ef7af3da86 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1009,6 +1009,7 @@ void EntityTree::entityChanged(EntityItemPointer entity) { void EntityTree::fixupMissingParents() { MovingEntitiesOperator moveOperator(getThisPointer()); + if (!_missingParent.empty()) qCDebug(entities) << "HRS fixme fixupMissingParents" << _missingParent.count() << "entities"; QMutableVectorIterator iter(_missingParent); while (iter.hasNext()) { EntityItemWeakPointer entityWP = iter.next(); @@ -1027,6 +1028,7 @@ void EntityTree::fixupMissingParents() { bool doMove = false; if (entity->isParentIDValid()) { + qCDebug(entities) << "HRS fixme valid parent" << entity->getEntityItemID() << queryAACubeSuccess; // this entity's parent was previously not known, and now is. Update its location in the EntityTree... doMove = true; } else if (getIsServer() && _avatarIDs.contains(entity->getParentID())) { @@ -1038,6 +1040,7 @@ void EntityTree::fixupMissingParents() { _childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID(); doMove = true; } + else qCDebug(entities) << "HRS fixme failed parent" << entity->getEntityItemID() << queryAACubeSuccess; if (queryAACubeSuccess && doMove) { moveOperator.addEntityToMoveList(entity, newCube); @@ -1328,19 +1331,22 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen } bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extraData) { + qCDebug(entities) << "sendEntitiesOperation"; SendEntitiesOperationArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) { - EntityItemID newID(QUuid::createUuid()); + EntityItemID newID = entityItem->getEntityItemID(); // FIXME (QUuid::createUuid()); args->newEntityIDs->append(newID); EntityItemProperties properties = entityItem->getProperties(); properties.setPosition(properties.getPosition() + args->root); properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity + qCDebug(entities) << "sending" << newID << properties.getName() << "parent:" << properties.getParentID(); // queue the packet to send to the server args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, newID, properties); // also update the local tree instantly (note: this is not our tree, but an alternate tree) + // [Sure looks like the global application's tree to me. See callers. -HRS] if (args->localTree) { args->localTree->withWriteLock([&] { args->localTree->addEntity(newID, properties); @@ -1389,11 +1395,12 @@ bool EntityTree::readFromMap(QVariantMap& map) { } EntityItemPointer entity = addEntity(entityItemID, properties); + qCDebug(entities) << "HRS FIXME added" << entityItemID << properties.getName(); if (!entity) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); } } - + qCDebug(entities) << "HRS FIXME end of readFromMap"; return true; } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 0b85c5f32f..8cf6a7e45b 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -16,6 +16,7 @@ #include #include +#include class EntityTree; typedef std::shared_ptr EntityTreePointer; @@ -52,7 +53,7 @@ public: }; -class EntityTree : public Octree { +class EntityTree : public Octree, public SpatialParentTree { Q_OBJECT public: EntityTree(bool shouldReaverage = false); @@ -125,6 +126,7 @@ public: EntityItemPointer findClosestEntity(glm::vec3 position, float targetRadius); EntityItemPointer findEntityByID(const QUuid& id); EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID); + SpatiallyNestablePointer EntityTree::findByID(const QUuid& id) { return findEntityByID(id); } EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID diff --git a/libraries/shared/src/SpatialParentFinder.h b/libraries/shared/src/SpatialParentFinder.h index 9b49490fa5..ff2593c621 100644 --- a/libraries/shared/src/SpatialParentFinder.h +++ b/libraries/shared/src/SpatialParentFinder.h @@ -19,6 +19,10 @@ class SpatiallyNestable; using SpatiallyNestableWeakPointer = std::weak_ptr; using SpatiallyNestablePointer = std::shared_ptr; +class SpatialParentTree { +public: + SpatiallyNestablePointer findByID(const QUuid& id) { return nullptr; } +}; class SpatialParentFinder : public Dependency { @@ -31,7 +35,7 @@ public: SpatialParentFinder() { } virtual ~SpatialParentFinder() { } - virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const = 0; + virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const = 0; }; #endif // hifi_SpatialParentFinder_h diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 55da4822ef..4e091694de 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -70,7 +70,7 @@ Transform SpatiallyNestable::getParentTransform(bool& success, int depth) const return result; } -SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) const { +SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success, SpatialParentTree* entityTree) const { SpatiallyNestablePointer parent = _parent.lock(); QUuid parentID = getParentID(); // used for its locking @@ -105,7 +105,7 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) cons success = false; return nullptr; } - _parent = parentFinder->find(parentID, success); + _parent = parentFinder->find(parentID, success, entityTree); if (!success) { return nullptr; } diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index a65c7c7f2c..8800fc5ed7 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -139,14 +139,14 @@ public: void die() { _isDead = true; } bool isDead() const { return _isDead; } - bool isParentIDValid() const { bool success = false; getParentPointer(success); return success; } + bool isParentIDValid(SpatialParentTree* entityTree = nullptr) const { bool success = false; getParentPointer(success, entityTree); return success; } protected: const NestableType _nestableType; // EntityItem or an AvatarData QUuid _id; QUuid _parentID; // what is this thing's transform relative to? quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to? - SpatiallyNestablePointer getParentPointer(bool& success) const; + SpatiallyNestablePointer getParentPointer(bool& success, SpatialParentTree* entityTree = nullptr) const; mutable SpatiallyNestableWeakPointer _parent; From 7a066afa26ec5e5d3f95de381700fd322b80b40b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 28 Mar 2016 14:25:51 -0700 Subject: [PATCH 02/10] Checkpoint for freakin edit.js again. --- libraries/entities/src/EntityTree.cpp | 8 ++++---- libraries/entities/src/RecurseOctreeToMapOperator.cpp | 8 +++++--- libraries/entities/src/RecurseOctreeToMapOperator.h | 3 ++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ef7af3da86..f331d50893 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -90,7 +90,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) { _simulation->addEntity(entity); } - if (!entity->isParentIDValid()) { + if (!entity->isParentIDValid(this)) { _missingParent.append(entity); } @@ -260,7 +260,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI _missingParent.append(childEntity); continue; } - if (!childEntity->isParentIDValid()) { + if (!childEntity->isParentIDValid(this)) { _missingParent.append(childEntity); } @@ -1027,7 +1027,7 @@ void EntityTree::fixupMissingParents() { } bool doMove = false; - if (entity->isParentIDValid()) { + if (entity->isParentIDValid(this)) { qCDebug(entities) << "HRS fixme valid parent" << entity->getEntityItemID() << queryAACubeSuccess; // this entity's parent was previously not known, and now is. Update its location in the EntityTree... doMove = true; @@ -1367,7 +1367,7 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer entityDescription["Entities"] = QVariantList(); } QScriptEngine scriptEngine; - RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues, skipThoseWithBadParents); + RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues, skipThoseWithBadParents, this); recurseTreeWithOperator(&theOperator); return true; } diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.cpp b/libraries/entities/src/RecurseOctreeToMapOperator.cpp index e930d5ef5f..06a7ce3f27 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToMapOperator.cpp @@ -17,13 +17,15 @@ RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, OctreeElementPointer top, QScriptEngine* engine, bool skipDefaultValues, - bool skipThoseWithBadParents) : + bool skipThoseWithBadParents, + EntityTree* tree) : RecurseOctreeOperator(), _map(map), _top(top), _engine(engine), _skipDefaultValues(skipDefaultValues), - _skipThoseWithBadParents(skipThoseWithBadParents) + _skipThoseWithBadParents(skipThoseWithBadParents), + _entityTree(tree) { // if some element "top" was given, only save information for that element and its children. if (_top) { @@ -49,7 +51,7 @@ bool RecurseOctreeToMapOperator::postRecursion(OctreeElementPointer element) { QVariantList entitiesQList = qvariant_cast(_map["Entities"]); entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) { - if (_skipThoseWithBadParents && !entityItem->isParentIDValid()) { + if (_skipThoseWithBadParents && !entityItem->isParentIDValid(_entityTree)) { return; // we weren't able to resolve a parent from _parentID, so don't save this entity. } diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.h b/libraries/entities/src/RecurseOctreeToMapOperator.h index c64cf91b61..a774f0f7dd 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.h +++ b/libraries/entities/src/RecurseOctreeToMapOperator.h @@ -14,7 +14,7 @@ class RecurseOctreeToMapOperator : public RecurseOctreeOperator { public: RecurseOctreeToMapOperator(QVariantMap& map, OctreeElementPointer top, QScriptEngine* engine, bool skipDefaultValues, - bool skipThoseWithBadParents); + bool skipThoseWithBadParents, EntityTree* tree); bool preRecursion(OctreeElementPointer element); bool postRecursion(OctreeElementPointer element); private: @@ -24,4 +24,5 @@ public: bool _withinTop; bool _skipDefaultValues; bool _skipThoseWithBadParents; + EntityTree* _entityTree; }; From b09b9a4a0a434cf550d0e8e98b85c69bbc9c3870 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 28 Mar 2016 16:58:20 -0700 Subject: [PATCH 03/10] snapshot after using tree from entity item. --- libraries/entities/src/EntityItem.cpp | 4 ++++ libraries/entities/src/EntityItem.h | 1 + libraries/entities/src/EntityTree.cpp | 8 ++++---- libraries/entities/src/EntityTree.h | 2 +- libraries/entities/src/RecurseOctreeToMapOperator.cpp | 2 +- libraries/shared/src/SpatialParentFinder.h | 2 +- libraries/shared/src/SpatiallyNestable.cpp | 4 ++-- libraries/shared/src/SpatiallyNestable.h | 5 +++-- 8 files changed, 17 insertions(+), 11 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index bbc94e9910..c953ed819e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1010,6 +1010,10 @@ EntityTreePointer EntityItem::getTree() const { return tree; } +SpatialParentTree* EntityItem::getParentTree() const { + return getTree().get(); +} + bool EntityItem::wantTerseEditLogging() const { EntityTreePointer tree = getTree(); return tree ? tree->wantTerseEditLogging() : false; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 577bb406bc..535f2b747d 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -359,6 +359,7 @@ public: void setPhysicsInfo(void* data) { _physicsInfo = data; } EntityTreeElementPointer getElement() const { return _element; } EntityTreePointer getTree() const; + virtual SpatialParentTree* getParentTree() const; bool wantTerseEditLogging() const; glm::mat4 getEntityToWorldMatrix() const; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f331d50893..91700d0e80 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -90,7 +90,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) { _simulation->addEntity(entity); } - if (!entity->isParentIDValid(this)) { + if (!entity->isParentIDValid()) { _missingParent.append(entity); } @@ -260,7 +260,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI _missingParent.append(childEntity); continue; } - if (!childEntity->isParentIDValid(this)) { + if (!childEntity->isParentIDValid()) { _missingParent.append(childEntity); } @@ -1027,7 +1027,7 @@ void EntityTree::fixupMissingParents() { } bool doMove = false; - if (entity->isParentIDValid(this)) { + if (entity->isParentIDValid()) { qCDebug(entities) << "HRS fixme valid parent" << entity->getEntityItemID() << queryAACubeSuccess; // this entity's parent was previously not known, and now is. Update its location in the EntityTree... doMove = true; @@ -1040,7 +1040,7 @@ void EntityTree::fixupMissingParents() { _childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID(); doMove = true; } - else qCDebug(entities) << "HRS fixme failed parent" << entity->getEntityItemID() << queryAACubeSuccess; + else qCDebug(entities) << "HRS fixme failed parent" << entity->getEntityItemID() << queryAACubeSuccess << "parent:" << entity->getParentID() << !!findEntityByID(entity->getParentID()); if (queryAACubeSuccess && doMove) { moveOperator.addEntityToMoveList(entity, newCube); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 8cf6a7e45b..892cd86427 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -126,7 +126,7 @@ public: EntityItemPointer findClosestEntity(glm::vec3 position, float targetRadius); EntityItemPointer findEntityByID(const QUuid& id); EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID); - SpatiallyNestablePointer EntityTree::findByID(const QUuid& id) { return findEntityByID(id); } + virtual SpatiallyNestablePointer findByID(const QUuid& id) { return findEntityByID(id); } EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.cpp b/libraries/entities/src/RecurseOctreeToMapOperator.cpp index 06a7ce3f27..24b530db03 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToMapOperator.cpp @@ -51,7 +51,7 @@ bool RecurseOctreeToMapOperator::postRecursion(OctreeElementPointer element) { QVariantList entitiesQList = qvariant_cast(_map["Entities"]); entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) { - if (_skipThoseWithBadParents && !entityItem->isParentIDValid(_entityTree)) { + if (_skipThoseWithBadParents && !entityItem->isParentIDValid()) { return; // we weren't able to resolve a parent from _parentID, so don't save this entity. } diff --git a/libraries/shared/src/SpatialParentFinder.h b/libraries/shared/src/SpatialParentFinder.h index ff2593c621..aae7d9f040 100644 --- a/libraries/shared/src/SpatialParentFinder.h +++ b/libraries/shared/src/SpatialParentFinder.h @@ -21,7 +21,7 @@ using SpatiallyNestableWeakPointer = std::weak_ptr; using SpatiallyNestablePointer = std::shared_ptr; class SpatialParentTree { public: - SpatiallyNestablePointer findByID(const QUuid& id) { return nullptr; } + virtual SpatiallyNestablePointer findByID(const QUuid& id) { return nullptr; } }; class SpatialParentFinder : public Dependency { diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 4e091694de..13bf5d9054 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -70,7 +70,7 @@ Transform SpatiallyNestable::getParentTransform(bool& success, int depth) const return result; } -SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success, SpatialParentTree* entityTree) const { +SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) const { SpatiallyNestablePointer parent = _parent.lock(); QUuid parentID = getParentID(); // used for its locking @@ -105,7 +105,7 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success, Spat success = false; return nullptr; } - _parent = parentFinder->find(parentID, success, entityTree); + _parent = parentFinder->find(parentID, success, getParentTree()); if (!success) { return nullptr; } diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 8800fc5ed7..379f2facd7 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -139,14 +139,15 @@ public: void die() { _isDead = true; } bool isDead() const { return _isDead; } - bool isParentIDValid(SpatialParentTree* entityTree = nullptr) const { bool success = false; getParentPointer(success, entityTree); return success; } + bool isParentIDValid() const { bool success = false; getParentPointer(success); return success; } + virtual SpatialParentTree* getParentTree() const { return nullptr; } protected: const NestableType _nestableType; // EntityItem or an AvatarData QUuid _id; QUuid _parentID; // what is this thing's transform relative to? quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to? - SpatiallyNestablePointer getParentPointer(bool& success, SpatialParentTree* entityTree = nullptr) const; + SpatiallyNestablePointer getParentPointer(bool& success) const; mutable SpatiallyNestableWeakPointer _parent; From 5db1c33e4dbf6e6edeb76f7b206d0f5a81d93a97 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 29 Mar 2016 09:18:29 -0700 Subject: [PATCH 04/10] kill offsets --- interface/src/Application.cpp | 5 +++-- libraries/entities/src/EntityTree.cpp | 11 ++++++----- libraries/entities/src/RecurseOctreeToMapOperator.cpp | 6 ++---- libraries/entities/src/RecurseOctreeToMapOperator.h | 3 +-- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bc2bbc11e0..e5ecd9ce92 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2849,13 +2849,14 @@ bool Application::exportEntities(const QString& filename, const QVectoraddEntity(datum.mappedID, properties); qCDebug(interfaceapp) << "mapped" << properties.getName(); qCDebug(interfaceapp) << " " << originalID << "p:" << datum.originalParentID; qCDebug(interfaceapp) << " =>" << datum.mappedID << "p:" << parentID; - + qCDebug(interfaceapp) << " @" << properties.getPosition() << "/" << properties.getLocalPosition(); + return datum.mappedID; }; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 91700d0e80..76e1f618ee 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1335,12 +1335,13 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra SendEntitiesOperationArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) { - EntityItemID newID = entityItem->getEntityItemID(); // FIXME (QUuid::createUuid()); + EntityItemID newID = /*entityItem->getEntityItemID(); // FIXME*/ (QUuid::createUuid()); + // FIXME: add map to SendEntitiesOperationArgs, and recurse through parent using the map args->newEntityIDs->append(newID); EntityItemProperties properties = entityItem->getProperties(); - properties.setPosition(properties.getPosition() + args->root); + //FIXME properties.setPosition(properties.getPosition() + args->root); properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity - qCDebug(entities) << "sending" << newID << properties.getName() << "parent:" << properties.getParentID(); + qCDebug(entities) << "sending" << newID << properties.getName() << "parent:" << properties.getParentID() << "pos:" << properties.getPosition(); // queue the packet to send to the server args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, newID, properties); @@ -1367,7 +1368,7 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer entityDescription["Entities"] = QVariantList(); } QScriptEngine scriptEngine; - RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues, skipThoseWithBadParents, this); + RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues, skipThoseWithBadParents); recurseTreeWithOperator(&theOperator); return true; } @@ -1395,7 +1396,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { } EntityItemPointer entity = addEntity(entityItemID, properties); - qCDebug(entities) << "HRS FIXME added" << entityItemID << properties.getName(); + qCDebug(entities) << "HRS FIXME added" << entityItemID << properties.getName() << "@" << properties.getPosition(); if (!entity) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); } diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.cpp b/libraries/entities/src/RecurseOctreeToMapOperator.cpp index 24b530db03..e930d5ef5f 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToMapOperator.cpp @@ -17,15 +17,13 @@ RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, OctreeElementPointer top, QScriptEngine* engine, bool skipDefaultValues, - bool skipThoseWithBadParents, - EntityTree* tree) : + bool skipThoseWithBadParents) : RecurseOctreeOperator(), _map(map), _top(top), _engine(engine), _skipDefaultValues(skipDefaultValues), - _skipThoseWithBadParents(skipThoseWithBadParents), - _entityTree(tree) + _skipThoseWithBadParents(skipThoseWithBadParents) { // if some element "top" was given, only save information for that element and its children. if (_top) { diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.h b/libraries/entities/src/RecurseOctreeToMapOperator.h index a774f0f7dd..c64cf91b61 100644 --- a/libraries/entities/src/RecurseOctreeToMapOperator.h +++ b/libraries/entities/src/RecurseOctreeToMapOperator.h @@ -14,7 +14,7 @@ class RecurseOctreeToMapOperator : public RecurseOctreeOperator { public: RecurseOctreeToMapOperator(QVariantMap& map, OctreeElementPointer top, QScriptEngine* engine, bool skipDefaultValues, - bool skipThoseWithBadParents, EntityTree* tree); + bool skipThoseWithBadParents); bool preRecursion(OctreeElementPointer element); bool postRecursion(OctreeElementPointer element); private: @@ -24,5 +24,4 @@ public: bool _withinTop; bool _skipDefaultValues; bool _skipThoseWithBadParents; - EntityTree* _entityTree; }; From 4a28dadae57e8e8332331c9b6b7abc3e8973a3f6 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 29 Mar 2016 13:26:50 -0700 Subject: [PATCH 05/10] Working, but not cleaned up. --- interface/src/Application.cpp | 62 ++++++++++++++---------- libraries/entities/src/EntityTree.cpp | 68 +++++++++++++++++++++------ libraries/entities/src/EntityTree.h | 5 +- 3 files changed, 92 insertions(+), 43 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 83da078ddd..95990d7c63 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2791,18 +2791,18 @@ void Application::calibrateEyeTracker5Points() { } #endif -class EntityDatum { // For parent-first sorting and mapping. -public: - EntityItemPointer item; - EntityItemProperties properties; - EntityItemID originalParentID; - EntityItemID mappedID; - EntityDatum() {}; - EntityDatum(EntityItemPointer itemArg, EntityItemProperties propertiesArg, EntityItemID parentID) : - item(itemArg), properties(propertiesArg), originalParentID(parentID) { - }; -}; bool Application::exportEntities(const QString& filename, const QVector& entityIDs) { + class EntityDatum { // For parent-first sorting and mapping. + public: + EntityItemPointer item; + EntityItemProperties properties; + EntityItemID originalParentID; + EntityItemID mappedID; + EntityDatum() {}; + EntityDatum(EntityItemPointer itemArg, EntityItemProperties propertiesArg, EntityItemID parentID) : + item(itemArg), properties(propertiesArg), originalParentID(parentID) { + }; + }; QHash entities; auto entityTree = getEntities()->getTree(); @@ -2818,13 +2818,15 @@ bool Application::exportEntities(const QString& filename, const QVectorgetProperties(); - auto position = properties.getPosition(); // see setPosition, below. - root.x = glm::min(root.x, position.x); - root.y = glm::min(root.y, position.y); - root.z = glm::min(root.z, position.z); - + EntityItemID parentID = properties.getParentID(); + if (parentID.isInvalidID() || !entityIDs.contains(parentID)) { + auto position = entityItem->getPosition(); // If parent wasn't selected, we want absolute position, which isn't in properties. + root.x = glm::min(root.x, position.x); + root.y = glm::min(root.y, position.y); + root.z = glm::min(root.z, position.z); + } qCDebug(interfaceapp) << "Exporting" << entityItem->getEntityItemID() << entityItem->getName(); - entities[entityID] = EntityDatum(entityItem, properties, properties.getParentID()); + entities[entityID] = EntityDatum(entityItem, properties, parentID); } if (entities.size() == 0) { @@ -2833,7 +2835,7 @@ bool Application::exportEntities(const QString& filename, const QVector getMapped = [&](EntityDatum& datum) { + std::function getMapped = [&](EntityDatum& datum) { // FIXME: move definition outside the loop auto originalID = datum.item->getEntityItemID(); if (!datum.mappedID.isInvalidID()) { qCDebug(interfaceapp) << "already mapped" << datum.properties.getName() << originalID << "=>" << datum.mappedID; @@ -2841,16 +2843,26 @@ bool Application::exportEntities(const QString& filename, const QVectorgetPosition(success); + if (success) { + properties.setPosition(globalPosition - root); + if (!parentID.isInvalidID()) { // There's a parent that we won't output. Make the other data global. + properties.setRotation(datum.item->getRotation()); + properties.setDimensions(datum.item->getDimensions()); + // Should we do velocities and accelerations, too? + } + } else { + properties.setPosition(datum.item->getQueryAACube().calcCenter() - root); // best we can do + } + } else {// Recurse over ancestors, updating properties. qCDebug(interfaceapp) << "FIXME recursing" << datum.originalParentID << "parent of" << datum.item->getEntityItemID(); - // Warning: this is not a tail-call, so exporting a REALLY deep parent hierarchy will blow the call stack. + // Warning: could blow the call stack if the parent hierarchy is VERY deep. parentID = getMapped(entities[parentID]); properties.setParentID(parentID); } - // The so-called root offset (which isn't) is confusing and not what content developers want. And why would queryAACube not then be offset? - // But leaving it in for bug-compatibility right now. -HRS - // FIXME properties.setPosition(properties.getPosition() - root); - datum.mappedID = originalID; //EntityItemID(QUuid::createUuid()); + datum.mappedID = originalID; // FIXME: simplify because we don't have to map ids. auto newEntity = exportTree->addEntity(datum.mappedID, properties); qCDebug(interfaceapp) << "mapped" << properties.getName(); qCDebug(interfaceapp) << " " << originalID << "p:" << datum.originalParentID; @@ -2862,8 +2874,6 @@ bool Application::exportEntities(const QString& filename, const QVectorremapIDs(); exportTree->writeToJSONFile(filename.toLocal8Bit().constData()); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 76e1f618ee..2d8e20c69a 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1320,26 +1320,62 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen float x, float y, float z) { SendEntitiesOperationArgs args; args.packetSender = packetSender; - args.localTree = localTree; + args.ourTree = this; + args.otherTree = localTree; args.root = glm::vec3(x, y, z); - QVector newEntityIDs; - args.newEntityIDs = &newEntityIDs; + // If this is called repeatedly (e.g., multiple pastes with the same data), the new elements will clash unless we use new identifiers. + // We need to keep a map so that we can map parent identifiers correctly. + QHash map; + args.map = ↦ recurseTreeWithOperation(sendEntitiesOperation, &args); packetSender->releaseQueuedMessages(); - return newEntityIDs; + return map.values().toVector(); } bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extraData) { qCDebug(entities) << "sendEntitiesOperation"; SendEntitiesOperationArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) { - EntityItemID newID = /*entityItem->getEntityItemID(); // FIXME*/ (QUuid::createUuid()); - // FIXME: add map to SendEntitiesOperationArgs, and recurse through parent using the map - args->newEntityIDs->append(newID); - EntityItemProperties properties = entityItem->getProperties(); - //FIXME properties.setPosition(properties.getPosition() + args->root); + std::function getMapped = [&](EntityItemPointer& item) -> const EntityItemID { + EntityItemID oldID = item->getEntityItemID(); + if (args->map->contains(oldID)) { // Already been handled (e.g., as a parent of somebody that we've processed). + return args->map->value(oldID); + } + EntityItemID newID = QUuid::createUuid(); + args->map->insert(oldID, newID); + EntityItemProperties properties = item->getProperties(); + EntityItemID oldParentID = properties.getParentID(); + if (oldParentID.isInvalidID()) { // no parent + properties.setPosition(properties.getPosition() + args->root); + } else { + EntityItemPointer parentEntity = args->ourTree->findEntityByEntityItemID(oldParentID); + if (parentEntity) { // map the parent + // Warning: could blow the call stack if the parent hierarchy is VERY deep. + properties.setParentID(getMapped(parentEntity)); + // But do not add root offset in this case. + } else { // Should not happen, but let's try to be helpful... + // Log what we can. + QString name = properties.getName(); + if (name.isEmpty()) { + name = EntityTypes::getEntityTypeName(properties.getType()); + } + bool success; + glm::vec3 position = item->getPosition(success); + qCWarning(entities) << "Cannot find" << oldParentID << "parent of" << oldID << name << (success ? "" : "and unable to resolve geometry"); + // Adjust geometry with absolute/global values. + if (success) { + properties.setPosition(position + args->root); + properties.setRotation(item->getRotation()); + properties.setDimensions(item->getDimensions()); + // Should we do velocities and accelerations, too? + } else { + properties.setPosition(item->getQueryAACube().calcCenter() + args->root); // best we can do + } + QUuid empty; + properties.setParentID(empty); + } + } properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity qCDebug(entities) << "sending" << newID << properties.getName() << "parent:" << properties.getParentID() << "pos:" << properties.getPosition(); @@ -1347,13 +1383,15 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, newID, properties); // also update the local tree instantly (note: this is not our tree, but an alternate tree) - // [Sure looks like the global application's tree to me. See callers. -HRS] - if (args->localTree) { - args->localTree->withWriteLock([&] { - args->localTree->addEntity(newID, properties); + if (args->otherTree) { + args->otherTree->withWriteLock([&] { + args->otherTree->addEntity(newID, properties); }); } - }); + return newID; + }; + + entityTreeElement->forEachEntity(getMapped); return true; } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 892cd86427..f3400feb8e 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -47,9 +47,10 @@ public: class SendEntitiesOperationArgs { public: glm::vec3 root; - EntityTreePointer localTree; + EntityTree* ourTree; + EntityTreePointer otherTree; EntityEditPacketSender* packetSender; - QVector* newEntityIDs; + QHash* map; }; From bcb729eac2eaa3b0f5d2b5bcec403d5563304c94 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 29 Mar 2016 15:21:06 -0700 Subject: [PATCH 06/10] Abstract out globalizeProperties. --- interface/src/Application.cpp | 65 +++++++-------------------- libraries/entities/src/EntityItem.cpp | 22 +++++++++ libraries/entities/src/EntityItem.h | 2 + libraries/entities/src/EntityTree.cpp | 26 +---------- 4 files changed, 43 insertions(+), 72 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 95990d7c63..362658360e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2792,7 +2792,7 @@ void Application::calibrateEyeTracker5Points() { #endif bool Application::exportEntities(const QString& filename, const QVector& entityIDs) { - class EntityDatum { // For parent-first sorting and mapping. + /* class EntityDatum { // For parent-first sorting and mapping. public: EntityItemPointer item; EntityItemProperties properties; @@ -2803,7 +2803,8 @@ bool Application::exportEntities(const QString& filename, const QVector entities; + QHash entities;*/ + QHash entities; auto entityTree = getEntities()->getTree(); auto exportTree = std::make_shared(); @@ -2813,66 +2814,34 @@ bool Application::exportEntities(const QString& filename, const QVectorfindEntityByEntityItemID(entityID); if (!entityItem) { - qCDebug(interfaceapp) << "Skipping export of" << entityID << "that is not in scene."; + qCWarning(interfaceapp) << "Skipping export of" << entityID << "that is not in scene."; continue; } - auto properties = entityItem->getProperties(); - EntityItemID parentID = properties.getParentID(); - if (parentID.isInvalidID() || !entityIDs.contains(parentID)) { + EntityItemID parentID = entityItem->getParentID(); + if (parentID.isInvalidID() || !entityIDs.contains(parentID) || !entityTree->findEntityByEntityItemID(parentID)) { auto position = entityItem->getPosition(); // If parent wasn't selected, we want absolute position, which isn't in properties. root.x = glm::min(root.x, position.x); root.y = glm::min(root.y, position.y); root.z = glm::min(root.z, position.z); } - qCDebug(interfaceapp) << "Exporting" << entityItem->getEntityItemID() << entityItem->getName(); - entities[entityID] = EntityDatum(entityItem, properties, parentID); + entities[entityID] = entityItem; // EntityDatum(entityItem, entityItem->getProperties(), parentID); } if (entities.size() == 0) { return false; } - for (EntityDatum& entityDatum : entities) { - // Recursively add the parents of entities to the exportTree, mapping their new identifiers as we go. - std::function getMapped = [&](EntityDatum& datum) { // FIXME: move definition outside the loop - auto originalID = datum.item->getEntityItemID(); - if (!datum.mappedID.isInvalidID()) { - qCDebug(interfaceapp) << "already mapped" << datum.properties.getName() << originalID << "=>" << datum.mappedID; - return datum.mappedID; // We are a parent that has already been added/mapped. - } - auto properties = datum.properties; - auto parentID = datum.originalParentID; - if (parentID.isInvalidID() || !entityIDs.contains(parentID)) { - bool success; - auto globalPosition = datum.item->getPosition(success); - if (success) { - properties.setPosition(globalPosition - root); - if (!parentID.isInvalidID()) { // There's a parent that we won't output. Make the other data global. - properties.setRotation(datum.item->getRotation()); - properties.setDimensions(datum.item->getDimensions()); - // Should we do velocities and accelerations, too? - } - } else { - properties.setPosition(datum.item->getQueryAACube().calcCenter() - root); // best we can do - } - } else {// Recurse over ancestors, updating properties. - qCDebug(interfaceapp) << "FIXME recursing" << datum.originalParentID << "parent of" << datum.item->getEntityItemID(); - // Warning: could blow the call stack if the parent hierarchy is VERY deep. - parentID = getMapped(entities[parentID]); - properties.setParentID(parentID); - } - datum.mappedID = originalID; // FIXME: simplify because we don't have to map ids. - auto newEntity = exportTree->addEntity(datum.mappedID, properties); - qCDebug(interfaceapp) << "mapped" << properties.getName(); - qCDebug(interfaceapp) << " " << originalID << "p:" << datum.originalParentID; - qCDebug(interfaceapp) << " =>" << datum.mappedID << "p:" << parentID; - qCDebug(interfaceapp) << " @" << properties.getPosition() << "/" << properties.getLocalPosition(); - - return datum.mappedID; - }; - - getMapped(entityDatum); + //for (EntityDatum& entityDatum : entities) { + for (EntityItemPointer& entityDatum : entities) { + auto properties = entityDatum->getProperties(); + EntityItemID parentID = properties.getParentID(); + if (parentID.isInvalidID()) { + properties.setPosition(properties.getPosition() - root); + } else if (!entities.contains(parentID)) { + entityDatum->globalizeProperties(properties, "Parent %3 of %2 %1 is not selected for export.", -root); + } // else valid parent -- don't offset + exportTree->addEntity(entityDatum->getEntityItemID(), properties); } exportTree->writeToJSONFile(filename.toLocal8Bit().constData()); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c953ed819e..6a016720d8 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1982,3 +1982,25 @@ void EntityItem::dimensionsChanged() { requiresRecalcBoxes(); SpatiallyNestable::dimensionsChanged(); // Do what you have to do } + +void EntityItem::globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate, const glm::vec3& offset) const { + bool success; + auto globalPosition = getPosition(success); + if (success) { + properties.setPosition(globalPosition + offset); + properties.setRotation(getRotation()); + properties.setDimensions(getDimensions()); + // Should we do velocities and accelerations, too? This could end up being quite involved, which is why the method exists. + } else { + properties.setPosition(getQueryAACube().calcCenter() + offset); // best we can do + } + if (!messageTemplate.isEmpty()) { + QString name = properties.getName(); + if (name.isEmpty()) { + name = EntityTypes::getEntityTypeName(properties.getType()); + } + qCWarning(entities) << messageTemplate.arg(getEntityItemID().toString()).arg(name).arg(properties.getParentID().toString()); + } + QUuid empty; + properties.setParentID(empty); +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 535f2b747d..622f78b2d3 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -86,6 +86,8 @@ public: /// returns true if something changed virtual bool setProperties(const EntityItemProperties& properties); + // Update properties with empty parent id and globalized/absolute values (applying offset), and apply (non-empty) log template to args id, name-or-type, parent id. + void globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate = QString(), const glm::vec3& offset = glm::vec3(0.0f)) const; /// Override this in your derived class if you'd like to be informed when something about the state of the entity /// has changed. This will be called with properties change or when new data is loaded from a stream diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 2d8e20c69a..0d714eaafb 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1334,7 +1334,6 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen } bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extraData) { - qCDebug(entities) << "sendEntitiesOperation"; SendEntitiesOperationArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); std::function getMapped = [&](EntityItemPointer& item) -> const EntityItemID { @@ -1351,33 +1350,14 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra } else { EntityItemPointer parentEntity = args->ourTree->findEntityByEntityItemID(oldParentID); if (parentEntity) { // map the parent - // Warning: could blow the call stack if the parent hierarchy is VERY deep. + // Warning: (non-tail) recursion of getMapped could blow the call stack if the parent hierarchy is VERY deep. properties.setParentID(getMapped(parentEntity)); // But do not add root offset in this case. } else { // Should not happen, but let's try to be helpful... - // Log what we can. - QString name = properties.getName(); - if (name.isEmpty()) { - name = EntityTypes::getEntityTypeName(properties.getType()); - } - bool success; - glm::vec3 position = item->getPosition(success); - qCWarning(entities) << "Cannot find" << oldParentID << "parent of" << oldID << name << (success ? "" : "and unable to resolve geometry"); - // Adjust geometry with absolute/global values. - if (success) { - properties.setPosition(position + args->root); - properties.setRotation(item->getRotation()); - properties.setDimensions(item->getDimensions()); - // Should we do velocities and accelerations, too? - } else { - properties.setPosition(item->getQueryAACube().calcCenter() + args->root); // best we can do - } - QUuid empty; - properties.setParentID(empty); + item->globalizeProperties(properties, "Cannot find %3 parent of %2 %1", args->root); } } properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity - qCDebug(entities) << "sending" << newID << properties.getName() << "parent:" << properties.getParentID() << "pos:" << properties.getPosition(); // queue the packet to send to the server args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, newID, properties); @@ -1434,12 +1414,10 @@ bool EntityTree::readFromMap(QVariantMap& map) { } EntityItemPointer entity = addEntity(entityItemID, properties); - qCDebug(entities) << "HRS FIXME added" << entityItemID << properties.getName() << "@" << properties.getPosition(); if (!entity) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); } } - qCDebug(entities) << "HRS FIXME end of readFromMap"; return true; } From 4ebf81616689e215c7478f234abf2fb0312c1491 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 29 Mar 2016 16:17:19 -0700 Subject: [PATCH 07/10] cleanup --- interface/src/Application.cpp | 66 ++++++++------------------- interface/src/Application.h | 2 +- libraries/entities/src/EntityTree.cpp | 5 +- 3 files changed, 21 insertions(+), 52 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 362658360e..8285d92004 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2791,19 +2791,7 @@ void Application::calibrateEyeTracker5Points() { } #endif -bool Application::exportEntities(const QString& filename, const QVector& entityIDs) { - /* class EntityDatum { // For parent-first sorting and mapping. - public: - EntityItemPointer item; - EntityItemProperties properties; - EntityItemID originalParentID; - EntityItemID mappedID; - EntityDatum() {}; - EntityDatum(EntityItemPointer itemArg, EntityItemProperties propertiesArg, EntityItemID parentID) : - item(itemArg), properties(propertiesArg), originalParentID(parentID) { - }; - }; - QHash entities;*/ +bool Application::exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset) { QHash entities; auto entityTree = getEntities()->getTree(); @@ -2818,21 +2806,25 @@ bool Application::exportEntities(const QString& filename, const QVectorgetParentID(); - if (parentID.isInvalidID() || !entityIDs.contains(parentID) || !entityTree->findEntityByEntityItemID(parentID)) { - auto position = entityItem->getPosition(); // If parent wasn't selected, we want absolute position, which isn't in properties. - root.x = glm::min(root.x, position.x); - root.y = glm::min(root.y, position.y); - root.z = glm::min(root.z, position.z); + if (!givenOffset) { + EntityItemID parentID = entityItem->getParentID(); + if (parentID.isInvalidID() || !entityIDs.contains(parentID) || !entityTree->findEntityByEntityItemID(parentID)) { + auto position = entityItem->getPosition(); // If parent wasn't selected, we want absolute position, which isn't in properties. + root.x = glm::min(root.x, position.x); + root.y = glm::min(root.y, position.y); + root.z = glm::min(root.z, position.z); + } } - entities[entityID] = entityItem; // EntityDatum(entityItem, entityItem->getProperties(), parentID); + entities[entityID] = entityItem; } if (entities.size() == 0) { return false; } - //for (EntityDatum& entityDatum : entities) { + if (givenOffset) { + root = *givenOffset; + } for (EntityItemPointer& entityDatum : entities) { auto properties = entityDatum->getProperties(); EntityItemID parentID = properties.getParentID(); @@ -2852,33 +2844,14 @@ bool Application::exportEntities(const QString& filename, const QVector entities; - getEntities()->getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities); - - if (entities.size() > 0) { - glm::vec3 root(x, y, z); - auto exportTree = std::make_shared(); - exportTree->createRootElement(); - - for (int i = 0; i < entities.size(); i++) { - EntityItemProperties properties = entities.at(i)->getProperties(); - EntityItemID id = entities.at(i)->getEntityItemID(); - properties.setPosition(properties.getPosition() - root); - exportTree->addEntity(id, properties); - } - - // remap IDs on export so that we aren't publishing the IDs of entities in our domain - exportTree->remapIDs(); - - exportTree->writeToSVOFile(filename.toLocal8Bit().constData()); - } else { - qCDebug(interfaceapp) << "No models were selected"; - return false; + QVector ids; + getEntities()->getTree()->findEntities(AACube(offset, scale), entities); + foreach(EntityItemPointer entity, entities) { + ids << entity->getEntityItemID(); } - - // restore the main window's active state - _window->activateWindow(); - return true; + return exportEntities(filename, ids, &offset); } void Application::loadSettings() { @@ -2911,7 +2884,6 @@ bool Application::importEntities(const QString& urlOrFilename) { bool success = _entityClipboard->readFromURL(urlOrFilename); if (success) { - // FIXME _entityClipboard->remapIDs(); _entityClipboard->reaverageOctreeElements(); } return success; diff --git a/interface/src/Application.h b/interface/src/Application.h index d21e647bc7..f20d72fcb6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -233,7 +233,7 @@ signals: public slots: QVector pasteEntities(float x, float y, float z); - bool exportEntities(const QString& filename, const QVector& entityIDs); + bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); bool exportEntities(const QString& filename, float x, float y, float z, float scale); bool importEntities(const QString& url); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 0d714eaafb..ab5edb88d8 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1009,7 +1009,6 @@ void EntityTree::entityChanged(EntityItemPointer entity) { void EntityTree::fixupMissingParents() { MovingEntitiesOperator moveOperator(getThisPointer()); - if (!_missingParent.empty()) qCDebug(entities) << "HRS fixme fixupMissingParents" << _missingParent.count() << "entities"; QMutableVectorIterator iter(_missingParent); while (iter.hasNext()) { EntityItemWeakPointer entityWP = iter.next(); @@ -1028,7 +1027,6 @@ void EntityTree::fixupMissingParents() { bool doMove = false; if (entity->isParentIDValid()) { - qCDebug(entities) << "HRS fixme valid parent" << entity->getEntityItemID() << queryAACubeSuccess; // this entity's parent was previously not known, and now is. Update its location in the EntityTree... doMove = true; } else if (getIsServer() && _avatarIDs.contains(entity->getParentID())) { @@ -1040,7 +1038,6 @@ void EntityTree::fixupMissingParents() { _childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID(); doMove = true; } - else qCDebug(entities) << "HRS fixme failed parent" << entity->getEntityItemID() << queryAACubeSuccess << "parent:" << entity->getParentID() << !!findEntityByID(entity->getParentID()); if (queryAACubeSuccess && doMove) { moveOperator.addEntityToMoveList(entity, newCube); @@ -1342,7 +1339,6 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra return args->map->value(oldID); } EntityItemID newID = QUuid::createUuid(); - args->map->insert(oldID, newID); EntityItemProperties properties = item->getProperties(); EntityItemID oldParentID = properties.getParentID(); if (oldParentID.isInvalidID()) { // no parent @@ -1368,6 +1364,7 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra args->otherTree->addEntity(newID, properties); }); } + args->map->insert(oldID, newID); return newID; }; From eef9de4d4f8b82755025ba121611afc2821403f5 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 29 Mar 2016 16:28:02 -0700 Subject: [PATCH 08/10] Remove obsolete remapIDs. --- libraries/entities/src/EntityTree.cpp | 5 ---- libraries/entities/src/EntityTree.h | 2 -- libraries/entities/src/RemapIDOperator.cpp | 33 ---------------------- libraries/entities/src/RemapIDOperator.h | 30 -------------------- 4 files changed, 70 deletions(-) delete mode 100644 libraries/entities/src/RemapIDOperator.cpp delete mode 100644 libraries/entities/src/RemapIDOperator.h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ab5edb88d8..458ca90435 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1372,11 +1372,6 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra return true; } -void EntityTree::remapIDs() { - RemapIDOperator theOperator; - recurseTreeWithOperator(&theOperator); -} - bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues, bool skipThoseWithBadParents) { if (! entityDescription.contains("Entities")) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index f3400feb8e..5c5f5b4eb9 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -203,8 +203,6 @@ public: bool wantTerseEditLogging() const { return _wantTerseEditLogging; } void setWantTerseEditLogging(bool value) { _wantTerseEditLogging = value; } - void remapIDs(); - virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues, bool skipThoseWithBadParents) override; virtual bool readFromMap(QVariantMap& entityDescription) override; diff --git a/libraries/entities/src/RemapIDOperator.cpp b/libraries/entities/src/RemapIDOperator.cpp deleted file mode 100644 index eee6e49a1c..0000000000 --- a/libraries/entities/src/RemapIDOperator.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// -// RemapIDOperator.cpp -// libraries/entities/src -// -// Created by Seth Alves on 2015-12-6. -// Copyright 2015 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 "EntityTree.h" -#include "RemapIDOperator.h" - -QUuid RemapIDOperator::remap(const QUuid& oldID) { - if (oldID.isNull()) { - return oldID; - } - if (!_oldToNew.contains(oldID)) { - _oldToNew[oldID] = QUuid::createUuid(); - } - return _oldToNew[oldID]; -} - -bool RemapIDOperator::postRecursion(OctreeElementPointer element) { - EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) { - entityItem->setID(remap(entityItem->getID())); - entityItem->setParentID(remap(entityItem->getParentID())); - }); - return true; -} diff --git a/libraries/entities/src/RemapIDOperator.h b/libraries/entities/src/RemapIDOperator.h deleted file mode 100644 index 439aec28fc..0000000000 --- a/libraries/entities/src/RemapIDOperator.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// RemapIDOperator.h -// libraries/entities/src -// -// Created by Seth Alves on 2015-12-6. -// Copyright 2015 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_RemapIDOperator_h -#define hifi_RemapIDOperator_h - -#include "Octree.h" - -// this will change all the IDs in an EntityTree. Parent/Child relationships are maintained. - -class RemapIDOperator : public RecurseOctreeOperator { -public: - RemapIDOperator() : RecurseOctreeOperator() {} - ~RemapIDOperator() {} - virtual bool preRecursion(OctreeElementPointer element) { return true; } - virtual bool postRecursion(OctreeElementPointer element); -private: - QUuid remap(const QUuid& oldID); - QHash _oldToNew; -}; - -#endif // hifi_RemapIDOperator_h From 86a77d4f55eee99394c59343db34cb8c5552305a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 29 Mar 2016 16:55:17 -0700 Subject: [PATCH 09/10] Remove unused/non-existent include. --- libraries/entities/src/EntityTree.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 458ca90435..5292e12ae8 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -24,7 +24,6 @@ #include "EntitiesLogging.h" #include "RecurseOctreeToMapOperator.h" #include "LogHandler.h" -#include "RemapIDOperator.h" static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50; From 5381be6902b8fd774ba953e87610d9e098c6d7b9 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 29 Mar 2016 17:06:19 -0700 Subject: [PATCH 10/10] Whitespace --- interface/src/Application.cpp | 3 +-- libraries/entities/src/EntityItem.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8285d92004..4f1dfe77f3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -270,7 +270,7 @@ public: void run() override { while (!_quit) { QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); -/* fixme + uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us uint64_t now = usecTimestampNow(); auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0; @@ -319,7 +319,6 @@ public: deadlockDetectionCrash(); } #endif - */ } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 6a016720d8..431d638063 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2003,4 +2003,4 @@ void EntityItem::globalizeProperties(EntityItemProperties& properties, const QSt } QUuid empty; properties.setParentID(empty); -} \ No newline at end of file +}