1 /* ========================================================================= *
2  *                                                                           *
3  *                               OpenMesh                                    *
4  *           Copyright (c) 2001-2015, RWTH-Aachen University                 *
5  *           Department of Computer Graphics and Multimedia                  *
6  *                          All rights reserved.                             *
7  *                            www.openmesh.org                               *
8  *                                                                           *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenMesh.                                            *
11  *---------------------------------------------------------------------------*
12  *                                                                           *
13  * Redistribution and use in source and binary forms, with or without        *
14  * modification, are permitted provided that the following conditions        *
15  * are met:                                                                  *
16  *                                                                           *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  *    this list of conditions and the following disclaimer.                  *
19  *                                                                           *
20  * 2. Redistributions in binary form must reproduce the above copyright      *
21  *    notice, this list of conditions and the following disclaimer in the    *
22  *    documentation and/or other materials provided with the distribution.   *
23  *                                                                           *
24  * 3. Neither the name of the copyright holder nor the names of its          *
25  *    contributors may be used to endorse or promote products derived from   *
26  *    this software without specific prior written permission.               *
27  *                                                                           *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A           *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              *
39  *                                                                           *
40  * ========================================================================= */
41 
42 
43 
44 
45 //== INCLUDES =================================================================
46 
47 
48 //STL
49 #include <fstream>
50 #include <limits>
51 
52 // OpenMesh
53 #include <OpenMesh/Core/IO/BinaryHelper.hh>
54 #include <OpenMesh/Core/IO/writer/OBJWriter.hh>
55 #include <OpenMesh/Core/IO/IOManager.hh>
56 #include <OpenMesh/Core/Utils/color_cast.hh>
57 
58 //=== NAMESPACES ==============================================================
59 
60 
61 namespace OpenMesh {
62 namespace IO {
63 
64 
65 //=== INSTANCIATE =============================================================
66 
67 
68 // register the OBJLoader singleton with MeshLoader
69 _OBJWriter_  __OBJWriterinstance;
OBJWriter()70 _OBJWriter_& OBJWriter() { return __OBJWriterinstance; }
71 
72 
73 //=== IMPLEMENTATION ==========================================================
74 
75 
_OBJWriter_()76 _OBJWriter_::_OBJWriter_() { IOManager().register_module(this); }
77 
78 
79 //-----------------------------------------------------------------------------
80 
81 
82 bool
83 _OBJWriter_::
write(const std::string & _filename,BaseExporter & _be,Options _opt,std::streamsize _precision) const84 write(const std::string& _filename, BaseExporter& _be, Options _opt, std::streamsize _precision) const
85 {
86   std::fstream out(_filename.c_str(), std::ios_base::out );
87 
88   if (!out)
89   {
90     omerr() << "[OBJWriter] : cannot open file "
91 	  << _filename << std::endl;
92     return false;
93   }
94 
95   // Set precision on output stream. The default is set via IOManager and passed through to all writers.
96   out.precision(_precision);
97 
98   // Set fixed output to avoid problems with programs not reading scientific notation correctly
99   out << std::fixed;
100 
101   {
102 #if defined(WIN32)
103     std::string::size_type dotposition = _filename.find_last_of("\\/");
104 #else
105     std::string::size_type dotposition = _filename.rfind("/");
106 #endif
107 
108     if (dotposition == std::string::npos){
109       path_ = "./";
110       objName_ = _filename;
111     }else{
112       path_ = _filename.substr(0,dotposition+1);
113       objName_ = _filename.substr(dotposition+1);
114     }
115 
116     //remove the file extension
117     dotposition = objName_.find_last_of(".");
118 
119     if(dotposition != std::string::npos)
120       objName_ = objName_.substr(0,dotposition);
121   }
122 
123   bool result = write(out, _be, _opt, _precision);
124 
125   out.close();
126   return result;
127 }
128 
129 //-----------------------------------------------------------------------------
130 
getMaterial(OpenMesh::Vec3f _color) const131 size_t _OBJWriter_::getMaterial(OpenMesh::Vec3f _color) const
132 {
133   for (size_t i=0; i < material_.size(); i++)
134     if(material_[i] == _color)
135       return i;
136 
137   //not found add new material
138   material_.push_back( _color );
139   return material_.size()-1;
140 }
141 
142 //-----------------------------------------------------------------------------
143 
getMaterial(OpenMesh::Vec4f _color) const144 size_t _OBJWriter_::getMaterial(OpenMesh::Vec4f _color) const
145 {
146   for (size_t i=0; i < materialA_.size(); i++)
147     if(materialA_[i] == _color)
148       return i;
149 
150   //not found add new material
151   materialA_.push_back( _color );
152   return materialA_.size()-1;
153 }
154 
155 //-----------------------------------------------------------------------------
156 
157 bool
158 _OBJWriter_::
writeMaterial(std::ostream & _out,BaseExporter & _be,Options _opt) const159 writeMaterial(std::ostream& _out, BaseExporter& _be, Options _opt) const
160 {
161   OpenMesh::Vec3f c;
162   OpenMesh::Vec4f cA;
163 
164   material_.clear();
165   materialA_.clear();
166 
167   //iterate over faces
168   for (size_t i=0, nF=_be.n_faces(); i<nF; ++i)
169   {
170     //color with alpha
171     if ( _opt.color_has_alpha() ){
172       cA  = color_cast<OpenMesh::Vec4f> (_be.colorA( FaceHandle(int(i)) ));
173       getMaterial(cA);
174     }else{
175     //and without alpha
176       c  = color_cast<OpenMesh::Vec3f> (_be.color( FaceHandle(int(i)) ));
177       getMaterial(c);
178     }
179   }
180 
181   //write the materials
182   if ( _opt.color_has_alpha() )
183     for (size_t i=0; i < materialA_.size(); i++){
184       _out << "newmtl " << "mat" << i << '\n';
185       _out << "Ka 0.5000 0.5000 0.5000" << '\n';
186       _out << "Kd " << materialA_[i][0] << ' ' << materialA_[i][1] << ' ' << materialA_[i][2] << '\n';
187       _out << "Tr " << materialA_[i][3] << '\n';
188       _out << "illum 1" << '\n';
189     }
190   else
191     for (size_t i=0; i < material_.size(); i++){
192       _out << "newmtl " << "mat" << i << '\n';
193       _out << "Ka 0.5000 0.5000 0.5000" << '\n';
194       _out << "Kd " << material_[i][0] << ' ' << material_[i][1] << ' ' << material_[i][2] << '\n';
195       _out << "illum 1" << '\n';
196     }
197 
198   return true;
199 }
200 
201 //-----------------------------------------------------------------------------
202 
203 
204 bool
205 _OBJWriter_::
write(std::ostream & _out,BaseExporter & _be,Options _opt,std::streamsize _precision) const206 write(std::ostream& _out, BaseExporter& _be, Options _opt, std::streamsize _precision) const
207 {
208   unsigned int idx;
209   Vec3f v, n;
210   Vec2f t;
211   VertexHandle vh;
212   std::vector<VertexHandle> vhandles;
213   bool useMatrial = false;
214   OpenMesh::Vec3f c;
215   OpenMesh::Vec4f cA;
216 
217   omlog() << "[OBJWriter] : write file\n";
218 
219   _out.precision(_precision);
220 
221   // check exporter features
222   if (!check( _be, _opt))
223      return false;
224 
225   // No binary mode for OBJ
226   if ( _opt.check(Options::Binary) ) {
227     omout() << "[OBJWriter] : Warning, Binary mode requested for OBJ Writer (No support for Binary mode), falling back to standard." <<  std::endl;
228   }
229 
230   // check for unsupported writer features
231   if (_opt.check(Options::FaceNormal) ) {
232     omerr() << "[OBJWriter] : FaceNormal not supported by OBJ Writer" <<  std::endl;
233     return false;
234   }
235 
236   // check for unsupported writer features
237   if (_opt.check(Options::VertexColor) ) {
238     omerr() << "[OBJWriter] : VertexColor not supported by OBJ Writer" <<  std::endl;
239     return false;
240   }
241 
242   //create material file if needed
243   if ( _opt.check(Options::FaceColor) ){
244 
245     std::string matFile = path_ + objName_ + ".mat";
246 
247     std::fstream matStream(matFile.c_str(), std::ios_base::out );
248 
249     if (!matStream)
250     {
251       omerr() << "[OBJWriter] : cannot write material file " << matFile << std::endl;
252 
253     }else{
254       useMatrial = writeMaterial(matStream, _be, _opt);
255 
256       matStream.close();
257     }
258   }
259 
260   // header
261   _out << "# " << _be.n_vertices() << " vertices, ";
262   _out << _be.n_faces() << " faces" << '\n';
263 
264   // material file
265   if (useMatrial &&  _opt.check(Options::FaceColor) )
266     _out << "mtllib " << objName_ << ".mat" << '\n';
267 
268   std::map<Vec2f,int> texMap;
269   //collect Texturevertices from halfedges
270   if(_opt.check(Options::FaceTexCoord))
271   {
272     std::vector<Vec2f> texCoords;
273     //add all texCoords to map
274     unsigned int num = _be.get_face_texcoords(texCoords);
275     for(size_t i = 0; i < num ; ++i)
276     {
277       texMap[texCoords[i]] = i;
278     }
279   }
280 
281   //collect Texture coordinates from vertices
282   if(_opt.check(Options::VertexTexCoord))
283   {
284     for (size_t i=0, nV=_be.n_vertices(); i<nV; ++i)
285     {
286       vh = VertexHandle(static_cast<int>(i));
287       t  = _be.texcoord(vh);
288       texMap[t] = static_cast<int>(i);
289     }
290   }
291 
292   // assign each texcoord in the map its id
293   // and write the vt entries
294   if(_opt.check(Options::VertexTexCoord) || _opt.check(Options::FaceTexCoord))
295   {
296     int texCount = 0;
297     for(std::map<Vec2f,int>::iterator it = texMap.begin(); it != texMap.end() ; ++it)
298     {
299       _out << "vt " << it->first[0] << " " << it->first[1] << '\n';
300       it->second = ++texCount;
301     }
302   }
303 
304   // vertex data (point, normals, texcoords)
305   for (size_t i=0, nV=_be.n_vertices(); i<nV; ++i)
306   {
307     vh = VertexHandle(int(i));
308     v  = _be.point(vh);
309     n  = _be.normal(vh);
310     t  = _be.texcoord(vh);
311 
312     _out << "v " << v[0] <<" "<< v[1] <<" "<< v[2] << '\n';
313 
314     if (_opt.check(Options::VertexNormal))
315       _out << "vn " << n[0] <<" "<< n[1] <<" "<< n[2] << '\n';
316   }
317 
318   size_t lastMat = std::numeric_limits<std::size_t>::max();
319 
320   // we do not want to write seperators if we only write vertex indices
321   bool onlyVertices =    !_opt.check(Options::VertexTexCoord)
322                       && !_opt.check(Options::VertexNormal)
323                       && !_opt.check(Options::FaceTexCoord);
324 
325   // faces (indices starting at 1 not 0)
326   for (size_t i=0, nF=_be.n_faces(); i<nF; ++i)
327   {
328 
329     if (useMatrial &&  _opt.check(Options::FaceColor) ){
330       size_t material = std::numeric_limits<std::size_t>::max();
331 
332       //color with alpha
333       if ( _opt.color_has_alpha() ){
334         cA  = color_cast<OpenMesh::Vec4f> (_be.colorA( FaceHandle(int(i)) ));
335         material = getMaterial(cA);
336       } else{
337       //and without alpha
338         c  = color_cast<OpenMesh::Vec3f> (_be.color( FaceHandle(int(i)) ));
339         material = getMaterial(c);
340       }
341 
342       // if we are ina a new material block, specify in the file which material to use
343       if(lastMat != material) {
344         _out << "usemtl mat" << material << '\n';
345         lastMat = material;
346       }
347     }
348 
349     _out << "f";
350 
351     _be.get_vhandles(FaceHandle(int(i)), vhandles);
352 
353     for (size_t j=0; j< vhandles.size(); ++j)
354     {
355 
356       // Write vertex index
357       idx = vhandles[j].idx() + 1;
358       _out << " " << idx;
359 
360       if (!onlyVertices) {
361         // write separator
362         _out << "/" ;
363 
364         //write texCoords index from halfedge
365         if(_opt.check(Options::FaceTexCoord))
366         {
367           _out << texMap[_be.texcoord(_be.getHeh(FaceHandle(int(i)),vhandles[j]))];
368         }
369 
370         else
371         {
372           // write vertex texture coordinate index
373           if (_opt.check(Options::VertexTexCoord))
374             _out  << texMap[_be.texcoord(vhandles[j])];
375         }
376 
377         // write vertex normal index
378         if ( _opt.check(Options::VertexNormal) ) {
379           // write separator
380           _out << "/" ;
381           _out << idx;
382         }
383       }
384     }
385 
386     _out << '\n';
387   }
388 
389   material_.clear();
390   materialA_.clear();
391 
392   return true;
393 }
394 
395 
396 //=============================================================================
397 } // namespace IO
398 } // namespace OpenMesh
399 //=============================================================================
400