assimp_parser.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
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; // TODO is this right?  can offset really not be negative
00091       break;
00092     case aiOrigin_END:
00093       new_pos = res_.data.get() + res_.size - offset; // TODO is this right?
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   // Check whether a specific file exists
00137   bool Exists(const char* file) const
00138   {
00139     // Ugly -- two retrievals where there should be one (Exists + Open)
00140     // resource_retriever needs a way of checking for existence
00141     // TODO: cache this
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   // Get the path delimiter character we'd like to see
00156   char getOsSeparator() const
00157   {
00158     return '/';
00159   }
00160 
00161   // ... and finally a method to open a custom stream
00162   Assimp::IOStream* Open(const char* file, const char* mode)
00163   {
00164     ROS_ASSERT(mode == std::string("r") || mode == std::string("rb"));
00165 
00166     // Ugly -- two retrievals where there should be one (Exists + Open)
00167     // resource_retriever needs a way of checking for existence
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 // Mostly stolen from gazebo
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     // Don't convert to y-up orientation, which is what the root node in
00209     // Assimp does
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     // Add the vertices
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     // add the indices
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     // Just pull out texture and diffuse color for now
00302 
00303     aiString tex_name;
00304     if (amat->GetTexture(aiTextureType_DIFFUSE, 0, &tex_name) == aiReturn_SUCCESS)
00305     {
00306       // Assume textures are in paths relative to the mesh
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     // From looking at the assimp code I'm not entirely sure if opacity will remain unchanged here
00321     // if the value does not exist
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 } // namespace rve_mesh_loader


rve_mesh_loader
Author(s): Josh Faust
autogenerated on Wed Dec 11 2013 14:31:10