00001
00032 #include <urdf2inventor/Urdf2Inventor.h>
00033 #include <urdf2inventor/Helpers.h>
00034 #include <urdf2inventor/IVHelpers.h>
00035 #include <urdf2inventor/ConvertMesh.h>
00036
00037 #include <string>
00038 #include <ros/ros.h>
00039 #include <ros/package.h>
00040
00041 #include <Inventor/SoDB.h>
00042 #include <Inventor/SoInput.h>
00043 #include <Inventor/actions/SoWriteAction.h>
00044 #include <Inventor/nodes/SoTransform.h>
00045 #include <Inventor/nodes/SoSelection.h>
00046 #include <Inventor/nodekits/SoNodeKit.h>
00047
00048 #include <urdf_traverser/Functions.h>
00049 #include <urdf_transform/JoinFixedLinks.h>
00050 #include <urdf_transform/ScaleModel.h>
00051 #include <urdf_transform/AlignRotationAxis.h>
00052
00053 #include <map>
00054 #include <vector>
00055 #include <set>
00056 #include <fstream>
00057 #include <algorithm>
00058
00059 #define RAD_TO_DEG 180/M_PI
00060
00061
00062
00063 #define TEMP_MESH_DIR "/tmp/tempMeshes"
00064
00065 using urdf2inventor::Urdf2Inventor;
00066
00067 std::string Urdf2Inventor::OUTPUT_EXTENSION = ".iv";
00068 std::string Urdf2Inventor::MESH_OUTPUT_DIRECTORY_NAME = "iv/";
00069 std::string Urdf2Inventor::TEX_OUTPUT_DIRECTORY_NAME = "textures/";
00070
00071 bool Urdf2Inventor::joinFixedLinks(const std::string& fromLink)
00072 {
00073 std::string startLinkName = fromLink;
00074 if (startLinkName.empty())
00075 {
00076 startLinkName = urdf_traverser->getRootLinkName();
00077 }
00078
00079 ROS_INFO("############### Joining fixed links");
00080 ROS_INFO_STREAM("Start from root link: "<<startLinkName);
00081
00082 LinkPtr startLink = urdf_traverser->getLink(startLinkName);
00083 if (!startLink.get())
00084 {
00085 ROS_ERROR_STREAM("No link named '" << startLinkName << "'");
00086 return false;
00087 }
00088
00089 if (!urdf_transform::joinFixedLinks(*urdf_traverser, startLinkName))
00090 {
00091 ROS_ERROR_STREAM("Could not join fixed links");
00092 return false;
00093 }
00094 return true;
00095 }
00096
00097 bool Urdf2Inventor::allRotationsToAxis(const std::string& fromLinkName, const Eigen::Vector3d& axis)
00098 {
00099 std::string startLinkName = fromLinkName;
00100 if (startLinkName.empty())
00101 {
00102 startLinkName = urdf_traverser->getRootLinkName();
00103 }
00104
00105 LinkPtr startLink = urdf_traverser->getLink(startLinkName);
00106 if (!startLink.get())
00107 {
00108 ROS_ERROR_STREAM("No link named '" << startLink << "'");
00109 return false;
00110 }
00111
00112 ROS_INFO_STREAM("############### Aligning rotation axis to " << axis);
00113 if (!urdf_transform::allRotationsToAxis(*urdf_traverser, fromLinkName, axis))
00114 {
00115 ROS_ERROR_STREAM("Could not align to axis");
00116 return false;
00117 }
00118 return true;
00119 }
00120
00121 bool Urdf2Inventor::scale()
00122 {
00123 if (fabs(scaleFactor - 1.0) < 1e-04)
00124 {
00125
00126 return true;
00127 }
00128 ROS_INFO("############### Scaling model");
00129
00130 if (!urdf_transform::scaleModel(*urdf_traverser, scaleFactor))
00131 {
00132 ROS_ERROR("Could not scale model");
00133 return false;
00134 }
00135
00136 isScaled = true;
00137 return true;
00138 }
00139
00140
00141 bool Urdf2Inventor::loadModelFromFile(const std::string& urdfFilename)
00142 {
00143 return urdf_traverser->loadModelFromFile(urdfFilename);
00144 }
00145
00146
00147 urdf_traverser::EigenTransform Urdf2Inventor::getTransform(const LinkPtr& from_link, const JointPtr& to_joint)
00148 {
00149 LinkPtr link1 = from_link;
00150 LinkPtr link2 = urdf_traverser->getLink(to_joint->child_link_name);
00151 if (!link1 || !link2)
00152 {
00153 ROS_ERROR("Invalid joint specifications (%s, %s), first needs parent and second child",
00154 link1->name.c_str(), link2->name.c_str());
00155 }
00156 return urdf_traverser::getTransform(link1, link2);
00157 }
00158
00159 Urdf2Inventor::ConversionResultPtr Urdf2Inventor::preConvert(const ConversionParametersPtr& params)
00160 {
00161 ConversionResultPtr res(new ConversionResultT(OUTPUT_EXTENSION, MESH_OUTPUT_DIRECTORY_NAME, TEX_OUTPUT_DIRECTORY_NAME));
00162 res->success = false;
00163 return res;
00164 }
00165
00166 Urdf2Inventor::ConversionResultPtr Urdf2Inventor::postConvert(const ConversionParametersPtr& params, ConversionResultPtr& result)
00167 {
00168 result->success = true;
00169 return result;
00170 }
00171
00172
00173 Urdf2Inventor::ConversionResultPtr Urdf2Inventor::convert(const ConversionParametersPtr& params,
00174 const MeshConvertRecursionParamsPtr& meshParams)
00175 {
00176 ConversionResultPtr res = preConvert(params);
00177 if (!res.get())
00178 {
00179 ROS_ERROR("Failed to pre-convert");
00180 return res;
00181 }
00182 res->success = false;
00183
00184 if (!isScaled && !scale())
00185 {
00186 ROS_ERROR("Failed to scale model");
00187 }
00188
00189 ROS_INFO_STREAM("############### Converting meshes");
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203 MeshConvertRecursionParamsPtr mParams=meshParams;
00204 if (!mParams)
00205 {
00206 mParams.reset(new MeshConvertRecursionParamsT(scaleFactor, params->material,
00207 OUTPUT_EXTENSION, params->addVisualTransform));
00208 }
00209
00210 if (!urdf2inventor::convertMeshes<MeshFormat>(*urdf_traverser, params->rootLinkName, mParams))
00211 {
00212 ROS_ERROR("Could not convert meshes");
00213 return res;
00214 }
00215
00216 res->meshes = mParams->resultMeshes;
00217
00218 if (!urdf2inventor::fixTextureReferences(
00219 res->meshOutputDirectoryName,
00220 res->texOutputDirectoryName,
00221 mParams->textureFiles,
00222
00223 res->meshes, res->textureFiles))
00224 {
00225 ROS_ERROR("Could not fix texture references");
00226 return res;
00227 }
00228
00229
00230
00231
00232
00233
00234
00235 return postConvert(params, res);
00236 }
00237
00238
00239 bool Urdf2Inventor::printModel(const std::string& fromLink)
00240 {
00241 return urdf_traverser->printModel(fromLink, false);
00242 }
00243
00244 bool Urdf2Inventor::printModel()
00245 {
00246 return urdf_traverser->printModel(false);
00247 }
00248
00249
00250 bool Urdf2Inventor::getJointNames(const std::string& fromLink, const bool skipFixed, std::vector<std::string>& result)
00251 {
00252 return urdf_traverser->getJointNames(fromLink, skipFixed, result);
00253 }
00254
00255
00256
00257 void Urdf2Inventor::cleanup()
00258 {
00259 if (urdf_traverser::helpers::fileExists(TMP_FILE_IV))
00260 {
00261 urdf_traverser::helpers::deleteFile(TMP_FILE_IV);
00262 }
00263 }
00264
00265 bool Urdf2Inventor::writeInventorFile(SoNode * node, const std::string& filename)
00266 {
00267 SoOutput out;
00268 if (!out.openFile(filename.c_str())) return false;
00269 out.setBinary(false);
00270 SoWriteAction write(&out);
00271 write.apply(node);
00272 write.getOutput()->closeFile();
00273 return true;
00274 }
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292 void Urdf2Inventor::printJointNames(const std::string& fromLink)
00293 {
00294 std::vector<std::string> jointNames;
00295 if (!getJointNames(fromLink, false, jointNames))
00296 {
00297 ROS_WARN("Could not retrieve joint names to print on screen");
00298 }
00299 else
00300 {
00301 ROS_INFO_STREAM("Joint names starting from " << fromLink << ":");
00302 for (int i = 0; i < jointNames.size(); ++i) ROS_INFO_STREAM(jointNames[i]);
00303 ROS_INFO("---");
00304 }
00305 }
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328 void Urdf2Inventor::addLocalAxes(const LinkConstPtr& link, SoSeparator * addToNode, bool useScaleFactor,
00329 float _axesRadius, float _axesLength) const
00330 {
00331 bool active = urdf_traverser::isActive(link->parent_joint);
00332 float rad = _axesRadius;
00333 float rotRad = _axesRadius / 3;
00334 float h = _axesLength;
00335 float rotH = h * 1.8;
00336 float rtr, rtg, rtb;
00337
00338 rtr = rtb = 1;
00339 rtg = 0;
00340 if (useScaleFactor)
00341 {
00342 rad *= scaleFactor;
00343 h *= scaleFactor;
00344 rotRad *= scaleFactor;
00345 rotH *= scaleFactor;
00346 }
00347 if (!active)
00348 {
00349 rad /= 2;
00350 h *= 1.5;
00351 }
00352 urdf2inventor::addLocalAxes(addToNode, rad, h);
00353
00354
00355 Eigen::Vector3d rotAxis(0, 0, 1);
00356 if (link->parent_joint)
00357 rotAxis = urdf_traverser::getRotationAxis(link->parent_joint);
00358 Eigen::Vector3d z(0, 0, 1);
00359 Eigen::Quaterniond q;
00360 q.setIdentity();
00361 if (active) q = Eigen::Quaterniond::FromTwoVectors(z, rotAxis);
00362 urdf2inventor::addCylinder(addToNode, Eigen::Vector3d(0, 0, 0), q, rotRad, rotH, rtr, rtg, rtb);
00363 }
00364
00365
00366 SoNode * Urdf2Inventor::getAsInventor(const LinkPtr& from_link, bool useScaleFactor,
00367 bool _addAxes, float _axesRadius, float _axesLength,
00368 const EigenTransform& addVisualTransform,
00369 std::set<std::string> * textureFiles
00370 )
00371 {
00372 if (!from_link.get())
00373 {
00374 ROS_ERROR("getAsInventor: from_link is NULL");
00375 return NULL;
00376 }
00377
00378 bool useVisuals = true;
00379
00380
00381 SoNode * allVisuals = getAllGeometry(from_link,
00382 useScaleFactor ? scaleFactor : 1.0,
00383 addVisualTransform,
00384 useVisuals,
00385 useScaleFactor);
00386 if (!allVisuals)
00387 {
00388 ROS_ERROR("Could not get visuals");
00389 return NULL;
00390 }
00391
00392 if (textureFiles)
00393 {
00394
00395 std::set<std::string> allFiles = urdf2inventor::getAllTexturePaths(allVisuals);
00396 textureFiles->insert(allFiles.begin(), allFiles.end());
00397 }
00398
00399 if (_addAxes)
00400 {
00401 SoSeparator * _allVisuals = dynamic_cast<SoSeparator*>(allVisuals);
00402 if (!_allVisuals)
00403 {
00404 ROS_WARN_STREAM("The node for link " << from_link->name << " is not a separator, so cannot add axes");
00405 }
00406 else
00407 {
00408
00409 addLocalAxes(from_link, _allVisuals, useScaleFactor, _axesRadius, _axesLength);
00410 }
00411 }
00412
00413 for (std::vector<JointPtr>::const_iterator pj = from_link->child_joints.begin();
00414 pj != from_link->child_joints.end(); pj++)
00415 {
00416 JointPtr joint = *pj;
00417
00418 LinkPtr childLink = urdf_traverser->getLink(joint->child_link_name);
00419 if (!childLink.get())
00420 {
00421 ROS_ERROR_STREAM("Consistency: Link " << joint->child_link_name << " does not exist.");
00422 return NULL;
00423 }
00424 SoNode * childNode = getAsInventor(childLink, useScaleFactor,
00425 _addAxes, _axesRadius, _axesLength, addVisualTransform, textureFiles);
00426 if (!childNode)
00427 {
00428 ROS_ERROR_STREAM("Could not get child node for " << childLink->name);
00429 return NULL;
00430 }
00431 EigenTransform jointTransform = urdf_traverser::getTransform(joint);
00432 if (useScaleFactor) urdf_traverser::scaleTranslation(jointTransform, scaleFactor);
00433
00434
00435
00436
00437 urdf2inventor::addSubNode(childNode, allVisuals, jointTransform);
00438 }
00439
00440 return allVisuals;
00441 }
00442
00443
00444 SoNode * Urdf2Inventor::getAsInventor(const std::string& fromLink, bool useScaleFactor,
00445 bool _addAxes, float _axesRadius, float _axesLength, const EigenTransform& addVisualTransform,
00446 std::set<std::string> * textureFiles
00447 )
00448 {
00449 std::string startLinkName = fromLink;
00450 if (startLinkName.empty())
00451 {
00452 startLinkName = urdf_traverser->getRootLinkName();
00453 }
00454
00455 LinkPtr startLink = urdf_traverser->getLink(startLinkName);
00456 if (!startLink.get())
00457 {
00458 ROS_ERROR_STREAM("No link named '" << startLink << "'");
00459 return NULL;
00460 }
00461 SoNode * root = getAsInventor(startLink, useScaleFactor, _addAxes, _axesRadius, _axesLength,
00462 addVisualTransform, textureFiles);
00463 urdf2inventor::removeTextureCopies(root);
00464 return root;
00465 }
00466
00467
00468 bool Urdf2Inventor::writeAsInventor(const std::string& ivFilename,
00469 const std::string& fromLink,
00470 bool useScaleFactor, const EigenTransform& addVisualTransform,
00471 bool _addAxes, float _axesRadius, float _axesLength)
00472 {
00473 std::string startLinkName = fromLink;
00474 if (startLinkName.empty())
00475 {
00476 startLinkName = urdf_traverser->getRootLinkName();
00477 }
00478
00479 LinkPtr startLink = urdf_traverser->getLink(startLinkName);
00480 if (!startLink.get())
00481 {
00482 ROS_ERROR_STREAM("No link named '" << startLinkName << "'");
00483 return false;
00484 }
00485
00486 ROS_INFO_STREAM("Writing from link '" << startLinkName << "' to file " << ivFilename);
00487 return writeAsInventor(ivFilename, startLink, useScaleFactor, addVisualTransform, _addAxes, _axesRadius, _axesLength);
00488 }
00489
00490 bool Urdf2Inventor::writeAsInventor(const std::string& ivFilename, const LinkPtr& from_link,
00491 bool useScaleFactor, const EigenTransform& addVisualTransform,
00492 bool _addAxes, float _axesRadius, float _axesLength)
00493 {
00494 ROS_INFO("Converting model...");
00495
00496 std::set<std::string> textureFiles;
00497 SoNode * inv = getAsInventor(from_link, useScaleFactor, _addAxes, _axesRadius, _axesLength,
00498 addVisualTransform, &textureFiles);
00499 if (!inv)
00500 {
00501 ROS_ERROR("could not generate overall inventor file");
00502 return false;
00503 }
00504
00505
00506 std::string resultFileContent;
00507 if (!urdf2inventor::writeInventorFileString(inv, resultFileContent))
00508 {
00509 ROS_ERROR("Could not get the mesh file content");
00510 return false;
00511 }
00512
00513
00514 if (!textureFiles.empty())
00515 {
00516
00517
00518 std::string commonParent;
00519 if (!urdf_traverser::helpers::getCommonParentPath(textureFiles, commonParent))
00520 {
00521 ROS_ERROR_STREAM("Could not find common parent path of all files");
00522 return false;
00523 }
00524
00525
00526
00527 std::string fileDir = urdf_traverser::helpers::getDirectory(ivFilename);
00528
00529
00530
00531
00532 std::map<std::string, std::set<std::string> > texToCopy;
00533
00534 ROS_INFO("Fixing texture file references...");
00535 if (!urdf2inventor::helpers::fixFileReferences(
00536 fileDir,
00537 fileDir + TEX_OUTPUT_DIRECTORY_NAME,
00538 commonParent,
00539 textureFiles,
00540 resultFileContent, texToCopy))
00541 {
00542 ROS_ERROR("Could not fix texture references");
00543 return false;
00544 }
00545
00546 ROS_INFO("Copying texture files...");
00547 if (!urdf2inventor::helpers::writeFiles(texToCopy, fileDir))
00548 {
00549 ROS_ERROR("Could not write textures");
00550 return false;
00551 }
00552 }
00553
00554 ROS_INFO("Writing model...");
00555
00556
00557 if (!urdf_traverser::helpers::writeToFile(resultFileContent, ivFilename))
00558 {
00559 ROS_ERROR_STREAM("Could not write file " << ivFilename);
00560 return false;
00561 }
00562
00563 ROS_INFO_STREAM("Whole robot model written to " << ivFilename);
00564
00565
00566 return true;
00567 }
00568
00569 Urdf2Inventor::ConversionResultPtr Urdf2Inventor::loadAndConvert(const std::string& urdfFilename,
00570 bool joinFixed,
00571 const ConversionParametersPtr& params)
00572 {
00573 ConversionResultPtr failResult(new ConversionResultT(OUTPUT_EXTENSION, MESH_OUTPUT_DIRECTORY_NAME, TEX_OUTPUT_DIRECTORY_NAME));
00574 failResult->success = false;
00575
00576 ROS_INFO_STREAM("Loading model from file " << urdfFilename);
00577
00578 if (!urdf_traverser->loadModelFromFile(urdfFilename))
00579 {
00580 ROS_ERROR("Could not load file");
00581 return failResult;
00582 }
00583
00584 if (joinFixed) ROS_INFO("Joining fixed links..");
00585 if (joinFixed && !joinFixedLinks(params->rootLinkName))
00586 {
00587 ROS_ERROR("Could not join fixed links");
00588 return failResult;
00589 }
00590
00591
00592
00593
00594 ConversionResultPtr result = convert(params);
00595 if (!result.get() || !result->success)
00596 {
00597 ROS_ERROR("Could not do the conversion");
00598 return result;
00599 }
00600
00601
00602 return result;
00603 }