diff --git a/scenarios/ArVizExample/config/RobotToArVizApp.cfg b/scenarios/ArVizExample/config/RobotToArVizApp.cfg index b33276fe2dc50c8d6eadcb7beb004710f336e825..55d034d9c687c16ad2f34f458f85ca66da54cdff 100644 --- a/scenarios/ArVizExample/config/RobotToArVizApp.cfg +++ b/scenarios/ArVizExample/config/RobotToArVizApp.cfg @@ -109,6 +109,14 @@ # ArmarX.RemoteHandlesDeletionTimeout = 3000 +# ArmarX.RobotToArViz.ArVizStorageName: Name of the ArViz storage +# Attributes: +# - Default: ArVizStorage +# - Case sensitivity: yes +# - Required: no +# ArmarX.RobotToArViz.ArVizStorageName = ArVizStorage + + # ArmarX.RobotToArViz.ArVizTopicName: Name of the ArViz topic # Attributes: # - Default: ArVizTopic diff --git a/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp b/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp index 0b5826a8975afca18a2b4181238b20e2d3a0fb18..c47f98b7773f4bb07cabf82ac2a6ca003a5b50f1 100644 --- a/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp +++ b/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp @@ -4,6 +4,7 @@ #include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/util/CPPUtility/GetTypeString.h> +#include <Inventor/nodes/SoUnits.h> #include <Inventor/SoPath.h> @@ -14,6 +15,12 @@ namespace coin void clearRobotCache(); void clearObjectCache(); } + static const int ANY_TRANSFORM = data::InteractionEnableFlags::TRANSLATION_X | + data::InteractionEnableFlags::TRANSLATION_Y | + data::InteractionEnableFlags::TRANSLATION_Z | + data::InteractionEnableFlags::ROTATION_X | + data::InteractionEnableFlags::ROTATION_Y | + data::InteractionEnableFlags::ROTATION_Z; struct CoinVisualizerWrapper : IceUtil::Shared { @@ -89,7 +96,7 @@ namespace coin &CoinVisualizerWrapper::onUpdateSuccess, &CoinVisualizerWrapper::onUpdateFailure); - root = new SoSeparator; + manipulatorGroup = new SoSeparator; // The SoSelection node enable selection of nodes via mouse click / ray casting selection = new SoSelection; @@ -97,7 +104,9 @@ namespace coin selection->addDeselectionCallback(&deselectionCallback, this); selection->setUserData(this); - selection->addChild(root); + root = new SoSeparator; + root->addChild(manipulatorGroup); + root->addChild(selection); // Preallocate some space for layers layers.data.reserve(32); @@ -178,7 +187,7 @@ namespace coin // Create a new layer SoSeparator* coinNode = new SoSeparator; coinNode->ref(); - root->addChild(coinNode); + selection->addChild(coinNode); layerIt = layers.data.insert(layerIt, CoinLayer(layerID, coinNode)); layerIt->elements.reserve(64); @@ -236,33 +245,10 @@ namespace coin // Has an interaction been added? viz::data::InteractionDescription& oldInteraction = oldElement->data->interaction; viz::data::InteractionDescription& newInteraction = updatedElementPtr->interaction; - // TODO: Also handle the case, when an interaction is removed! if (newInteraction.enableFlags != oldInteraction.enableFlags || oldInteraction.contextMenuOptions != newInteraction.contextMenuOptions) { - // Lookup the interaction entry - ElementInteractionData* foundInteraction = nullptr; - for (auto& interaction : elementInteractions) - { - if (interaction->layer == layer->id - && interaction->element == updatedElement.id) - { - foundInteraction = interaction.get(); - } - } - if (foundInteraction == nullptr) - { - // Need to add a new entry - foundInteraction = elementInteractions.emplace_back( - new ElementInteractionData).get(); - } - foundInteraction->layer = layer->id; - foundInteraction->element = updatedElement.id; - foundInteraction->interaction = newInteraction; - - // Add user data to Coin node - oldElement->visu->separator->setUserData(foundInteraction); - oldElement->visu->separator->setName("InteractiveNode"); + addOrUpdateInteraction(layer->id, updatedElement.id, newInteraction, oldElement->visu.get()); } oldElement->data = updatedElementPtr; @@ -282,29 +268,7 @@ namespace coin viz::data::InteractionDescription& newInteraction = updatedElementPtr->interaction; if (newInteraction.enableFlags != 0) { - // Lookup the interaction entry - ElementInteractionData* foundInteraction = nullptr; - for (auto& interaction : elementInteractions) - { - if (interaction->layer == layer->id - && interaction->element == updatedElement.id) - { - foundInteraction = interaction.get(); - } - } - if (foundInteraction == nullptr) - { - // Need to add a new entry - foundInteraction = elementInteractions.emplace_back( - new ElementInteractionData).get(); - } - foundInteraction->layer = layer->id; - foundInteraction->element = updatedElement.id; - foundInteraction->interaction = newInteraction; - - // Add user data to Coin node - elementVisu->separator->setUserData(foundInteraction); - elementVisu->separator->setName("InteractiveNode"); + addOrUpdateInteraction(layer->id, updatedElement.id, newInteraction, elementVisu.get()); } layer->node->addChild(elementVisu->separator); @@ -329,6 +293,36 @@ namespace coin } } + void CoinVisualizer::addOrUpdateInteraction(CoinLayerID const& layerID, std::string const& elementID, + data::InteractionDescription const& interactionDesc, + coin::ElementVisualization* visu) + { + // Lookup the interaction entry + ElementInteractionData* foundInteraction = nullptr; + for (auto& interaction : elementInteractions) + { + if (interaction->layer == layerID + && interaction->element == elementID) + { + foundInteraction = interaction.get(); + } + } + if (foundInteraction == nullptr) + { + // Need to add a new entry + foundInteraction = elementInteractions.emplace_back( + new ElementInteractionData).get(); + } + foundInteraction->layer = layerID; + foundInteraction->element = elementID; + + foundInteraction->interaction = interactionDesc; + + // Add user data to Coin node + visu->separator->setUserData(foundInteraction); + visu->separator->setName("InteractiveNode"); + } + void CoinVisualizer::removeElementsIfNotUpdated(CoinLayer* layer) { for (auto iter = layer->elements.begin(); iter != layer->elements.end();) @@ -374,7 +368,8 @@ namespace coin std::unique_lock<std::mutex> lock(stateMutex); storage = stateStorage; selection->deselectAll(); - root->removeAllChildren(); + selection->removeAllChildren(); + manipulatorGroup->removeAllChildren(); interactionFeedbackBuffer.clear(); elementInteractions.clear(); selectedElement = nullptr; @@ -493,13 +488,13 @@ namespace coin return; } - int childIndex = root->findChild(layer->node); + int childIndex = selection->findChild(layer->node); if (childIndex < 0) { // Layer is currently not visible if (visible) { - root->addChild(layer->node); + selection->addChild(layer->node); } } else @@ -513,7 +508,7 @@ namespace coin { selection->deselectAll(); } - root->removeChild(childIndex); + selection->removeChild(childIndex); } } } @@ -626,14 +621,51 @@ namespace coin } return; } + int enableFlags = id->interaction.enableFlags; + if ((enableFlags & data::InteractionEnableFlags::SELECT) == 0) + { + // Element was not marked for selection + return; + } if (eventType == data::InteractionFeedbackType::SELECT) { selectedElement = id; + + // Does the element support transform interactions? + if (enableFlags & ANY_TRANSFORM) + { + // TODO: Look up the element and add it to the manipulator group + CoinLayer* layer = layers.findLayer(id->layer); + if (layer == nullptr) + { + ARMARX_WARNING << "Selected an element whose layer does not exist: \n" + << "Layer: " << id->layer.first << "/" << id->layer.second + << ", element: " << id->element; + return; + } + CoinLayerElement* element = layer->findElement(id->element); + if (element == nullptr) + { + ARMARX_WARNING << "Selected an element which does not exist: \n" + << "Layer: " << id->layer.first << "/" << id->layer.second + << ", element: " << id->element; + return; + } + + manipulatorGroup->removeAllChildren(); + // We need to create the manipulator everytime to achieve the desired effect + // If we reuse an instance, the manipulator gets stuck to old objects... + manipulator = new SoTransformerManip; + manipulatorGroup->addChild(manipulator); + manipulatorGroup->addChild(element->visu->separator); + } } else { selectedElement = nullptr; + + // TODO: Remove the element from the manipulatorGroup if it was in there } viz::data::InteractionFeedback& feedback = interactionFeedbackBuffer.emplace_back(); @@ -646,6 +678,6 @@ namespace coin void CoinVisualizer::exportToVRML(const std::string& exportFilePath) { - coin::exportToVRML(root, exportFilePath); + coin::exportToVRML(selection, exportFilePath); } } diff --git a/source/RobotAPI/components/ArViz/Coin/Visualizer.h b/source/RobotAPI/components/ArViz/Coin/Visualizer.h index adda36b9ae47404a3f59062c649dad681cd0502f..bbcb9ed6604f50bfda38c3ce75326341da2c9b28 100644 --- a/source/RobotAPI/components/ArViz/Coin/Visualizer.h +++ b/source/RobotAPI/components/ArViz/Coin/Visualizer.h @@ -4,6 +4,7 @@ #include <RobotAPI/interface/ArViz/Component.h> +#include <Inventor/manips/SoTransformerManip.h> #include <Inventor/nodes/SoSelection.h> #include <Inventor/nodes/SoSeparator.h> @@ -230,6 +231,9 @@ namespace armarx::viz CoinLayer& findOrAddLayer(CoinLayerID const& layerID); void addOrUpdateElements(CoinLayer* layer, data::LayerUpdate const& update); + void addOrUpdateInteraction(CoinLayerID const& layerID, std::string const& elementID, + data::InteractionDescription const& interactionDesc, + coin::ElementVisualization* visu); void removeElementsIfNotUpdated(CoinLayer* layer); std::vector<CoinLayerID> getLayerIDs(); @@ -261,6 +265,8 @@ namespace armarx::viz SoSelection* selection = nullptr; SoSeparator* root = nullptr; + SoSeparator* manipulatorGroup = nullptr; + SoTransformerManip* manipulator = nullptr; std::atomic<CoinVisualizerUpdateResult> updateResult{CoinVisualizerUpdateResult::SUCCESS}; data::LayerUpdates pulledUpdates; diff --git a/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp b/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp index d772854de1792982ecf4015f7b61fd412ab6fb09..7ac979f030a7da3f1752a47c8076d8cb6d56fb29 100644 --- a/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp +++ b/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp @@ -511,7 +511,8 @@ namespace armarx // Enable some interaction possibilities box.enable(viz::interaction() .selection() - .contextMenu({"First Option", "Second Option", "Third Option"})); + .contextMenu({"First Option", "Second Option", "Third Option"}) + .fullTransform()); layer.add(box); @@ -524,7 +525,8 @@ namespace armarx // Enable some interaction possibilities cyl.enable(viz::interaction() .selection() - .contextMenu({"Cyl Option 1", "Cyl Option 2"})); + .contextMenu({"Cyl Option 1", "Cyl Option 2"}) + .fullTransform()); layer.add(cyl); } diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp index 7aa05e3a197d4054ea04eaaa8ebc0f68304c50cc..1962d259c0d2329c9870f1e3d314fdea17a1f60d 100644 --- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp @@ -1121,7 +1121,7 @@ namespace armarx SoNode* ArVizWidgetController::getScene() { - return visualizer.selection; + return visualizer.root; } static const std::string CONFIG_KEY_STORAGE = "Storage";