From 9b075992182be6ff6053e7752e24e643c00531ba Mon Sep 17 00:00:00 2001 From: codereader Date: Tue, 10 Mar 2020 14:35:18 +0100 Subject: [PATCH] Expand the map marge/import code to handle incoming selection group assignments, assigning new group IDs not in conflict with the target scene. --- include/iselectiongroup.h | 2 +- libs/scene/SelectableNode.cpp | 2 +- libs/scene/SelectableNode.h | 2 +- radiant/map/algorithm/Import.cpp | 104 ++++++++++++++++++++++++++++--- 4 files changed, 99 insertions(+), 11 deletions(-) diff --git a/include/iselectiongroup.h b/include/iselectiongroup.h index e02ed361f7..d44e9b3901 100644 --- a/include/iselectiongroup.h +++ b/include/iselectiongroup.h @@ -30,7 +30,7 @@ class IGroupSelectable : // Returns all group assignments of this node // The most recently added group is at the back of the list - virtual const GroupIds& getGroupIds() = 0; + virtual const GroupIds& getGroupIds() const = 0; // Special overload to control whether this selectable should propagate // the status change to the group it belongs to. diff --git a/libs/scene/SelectableNode.cpp b/libs/scene/SelectableNode.cpp index 5a89452be4..e7e0b952bd 100644 --- a/libs/scene/SelectableNode.cpp +++ b/libs/scene/SelectableNode.cpp @@ -126,7 +126,7 @@ std::size_t SelectableNode::getMostRecentGroupId() return _groups.back(); } -const SelectableNode::GroupIds& SelectableNode::getGroupIds() +const SelectableNode::GroupIds& SelectableNode::getGroupIds() const { return _groups; } diff --git a/libs/scene/SelectableNode.h b/libs/scene/SelectableNode.h index 798b17f8e7..54d3ba63e2 100644 --- a/libs/scene/SelectableNode.h +++ b/libs/scene/SelectableNode.h @@ -51,7 +51,7 @@ class SelectableNode : virtual bool isGroupMember() override; virtual std::size_t getMostRecentGroupId() override; - virtual const GroupIds& getGroupIds() override; + virtual const GroupIds& getGroupIds() const override; virtual void setSelected(bool select, bool changeGroupStatus) override; virtual bool isSelected() const override; diff --git a/radiant/map/algorithm/Import.cpp b/radiant/map/algorithm/Import.cpp index d309e18502..436d306111 100644 --- a/radiant/map/algorithm/Import.cpp +++ b/radiant/map/algorithm/Import.cpp @@ -1,8 +1,11 @@ #include "Import.h" +#include + #include "imap.h" #include "imapformat.h" #include "inamespace.h" +#include "iselectiongroup.h" #include "ientity.h" #include "iscenegraph.h" #include "scene/BasicRootNode.h" @@ -12,18 +15,100 @@ #include "entitylib.h" #include "wxutil/dialog/MessageBox.h" +#include "string/join.h" + namespace map { namespace algorithm { +// Will map source group IDs to new groups created in the target map +class SelectionGroupRemapper : + private std::map +{ +private: + selection::ISelectionGroupManager& _targetGroupManager; + +public: + // The given groupManager will be used to create one corresponding group + // for each distinct group found in the source nodes + SelectionGroupRemapper(selection::ISelectionGroupManager& targetGroupManager) : + _targetGroupManager(targetGroupManager) + {} + + void assignRemappedGroups(const scene::INodePtr& node, const IGroupSelectable::GroupIds& oldGroupIds) + { + rMessage() << "Node " << node->name() << " had the groups " << string::join(oldGroupIds, "|"); + + // Get the Groups the source node was assigned to, and add the + // cloned node to the mapped group, one by one, keeping the order intact + for (std::size_t id : oldGroupIds) + { + // Try to insert the ID, ignore if already exists + // Get a new mapping for the given group ID + const selection::ISelectionGroupPtr& mappedGroup = getMappedGroup(id); + + // Assign the new group ID to this clone + mappedGroup->addNode(node); + } + + rMessage() << " => " << string::join(std::dynamic_pointer_cast(node)->getGroupIds(), "|") << std::endl; + } + + void remapSelectionGroups(const scene::INodePtr& node) + { + std::shared_ptr groupSelectable = std::dynamic_pointer_cast(node); + + if (groupSelectable) + { + auto sourceRoot = node->getRootNode(); + assert(sourceRoot); + + // Save the current set of group IDs + IGroupSelectable::GroupIds oldGroupIds = groupSelectable->getGroupIds(); + + // Remove the node from all its groups + for (auto id : oldGroupIds) + { + auto group = sourceRoot->getSelectionGroupManager().getSelectionGroup(id); + + group->removeNode(node); + } + + // Assign the new set of groups for this node + assignRemappedGroups(node, oldGroupIds); + } + } + +private: + const selection::ISelectionGroupPtr& getMappedGroup(std::size_t id) + { + auto found = emplace(id, selection::ISelectionGroupPtr()); + + if (!found.second) + { + // We already covered this ID, return the mapped group + return found.first->second; + } + + // Insertion was successful, so we didn't cover this ID yet + found.first->second = _targetGroupManager.createSelectionGroup(); + + return found.first->second; + } +}; + class PrimitiveMerger : public scene::PrimitiveReparentor { +private: + SelectionGroupRemapper _groupRemapper; + public: - PrimitiveMerger(const scene::INodePtr& newParent) : - PrimitiveReparentor(newParent) + PrimitiveMerger(const scene::INodePtr& newParent, SelectionGroupRemapper& remapper) : + PrimitiveReparentor(newParent), + _groupRemapper(remapper) {} void post(const scene::INodePtr& node) override @@ -31,11 +116,11 @@ class PrimitiveMerger : // Base class is doing the reparenting PrimitiveReparentor::post(node); + // Re-generate the group IDs of this node + _groupRemapper.remapSelectionGroups(node); + // After reparenting, highlight the imported node Node_setSelected(node, true); - - // Re-generate the group IDs of this node - // TODO } }; @@ -46,9 +131,12 @@ class EntityMerger : // The target path mutable scene::Path _path; + SelectionGroupRemapper _groupRemapper; + public: EntityMerger(const scene::INodePtr& root) : - _path(scene::Path(root)) + _path(scene::Path(root)), + _groupRemapper(root->getRootNode()->getSelectionGroupManager()) {} bool pre(const scene::INodePtr& originalNode) override @@ -93,7 +181,7 @@ class EntityMerger : _path.push(worldSpawn); // Move all children of this node to the target worldspawn - PrimitiveMerger visitor(worldSpawn); + PrimitiveMerger visitor(worldSpawn, _groupRemapper); node->traverseChildren(visitor); } } @@ -124,7 +212,7 @@ class EntityMerger : void post(const scene::INodePtr& node) override { // Re-generate the group IDs of this entity node - // TODO + _groupRemapper.remapSelectionGroups(node); _path.pop(); }