#include "VisualizationObject.h" #include <RobotAPI/libraries/ArmarXObjects/ObjectFinder.h> #include <ArmarXCore/core/system/cmake/CMakePackageFinder.h> #include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/system/ArmarXDataPath.h> #include <VirtualRobot/ManipulationObject.h> #include <VirtualRobot/SceneObject.h> #include <VirtualRobot/XML/RobotIO.h> #include <VirtualRobot/XML/ObjectIO.h> #include <VirtualRobot/Visualization/CoinVisualization/CoinVisualization.h> #include <VirtualRobot/Import/MeshImport/AssimpReader.h> #include <SimoxUtility/algorithm/string/string_tools.h> namespace armarx::viz::coin { namespace { std::string findObjectInArmarXObjects(const std::string& filename) { IceUtil::Time start = IceUtil::Time::now(); std::string objectName = std::filesystem::path(filename).filename().stem(); // ARMARX_INFO << "Trying to find object '" << objectName << "' in ArmarXObjects."; std::string fullFilename; std::stringstream ss; armarx::ObjectFinder objectFinder; if (std::optional<armarx::ObjectInfo> info = objectFinder.findObject("", objectName)) { fullFilename = info->simoxXML().absolutePath; ss << "Found '" << objectName << "' in ArmarXObjects as " << *info << " \nat '" << fullFilename << "'."; } else { ss << "Did not find '" << objectName << "' in ArmarXObjects."; } ss << "\n(Lookup took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.)"; ARMARX_INFO << ss.str(); return fullFilename; } VirtualRobot::ManipulationObjectPtr loadObject(std::string const& project, std::string const& filename) { VirtualRobot::ManipulationObjectPtr result; if (filename.empty()) { ARMARX_INFO << deactivateSpam() << "No filename provided for object"; return result; } bool checkArmarXObjects = true; // This check cannot be done here! // filename is a relative path in the project // Also the code below does check whether the file is readable! // std::ifstream fs(filename); // if (!fs.good()) // { // ARMARX_WARNING << "unable to read from file " << VAROUT(filename); // return result; // } ArmarXDataPath::FindPackageAndAddDataPath(project); std::string fullFilename; if (!ArmarXDataPath::SearchReadableFile(filename, fullFilename)) { fullFilename = ""; if (checkArmarXObjects) { fullFilename = findObjectInArmarXObjects(filename); } if (fullFilename.empty()) { ARMARX_INFO << deactivateSpam() << "Unable to find readable file for name " << filename; return result; } } try { ARMARX_INFO << "Loading object from " << fullFilename; std::filesystem::path path{fullFilename}; const std::string ext = simox::alg::to_lower(path.extension()); if (ext == ".wrl" || ext == ".iv") { VirtualRobot::VisualizationFactoryPtr factory = VirtualRobot::VisualizationFactory::fromName("inventor", nullptr); VirtualRobot::VisualizationNodePtr vis = factory->getVisualizationFromFile(fullFilename); result = VirtualRobot::ManipulationObjectPtr(new VirtualRobot::ManipulationObject(filename, vis)); } else if (ext == ".xml" || ext == ".moxml") { result = VirtualRobot::ObjectIO::loadManipulationObject(fullFilename); } else if (VirtualRobot::AssimpReader::can_load(fullFilename)) { const auto& tri = VirtualRobot::AssimpReader{}.readFileAsTriMesh(fullFilename); result = VirtualRobot::ManipulationObjectPtr(new VirtualRobot::ManipulationObject(filename, tri)); } else { ARMARX_WARNING << "Could not load object from file: " << fullFilename << "\nunknown extension '" << ext << "'"; } } catch (std::exception const& ex) { ARMARX_WARNING << "Could not load object from file: " << fullFilename << "\nReason: " << ex.what(); } return result; } static std::vector<LoadedObject> objectcache; LoadedObject getObjectFromCache(std::string const& project, std::string const& filename) { // We can use a global variable, since this code is only executed in the GUI thread LoadedObject result; for (LoadedObject const& loaded : objectcache) { if (loaded.project == project && loaded.filename == filename) { result = loaded; result.object = loaded.object; return result; } } result.project = project; result.filename = filename; result.object = loadObject(project, filename); objectcache.push_back(result); return result; } } bool VisualizationObject::update(ElementType const& element) { bool fileChanged = loaded.project != element.project || loaded.filename != element.filename; if (fileChanged) { // The robot file changed, so reload the robot loaded = getObjectFromCache(element.project, element.filename); } if (!loaded.object) { ARMARX_WARNING << deactivateSpam(120) << "Object will not be visualized since it could not be loaded." << "\nID: " << element.id << "\nProject: " << element.project << "\nFilename: " << element.filename; return true; } bool drawStyleChanged = loadedDrawStyle != element.drawStyle; if (fileChanged || drawStyleChanged) { recreateVisualizationNodes(element.drawStyle); loadedDrawStyle = element.drawStyle; } if (loadedDrawStyle & data::ModelDrawStyle::OVERRIDE_COLOR) { int numChildren = node->getNumChildren(); for (int i = 0; i < numChildren; i++) { SoSeparator* nodeSep = static_cast<SoSeparator*>(node->getChild(i)); // The first entry must be a SoMaterial (see recreateVisualizationNodes) SoMaterial* m = dynamic_cast<SoMaterial*>(nodeSep->getChild(0)); if (!m) { ARMARX_WARNING << "Error at node with index: " << i; continue; } auto color = element.color; const float conv = 1.0f / 255.0f; float a = color.a * conv; SbColor coinColor(conv * color.r, conv * color.g, conv * color.b); m->diffuseColor = coinColor; m->ambientColor = coinColor; m->transparency = 1.0f - a; m->setOverride(true); } } return true; } void VisualizationObject::recreateVisualizationNodes(int drawStyle) { VirtualRobot::SceneObject::VisualizationType visuType = VirtualRobot::SceneObject::Full; if (drawStyle & data::ModelDrawStyle::COLLISION) { visuType = VirtualRobot::SceneObject::Collision; } node->removeAllChildren(); VirtualRobot::ManipulationObject& object = *loaded.object; SoSeparator* nodeSep = new SoSeparator; // This material is used to color the nodes individually // We require it to be the first node in the separator for updates SoMaterial* nodeMat = new SoMaterial; nodeMat->setOverride(false); nodeSep->addChild(nodeMat); VirtualRobot::CoinVisualizationPtr nodeVisu = object.getVisualization<VirtualRobot::CoinVisualization>(visuType); if (nodeVisu) { SoNode* sepRobNode = nodeVisu->getCoinVisualization(); if (sepRobNode) { nodeSep->addChild(sepRobNode); } } node->addChild(nodeSep); } void clearObjectCache() { objectcache.clear(); } }