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 #ifndef __VCGLIB_EXPORT_OBJ
00027 #define __VCGLIB_EXPORT_OBJ
00028
00029 #include <wrap/callback.h>
00030 #include <vcg/complex/trimesh/allocate.h>
00031 #include <vcg/space/texcoord2.h>
00032 #include <wrap/io_trimesh/io_mask.h>
00033 #include "io_material.h"
00034 #include <iostream>
00035 #include <fstream>
00036 #include <map>
00037
00038 namespace vcg {
00039 namespace tri {
00040 namespace io {
00041
00042 template <class SaveMeshType>
00043 class ExporterOBJ
00044 {
00045 public:
00046 typedef typename SaveMeshType::FaceIterator FaceIterator;
00047 typedef typename SaveMeshType::VertexIterator VertexIterator;
00048 typedef typename SaveMeshType::VertexType VertexType;
00049
00050
00051
00052
00053 enum SaveError
00054 {
00055 E_NOERROR,
00056 E_CANTOPENFILE,
00057 E_CANTCLOSEFILE,
00058 E_UNESPECTEDEOF,
00059 E_ABORTED,
00060 E_NOTDEFINITION,
00061 E_NOTVEXTEXVALID,
00062 E_NOTFACESVALID
00063 };
00064
00065
00066
00067
00068 static const char* ErrorMsg(int error)
00069 {
00070 static const char* obj_error_msg[] =
00071 {
00072 "No errors",
00073 "Can't open file",
00074 "can't close file",
00075 "Premature End of file",
00076 "File saving aborted",
00077 "Function not defined",
00078 "Vertices not valid",
00079 "Faces not valid"
00080 };
00081
00082 if(error>7 || error<0) return "Unknown error";
00083 else return obj_error_msg[error];
00084 };
00085
00086
00087
00088
00089 static int GetExportMaskCapability()
00090 {
00091 int capability = 0;
00092
00093
00094 capability |= vcg::tri::io::Mask::IOM_VERTNORMAL;
00095 capability |= vcg::tri::io::Mask::IOM_VERTTEXCOORD;
00096
00097
00098 capability |= vcg::tri::io::Mask::IOM_FACECOLOR;
00099
00100
00101 capability |= vcg::tri::io::Mask::IOM_WEDGTEXCOORD;
00102 capability |= vcg::tri::io::Mask::IOM_WEDGNORMAL;
00103
00104 return capability;
00105 }
00106
00107
00108
00109
00110 static int Save(SaveMeshType &m, const char * filename, int mask, CallBackPos *cb=0)
00111 {
00112
00113 if (mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD &&
00114 mask & vcg::tri::io::Mask::IOM_VERTTEXCOORD ) {
00115 mask &= ~vcg::tri::io::Mask::IOM_VERTTEXCOORD;
00116 }
00117 if (mask & vcg::tri::io::Mask::IOM_WEDGCOLOR &&
00118 mask & vcg::tri::io::Mask::IOM_VERTCOLOR ) {
00119 mask &= ~vcg::tri::io::Mask::IOM_VERTCOLOR;
00120 }
00121 if(m.vn == 0) return E_NOTVEXTEXVALID;
00122
00123
00124
00125 int current = 0;
00126 int totalPrimitives = m.vn+m.fn;
00127
00128 std::vector<Material> materialVec;
00129
00130 std::string fn(filename);
00131 int LastSlash=fn.size()-1;
00132 while(LastSlash>=0 && fn[LastSlash]!='/')
00133 --LastSlash;
00134
00135 FILE *fp;
00136 fp = fopen(filename,"w");
00137 if(fp == NULL) return E_CANTOPENFILE;
00138
00139 fprintf(fp,"####\n#\n# OBJ File Generated by Meshlab\n#\n####\n");
00140 fprintf(fp,"# Object %s\n#\n# Vertices: %d\n# Faces: %d\n#\n####\n",fn.substr(LastSlash+1).c_str(),m.vn,m.fn);
00141
00142
00143 if( (mask & vcg::tri::io::Mask::IOM_FACECOLOR) || (mask & Mask::IOM_WEDGTEXCOORD) )
00144 fprintf(fp,"mtllib ./%s.mtl\n\n",fn.substr(LastSlash+1).c_str());
00145
00146
00147 VertexIterator vi;
00148 std::map<Point3f,int> NormalVertex;
00149 std::vector<int> VertexId(m.vert.size());
00150 int numvert = 0;
00151 int curNormalIndex = 1;
00152 for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi) if( !(*vi).IsD() )
00153 {
00154 VertexId[vi-m.vert.begin()]=numvert;
00155
00156 if (mask & Mask::IOM_WEDGNORMAL )
00157 {
00158 if(AddNewNormalVertex(NormalVertex,(*vi).N(),curNormalIndex))
00159 {
00160 fprintf(fp,"vn %f %f %f\n",(*vi).N()[0],(*vi).N()[1],(*vi).N()[2]);
00161 curNormalIndex++;
00162 }
00163 }
00164 if (mask & Mask::IOM_VERTNORMAL ) {
00165 fprintf(fp,"vn %f %f %f\n",(*vi).N()[0],(*vi).N()[1],(*vi).N()[2]);
00166 }
00167 if (mask & Mask::IOM_VERTTEXCOORD ) {
00168 fprintf(fp,"vt %f %f\n",(*vi).T().P()[0],(*vi).T().P()[1]);
00169 }
00170
00171
00172
00173
00174
00175 fprintf(fp,"v %f %f %f\n",(*vi).P()[0],(*vi).P()[1],(*vi).P()[2]);
00176
00177 if (cb !=NULL) {
00178 if(!(*cb)((100*++current)/totalPrimitives, "writing vertices "))
00179 { fclose(fp); return E_ABORTED;} }
00180 numvert++;
00181 }
00182 assert(numvert == m.vn);
00183
00184 fprintf(fp,"# %d vertices, %d vertices normals\n\n",m.vn,int(NormalVertex.size()));
00185
00186
00187 FaceIterator fi;
00188 std::map<vcg::TexCoord2<float>,int> CoordIndexTexture;
00189 unsigned int material_num = 0;
00190 int mem_index = 0;
00191 int curTexCoordIndex = 1;
00192 for(fi=m.face.begin(); fi!=m.face.end(); ++fi) if( !(*fi).IsD() )
00193 {
00194 if((mask & Mask::IOM_FACECOLOR) || (mask & Mask::IOM_WEDGTEXCOORD) )
00195 {
00196 int index = Materials<SaveMeshType>::CreateNewMaterial(m,materialVec,material_num,fi);
00197
00198 if(index == (int)materialVec.size())
00199 {
00200 material_num++;
00201 fprintf(fp,"\nusemtl material_%d\n",materialVec[index-1].index);
00202 mem_index = index-1;
00203 }
00204 else
00205 {
00206 if(index != mem_index)
00207 {
00208 fprintf(fp,"\nusemtl material_%d\n",materialVec[index].index);
00209 mem_index=index;
00210 }
00211 }
00212 }
00213
00214
00215 if(HasPerWedgeTexCoord(m) && (mask & Mask::IOM_WEDGTEXCOORD))
00216 for(int k=0;k<(*fi).VN();k++)
00217 {
00218 {
00219 if(AddNewTextureCoord(CoordIndexTexture,(*fi).WT(k),curTexCoordIndex))
00220 {
00221 fprintf(fp,"vt %f %f\n",(*fi).WT(k).u(),(*fi).WT(k).v());
00222 curTexCoordIndex++;
00223 }
00224 }
00225 }
00226
00227
00228 fprintf(fp,"f ");
00229 for(int k=0;k<(*fi).VN();k++)
00230 {
00231 if(k!=0) fprintf(fp," ");
00232 int vInd = -1;
00233
00234 vInd = VertexId[GetIndexVertex(m, (*fi).V(k))] + 1;
00235
00236 int vt = -1;
00237 if(mask & Mask::IOM_WEDGTEXCOORD)
00238 vt = GetIndexVertexTexture(CoordIndexTexture,(*fi).WT(k));
00239 if (mask & Mask::IOM_VERTTEXCOORD)
00240 vt = vInd;
00241
00242 int vn = -1;
00243 if(mask & Mask::IOM_WEDGNORMAL )
00244 vn = GetIndexVertexNormal(m, NormalVertex, (*fi).V(k)->cN());
00245 if (mask & Mask::IOM_VERTNORMAL)
00246 vn = vInd;
00247
00248
00249 WriteFacesElement(fp,vInd,vt,vn);
00250 }
00251 fprintf(fp,"\n");
00252 if (cb !=NULL) {
00253 if(!(*cb)((100*++current)/totalPrimitives, "writing vertices "))
00254 { fclose(fp); return E_ABORTED;}
00255 }
00256
00257 }
00258 fprintf(fp,"# %d faces, %d coords texture\n\n",m.fn,int(CoordIndexTexture.size()));
00259
00260 fprintf(fp,"# End of File");
00261 fclose(fp);
00262
00263 int r = 0;
00264 if((mask & Mask::IOM_WEDGTEXCOORD) || (mask & Mask::IOM_FACECOLOR) )
00265 r = WriteMaterials(materialVec, filename,cb);
00266
00267 if(r!= E_NOERROR)
00268 return r;
00269 return E_NOERROR;
00270 }
00271
00272
00273
00274
00275 inline static int GetIndexVertex(SaveMeshType &m, VertexType *p)
00276 {
00277 return p-&*(m.vert.begin());
00278 }
00279
00280
00281
00282
00283 inline static int GetIndexVertexTexture(std::map<TexCoord2<float>,int> &mapTexToInt, const vcg::TexCoord2<float> &wt)
00284 {
00285 std::map<vcg::TexCoord2<float>,int>::iterator iter= mapTexToInt.find(wt);
00286 if(iter != mapTexToInt.end()) return (*iter).second;
00287 else return -1;
00288
00289
00290
00291 }
00292
00293
00294
00295
00296 inline static int GetIndexVertexNormal(SaveMeshType &, std::map<Point3f,int> &mapNormToInt, const Point3f &norm )
00297 {
00298 std::map<Point3f,int>::iterator iter= mapNormToInt.find(norm);
00299 if(iter != mapNormToInt.end()) return (*iter).second;
00300 else return -1;
00301
00302
00303
00304 }
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315 inline static void WriteFacesElement(FILE *fp,int v,int vt, int vn)
00316 {
00317 fprintf(fp,"%d",v);
00318 if(vt!=-1)
00319 {
00320 fprintf(fp,"/%d",vt);
00321 if(vn!=-1)
00322 fprintf(fp,"/%d",vn);
00323 }
00324 else if(vn!=-1)
00325 fprintf(fp,"//%d",vn);
00326 }
00327
00328
00329
00330
00331
00332 inline static bool AddNewTextureCoord(std::map<vcg::TexCoord2<float>,int> &m, const vcg::TexCoord2<float> &wt,int value)
00333 {
00334 int index = m[wt];
00335 if(index==0){m[wt]=value;return true;}
00336 return false;
00337 }
00338
00339
00340
00341
00342
00343 inline static bool AddNewNormalVertex(std::map<Point3f,int> &m, Point3f &n ,int value)
00344 {
00345 int index = m[n];
00346 if(index==0){m[n]=value;return true;}
00347 return false;
00348 }
00349
00350
00351
00352
00353 inline static int WriteMaterials(std::vector<Material> &materialVec, const char * filename, CallBackPos *cb=0)
00354 {
00355 std::string fileName = std::string(filename);
00356 fileName+=".mtl";
00357
00358 if(materialVec.size() > 0)
00359 {
00360 FILE *fp;
00361 fp = fopen(fileName.c_str(),"w");
00362 if(fp==NULL)return E_ABORTED;
00363
00364 fprintf(fp,"#\n# Wavefront material file\n# Converted by Meshlab Group\n#\n\n");
00365
00366 int current = 0;
00367
00368 for(unsigned int i=0;i<materialVec.size();i++)
00369 {
00370 if (cb !=NULL)
00371 (*cb)((100 * ++current)/materialVec.size(), "saving material file ");
00372 else
00373 { }
00374
00375 fprintf(fp,"newmtl material_%d\n",materialVec[i].index);
00376 fprintf(fp,"Ka %f %f %f\n",materialVec[i].Ka[0],materialVec[i].Ka[1],materialVec[i].Ka[2]);
00377 fprintf(fp,"Kd %f %f %f\n",materialVec[i].Kd[0],materialVec[i].Kd[1],materialVec[i].Kd[2]);
00378 fprintf(fp,"Ks %f %f %f\n",materialVec[i].Ks[0],materialVec[i].Ks[1],materialVec[i].Ks[2]);
00379 fprintf(fp,"Tr %f\n",materialVec[i].Tr);
00380 fprintf(fp,"illum %d\n",materialVec[i].illum);
00381 fprintf(fp,"Ns %f\n",materialVec[i].Ns);
00382
00383 if(materialVec[i].map_Kd.size()>0)
00384 fprintf(fp,"map_Kd %s\n",materialVec[i].map_Kd.c_str());
00385 fprintf(fp,"\n");
00386 }
00387 fclose(fp);
00388 }
00389 return E_NOERROR;
00390 }
00391
00392 };
00393 }
00394 }
00395 }
00396
00397 #endif