From 4a28dadae57e8e8332331c9b6b7abc3e8973a3f6 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 29 Mar 2016 13:26:50 -0700 Subject: [PATCH] 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; };