load_mesh.cpp
Go to the documentation of this file.
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2008, Willow Garage, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the Willow Garage nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34 
35 #include <cstdio>
36 #include <cmath>
37 #include <algorithm>
38 #include <set>
41 #include <ros/assert.h>
42 #include <tinyxml.h>
43 #if defined(ASSIMP_UNIFIED_HEADER_NAMES)
44 #include <assimp/Importer.hpp>
45 #include <assimp/scene.h>
46 #include <assimp/postprocess.h>
47 #include <assimp/IOStream.hpp>
48 #include <assimp/IOSystem.hpp>
49 #else
50 #include <assimp/assimp.hpp>
51 #include <assimp/aiScene.h>
52 #include <assimp/aiPostProcess.h>
53 #include <assimp/IOStream.h>
54 #include <assimp/IOSystem.h>
55 #endif
56 
57 
58 // \author Ioan Sucan ; based on stl_to_mesh
59 namespace robot_self_filter
60 {
61 namespace shapes
62 {
63 
64  // Assimp wrappers
65  class ResourceIOStream : public Assimp::IOStream
66  {
67  public:
69  : res_(res)
70  , pos_(res.data.get())
71  {}
72 
74  {}
75 
76  size_t Read(void* buffer, size_t size, size_t count)
77  {
78  size_t to_read = size * count;
79  if (pos_ + to_read > res_.data.get() + res_.size)
80  {
81  to_read = res_.size - (pos_ - res_.data.get());
82  }
83 
84  memcpy(buffer, pos_, to_read);
85  pos_ += to_read;
86 
87  return to_read;
88  }
89 
90  size_t Write( const void* buffer, size_t size, size_t count) { ROS_BREAK(); return 0; }
91 
92  aiReturn Seek( size_t offset, aiOrigin origin)
93  {
94  uint8_t* new_pos = 0;
95  switch (origin)
96  {
97  case aiOrigin_SET:
98  new_pos = res_.data.get() + offset;
99  break;
100  case aiOrigin_CUR:
101  new_pos = pos_ + offset; // TODO is this right? can offset really not be negative
102  break;
103  case aiOrigin_END:
104  new_pos = res_.data.get() + res_.size - offset; // TODO is this right?
105  break;
106  default:
107  ROS_BREAK();
108  }
109 
110  if (new_pos < res_.data.get() || new_pos > res_.data.get() + res_.size)
111  {
112  return aiReturn_FAILURE;
113  }
114 
115  pos_ = new_pos;
116  return aiReturn_SUCCESS;
117  }
118 
119  size_t Tell() const
120  {
121  return pos_ - res_.data.get();
122  }
123 
124  size_t FileSize() const
125  {
126  return res_.size;
127  }
128 
129  void Flush() {}
130 
131  private:
133  uint8_t* pos_;
134  };
135 
136 
137  class ResourceIOSystem : public Assimp::IOSystem
138  {
139  public:
141  {
142  }
143 
145  {
146  }
147 
148  // Check whether a specific file exists
149  bool Exists(const char* file) const
150  {
151  // Ugly -- two retrievals where there should be one (Exists + Open)
152  // resource_retriever needs a way of checking for existence
153  // TODO: cache this
155  try
156  {
157  res = retriever_.get(file);
158  }
160  {
161  return false;
162  }
163 
164  return true;
165  }
166 
167  // Get the path delimiter character we'd like to see
168  char getOsSeparator() const
169  {
170  return '/';
171  }
172 
173  // ... and finally a method to open a custom stream
174  Assimp::IOStream* Open(const char* file, const char* mode = "rb")
175  {
176  // Ugly -- two retrievals where there should be one (Exists + Open)
177  // resource_retriever needs a way of checking for existence
179  try
180  {
181  res = retriever_.get(file);
182  }
184  {
185  return 0;
186  }
187 
188  return new ResourceIOStream(res);
189  }
190 
191  void Close(Assimp::IOStream* stream);
192 
193  private:
195  };
196 
197  void ResourceIOSystem::Close(Assimp::IOStream* stream)
198  {
199  delete stream;
200  }
201 
202  float getMeshUnitRescale(const std::string& resource_path)
203  {
204  static std::map<std::string, float> rescale_cache;
205 
206  // Try to read unit to meter conversion ratio from mesh. Only valid in Collada XML formats.
207  TiXmlDocument xmlDoc;
208  float unit_scale(1.0);
211  try
212  {
213  res = retriever.get(resource_path);
214  }
216  {
217  ROS_ERROR("%s", e.what());
218  return unit_scale;
219  }
220 
221  if (res.size == 0)
222  {
223  return unit_scale;
224  }
225 
226 
227  // Use the resource retriever to get the data.
228  const char * data = reinterpret_cast<const char * > (res.data.get());
229  xmlDoc.Parse(data);
230 
231  // Find the appropriate element if it exists
232  if(!xmlDoc.Error())
233  {
234  TiXmlElement * colladaXml = xmlDoc.FirstChildElement("COLLADA");
235  if(colladaXml)
236  {
237  TiXmlElement *assetXml = colladaXml->FirstChildElement("asset");
238  if(assetXml)
239  {
240  TiXmlElement *unitXml = assetXml->FirstChildElement("unit");
241  if (unitXml && unitXml->Attribute("meter"))
242  {
243  // Failing to convert leaves unit_scale as the default.
244  if(unitXml->QueryFloatAttribute("meter", &unit_scale) != 0)
245  ROS_WARN_STREAM("getMeshUnitRescale::Failed to convert unit element meter attribute to determine scaling. unit element: "
246  << *unitXml);
247  }
248  }
249  }
250  }
251  return unit_scale;
252  }
253 
254  std::vector<tf::Vector3> getVerticesFromAssimpNode(const aiScene* scene, const aiNode* node, const float scale)
255  {
256  std::vector<tf::Vector3> vertices;
257  if (!node)
258  {
259  return vertices;
260  }
261 
262  aiMatrix4x4 transform = node->mTransformation;
263  aiNode *pnode = node->mParent;
264  while (pnode)
265  {
266  // Don't convert to y-up orientation, which is what the root node in
267  // Assimp does
268  if (pnode->mParent != NULL)
269  transform = pnode->mTransformation * transform;
270  pnode = pnode->mParent;
271  }
272 
273  aiMatrix3x3 rotation(transform);
274  aiMatrix3x3 inverse_transpose_rotation(rotation);
275  inverse_transpose_rotation.Inverse();
276  inverse_transpose_rotation.Transpose();
277 
278  for (uint32_t i = 0; i < node->mNumMeshes; i++)
279  {
280  aiMesh* input_mesh = scene->mMeshes[node->mMeshes[i]];
281  // Add the vertices
282  for (uint32_t j = 0; j < input_mesh->mNumVertices; j++)
283  {
284  aiVector3D p = input_mesh->mVertices[j];
285  p *= transform;
286  p *= scale;
287  tf::Vector3 v(p.x, p.y, p.z);
288  vertices.push_back(v);
289  }
290  }
291 
292  for (uint32_t i=0; i < node->mNumChildren; ++i)
293  {
294  std::vector<tf::Vector3> sub_vertices = getVerticesFromAssimpNode(scene,node->mChildren[i], scale);
295  // Add vertices
296  for (size_t j = 0; j < sub_vertices.size(); j++) {
297  vertices.push_back(sub_vertices[j]);
298  }
299  }
300  return vertices;
301  }
302 
303  shapes::Mesh* meshFromAssimpScene(const std::string& name, const aiScene* scene)
304  {
305  if (!scene->HasMeshes())
306  {
307  ROS_ERROR("No meshes found in file [%s]", name.c_str());
308  return NULL;
309  }
310 
311  float scale = getMeshUnitRescale(name);
312 
313  std::vector<tf::Vector3> vertices = getVerticesFromAssimpNode(scene, scene->mRootNode, scale);
314 
315  return createMeshFromVertices(vertices);
316  }
317 
318  namespace detail
319  {
320  struct myVertex
321  {
323  unsigned int index;
324  };
325 
327  {
328  bool operator()(const myVertex &p1, const myVertex &p2) const
329  {
330  const tf::Vector3 &v1 = p1.point;
331  const tf::Vector3 &v2 = p2.point;
332  if (v1.getX() < v2.getX())
333  return true;
334  if (v1.getX() > v2.getX())
335  return false;
336  if (v1.getY() < v2.getY())
337  return true;
338  if (v1.getY() > v2.getY())
339  return false;
340  if (v1.getZ() < v2.getZ())
341  return true;
342  return false;
343  }
344  };
345 
347  {
348  bool operator()(const myVertex &p1, const myVertex &p2) const
349  {
350  return p1.index < p2.index;
351  }
352  };
353  }
354 
355  shapes::Mesh* createMeshFromVertices(const std::vector<tf::Vector3> &vertices, const std::vector<unsigned int> &triangles)
356  {
357  unsigned int nt = triangles.size() / 3;
358  shapes::Mesh *mesh = new shapes::Mesh(vertices.size(), nt);
359  for (unsigned int i = 0 ; i < vertices.size() ; ++i)
360  {
361  mesh->vertices[3 * i ] = vertices[i].getX();
362  mesh->vertices[3 * i + 1] = vertices[i].getY();
363  mesh->vertices[3 * i + 2] = vertices[i].getZ();
364  }
365 
366  std::copy(triangles.begin(), triangles.end(), mesh->triangles);
367 
368  // compute normals
369  for (unsigned int i = 0 ; i < nt ; ++i)
370  {
371  tf::Vector3 s1 = vertices[triangles[i * 3 ]] - vertices[triangles[i * 3 + 1]];
372  tf::Vector3 s2 = vertices[triangles[i * 3 + 1]] - vertices[triangles[i * 3 + 2]];
373  tf::Vector3 normal = s1.cross(s2);
374  normal.normalize();
375  mesh->normals[3 * i ] = normal.getX();
376  mesh->normals[3 * i + 1] = normal.getY();
377  mesh->normals[3 * i + 2] = normal.getZ();
378  }
379  return mesh;
380  }
381 
382  shapes::Mesh* createMeshFromVertices(const std::vector<tf::Vector3> &source)
383  {
384  if (source.size() < 3)
385  return NULL;
386 
387  std::set<detail::myVertex, detail::ltVertexValue> vertices;
388  std::vector<unsigned int> triangles;
389 
390  for (unsigned int i = 0 ; i < source.size() / 3 ; ++i)
391  {
392  // check if we have new vertices
393  detail::myVertex vt;
394 
395  vt.point = source[3 * i];
396  std::set<detail::myVertex, detail::ltVertexValue>::iterator p1 = vertices.find(vt);
397  if (p1 == vertices.end())
398  {
399  vt.index = vertices.size();
400  vertices.insert(vt);
401  }
402  else
403  vt.index = p1->index;
404  triangles.push_back(vt.index);
405 
406  vt.point = source[3 * i + 1];
407  std::set<detail::myVertex, detail::ltVertexValue>::iterator p2 = vertices.find(vt);
408  if (p2 == vertices.end())
409  {
410  vt.index = vertices.size();
411  vertices.insert(vt);
412  }
413  else
414  vt.index = p2->index;
415  triangles.push_back(vt.index);
416 
417  vt.point = source[3 * i + 2];
418  std::set<detail::myVertex, detail::ltVertexValue>::iterator p3 = vertices.find(vt);
419  if (p3 == vertices.end())
420  {
421  vt.index = vertices.size();
422  vertices.insert(vt);
423  }
424  else
425  vt.index = p3->index;
426 
427  triangles.push_back(vt.index);
428  }
429 
430  // sort our vertices
431  std::vector<detail::myVertex> vt;
432  vt.insert(vt.begin(), vertices.begin(), vertices.end());
433  std::sort(vt.begin(), vt.end(), detail::ltVertexIndex());
434 
435  // copy the data to a mesh structure
436  unsigned int nt = triangles.size() / 3;
437 
438  shapes::Mesh *mesh = new shapes::Mesh(vt.size(), nt);
439  for (unsigned int i = 0 ; i < vt.size() ; ++i)
440  {
441  mesh->vertices[3 * i ] = vt[i].point.getX();
442  mesh->vertices[3 * i + 1] = vt[i].point.getY();
443  mesh->vertices[3 * i + 2] = vt[i].point.getZ();
444  }
445 
446  std::copy(triangles.begin(), triangles.end(), mesh->triangles);
447 
448  // compute normals
449  for (unsigned int i = 0 ; i < nt ; ++i)
450  {
451  tf::Vector3 s1 = vt[triangles[i * 3 ]].point - vt[triangles[i * 3 + 1]].point;
452  tf::Vector3 s2 = vt[triangles[i * 3 + 1]].point - vt[triangles[i * 3 + 2]].point;
453  tf::Vector3 normal = s1.cross(s2);
454  normal.normalize();
455  mesh->normals[3 * i ] = normal.getX();
456  mesh->normals[3 * i + 1] = normal.getY();
457  mesh->normals[3 * i + 2] = normal.getZ();
458  }
459 
460  return mesh;
461  }
462 
463  shapes::Mesh* createMeshFromBinaryStlData(const char *data, unsigned int size)
464  {
465  const char* pos = data;
466  pos += 80; // skip the 80 byte header
467 
468  unsigned int numTriangles = *(unsigned int*)pos;
469  pos += 4;
470 
471  // make sure we have read enough data
472  if ((long)(50 * numTriangles + 84) <= size)
473  {
474  std::vector<tf::Vector3> vertices;
475 
476  for (unsigned int currentTriangle = 0 ; currentTriangle < numTriangles ; ++currentTriangle)
477  {
478  // skip the normal
479  pos += 12;
480 
481  // read vertices
482  tf::Vector3 v1(0,0,0);
483  tf::Vector3 v2(0,0,0);
484  tf::Vector3 v3(0,0,0);
485 
486  v1.setX(*(float*)pos);
487  pos += 4;
488  v1.setY(*(float*)pos);
489  pos += 4;
490  v1.setZ(*(float*)pos);
491  pos += 4;
492 
493  v2.setX(*(float*)pos);
494  pos += 4;
495  v2.setY(*(float*)pos);
496  pos += 4;
497  v2.setZ(*(float*)pos);
498  pos += 4;
499 
500  v3.setX(*(float*)pos);
501  pos += 4;
502  v3.setY(*(float*)pos);
503  pos += 4;
504  v3.setZ(*(float*)pos);
505  pos += 4;
506 
507  // skip attribute
508  pos += 2;
509 
510  vertices.push_back(v1);
511  vertices.push_back(v2);
512  vertices.push_back(v3);
513  }
514 
515  return createMeshFromVertices(vertices);
516  }
517 
518  return NULL;
519  }
520 
521  shapes::Mesh* createMeshFromBinaryDAE(const char* filename)
522  {
523  std::string resource_path(filename);
524  Assimp::Importer importer;
525  importer.SetIOHandler(new ResourceIOSystem());
526  const aiScene* scene = importer.ReadFile(resource_path, aiProcess_SortByPType|aiProcess_GenNormals|aiProcess_Triangulate|aiProcess_GenUVCoords|aiProcess_FlipUVs);
527  if (!scene)
528  {
529  ROS_ERROR("Could not load resource [%s]: %s", resource_path.c_str(), importer.GetErrorString());
530  return NULL;
531  }
532  return meshFromAssimpScene(resource_path, scene);
533  }
534 
535  shapes::Mesh* createMeshFromBinaryStl(const char *filename)
536  {
537  FILE* input = fopen(filename, "r");
538  if (!input)
539  return NULL;
540 
541  fseek(input, 0, SEEK_END);
542  long fileSize = ftell(input);
543  fseek(input, 0, SEEK_SET);
544 
545  char* buffer = new char[fileSize];
546  size_t rd = fread(buffer, fileSize, 1, input);
547 
548  fclose(input);
549 
550  shapes::Mesh *result = NULL;
551 
552  if (rd == 1)
553  result = createMeshFromBinaryStlData(buffer, fileSize);
554 
555  delete[] buffer;
556 
557  return result;
558  }
559 
560 }
561 }
std::vector< tf::Vector3 > getVerticesFromAssimpNode(const aiScene *scene, const aiNode *node, const float scale)
Definition: load_mesh.cpp:254
bool Exists(const char *file) const
Definition: load_mesh.cpp:149
TFSIMD_FORCE_INLINE void setX(tfScalar x)
TFSIMD_FORCE_INLINE void setY(tfScalar y)
float getMeshUnitRescale(const std::string &resource_path)
Definition: load_mesh.cpp:202
double * normals
the normal to each triangle unit vector represented as (x,y,z)
Definition: shapes.h:201
ResourceIOStream(const resource_retriever::MemoryResource &res)
Definition: load_mesh.cpp:68
TFSIMD_FORCE_INLINE void setZ(tfScalar z)
TFSIMD_FORCE_INLINE const tfScalar & getY() const
Definition of a mesh.
Definition: shapes.h:152
TFSIMD_FORCE_INLINE const tfScalar & getZ() const
bool operator()(const myVertex &p1, const myVertex &p2) const
Definition: load_mesh.cpp:328
TFSIMD_FORCE_INLINE Vector3 cross(const Vector3 &v) const
def get(url)
double * vertices
the position for each vertex vertex k has values at index (3k, 3k+1, 3k+2) = (x,y,z)
Definition: shapes.h:190
resource_retriever::Retriever retriever_
Definition: load_mesh.cpp:194
boost::shared_array< uint8_t > data
shapes::Mesh * meshFromAssimpScene(const std::string &name, const aiScene *scene)
Definition: load_mesh.cpp:303
aiReturn Seek(size_t offset, aiOrigin origin)
Definition: load_mesh.cpp:92
size_t Read(void *buffer, size_t size, size_t count)
Definition: load_mesh.cpp:76
Mesh * createMeshFromBinaryStl(const char *filename)
Load a mesh from a binary STL file. Normals are recomputed and repeating vertices are identified...
Definition: load_mesh.cpp:535
resource_retriever::MemoryResource res_
Definition: load_mesh.cpp:132
#define ROS_WARN_STREAM(args)
TFSIMD_FORCE_INLINE Vector3 & normalize()
MemoryResource get(const std::string &url)
bool operator()(const myVertex &p1, const myVertex &p2) const
Definition: load_mesh.cpp:348
Mesh * createMeshFromBinaryDAE(const char *filename)
Load a mesh from a binary DAE file. Normals are recomputed and repeating vertices are identified...
Definition: load_mesh.cpp:521
TFSIMD_FORCE_INLINE const tfScalar & getX() const
Mesh * createMeshFromBinaryStlData(const char *data, unsigned int size)
Load a mesh from a binary STL stream. Normals are recomputed and repeating vertices are identified...
Definition: load_mesh.cpp:463
Assimp::IOStream * Open(const char *file, const char *mode="rb")
Definition: load_mesh.cpp:174
unsigned int * triangles
the vertex indices for each triangle triangle k has vertices at index (3k, 3k+1, 3k+2) = (v1...
Definition: shapes.h:197
Mesh * createMeshFromVertices(const std::vector< tf::Vector3 > &vertices, const std::vector< unsigned int > &triangles)
Load a mesh from a set of vertices. Triangles are constructed using index values from the triangles v...
Definition: load_mesh.cpp:355
#define ROS_BREAK()
#define ROS_ERROR(...)
size_t Write(const void *buffer, size_t size, size_t count)
Definition: load_mesh.cpp:90
void Close(Assimp::IOStream *stream)
Definition: load_mesh.cpp:197


robot_self_filter
Author(s): Eitan Marder-Eppstein
autogenerated on Thu Jun 6 2019 19:59:05