ed
shape_loader.cpp
Go to the documentation of this file.
1 #include "shape_loader_private.h"
3 
4 #include "xml_shape_parser.h"
5 
6 #include <tue/filesystem/path.h>
7 
9 #include <geolib/io/import.h>
10 #include <geolib/serialization.h>
11 #include <geolib/Shape.h>
12 
13 // Heightmap generation
14 #include "polypartition/polypartition.h"
15 #include <opencv2/imgproc/imgproc.hpp>
16 #include <opencv2/highgui/highgui.hpp>
17 
18 // string split
19 #include <sstream>
20 #include <iostream>
21 
22 namespace ed
23 {
24 namespace models
25 {
26 
33 std::vector<std::string> split(const std::string& strToSplit, char delimeter)
34 {
35  std::stringstream ss(strToSplit);
36  std::string item;
37  std::vector<std::string> splittedStrings;
38  while (std::getline(ss, item, delimeter))
39  {
40  if (!item.empty() && item[0] != delimeter)
41  splittedStrings.push_back(item);
42  }
43  return splittedStrings;
44 }
45 
46 // ----------------------------------------------------------------------------------------------------
47 
49 {
50  static const std::string model_prefix = "model://";
51  static const std::string file_prefix = "file://";
52 
53  std::string type(uri);
54  std::string::size_type i = type.find(file_prefix);
55  if (i != std::string::npos)
56  {
57  uri_type = FILE;
58  type.erase(i, file_prefix.length());
59  return type;
60  }
61  i = uri.find(model_prefix);
62  if (i != std::string::npos)
63  {
64  uri_type = MODEL;
65  type.erase(i, model_prefix.length());
66  return type;
67  }
68  return "";
69 }
70 
71 // ----------------------------------------------------------------------------------------------------
72 
79 {
80  static const char * mpath = ::getenv("GAZEBO_MODEL_PATH");
81  static const char * rpath = ::getenv("GAZEBO_RESOURCE_PATH");
82  if (!mpath && !rpath)
83  return "";
84 
85  static std::vector<std::string> model_paths;
86  static std::vector<std::string> file_paths;
87 
88  if (model_paths.empty() && file_paths.empty())
89  {
90  std::string item;
91  std::stringstream ssm(mpath);
92  while (std::getline(ssm, item, ':'))
93  model_paths.push_back(item);
94 
95  // romove duplicate elements
96  std::sort(model_paths.begin(), model_paths.end());
97  model_paths.erase(unique(model_paths.begin(), model_paths.end()), model_paths.end());
98 
99  std::stringstream ssr(rpath);
100  while (std::getline(ssr, item, ':'))
101  file_paths.push_back(item);
102 
103  // remove duplicate elements
104  std::sort(file_paths.begin(), file_paths.end());
105  file_paths.erase(unique(file_paths.begin(), file_paths.end()), file_paths.end());
106  }
107 
108 
109  ModelOrFile uri_type;
110  std::string parsed_uri = parseURI(type, uri_type);
111  if (parsed_uri.empty())
112  return "";
113 
114 
115  std::vector<std::string>* type_paths;
116  if (uri_type == MODEL)
117  type_paths = &model_paths;
118  else
119  type_paths = &file_paths;
120 
121  for(std::vector<std::string>::const_iterator it = type_paths->cbegin(); it != type_paths->cend(); ++it)
122  {
123  tue::filesystem::Path file_path(*it + "/" + parsed_uri);
124  if (file_path.exists())
125  return file_path.string();
126  }
127 
128  return "";
129 }
130 
131 
132 // ----------------------------------------------------------------------------------------------------
133 
143 void findContours(const cv::Mat& image, const geo::Vec2i& p_start, int d_start, std::vector<geo::Vec2i>& points,
144  std::vector<geo::Vec2i>& line_starts, cv::Mat& contour_map)
145 {
146  static int dx[4] = {1, 0, -1, 0 };
147  static int dy[4] = {0, 1, 0, -1 };
148 
149  unsigned char v = image.at<unsigned char>(p_start.y, p_start.x);
150 
151  int d = d_start; // Current direction
152  geo::Vec2i p = p_start;
153 
154  while (true)
155  {
156  if (d == 3) // up
157  line_starts.push_back(p);
158 
159  if (d == 3)
160  contour_map.at<unsigned char>(p.y, p.x - 1) = 1;
161  else if (d == 1)
162  contour_map.at<unsigned char>(p.y, p.x) = 1;
163 
164  if (image.at<unsigned char>(p.y + dy[(d + 3) % 4], p.x + dx[(d + 3) % 4]) == v) // left
165  {
166  switch (d)
167  {
168  case 0: points.push_back(p - geo::Vec2i(1, 1)); break;
169  case 1: points.push_back(p - geo::Vec2i(0, 1)); break;
170  case 2: points.push_back(p); break;
171  case 3: points.push_back(p - geo::Vec2i(1, 0)); break;
172  }
173 
174  d = (d + 3) % 4;
175  p.x += dx[d];
176  p.y += dy[d];
177  }
178  else if (image.at<unsigned char>(p.y + dy[d], p.x + dx[d]) == v) // straight ahead
179  {
180  p.x += dx[d];
181  p.y += dy[d];
182  }
183  else // right
184  {
185  switch (d)
186  {
187  case 0: points.push_back(p - geo::Vec2i(0, 1)); break;
188  case 1: points.push_back(p); break;
189  case 2: points.push_back(p - geo::Vec2i(1, 0)); break;
190  case 3: points.push_back(p - geo::Vec2i(1, 1)); break;
191 
192  }
193 
194  d = (d + 1) % 4;
195  }
196 
197  if (p.x == p_start.x && p.y == p_start.y && d == d_start)
198  return;
199  }
200 }
201 
202 // ----------------------------------------------------------------------------------------------------
203 
213 geo::ShapePtr getHeightMapShape(cv::Mat& image_orig, const geo::Vec3& pos, const geo::Vec3& size, const bool inverted, std::stringstream& error)
214 {
215  double resolution_x = size.x/image_orig.cols;
216  double resolution_y = size.y/image_orig.rows;
217  double blockheight = size.z;
218 
219  // invert grayscale for SDF
220  if (inverted)
221  {
222  for (int i = 0; i < image_orig.rows; i++)
223  {
224  for (int j = 0; j < image_orig.cols; j++)
225  {
226  image_orig.at<uchar>(i, j) = 255 - image_orig.at<uchar>(i, j);
227  }
228  }
229  }
230 
231  // Add borders
232  cv::Mat image(image_orig.rows + 2, image_orig.cols + 2, CV_8UC1, cv::Scalar(255));
233  image_orig.copyTo(image(cv::Rect(cv::Point(1, 1), cv::Size(image_orig.cols, image_orig.rows))));
234 
235  cv::Mat vertex_index_map(image.rows, image.cols, CV_32SC1, -1);
236  cv::Mat contour_map(image.rows, image.cols, CV_8UC1, cv::Scalar(0));
237 
239 
240  for(int y = 0; y < image.rows; ++y)
241  {
242  for(int x = 0; x < image.cols; ++x)
243  {
244  unsigned char v = image.at<unsigned char>(y, x);
245 
246  if (v < 255)
247  {
248  std::vector<geo::Vec2i> points, line_starts;
249  findContours(image, geo::Vec2i(x, y), 0, points, line_starts, contour_map);
250 
251  unsigned int num_points = (unsigned int) points.size();
252 
253  if (num_points > 2)
254  {
255  geo::Mesh mesh;
256 
257  double min_z = pos.z;
258  double max_z = pos.z + (double)(255 - v) / 255 * blockheight;
259 
260  std::list<TPPLPoly> testpolys;
261 
262  TPPLPoly poly;
263  poly.Init(num_points);
264 
265  for(unsigned int i = 0; i < num_points; ++i)
266  {
267  poly[i].x = points[i].x;
268  poly[i].y = points[i].y;
269 
270  // Convert to world coordinates
271  double wx = points[i].x * resolution_x + pos.x;
272  double wy = (image.rows - points[i].y - 2) * resolution_y + pos.y;
273 
274  vertex_index_map.at<int>(points[i].y, points[i].x) = mesh.addPoint(geo::Vector3(wx, wy, min_z));
275  mesh.addPoint(geo::Vector3(wx, wy, max_z));
276  }
277 
278  testpolys.push_back(poly);
279 
280  // Calculate side triangles
281  for(unsigned int i = 0; i < num_points; ++i)
282  {
283  int j = (i + 1) % num_points;
284  mesh.addTriangle(i * 2, i * 2 + 1, j * 2);
285  mesh.addTriangle(i * 2 + 1, j * 2 + 1, j * 2);
286  }
287 
288  for(unsigned int i = 0; i < line_starts.size(); ++i)
289  {
290  int x2 = line_starts[i].x;
291  int y2 = line_starts[i].y;
292 
293  while(image.at<unsigned char>(y2, x2) == v)
294  ++x2;
295 
296  if (contour_map.at<unsigned char>(y2, x2 - 1) == 0)
297  {
298  // found a hole, so find the contours of this hole
299  std::vector<geo::Vec2i> hole_points;
300  findContours(image, geo::Vec2i(x2 - 1, y2 + 1), 1, hole_points, line_starts, contour_map);
301 
302  if (hole_points.size() > 2)
303  {
304  TPPLPoly poly_hole;
305  poly_hole.Init(hole_points.size());
306  poly_hole.SetHole(true);
307 
308  for(unsigned int j = 0; j < hole_points.size(); ++j)
309  {
310  poly_hole[j].x = hole_points[j].x;
311  poly_hole[j].y = hole_points[j].y;
312 
313  // Convert to world coordinates
314  double wx = hole_points[j].x * resolution_x + pos.x;
315  double wy = (image.rows - hole_points[j].y - 2) * resolution_y + pos.y;
316 
317  vertex_index_map.at<int>(hole_points[j].y, hole_points[j].x) = mesh.addPoint(geo::Vector3(wx, wy, min_z));
318  mesh.addPoint(geo::Vector3(wx, wy, max_z));
319  }
320  testpolys.push_back(poly_hole);
321 
322  // Calculate side triangles
323  for(unsigned int j = 0; j < hole_points.size(); ++j)
324  {
325  const geo::Vec2i& hp1 = hole_points[j];
326  const geo::Vec2i& hp2 = hole_points[(j + 1) % hole_points.size()];
327 
328  int i1 = vertex_index_map.at<int>(hp1.y, hp1.x);
329  int i2 = vertex_index_map.at<int>(hp2.y, hp2.x);
330 
331  mesh.addTriangle(i1, i1 + 1, i2);
332  mesh.addTriangle(i2, i1 + 1, i2 + 1);
333  }
334  }
335  }
336  }
337 
338  TPPLPartition pp;
339  std::list<TPPLPoly> result;
340 
341  if (!pp.Triangulate_EC(&testpolys, &result))
342  {
343  error << "[ED::MODELS::LOADSHAPE] Error while creating heightmap: could not triangulate polygon." << std::endl;
344  return geo::ShapePtr();
345  }
346 
347  for(std::list<TPPLPoly>::iterator it = result.begin(); it != result.end(); ++it)
348  {
349  TPPLPoly& cp = *it;
350 
351  int i1 = vertex_index_map.at<int>(cp[0].y, cp[0].x) + 1;
352  int i2 = vertex_index_map.at<int>(cp[1].y, cp[1].x) + 1;
353  int i3 = vertex_index_map.at<int>(cp[2].y, cp[2].x) + 1;
354 
355  mesh.addTriangle(i1, i3, i2);
356  }
357 
358  geo::Shape sub_shape;
359  sub_shape.setMesh(mesh);
360 
361  shape->addShape(sub_shape, geo::Pose3D::identity());
362 
363  cv::floodFill(image, cv::Point(x, y), 255);
364  }
365  }
366  }
367  }
368 
369  return shape;
370 }
371 
372 // ----------------------------------------------------------------------------------------------------
373 
383 geo::ShapePtr getHeightMapShape(const std::string& image_filename, const geo::Vec3& pos, const geo::Vec3& size,
384  const bool inverted, std::stringstream& error)
385 {
386  cv::Mat image_orig = cv::imread(image_filename, cv::IMREAD_GRAYSCALE); // Read the file
387 
388  if (!image_orig.data)
389  {
390  error << "[ED::MODELS::LOADSHAPE] Error while loading heightmap '" << image_filename << "'. Image could not be loaded." << std::endl;
391  return geo::ShapePtr();
392  }
393 
394  return getHeightMapShape(image_orig, pos, size, inverted, error);
395 }
396 
397 
398 // ----------------------------------------------------------------------------------------------------
399 
400 geo::ShapePtr getHeightMapShape(const std::string& image_filename, const geo::Vec3& pos, const double blockheight,
401  const double resolution_x, const double resolution_y, const bool inverted, std::stringstream& error)
402 {
403  cv::Mat image_orig = cv::imread(image_filename, cv::IMREAD_GRAYSCALE); // Read the file
404 
405  if (!image_orig.data)
406  {
407  error << "[ED::MODELS::LOADSHAPE] Error while loading heightmap '" << image_filename << "'. Image could not be loaded." << std::endl;
408  return geo::ShapePtr();
409  }
410 
411  double size_x = resolution_x * image_orig.cols;
412  double size_y = resolution_y * image_orig.rows;
413  geo::Vec3 size(size_x, size_y, blockheight);
414 
415  return getHeightMapShape(image_orig, pos, size, inverted, error);
416 }
417 
418 
419 // ----------------------------------------------------------------------------------------------------
420 
429 {
430  double resolution, origin_x, origin_y, origin_z, blockheight;
431  if (!(cfg.value("origin_x", origin_x) &&
432  cfg.value("origin_y", origin_y) &&
433  cfg.value("origin_z", origin_z) &&
434  cfg.value("resolution", resolution) &&
435  cfg.value("blockheight", blockheight)))
436  {
437  error << "[ED::MODELS::LOADSHAPE] Error while loading heightmap parameters at '" << image_filename
438  << "'. Required shape parameters: resolution, origin_x, origin_y, origin_z, blockheight" << std::endl;
439  return geo::ShapePtr();
440  }
441 
442  int inverted = 0;
443  cfg.value("inverted", inverted);
444 
445  return getHeightMapShape(image_filename, geo::Vec3(origin_x, origin_y, origin_z), blockheight, resolution, resolution,
446  static_cast<bool>(inverted), error);
447 }
448 
449 // ----------------------------------------------------------------------------------------------------
450 
451 void createPolygon(geo::Shape& shape, const std::vector<geo::Vec2>& points, double height, std::stringstream& error, bool create_bottom)
452 {
453  TPPLPoly poly;
454  poly.Init((unsigned int) points.size());
455 
456  double min_z = -height / 2;
457  double max_z = height / 2;
458 
459  geo::Mesh mesh;
460 
461  for(unsigned int i = 0; i < points.size(); ++i)
462  {
463  poly[i].x = points[i].x;
464  poly[i].y = points[i].y;
465 
466  mesh.addPoint(geo::Vector3(points[i].x, points[i].y, min_z));
467  mesh.addPoint(geo::Vector3(points[i].x, points[i].y, max_z));
468  }
469 
470  // Add side triangles
471  for(unsigned int i = 0; i < points.size(); ++i)
472  {
473  int j = (i + 1) % points.size();
474  mesh.addTriangle(i * 2, j * 2, i * 2 + 1);
475  mesh.addTriangle(i * 2 + 1, j * 2, j * 2 + 1);
476  }
477 
478  std::list<TPPLPoly> polys;
479  polys.push_back(poly);
480 
481  TPPLPartition pp;
482  std::list<TPPLPoly> result;
483 
484  if (!pp.Triangulate_EC(&polys, &result))
485  {
486  error << "[ED::MODELS::LOADSHAPE](createPolygon) TRIANGULATION FAILED" << std::endl;
487  return;
488  }
489 
490  for(std::list<TPPLPoly>::iterator it = result.begin(); it != result.end(); ++it)
491  {
492  TPPLPoly& cp = *it;
493 
494  int i1 = mesh.addPoint(cp[0].x, cp[0].y, max_z);
495  int i2 = mesh.addPoint(cp[1].x, cp[1].y, max_z);
496  int i3 = mesh.addPoint(cp[2].x, cp[2].y, max_z);
497  mesh.addTriangle(i1, i2, i3);
498 
499  if (create_bottom)
500  {
501  int i1 = mesh.addPoint(cp[0].x, cp[0].y, min_z);
502  int i2 = mesh.addPoint(cp[1].x, cp[1].y, min_z);
503  int i3 = mesh.addPoint(cp[2].x, cp[2].y, min_z);
504  mesh.addTriangle(i1, i3, i2);
505  }
506  }
507 
509 
510  shape.setMesh(mesh);
511 }
512 
513 // ----------------------------------------------------------------------------------------------------
514 
522 {
523  cfg.value("x", v.x, pos_req);
524  cfg.value("y", v.y, pos_req);
525  cfg.value("z", v.z, pos_req);
526 }
527 
528 // ----------------------------------------------------------------------------------------------------
529 
539 {
540  std::string vector_string;
541  if (cfg.readGroup(vector_name))
542  {
543  readVec3(cfg, v);
544  cfg.endGroup();
545  }
546  else if (cfg.value(vector_name, vector_string))
547  {
548  std::vector<std::string> vector_vector = split(vector_string, ' ');
549  v.x = std::stod(vector_vector[0]);
550  v.y = std::stod(vector_vector[1]);
551  v.z = std::stod(vector_vector[2]);
552  }
553  else
554  return false;
555 
556  return true;
557 }
558 
559 // ----------------------------------------------------------------------------------------------------
560 
562 {
563  double roll = 0, pitch = 0, yaw = 0;
564  std::string pose_string = ""; //sdf pose will be a string
565  if (cfg.readGroup("pose"))
566  {
567  readVec3(cfg, pose.t, pos_req);
568 
569  cfg.value("X", roll, rot_req);
570  cfg.value("Y", pitch, rot_req);
571  cfg.value("Z", yaw, rot_req);
572  cfg.value("roll", roll, rot_req);
573  cfg.value("pitch", pitch, rot_req);
574  cfg.value("yaw", yaw, rot_req);
575 
576  cfg.endGroup();
577  }
578  else if (cfg.value("pose", pose_string, pos_req))
579  {
580  // pose is in SDF
581  std::vector<std::string> pose_vector = split(pose_string, ' ');
582  if (pose_vector.size() == 6)
583  {
584  // ignoring pose, when incorrect/incomplete
585  pose.t.x = std::stod(pose_vector[0]);
586  pose.t.y = std::stod(pose_vector[1]);
587  pose.t.z = std::stod(pose_vector[2]);
588  roll = std::stod(pose_vector[3]);
589  pitch = std::stod(pose_vector[4]);
590  yaw = std::stod(pose_vector[5]);
591  }
592  else
593  return false;
594  }
595  else if (cfg.value("pos", pose_string, pos_req))
596  {
597  // position is in SDF
598  std::vector<std::string> pose_vector = split(pose_string, ' ');
599  if (pose_vector.size() == 3)
600  {
601  // ignoring pose, when incorrect/incomplete
602  pose.t.x = std::stod(pose_vector[0]);
603  pose.t.y = std::stod(pose_vector[1]);
604  pose.t.z = std::stod(pose_vector[2]);
605  }
606  else
607  return false;
608  }
609  else
610  {
611  return false;
612  }
613 
614  // Set rotation
615  pose.R.setRPY(roll, pitch, yaw);
616  return true;
617 }
618 
619 // ----------------------------------------------------------------------------------------------------
620 
623 {
624  geo::ShapePtr shape;
626 
627  std::string path;
628  if (cfg.value("path", path)) // ED YAML ONLY
629  {
630  if (path.empty())
631  {
632  error << "[ED::MODELS::LOADSHAPE] Error while loading shape: provided path is empty.";
633  return shape;
634  }
635 
636  tue::filesystem::Path shape_path;
637 
638  if (model_path.empty() || path[0] == '/')
639  shape_path = path;
640  else
641  shape_path = model_path + "/" + path;
642 
643  // Check cache first
645  if (it != shape_cache.end())
646  return it->second;
647 
648  if (shape_path.exists())
649  {
650  std::string xt = shape_path.extension();
651  if (xt == ".pgm" || xt == ".png")
652  {
653  shape = getHeightMapShape(shape_path.string(), cfg, error);
654  }
655  else if (xt == ".geo")
656  {
657  geo::serialization::registerDeserializer<geo::Shape>();
658  shape = geo::serialization::fromFile(shape_path.string());
659  }
660  else if (xt == ".3ds" || xt == ".stl" || xt == ".dae")
661  {
662  shape = geo::io::readMeshFile(shape_path.string());
663  }
664  else if (xt == ".xml")
665  {
667  shape = parseXMLShape(shape_path.string(), error);
668  }
669 
670  if (!shape)
671  error << "[ED::MODELS::LOADSHAPE] Error while loading shape at " << shape_path.string() << std::endl;
672  else
673  // Add to cache
674  shape_cache[shape_path.string()] = shape;
675  }
676  else
677  {
678  error << "[ED::MODELS::LOADSHAPE] Error while loading shape at " << shape_path.string() << " ; file does not exist" << std::endl;
679  }
680  }
681  else if (cfg.readGroup("box")) // SDF AND ED YAML
682  {
683  geo::Vec3 min, max, size;
684  if (readVec3Group(cfg, min, "min"))
685  {
686  if (readVec3Group(cfg, max, "max"))
687  {
688  shape.reset(new geo::Box(min, max));
689  }
690  else
691  {
692  error << "[ED::MODELS::LOADSHAPE] Error while loading shape: box must contain 'min' and 'max' (only 'min' specified)";
693  }
694  }
695  else if (readVec3Group(cfg, size, "size"))
696  shape.reset(new geo::Box(-0.5 * size, 0.5 * size));
697  else
698  {
699  error << "[ED::MODELS::LOADSHAPE] Error while loading shape: box must contain 'min' and 'max' or 'size'.";
700  }
701 
702  readPose(cfg, pose);
703  cfg.endGroup();
704  }
705  else if (cfg.readGroup("cylinder")) // SDF AND ED YAML
706  {
707  int num_points = 12;
708  cfg.value("num_points", num_points, tue::config::OPTIONAL);
709 
710  double radius = 0, height = 0;
711  if (cfg.value("radius", radius) && (cfg.value("height", height) || cfg.value("length", height))) // length is used in SDF
712  {
713 
714  shape.reset(new geo::Shape());
715  createCylinder(*shape, radius, height, num_points);
716  }
717 
718  cfg.endGroup();
719  }
720  else if (cfg.readGroup("polygon")) // ED YAML ONLY
721  {
722  std::vector<geo::Vec2> points;
723  if (cfg.readArray("points", tue::config::REQUIRED) || cfg.readArray("point", tue::config::REQUIRED))
724  {
725  while(cfg.nextArrayItem())
726  {
727  points.push_back(geo::Vec2());
728  geo::Vec2& p = points.back();
729  cfg.value("x", p.x);
730  cfg.value("y", p.y);
731  }
732  cfg.endArray();
733  }
734 
735  double height;
736  if (cfg.value("height", height))
737  {
738  shape.reset(new geo::Shape());
739  createPolygon(*shape, points, height, error, true);
740  }
741 
742  cfg.endGroup();
743  }
744  else if (cfg.readGroup("polyline")) // SDF ONLY
745  {
746  std::vector<geo::Vec2> points;
747  if (cfg.readArray("point", tue::config::REQUIRED))
748  {
749  while (cfg.nextArrayItem())
750  {
751  std::string point_string;
752  cfg.value("point", point_string);
753  points.push_back(geo::Vec2());
754  geo::Vec2& p = points.back();
755  std::vector<std::string> point_vector = split(point_string, ' ');
756  p.x = std::stod(point_vector[0]);
757  p.y = std::stod(point_vector[1]);
758  }
759  cfg.endArray();
760  }
761 
762  double height;
763  if (cfg.value("height", height))
764  {
765  shape.reset(new geo::Shape());
766  createPolygon(*shape, points, height, error, true);
767  }
768 
769  cfg.endGroup();
770  }
771  else if (cfg.readGroup("mesh")) // SDF ONLY
772  {
773  std::string uri_path;
774  if (cfg.value("uri", uri_path))
775  {
776  geo::Vec3 scale(1, 1, 1);
777  std::string scale_str;
778  if (cfg.value("scale", scale_str))
779  {
780  std::vector<std::string> scale_vector = split(scale_str, ' ');
781  if(scale_vector.size() != 3)
782  {
783  error << "[ED::MODELS::LOADSHAPE] Mesh scale: '" << scale_str << "' should have 3 members." << std::endl;
784  return shape;
785  }
786  scale.x = std::stod(scale_vector[0]);
787  scale.y = std::stod(scale_vector[1]);
788  scale.z = std::stod(scale_vector[2]);
789  }
790  tue::filesystem::Path mesh_path = getUriPath(uri_path);
791  if (mesh_path.exists())
792  shape = geo::io::readMeshFile(mesh_path.string(), scale);
793  else
794  error << "[ED::MODELS::LOADSHAPE] Mesh File: '" << mesh_path.string() << "' doesn't exist." << std::endl;
795  }
796  else
797  error << "[ED::MODELS::LOADSHAPE] No uri found for mesh." << std::endl;
798 
799  std::string dummy;
800  if (cfg.value("submesh", dummy))
801  error << "[ED::MODELS::LOADSHAPE] 'submesh' of mesh is not supported by ED " << std::endl;
802 
803  cfg.endGroup();
804  }
805  else if (cfg.readGroup("heightmap")) // SDF AND ED YAML
806  {
807  std::string image_filename;
808  double height, resolution;
809 
810  geo::Vec3 size;
811  if ((cfg.value("image", image_filename) && !image_filename.empty() && cfg.value("resolution", resolution) &&
812  cfg.value("height", height))) // ED YAML ONLY
813  {
814  std::string image_filename_full = image_filename;
815 // if (image_filename[0] == '/')
816 // image_filename_full = image_filename;
817 // else
818 // image_filename_full = model_path + "/" + image_filename;
819 
820  shape = getHeightMapShape(image_filename_full, geo::Vec3(0, 0, 0), height, resolution, resolution, false, error);
821 
822  readPose(cfg, pose);
823  }
824  else if(cfg.value("uri", image_filename) && readVec3Group(cfg, size, "size")) // SDF ONLY
825  {
826  image_filename = getUriPath(image_filename);
827 
828  // Center is in the middle.
829  geo::Vec3 pos = -size/2;
830  pos.z = 0;
831 
832  shape = getHeightMapShape(image_filename, pos, size, true, error);
833  readPose(cfg, pose);
834  }
835  else
836  {
837  error << "[ED::MODELS::LOADSHAPE] Error while loading shape: heightmap must contain 'image', 'resolution' and 'height'." << std::endl;
838  }
839  cfg.endGroup();
840  }
841  else if (cfg.readGroup("sphere")) // SDF
842  {
843  double radius;
844  if (!cfg.value("radius", radius))
845  error << "[ED::MODELS::LOADSHAPE] Error while loading shape: sphere must contain 'radius'." << std::endl;
846  int recursion_level = 1;
847  shape.reset(new geo::Shape);
848  createSphere(*shape, radius, recursion_level);
849  cfg.endGroup();
850  }
851  else if (cfg.readArray("compound") || cfg.readArray("group")) // ED YAML ONLY
852  {
854  while(cfg.nextArrayItem())
855  {
856  std::map<std::string, geo::ShapePtr> dummy_shape_cache;
857  geo::ShapePtr sub_shape = loadShape(model_path, cfg, dummy_shape_cache, error);
858  composite->addShape(*sub_shape, geo::Pose3D::identity());
859  }
860  cfg.endArray();
861 
862  shape = composite;
863  }
864  else
865  {
866  error << "[ED::MODELS::LOADSHAPE] Error while loading shape with data:" << std::endl << cfg.data() << std::endl;
867  }
868 
869  // Extra pose is only allowed in ED yaml.
870  readPose(cfg, pose);
871  if (shape && pose != geo::Pose3D::identity())
872  {
873  // Transform shape according to pose
874  geo::ShapePtr shape_tr(new geo::Shape());
875  shape_tr->setMesh(shape->getMesh().getTransformed(pose));
876  shape = shape_tr;
877  }
878 
879  return shape;
880 }
881 
882 // ----------------------------------------------------------------------------------------------------
883 
884 void createCylinder(geo::Shape& shape, double radius, double height, int num_corners)
885 {
886  geo::Mesh mesh;
887 
888  // Calculate vertices
889  for(int i = 0; i < num_corners; ++i)
890  {
891  double a = 2 * M_PI * i / num_corners;
892  double x = sin(a) * radius;
893  double y = cos(a) * radius;
894 
895  mesh.addPoint(x, y, -height / 2);
896  mesh.addPoint(x, y, height / 2);
897  }
898 
899  // Calculate top and bottom triangles
900  for(int i = 1; i < num_corners - 1; ++i)
901  {
902  int i2 = 2 * i;
903 
904  // bottom
905  mesh.addTriangle(0, i2, i2 + 2);
906 
907  // top
908  mesh.addTriangle(1, i2 + 3, i2 + 1);
909  }
910 
911  // Calculate side triangles
912  for(int i = 0; i < num_corners; ++i)
913  {
914  int j = (i + 1) % num_corners;
915  mesh.addTriangle(i * 2, i * 2 + 1, j * 2);
916  mesh.addTriangle(i * 2 + 1, j * 2 + 1, j * 2);
917  }
918 
919  shape.setMesh(mesh);
920 }
921 
922 // ----------------------------------------------------------------------------------------------------
923 
924 uint getMiddlePoint(geo::Mesh& mesh, uint i1, uint i2, std::map<unsigned long, uint> cache, double radius)
925 {
926  // first check if we have it already
927  bool firstIsSmaller = i1 < i2;
928  unsigned long smallerIndex = firstIsSmaller ? i1 : i2;
929  unsigned long greaterIndex = firstIsSmaller ? i2 : i1;
930  unsigned long key = (smallerIndex << 32) + greaterIndex;
931 
933  if (it != cache.end())
934  return it->second;
935 
936  // not in cache, calculate it
937  const std::vector<geo::Vec3>& points = mesh.getPoints();
938  geo::Vec3 p1 = points[i1];
939  geo::Vec3 p2 = points[i2];
940  geo::Vec3 p3((p1+p2)/2);
941  p3 = p3.normalized() * radius;
942 
943  // add vertex makes sure point is on unit sphere
944  uint i3 = mesh.addPoint(p3);
945 
946  // store it, return index
947  cache.insert(std::pair<unsigned long, uint>(key, i3));
948  return i3;
949 }
950 
951 // ----------------------------------------------------------------------------------------------------
952 
953 void createSphere(geo::Shape& shape, double radius, uint recursion_level)
954 {
955  geo::Mesh mesh;
956 
957  // create 12 vertices of a icosahedron
958  double t = (1.0 + sqrt(5.0)) / 2.0;
959 
960  mesh.addPoint(geo::Vec3(-1, t, 0).normalized()*radius);
961  mesh.addPoint(geo::Vec3( 1, t, 0).normalized()*radius);
962  mesh.addPoint(geo::Vec3(-1, -t, 0).normalized()*radius);
963  mesh.addPoint(geo::Vec3( 1, -t, 0).normalized()*radius);
964 
965  mesh.addPoint(geo::Vec3( 0, -1, t).normalized()*radius);
966  mesh.addPoint(geo::Vec3( 0, 1, t).normalized()*radius);
967  mesh.addPoint(geo::Vec3( 0, -1, -t).normalized()*radius);
968  mesh.addPoint(geo::Vec3( 0, 1, -t).normalized()*radius);
969 
970  mesh.addPoint(geo::Vec3( t, 0, -1).normalized()*radius);
971  mesh.addPoint(geo::Vec3( t, 0, 1).normalized()*radius);
972  mesh.addPoint(geo::Vec3(-t, 0, -1).normalized()*radius);
973  mesh.addPoint(geo::Vec3(-t, 0, 1).normalized()*radius);
974 
975  // create 20 triangles of the icosahedron
976  // 5 faces around point 0
977  mesh.addTriangle(0, 11, 5);
978  mesh.addTriangle(0, 5, 1);
979  mesh.addTriangle(0, 1, 7);
980  mesh.addTriangle(0, 7, 10);
981  mesh.addTriangle(0, 10, 11);
982 
983  // 5 adjacent faces
984  mesh.addTriangle(1, 5, 9);
985  mesh.addTriangle(5, 11, 4);
986  mesh.addTriangle(11, 10, 2);
987  mesh.addTriangle(10, 7, 6);
988  mesh.addTriangle(7, 1, 8);
989 
990  // 5 faces around point 3
991  mesh.addTriangle(3, 9, 4);
992  mesh.addTriangle(3, 4, 2);
993  mesh.addTriangle(3, 2, 6);
994  mesh.addTriangle(3, 6, 8);
995  mesh.addTriangle(3, 8, 9);
996 
997  // 5 adjacent faces
998  mesh.addTriangle(4, 9, 5);
999  mesh.addTriangle(2, 4, 11);
1000  mesh.addTriangle(6, 2, 10);
1001  mesh.addTriangle(8, 6, 7);
1002  mesh.addTriangle(9, 8, 1);
1003 
1004  for (uint i = 0; i < recursion_level; i++)
1005  {
1006  geo::Mesh mesh2;
1008 
1009  const std::vector<geo::Vec3>& points = mesh.getPoints();
1010  for (std::vector<geo::Vec3>::const_iterator it = points.begin(); it != points.end(); ++it)
1011  mesh2.addPoint(*it);
1012 
1013  const std::vector<geo::TriangleI>& triangleIs = mesh.getTriangleIs();
1014  for (std::vector<geo::TriangleI>::const_iterator it = triangleIs.begin(); it != triangleIs.end(); ++it)
1015  {
1016  // replace triangle by 4 triangles
1017  uint a = getMiddlePoint(mesh2, it->i1_, it->i2_, cache, radius);
1018  uint b = getMiddlePoint(mesh2, it->i2_, it->i3_, cache, radius);
1019  uint c = getMiddlePoint(mesh2, it->i3_, it->i1_, cache, radius);
1020 
1021  mesh2.addTriangle(it->i1_, a, c);
1022  mesh2.addTriangle(it->i2_, b, a);
1023  mesh2.addTriangle(it->i3_, c, b);
1024  mesh2.addTriangle(a, b, c);
1025  }
1026  mesh = mesh2;
1027  }
1028  shape.setMesh(mesh);
1029 }
1030 
1031 // ----------------------------------------------------------------------------------------------------
1032 
1033 } // end namespace models
1034 
1035 } // end namespace ed
geo::Shape::setMesh
virtual void setMesh(const Mesh &mesh)
ed::models::parseURI
std::string parseURI(const std::string &uri, ModelOrFile &uri_type)
Definition: shape_loader.cpp:48
sstream
ed::models::readVec3
void readVec3(tue::config::Reader &cfg, geo::Vec3 &v, tue::config::RequiredOrOptional pos_req=tue::config::REQUIRED)
readVec3 read x, y and z into a vector
Definition: shape_loader.cpp:521
geo::ShapePtr
std::shared_ptr< Shape > ShapePtr
std::string
tue::config::Reader::nextArrayItem
bool nextArrayItem()
std::shared_ptr
std::list
geo::Mesh::getTriangleIs
const std::vector< TriangleI > & getTriangleIs() const
t
Timer t
std::pair
a
void a()
tue::config::Reader::value
bool value(const std::string &name, T &value, const RequiredOrOptional=REQUIRED) const
geo::Vec2T::y
T y
ed::models::createPolygon
void createPolygon(geo::Shape &shape, const std::vector< geo::Vec2 > &points, double height, std::stringstream &error, bool create_bottom)
createPolygon create polygon mesh from points
Definition: shape_loader.cpp:451
tue::filesystem::Path
geo::Transform3T::identity
static Transform3T identity()
import.h
std::vector< std::string >
std::string::find
T find(T... args)
std::string::length
T length(T... args)
tue::filesystem::Path::extension
std::string extension() const
geo::Box
geo::Vec3T
Shape.h
std::stringstream
geo::Vec2T::x
T x
tue::filesystem::Path::exists
bool exists() const
std::vector::back
T back(T... args)
geo::Transform3T
ed::models::getUriPath
static std::string getUriPath(std::string type)
getUriPath searches GAZEBO_MODEL_PATH and GAZEBO_RESOURCH_PATH for file
Definition: shape_loader.cpp:78
iostream
std::sort
T sort(T... args)
std::shared_ptr::reset
T reset(T... args)
ed::models::getMiddlePoint
uint getMiddlePoint(geo::Mesh &mesh, uint i1, uint i2, std::map< unsigned long, uint > cache, double radius)
getMiddlePoint Gets the middle point of two points in a mesh of a sphere. Uses a cache to not create ...
Definition: shape_loader.cpp:924
geo::serialization::fromFile
static ShapePtr fromFile(const std::string &filename)
std::vector::push_back
T push_back(T... args)
image
cv::Mat image
Definition: view_model.cpp:42
geo::Vec3T::y
T y
tue::config::Reader::readGroup
bool readGroup(const std::string &name, const RequiredOrOptional opt=OPTIONAL)
ed::models::ModelOrFile
ModelOrFile
The ModelOrFile enum This is used to determine the URI type in SDF.
Definition: shape_loader_private.h:25
geo::Mesh::getPoints
const std::vector< geo::Vector3 > & getPoints() const
xml_shape_parser.h
ed::log::error
std::ostream & error()
Definition: logging.cpp:74
std::vector::at
T at(T... args)
tue::config::Reader::data
DataConstPointer data() const
tue::config::Reader
ed::models::loadShape
geo::ShapePtr loadShape(const std::string &model_path, tue::config::Reader cfg, std::map< std::string, geo::ShapePtr > &shape_cache, std::stringstream &error)
loadShape load the shape of a model.
Definition: shape_loader.cpp:621
ed::models::split
std::vector< std::string > split(const std::string &strToSplit, char delimeter)
split Implementation by using delimiter as a character. Multiple delimeters are removed.
Definition: shape_loader.cpp:33
parseXMLShape
geo::ShapePtr parseXMLShape(const std::string &filename, std::string &error)
Definition: xml_shape_parser.cpp:35
serialization.h
tue::config::RequiredOrOptional
RequiredOrOptional
tue::config::Reader::endGroup
bool endGroup()
geo::io::readMeshFile
ShapePtr readMeshFile(const std::string &filename, const geo::Vec3 &scale)
geo::Transform3T::t
Vec3T< T > t
std::string::erase
T erase(T... args)
shape_loader_private.h
tue::config::OPTIONAL
OPTIONAL
CompositeShape.h
geo::Vector3
std::map
ed::models::createCylinder
void createCylinder(geo::Shape &shape, double radius, double height, int num_corners=12)
createCylinder create a mesh from radius and height
Definition: shape_loader.cpp:884
geo::CompositeShape
ed::models::readVec3Group
bool readVec3Group(tue::config::Reader &cfg, geo::Vec3 &v, const std::string &vector_name, tue::config::RequiredOrOptional=tue::config::REQUIRED)
readVec3Group read a config group into a Vec3
Definition: shape_loader.cpp:538
b
void b()
path.h
std::stod
T stod(T... args)
std::endl
T endl(T... args)
ed::models::createSphere
void createSphere(geo::Shape &shape, double radius, uint recursion_level=2)
createSphere Create a shape of sphere
Definition: shape_loader.cpp:953
shape_loader.h
geo::Mesh::filterOverlappingVertices
void filterOverlappingVertices()
std::vector::begin
T begin(T... args)
std::getline
T getline(T... args)
geo::Vec3T::normalized
Vec3T normalized() const
std::map::insert
T insert(T... args)
geo::Transform3T::R
Mat3T< T > R
geo::Mesh::addTriangle
void addTriangle(unsigned int i1, unsigned int i2, unsigned int i3)
ed::models::MODEL
@ MODEL
Definition: shape_loader_private.h:27
geo::Vec3T::z
T z
std::string::empty
T empty(T... args)
ed::models::findContours
void findContours(const cv::Mat &image, const geo::Vec2i &p_start, int d_start, std::vector< geo::Vec2i > &points, std::vector< geo::Vec2i > &line_starts, cv::Mat &contour_map)
findContours
Definition: shape_loader.cpp:143
ed
Definition: convex_hull.h:8
tue::filesystem::Path::string
const std::string & string() const
tue::config::Reader::readArray
bool readArray(const std::string &name, const RequiredOrOptional opt=OPTIONAL)
std::vector::end
T end(T... args)
ed::models::FILE
@ FILE
Definition: shape_loader_private.h:28
tue::config::REQUIRED
REQUIRED
c
void c()
ed::models::readPose
bool readPose(tue::config::Reader &cfg, geo::Pose3D &pose, tue::config::RequiredOrOptional pos_req, tue::config::RequiredOrOptional rot_req)
readPose read pose into Pose3D. Both ED yaml and SDF. Also reads pos(position) of SDF.
Definition: shape_loader.cpp:561
tue::config::Reader::endArray
bool endArray()
geo::Mesh
geo::Mat3T::setRPY
void setRPY(T roll, T pitch, T yaw)
ed::models::getHeightMapShape
geo::ShapePtr getHeightMapShape(cv::Mat &image_orig, const geo::Vec3 &pos, const geo::Vec3 &size, const bool inverted, std::stringstream &error)
getHeightMapShape convert grayscale image in a heigtmap mesh
Definition: shape_loader.cpp:213
geo::Mesh::addPoint
unsigned int addPoint(const geo::Vector3 &p)
geo::Vec3T::x
T x
geo::Vec2T
geo::Shape