Go to the documentation of this file.00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 #include <rve_mesh_loader/assimp_parser.h>
00031 #include <rve_mesh_loader/exception.h>
00032 
00033 #include <rve_msgs/Mesh.h>
00034 
00035 #include <rve_common/uuid.h>
00036 
00037 #include <resource_retriever/retriever.h>
00038 
00039 #include <boost/filesystem.hpp>
00040 
00041 #include <assimp/assimp.hpp>
00042 #include <assimp/aiScene.h>
00043 #include <assimp/aiPostProcess.h>
00044 #include <assimp/IOStream.h>
00045 #include <assimp/IOSystem.h>
00046 
00047 #include <ros/assert.h>
00048 
00049 namespace fs = boost::filesystem;
00050 
00051 namespace rve_mesh_loader
00052 {
00053 
00054 class ResourceIOStream : public Assimp::IOStream
00055 {
00056 public:
00057   ResourceIOStream(const resource_retriever::MemoryResource& res)
00058   : res_(res)
00059   , pos_(res.data.get())
00060   {}
00061 
00062   ~ResourceIOStream()
00063   {}
00064 
00065   size_t Read(void* buffer, size_t size, size_t count)
00066   {
00067     size_t to_read = size * count;
00068     if (pos_ + to_read > res_.data.get() + res_.size)
00069     {
00070       to_read = res_.size - (pos_ - res_.data.get());
00071     }
00072 
00073     memcpy(buffer, pos_, to_read);
00074     pos_ += to_read;
00075 
00076     return to_read;
00077   }
00078 
00079   size_t Write( const void* buffer, size_t size, size_t count) { ROS_BREAK(); return 0; }
00080 
00081   aiReturn Seek( size_t offset, aiOrigin origin)
00082   {
00083     uint8_t* new_pos = 0;
00084     switch (origin)
00085     {
00086     case aiOrigin_SET:
00087       new_pos = res_.data.get() + offset;
00088       break;
00089     case aiOrigin_CUR:
00090       new_pos = pos_ + offset; 
00091       break;
00092     case aiOrigin_END:
00093       new_pos = res_.data.get() + res_.size - offset; 
00094       break;
00095     default:
00096       ROS_BREAK();
00097     }
00098 
00099     if (new_pos < res_.data.get() || new_pos > res_.data.get() + res_.size)
00100     {
00101       return aiReturn_FAILURE;
00102     }
00103 
00104     pos_ = new_pos;
00105     return aiReturn_SUCCESS;
00106   }
00107 
00108   size_t Tell() const
00109   {
00110     return pos_ - res_.data.get();
00111   }
00112 
00113   size_t FileSize() const
00114   {
00115     return res_.size;
00116   }
00117 
00118   void Flush() {}
00119 
00120 private:
00121   resource_retriever::MemoryResource res_;
00122   uint8_t* pos_;
00123 };
00124 
00125 class ResourceIOSystem : public Assimp::IOSystem
00126 {
00127 public:
00128   ResourceIOSystem()
00129   {
00130   }
00131 
00132   ~ResourceIOSystem()
00133   {
00134   }
00135 
00136   
00137   bool Exists(const char* file) const
00138   {
00139     
00140     
00141     
00142     resource_retriever::MemoryResource res;
00143     try
00144     {
00145       res = retriever_.get(file);
00146     }
00147     catch (resource_retriever::Exception& e)
00148     {
00149       return false;
00150     }
00151 
00152     return true;
00153   }
00154 
00155   
00156   char getOsSeparator() const
00157   {
00158     return '/';
00159   }
00160 
00161   
00162   Assimp::IOStream* Open(const char* file, const char* mode)
00163   {
00164     ROS_ASSERT(mode == std::string("r") || mode == std::string("rb"));
00165 
00166     
00167     
00168     resource_retriever::MemoryResource res;
00169     try
00170     {
00171       res = retriever_.get(file);
00172     }
00173     catch (resource_retriever::Exception& e)
00174     {
00175       return 0;
00176     }
00177 
00178     return new ResourceIOStream(res);
00179   }
00180 
00181   void Close(Assimp::IOStream* stream) { delete stream; }
00182 
00183 private:
00184   mutable resource_retriever::Retriever retriever_;
00185 };
00186 
00187 rve_msgs::Vector3 assimpToMsg(const aiVector3D& vec)
00188 {
00189   rve_msgs::Vector3 out;
00190   out.x = vec.x;
00191   out.y = vec.y;
00192   out.z = vec.z;
00193   return out;
00194 }
00195 
00196 
00197 void buildMesh(const aiScene* scene, const aiNode* node, rve_msgs::Mesh& out_mesh)
00198 {
00199   if (!node)
00200   {
00201     return;
00202   }
00203 
00204   aiMatrix4x4 transform = node->mTransformation;
00205   aiNode *pnode = node->mParent;
00206   while (pnode)
00207   {
00208     
00209     
00210     if (pnode->mParent != NULL)
00211     {
00212       transform = pnode->mTransformation * transform;
00213     }
00214 
00215     pnode = pnode->mParent;
00216   }
00217 
00218   aiMatrix3x3 rotation(transform);
00219   aiMatrix3x3 inverse_transpose_rotation(rotation);
00220   inverse_transpose_rotation.Inverse();
00221   inverse_transpose_rotation.Transpose();
00222 
00223   for (uint32_t i = 0; i < node->mNumMeshes; i++)
00224   {
00225     aiMesh* input_mesh = scene->mMeshes[node->mMeshes[i]];
00226     out_mesh.submeshes.resize(out_mesh.submeshes.size() + 1);
00227     rve_msgs::SubMesh& submesh = out_mesh.submeshes.back();
00228     submesh.tex_coords.resize(input_mesh->GetNumUVChannels());
00229     submesh.colors.resize(input_mesh->GetNumColorChannels());
00230 
00231     
00232     for (uint32_t j = 0; j < input_mesh->mNumVertices; j++)
00233     {
00234       aiVector3D p = input_mesh->mVertices[j];
00235       p *= transform;
00236       rve_msgs::Vector3 pos;
00237       pos.x = p.x;
00238       pos.y = p.y;
00239       pos.z = p.z;
00240       submesh.positions.push_back(pos);
00241 
00242       if (input_mesh->HasNormals())
00243       {
00244         submesh.normals.push_back(assimpToMsg(inverse_transpose_rotation * input_mesh->mNormals[j]));
00245       }
00246 
00247       if (input_mesh->HasTangentsAndBitangents())
00248       {
00249         submesh.tangents.push_back(assimpToMsg(input_mesh->mTangents[j]));
00250         submesh.binormals.push_back(assimpToMsg(input_mesh->mBitangents[j]));
00251       }
00252 
00253       for (uint32_t k = 0; input_mesh->HasTextureCoords(k); ++k)
00254       {
00255         rve_msgs::TexCoordChannel& channel = submesh.tex_coords[k];
00256         rve_msgs::TexCoord coord;
00257         coord.uvw[0] = input_mesh->mTextureCoords[k][j].x;
00258         coord.uvw[1] = input_mesh->mTextureCoords[k][j].y;
00259         channel.dims = 2;
00260         channel.array.push_back(coord);
00261       }
00262 
00263       for (uint32_t k = 0; input_mesh->HasVertexColors(k); ++k)
00264       {
00265         rve_msgs::ColorChannel& channel = submesh.colors[k];
00266         std_msgs::ColorRGBA color;
00267         color.r = input_mesh->mColors[k][j].r;
00268         color.g = input_mesh->mColors[k][j].g;
00269         color.b = input_mesh->mColors[k][j].b;
00270         color.a = input_mesh->mColors[k][j].a;
00271         channel.array.push_back(color);
00272       }
00273     }
00274 
00275     
00276     for (uint32_t j = 0; j < input_mesh->mNumFaces; j++)
00277     {
00278       aiFace& face = input_mesh->mFaces[j];
00279       for (uint32_t k = 0; k < face.mNumIndices; ++k)
00280       {
00281         submesh.indices.push_back(face.mIndices[k]);
00282       }
00283     }
00284   }
00285 
00286   for (uint32_t i=0; i < node->mNumChildren; ++i)
00287   {
00288     buildMesh(scene, node->mChildren[i], out_mesh);
00289   }
00290 }
00291 
00292 void loadMaterialsForMesh(const std::string& resource_path, const aiScene* scene, rve_msgs::Mesh& mesh)
00293 {
00294   for (uint32_t i = 0; i < scene->mNumMaterials; i++)
00295   {
00296     rve_msgs::Material mat;
00297     mat.id = rve_common::UUID::generate();
00298 
00299     aiMaterial *amat = scene->mMaterials[i];
00300 
00301     
00302 
00303     aiString tex_name;
00304     if (amat->GetTexture(aiTextureType_DIFFUSE, 0, &tex_name) == aiReturn_SUCCESS)
00305     {
00306       
00307       std::string texture_path = fs::path(resource_path).parent_path().string() + "/" + tex_name.data;
00308       mat.texture = texture_path;
00309       mat.has_texture = true;
00310     }
00311 
00312     if (amat->GetTexture(aiTextureType_HEIGHT, 0, &tex_name) == aiReturn_SUCCESS)
00313     {
00314       std::string texture_path = fs::path(resource_path).parent_path().string() + "/" + tex_name.data;
00315       mat.normal_map = texture_path;
00316       mat.has_normal_map = true;
00317     }
00318 
00319     float opacity = 1.0;
00320     
00321     
00322     if (amat->Get(AI_MATKEY_OPACITY, opacity) != aiReturn_SUCCESS)
00323     {
00324       opacity = 1.0;
00325     }
00326 
00327     mat.opacity = opacity;
00328 
00329     aiColor3D clr;
00330     if (amat->Get(AI_MATKEY_COLOR_DIFFUSE, clr) == aiReturn_SUCCESS)
00331     {
00332       mat.color.r = clr.r;
00333       mat.color.b = clr.g;
00334       mat.color.g = clr.b;
00335       mat.has_color = true;
00336     }
00337 
00338     mesh.materials.push_back(mat);
00339   }
00340 
00341   for (size_t i = 0; i < mesh.submeshes.size(); ++i)
00342   {
00343     mesh.submeshes[i].material_index = scene->mMeshes[i]->mMaterialIndex;
00344   }
00345 }
00346 
00347 void meshFromAssimpScene(const std::string& filename, const aiScene* scene, rve_msgs::Mesh& out_mesh)
00348 {
00349   if (!scene->HasMeshes())
00350   {
00351     throw ParseException("No meshes found in file");
00352   }
00353 
00354   buildMesh(scene, scene->mRootNode, out_mesh);
00355   loadMaterialsForMesh(filename, scene, out_mesh);
00356 }
00357 
00358 void parseWithAssimp(uint8_t* buffer, size_t buffer_size, const std::string& filename, rve_msgs::Mesh& out_mesh)
00359 {
00360   Assimp::Importer importer;
00361   importer.SetIOHandler(new ResourceIOSystem());
00362   std::string extension = fs::extension(fs::path(filename)).substr(1);
00363   const aiScene* scene = importer.ReadFileFromMemory(buffer, buffer_size, aiProcess_SortByPType|aiProcess_GenNormals|aiProcess_Triangulate|aiProcess_GenUVCoords|aiProcess_FlipUVs|aiProcess_CalcTangentSpace, extension.c_str());
00364   if (!scene)
00365   {
00366     throw ParseException(importer.GetErrorString());
00367   }
00368 
00369   meshFromAssimpScene(filename, scene, out_mesh);
00370 }
00371 
00372 }