ConvertMesh.cpp
Go to the documentation of this file.
00001 
00031 #include <ros/ros.h>
00032 #include <urdf_traverser/Types.h>
00033 #include <urdf_traverser/UrdfTraverser.h>
00034 #include <urdf_traverser/Functions.h>
00035 #include <urdf2inventor/Helpers.h>
00036 #include <urdf2inventor/IVHelpers.h>
00037 #include <urdf2inventor/ConvertMesh.h>
00038 #include <urdf2inventor/MeshConvertRecursionParams.h>
00039 
00040 #include <urdf2inventor/AssimpImport.h>
00041 
00042 #include <Inventor/nodes/SoGroup.h>
00043 #include <Inventor/nodes/SoTransform.h>
00044 #include <Inventor/nodes/SoSelection.h>
00045 #include <Inventor/nodes/SoTexture2.h>
00046 #include <Inventor/nodekits/SoNodeKit.h>
00047 #include <Inventor/actions/SoWriteAction.h>
00048 #include <Inventor/actions/SoSearchAction.h>
00049 
00050 #include <assimp/Importer.hpp>
00051 #include <assimp/scene.h>
00052 #include <assimp/Importer.hpp>
00053 #include <assimp/importerdesc.h>
00054 #include <assimp/postprocess.h>
00055 
00056 
00057 using urdf_traverser::UrdfTraverser;
00058 using urdf_traverser::RecursionParams;
00059 using urdf_traverser::LinkRecursionParams;
00060 
00061 typedef urdf_traverser::VisualPtr VisualPtr;
00062 typedef urdf_traverser::CollisionPtr CollisionPtr;
00063 typedef urdf_traverser::GeometryPtr GeometryPtr;
00064 typedef urdf_traverser::MaterialPtr MaterialPtr;
00065 typedef urdf_traverser::MeshPtr MeshPtr;
00066 typedef urdf_traverser::SpherePtr SpherePtr;
00067 typedef urdf_traverser::CylinderPtr CylinderPtr;
00068 typedef urdf_traverser::BoxPtr BoxPtr;
00069 //typedef urdf_traverser::Ptr Ptr;
00070 
00074 SoNode * convertMeshFile(const std::string& filename, double scale_factor, bool setExplicitMaterial = false, double r = 0.5, double g = 0.5, double b = 0.5, double a = 1)
00075 {
00076 //    ROS_INFO("Reading file...");
00077     Assimp::Importer importer;
00078     const aiScene* scene = importer.ReadFile(filename, aiProcess_OptimizeGraph |
00079                            aiProcess_FindInvalidData);
00080     /*aiProcess_Triangulate |
00081     aiProcess_OptimizeMeshes |
00082     /aiProcess_CalcTangentSpace             |
00083     aiProcess_Triangulate                        |
00084     aiProcess_JoinIdenticalVertices    |
00085     aiProcess_SortByPType);*/
00086     if (!scene || !scene->mRootNode)
00087     {
00088         ROS_ERROR_STREAM("Could not import file " << filename);
00089         return NULL;
00090     }
00091 
00092     // scale the meshes if required
00093     if (fabs(scale_factor - 1.0) > 1e-06)
00094     {
00095         ROS_INFO_STREAM("Scaling the mesh " << filename << " with factor " << scale_factor);
00096         // get the scaling matrix
00097         aiMatrix4x4 scaleTransform;
00098         aiMatrix4x4::Scaling(aiVector3D(scale_factor, scale_factor, scale_factor), scaleTransform);
00099         // apply the scaling matrix
00100         scene->mRootNode->mTransformation *= scaleTransform;
00101     }
00102 
00103     std::string sceneDir = boost::filesystem::path(filename).parent_path().string();
00104 
00105     SoMaterial * overrideMaterial = NULL;
00106     if (setExplicitMaterial)
00107     {
00108         overrideMaterial = new SoMaterial();
00109         overrideMaterial->diffuseColor.setValue(r, g, b);
00110         overrideMaterial->transparency.setValue(1.0 - a);
00111     }
00112 //    ROS_INFO("Converting to inventor...");
00113     SoSeparator * ivScene = Assimp2Inventor(scene, sceneDir, overrideMaterial);
00114     if (!ivScene)
00115     {
00116         ROS_ERROR("Could not convert scene");
00117         return NULL;
00118     }
00119     return ivScene;
00120 }
00121 
00122 
00126 void urdf2inventor::removeTextureCopies(SoNode * root)
00127 {
00128     ROS_INFO("Removing texture copies in the model");
00129 
00130     SoSearchAction sa;
00131     sa.setType(SoTexture2::getClassTypeId());
00132     sa.setInterest(SoSearchAction::ALL);
00133     sa.setSearchingAll(TRUE);
00134     sa.apply(root);
00135     SoPathList & pl = sa.getPaths();
00136     SbDict namedict;
00137 
00138     for (int i = 0; i < pl.getLength(); i++)
00139     {
00140         SoFullPath * p = (SoFullPath*) pl[i];
00141         if (p->getTail()->isOfType(SoTexture2::getClassTypeId()))
00142         {
00143             SoTexture2 * tex = (SoTexture2*) p->getTail();
00144             if (tex->filename.getValue().getLength())
00145             {
00146                 SbName name = tex->filename.getValue().getString();
00147                 // ROS_INFO_STREAM("Texture file "<<tex->filename.getValue().getString());
00148                 unsigned long key = (unsigned long)((void*) name.getString());
00149                 void * tmp;
00150                 if (!namedict.find(key, tmp))
00151                 {
00152                     // new texture. just insert into list
00153                     (void) namedict.enter(key, tex);
00154                 }
00155                 else if (tmp != (void*) tex)   // replace with node found in dict
00156                 {
00157                     SoGroup * parent = (SoGroup*) p->getNodeFromTail(1);
00158                     int idx = p->getIndexFromTail(0);
00159                     parent->replaceChild(idx, (SoNode*) tmp);
00160                 }
00161             }
00162         }
00163     }
00164     sa.reset();
00165 }
00166 
00174 /*std::set<std::string> replaceAbsoluteTexturePaths(SoNode * root, const std::string& basePath)
00175 {
00176     std::set<std::string> allFiles;
00177     SoSearchAction sa;
00178     sa.setType(SoTexture2::getClassTypeId());
00179     sa.setInterest(SoSearchAction::ALL);
00180     sa.setSearchingAll(TRUE);
00181     sa.apply(root);
00182     SoPathList & pl = sa.getPaths();
00183 
00184     for (int i = 0; i < pl.getLength(); i++)
00185     {
00186         SoFullPath * p = (SoFullPath*) pl[i];
00187         if (!p->getTail()->isOfType(SoTexture2::getClassTypeId())) continue;
00188 
00189         SoTexture2 * tex = (SoTexture2*) p->getTail();
00190         if (tex->filename.getValue().getLength() == 0) continue;
00191 
00192         std::string _name(tex->filename.getValue().getString());
00193         SbName name(_name.c_str());
00194 
00195         // ROS_WARN_STREAM("Abs texture file "<<_name<<" relative to "<<basePath);
00196         boost::filesystem::path absPath(boost::filesystem::absolute(_name));
00197         std::string cParentPath=urdf_traverser::helpers::getCommonParentPath(basePath,_name);
00198         // ROS_INFO_STREAM("Parent path :"<<cParentPath);
00199 
00200         std::string relPath;
00201         if (!urdf_traverser::helpers::getSubdirPath(cParentPath,_name, relPath))
00202         {
00203             ROS_ERROR_STREAM("File "<<_name<<" is not a subdirectory of "<<basePath);
00204             continue;
00205         }
00206         ROS_INFO_STREAM("Relative file: "<<relPath);
00207 
00208         tex->filename.setValue(relPath.c_str());
00209         ROS_INFO("filename set");
00210         allFiles.insert(absPath.string());
00211     }
00212     sa.reset();
00213     return allFiles;
00214 }*/
00215 
00216 
00217 
00218 // XXX TODO could specify material as parameter to override any possibly already
00219 // existing materials. Propagate this to other functions using getAllGeometry().
00220 bool addGeometry(SoSeparator * addToNode, const std::string& linkName, double scale_factor,
00221                    const GeometryPtr& geom,
00222                    const int geomNum,  // number to assign to this visual
00223                    const MaterialPtr& mat,
00224                    const urdf_traverser::EigenTransform& geometryTransform,  // transform to the geometry
00225                    const urdf_traverser::EigenTransform& addMeshTransform, // transform to add only to mesh shapes
00226                    const bool scaleUrdfTransforms)
00227 {
00228     
00229         urdf_traverser::EigenTransform geomTransform = geometryTransform;
00230         // ROS_INFO_STREAM("Visual "<<i<<" of link "<<linkName<<" transform: "<<geomTransform);
00231         urdf_traverser::EigenTransform meshGeomTransform = geomTransform * addMeshTransform;
00232 
00233         if (scaleUrdfTransforms) urdf_traverser::scaleTranslation(geomTransform, scale_factor);
00234 
00235         switch (geom->type)
00236         {
00237         case urdf::Geometry::MESH:
00238         {
00239             // ROS_INFO_STREAM("Mesh for "<<visual->group_name);
00240             MeshPtr mesh = shr_lib::dynamic_pointer_cast<urdf::Mesh>(geom);
00241             if (!mesh.get())
00242             {
00243                 ROS_ERROR("Mesh cast error");
00244                 return false;
00245             }
00246             std::string meshFilename = urdf_traverser::helpers::packagePathToAbsolute(mesh->filename);
00247 
00248             ROS_INFO_STREAM("Converting mesh file " << meshFilename << " with factor " << scale_factor);
00249             float r = 0.5;
00250             float g = 0.5;
00251             float b = 0.5;
00252             float a = 1.0;
00253             if (mat)
00254             {
00255                 r = mat->color.r;
00256                 g = mat->color.g;
00257                 b = mat->color.b;
00258                 a = mat->color.a;
00259             }
00260             SoNode * somesh = convertMeshFile(meshFilename, scale_factor, mat != NULL, r, g, b, a);
00261             // ROS_INFO("Converted.");
00262             if (!somesh)
00263             {
00264                 ROS_ERROR("Mesh could not be read");
00265                 return false;
00266             }
00267             std::stringstream str;
00268             str << "_visual_" << geomNum << "_" << linkName;
00269             // ROS_INFO_STREAM("Visual name "<<str.str());
00270             somesh->setName(str.str().c_str());
00271             urdf2inventor::addSubNode(somesh, addToNode, meshGeomTransform);
00272             break;
00273         }
00274         case urdf::Geometry::SPHERE:
00275         {
00276             ROS_INFO("Urdf2Inventor: Model has a Sphere");
00277             SpherePtr sphere = shr_lib::dynamic_pointer_cast<urdf::Sphere>(geom);
00278             if (!sphere.get())
00279             {
00280                 ROS_ERROR("Sphere cast error");
00281                 return false;
00282             }
00283 
00284             SoSeparator * sphereNode = new SoSeparator();
00285             sphereNode->ref();
00286             urdf2inventor::addSphere(addToNode, geomTransform.translation(), sphere->radius * scale_factor, 1, 0, 0);
00287             break;
00288         }
00289         case urdf::Geometry::BOX:
00290         {
00291             ROS_INFO("Urdf2Inventor: Model has a box");
00292             BoxPtr box = shr_lib::dynamic_pointer_cast<urdf::Box>(geom);
00293             if (!box.get())
00294             {
00295                 ROS_ERROR("Box cast error");
00296                 return false;
00297             }
00298 
00299             SoSeparator * boxNode = new SoSeparator();
00300             boxNode->ref();
00301             ROS_INFO_STREAM("Geometry "<<geomNum<<" of link "<<linkName<<" transform: "<<geometryTransform);
00302             urdf2inventor::EigenTransform tmpT;
00303             urdf2inventor::EigenTransform geomTransformInv = geomTransform.inverse(); 
00304             tmpT.setIdentity();
00305             tmpT.translate(geomTransform.translation());
00306             tmpT.rotate(geomTransform.rotation());
00307             urdf2inventor::addBox(addToNode, tmpT, box->dim.x * scale_factor, box->dim.y * scale_factor, box->dim.z * scale_factor, 1, 0, 0, 0);
00308             break;
00309         }
00310         case urdf::Geometry::CYLINDER:
00311         {
00312             ROS_INFO("Urdf2Inventor: Model has a cylinder");
00313             CylinderPtr cylinder = shr_lib::dynamic_pointer_cast<urdf::Cylinder>(geom);
00314             if (!cylinder.get())
00315             {
00316                 ROS_ERROR("Cylinder cast error");
00317                 return false;
00318             }
00319 
00320             SoSeparator * cylinderNode = new SoSeparator();
00321             cylinderNode->ref();
00322             urdf2inventor::addCylinder(addToNode, geomTransform, cylinder->radius/2, cylinder->length, 1, 0, 0, 0); // for some reason, half the radius is required
00323             break;
00324         }
00325         default:
00326         {
00327             ROS_ERROR_STREAM("This geometry type not supported so far: " << geom->type);
00328             return false;
00329         }
00330         }
00331 
00332         return true;
00333 }
00334 
00335 
00336 
00337 
00338 
00339 
00340 // XXX TODO could specify material as parameter to override any possibly already
00341 // existing materials. Propagate this to other functions using getAllGeometry().
00342 SoNode * urdf2inventor::getAllGeometry(const urdf_traverser::LinkPtr link, double scale_factor,
00343                                       const urdf_traverser::EigenTransform& addVisualTransform,
00344                                       const bool useVisuals,
00345                                       const bool scaleUrdfTransforms)
00346 {
00347     SoNodeKit::init();
00348     SoSeparator * allVisuals = new SoSeparator();
00349     allVisuals->ref();
00350     std::string linkName = link->name;
00351 
00352     if (useVisuals)
00353     {
00354         unsigned int i = 0;
00355         for (std::vector<VisualPtr>::const_iterator vit = link->visual_array.begin();
00356                 vit != link->visual_array.end(); ++vit)
00357         {
00358             VisualPtr visual = (*vit);
00359             GeometryPtr geom = visual->geometry;
00360             MaterialPtr mat = visual->material;
00361 
00362             // ROS_INFO_STREAM("Visual "<<visual->group_name);
00363             // if (mat) ROS_INFO_STREAM("Material "<<mat->color.r<<", "<<mat->color.g<<", "<<mat->color.b<<", "<<mat->color.a);
00364 
00365             urdf_traverser::EigenTransform vTransform = urdf_traverser::getTransform(visual->origin);
00366             // ROS_INFO_STREAM("Visual "<<i<<" of link "<<link->name<<" transform: "<<visual->origin);
00367 
00368             if (!addGeometry(allVisuals, linkName, scale_factor,
00369                        geom, i, mat, vTransform, addVisualTransform, scaleUrdfTransforms))
00370             {
00371                 ROS_ERROR_STREAM("Could not add geometry of link "<<link->name);
00372                 return NULL;
00373             }
00374             ++i;
00375         }
00376     }
00377     else
00378     {
00379         unsigned int i = 0;
00380         for (std::vector<CollisionPtr>::const_iterator cit = link->collision_array.begin();
00381                 cit != link->collision_array.end(); ++cit)
00382         {
00383             CollisionPtr coll = (*cit);
00384             GeometryPtr geom = coll->geometry;
00385             // XXX TODO allow to set a material for collision shapes, which inherently don't have materials.
00386             // Because collision geometries don't match to visual geometries
00387             // (could be different numbers!), we can't infer from visual.
00388             MaterialPtr mat(new urdf::Material());
00389             mat->color.r=0.1;
00390             mat->color.g=0.1;
00391             mat->color.b=0.5;
00392             mat->color.a=1;
00393 
00394             urdf_traverser::EigenTransform cTransform = urdf_traverser::getTransform(coll->origin);
00395             // ROS_INFO_STREAM("Collision geometry "<<i<<" of link "<<link->name<<" transform: "<<coll->origin);
00396 
00397             if (!addGeometry(allVisuals, linkName, scale_factor,
00398                        geom, i, mat, cTransform, addVisualTransform, scaleUrdfTransforms))
00399             {
00400                 ROS_ERROR_STREAM("Could not add geometry of link "<<link->name);
00401                 return NULL;
00402             }
00403             ++i;
00404         }
00405     }
00406 
00407     std::stringstream str;
00408     str << "_" << linkName;
00409 
00410     allVisuals->setName(str.str().c_str());
00411 
00412     return allVisuals;
00413 }
00414 
00415 
00423 bool convertMeshToIVString(urdf_traverser::LinkPtr& link,
00424                            const float scale_factor,
00425                            const urdf_traverser::EigenTransform& addVisualTransform,
00426                            const bool useVisuals,
00427                            const bool scaleUrdfTransforms,
00428                            std::string& resultIV,
00429                            std::set<std::string>& textureFiles)
00430 {
00431     ROS_INFO("Convert mesh for link '%s'", link->name.c_str());
00432 
00433     SoNode * allVisuals = urdf2inventor::getAllGeometry(link, scale_factor, addVisualTransform, useVisuals, scaleUrdfTransforms);
00434     if (!allVisuals)
00435     {
00436         ROS_ERROR("Could not get visuals");
00437         return false;
00438     }
00439 
00440     // write the node to IV XML format
00441     if (!urdf2inventor::writeInventorFileString(allVisuals, resultIV))
00442     {
00443         ROS_ERROR("Could not get the mesh file content");
00444         return false;
00445     }
00446 
00447     //ROS_INFO_STREAM("Result file content: "<<resultIV);
00448 
00449     // collect all relative texture filenames from the absolute texture paths.
00450     textureFiles =  urdf2inventor::getAllTexturePaths(allVisuals);
00451     return true;
00452 }
00453 
00454 
00459 int convertMeshToIVString(urdf_traverser::RecursionParamsPtr& p)
00460 {
00461     typedef urdf2inventor::MeshConvertRecursionParams<std::string> MeshConvertRecursionParamsT;
00462     typename MeshConvertRecursionParamsT::Ptr param = baselib_binding_ns::dynamic_pointer_cast<MeshConvertRecursionParamsT>(p);
00463     if (!param.get())
00464     {
00465         ROS_ERROR("Wrong recursion parameter type");
00466         return -1;
00467     }
00468 
00469     bool useVisuals=true;  // XXX TODO: Parameterize
00470     urdf_traverser::LinkPtr link = param->getLink();
00471     std::string resultFileContent;
00472     std::set<std::string> textureFiles;
00473     if (!convertMeshToIVString(link, param->factor, param->getVisualTransform(), useVisuals, false, resultFileContent, textureFiles))
00474         return -1;
00475 
00476     //ROS_INFO_STREAM("Result file content: "<<resultFileContent);
00477     if (!param->resultMeshes.insert(std::make_pair(link->name, resultFileContent)).second)
00478     {
00479         ROS_ERROR("Could not insert the resulting mesh file for link %s to the map", link->name.c_str());
00480         return -1;
00481     }
00482 
00483     param->textureFiles[link->name].insert(textureFiles.begin(), textureFiles.end());
00484     return 1;
00485 }
00486 
00487 #if 0
00488 template <class MeshFormat>
00489 bool urdf2inventor::convertMeshesSimple(urdf_traverser::UrdfTraverser& traverser,
00490                                   const std::string& fromLink,
00491                                   const float scaleFactor,
00492                                   const std::string& material,
00493                                   const std::string& file_extension,
00494                                   const urdf_traverser::EigenTransform& addVisualTransform,
00495                                   std::map<std::string, MeshFormat>& meshes,
00496                                   std::map<std::string, std::set<std::string> >& textureFiles)
00497 {
00498     std::string startLinkName = fromLink;
00499     if (startLinkName.empty())
00500     {
00501         startLinkName = traverser.getRootLinkName();
00502     }
00503 
00504     urdf_traverser::LinkPtr startLink = traverser.getLink(startLinkName);
00505     if (!startLink.get())
00506     {
00507         ROS_ERROR("Link %s does not exist", startLinkName.c_str());
00508         return false;
00509     }
00510 
00511     // do one call of convertMeshes
00512     typedef urdf2inventor::MeshConvertRecursionParams<MeshFormat> MeshConvertRecursionParamsT;
00513     typename MeshConvertRecursionParamsT::Ptr meshParams(
00514         new MeshConvertRecursionParamsT(scaleFactor, material, file_extension, addVisualTransform));
00515 
00516     urdf_traverser::RecursionParamsPtr p(meshParams);
00517 
00518     // go through entire tree
00519     if (traverser.traverseTreeTopDown(startLinkName, shr_lib::bind(&convertMeshToIVString, _1), p, true) <= 0)
00520     {
00521         ROS_ERROR("Could nto convert meshes.");
00522         return false;
00523     }
00524     meshes = meshParams->resultMeshes;
00525     textureFiles = meshParams->textureFiles;
00526 
00527     return true;
00528 }
00529 #endif
00530 
00531 template <class MeshFormat>
00532 bool urdf2inventor::convertMeshes(urdf_traverser::UrdfTraverser& traverser,
00533                                   const std::string& fromLink,
00534                                   const typename urdf2inventor::MeshConvertRecursionParams<MeshFormat>::Ptr& meshParams)
00535 {
00536     if (!meshParams)
00537     {
00538       ROS_ERROR("Need to specify mesh parameters");
00539       return false;
00540     }
00541 
00542     std::string startLinkName = fromLink;
00543     if (startLinkName.empty())
00544     {
00545         startLinkName = traverser.getRootLinkName();
00546     }
00547 
00548     urdf_traverser::LinkPtr startLink = traverser.getLink(startLinkName);
00549     if (!startLink.get())
00550     {
00551         ROS_ERROR("Link %s does not exist", startLinkName.c_str());
00552         return false;
00553     }
00554 
00555     // go through entire tree
00556     urdf_traverser::RecursionParamsPtr p(meshParams);
00557     if (traverser.traverseTreeTopDown(startLinkName, boost::bind(&convertMeshToIVString, _1), p, true) <= 0)
00558     {
00559         ROS_ERROR("Could nto convert meshes.");
00560         return false;
00561     }
00562     return true;
00563 }
00564 
00565 bool urdf2inventor::fixTextureReferences(
00566     const std::string& relMeshDir,
00567     const std::string& relTexDir,
00568     const std::map<std::string, std::set<std::string> >& textureFiles,
00569     std::map<std::string, std::string>& meshes,
00570     std::map<std::string, std::set<std::string> >& texturesToCopy)
00571 {
00572     // determine common parent path of all textures. First, remove texture duplicates
00573     std::set<std::string> allTextures;
00574     for (std::map<std::string, std::set<std::string> >::const_iterator it = textureFiles.begin(); it != textureFiles.end(); ++it)
00575         allTextures.insert(it->second.begin(), it->second.end());
00576 
00577     // no textures to process
00578     if (allTextures.empty()) return true;
00579 
00580     ROS_INFO_STREAM("Fixing texture references to point from " << relMeshDir << " files to textures in " << relTexDir);
00581     std::string _relMeshDir(relMeshDir);
00582     std::string _relTexDir(relTexDir);
00583     urdf_traverser::helpers::enforceDirectory(_relMeshDir, true);
00584     urdf_traverser::helpers::enforceDirectory(_relTexDir, true);
00585 
00586     std::string commonParent;
00587     if (!urdf_traverser::helpers::getCommonParentPath(allTextures, commonParent))
00588     {
00589         ROS_ERROR_STREAM("Could not find common parent path of all textures");
00590         return false;
00591     }
00592 
00593     ROS_INFO_STREAM("Common parent path for all textures: " << commonParent);
00594 
00595     for (std::map<std::string, std::set<std::string> >::const_iterator it = textureFiles.begin(); it != textureFiles.end(); ++it)
00596     {
00597         std::string meshFile = it->first;
00598         std::string meshDirectory = urdf_traverser::helpers::getDirectory(meshFile);
00599 
00600         std::stringstream relMeshInstallFile;
00601         relMeshInstallFile << _relMeshDir << meshDirectory;
00602 
00603 
00604         typename std::map<std::string, std::string>::iterator meshString = meshes.find(meshFile);
00605         if (meshString == meshes.end())
00606         {
00607             ROS_ERROR_STREAM("Consistency: Mesh " << meshFile << " should have been in meshes map");
00608             return false;
00609         }
00610 
00611         if (!urdf2inventor::helpers::fixFileReferences(relMeshInstallFile.str(), _relTexDir, commonParent, it->second,
00612                 meshString->second, texturesToCopy))
00613         {
00614             ROS_ERROR_STREAM("Could not fix file references");
00615             return false;
00616         }
00617     }
00618     return true;
00619 }
00620 
00621 
00622 // instantiation for string meshes
00623 /*template bool urdf2inventor::convertMeshesSimple<std::string>(urdf_traverser::UrdfTraverser& traverser,
00624         const std::string& fromLink,
00625         const float scaleFactor,
00626         const std::string& material,
00627         const std::string& file_extension,
00628         const urdf_traverser::EigenTransform& addVisualTransform,
00629         std::map<std::string, std::string>& meshes,
00630         std::map<std::string, std::set<std::string> >& textureFiles);*/
00631 
00632 // instantiation for string meshes
00633 template bool urdf2inventor::convertMeshes<std::string>(urdf_traverser::UrdfTraverser& traverser,
00634         const std::string& fromLink,
00635         const urdf2inventor::MeshConvertRecursionParams<std::string>::Ptr& meshParams);


urdf2inventor
Author(s): Jennifer Buehler
autogenerated on Fri Mar 1 2019 03:38:11