Developer Documentation
OBJWriter.cc
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
70 _OBJWriter_& OBJWriter() { return __OBJWriterinstance; }
71 
72 
73 //=== IMPLEMENTATION ==========================================================
74 
75 
76 _OBJWriter_::_OBJWriter_() { IOManager().register_module(this); }
77 
78 
79 //-----------------------------------------------------------------------------
80 
81 
82 bool
84 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 dot = _filename.find_last_of("\\/");
104 #else
105  std::string::size_type dot = _filename.rfind("/");
106 #endif
107 
108  if (dot == std::string::npos){
109  path_ = "./";
110  objName_ = _filename;
111  }else{
112  path_ = _filename.substr(0,dot+1);
113  objName_ = _filename.substr(dot+1);
114  }
115 
116  //remove the file extension
117  dot = objName_.find_last_of(".");
118 
119  if(dot != std::string::npos)
120  objName_ = objName_.substr(0,dot);
121  }
122 
123  bool result = write(out, _be, _opt, _precision);
124 
125  out.close();
126  return result;
127 }
128 
129 //-----------------------------------------------------------------------------
130 
131 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 
144 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_::
159 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
206 write(std::ostream& _out, BaseExporter& _be, Options _opt, std::streamsize _precision) const
207 {
208  unsigned int idx;
209  size_t i, j,nV, nF;
210  Vec3f v, n;
211  Vec2f t;
212  VertexHandle vh;
213  std::vector<VertexHandle> vhandles;
214  bool useMatrial = false;
215  OpenMesh::Vec3f c;
216  OpenMesh::Vec4f cA;
217 
218  omlog() << "[OBJWriter] : write file\n";
219 
220  _out.precision(_precision);
221 
222  // check exporter features
223  if (!check( _be, _opt))
224  return false;
225 
226  // No binary mode for OBJ
227  if ( _opt.check(Options::Binary) ) {
228  omout() << "[OBJWriter] : Warning, Binary mode requested for OBJ Writer (No support for Binary mode), falling back to standard." << std::endl;
229  }
230 
231  // check for unsupported writer features
232  if (_opt.check(Options::FaceNormal) ) {
233  omerr() << "[OBJWriter] : FaceNormal not supported by OBJ Writer" << std::endl;
234  return false;
235  }
236 
237  // check for unsupported writer features
238  if (_opt.check(Options::VertexColor) ) {
239  omerr() << "[OBJWriter] : VertexColor not supported by OBJ Writer" << std::endl;
240  return false;
241  }
242 
243  //create material file if needed
244  if ( _opt.check(Options::FaceColor) ){
245 
246  std::string matFile = path_ + objName_ + ".mat";
247 
248  std::fstream matStream(matFile.c_str(), std::ios_base::out );
249 
250  if (!matStream)
251  {
252  omerr() << "[OBJWriter] : cannot write material file " << matFile << std::endl;
253 
254  }else{
255  useMatrial = writeMaterial(matStream, _be, _opt);
256 
257  matStream.close();
258  }
259  }
260 
261  // header
262  _out << "# " << _be.n_vertices() << " vertices, ";
263  _out << _be.n_faces() << " faces" << '\n';
264 
265  // material file
266  if (useMatrial && _opt.check(Options::FaceColor) )
267  _out << "mtllib " << objName_ << ".mat" << '\n';
268 
269  std::map<Vec2f,int> texMap;
270  //collect Texturevertices from halfedges
271  if(_opt.check(Options::FaceTexCoord))
272  {
273  std::vector<Vec2f> texCoords;
274  //add all texCoords to map
275  unsigned int num = _be.get_face_texcoords(texCoords);
276  for(unsigned int i = 0; i < num ; ++i)
277  {
278  texMap[texCoords[i]] = i;
279  }
280  }
281 
282  //collect Texture coordinates from vertices
283  if(_opt.check(Options::VertexTexCoord))
284  {
285  for (size_t i=0, nV=_be.n_vertices(); i<nV; ++i)
286  {
287  vh = VertexHandle(static_cast<int>(i));
288  t = _be.texcoord(vh);
289  texMap[t] = static_cast<int>(i);
290  }
291  }
292 
293  // assign each texcoord in the map its id
294  // and write the vt entries
295  if(_opt.check(Options::VertexTexCoord) || _opt.check(Options::FaceTexCoord))
296  {
297  int texCount = 0;
298  for(std::map<Vec2f,int>::iterator it = texMap.begin(); it != texMap.end() ; ++it)
299  {
300  _out << "vt " << it->first[0] << " " << it->first[1] << '\n';
301  it->second = ++texCount;
302  }
303  }
304 
305  // vertex data (point, normals, texcoords)
306  for (i=0, nV=_be.n_vertices(); i<nV; ++i)
307  {
308  vh = VertexHandle(int(i));
309  v = _be.point(vh);
310  n = _be.normal(vh);
311  t = _be.texcoord(vh);
312 
313  _out << "v " << v[0] <<" "<< v[1] <<" "<< v[2] << '\n';
314 
315  if (_opt.check(Options::VertexNormal))
316  _out << "vn " << n[0] <<" "<< n[1] <<" "<< n[2] << '\n';
317  }
318 
319  size_t lastMat = std::numeric_limits<std::size_t>::max();
320 
321  // we do not want to write seperators if we only write vertex indices
322  bool onlyVertices = !_opt.check(Options::VertexTexCoord)
323  && !_opt.check(Options::VertexNormal)
324  && !_opt.check(Options::FaceTexCoord);
325 
326  // faces (indices starting at 1 not 0)
327  for (i=0, nF=_be.n_faces(); i<nF; ++i)
328  {
329 
330  if (useMatrial && _opt.check(Options::FaceColor) ){
331  size_t material = std::numeric_limits<std::size_t>::max();
332 
333  //color with alpha
334  if ( _opt.color_has_alpha() ){
335  cA = color_cast<OpenMesh::Vec4f> (_be.colorA( FaceHandle(int(i)) ));
336  material = getMaterial(cA);
337  } else{
338  //and without alpha
339  c = color_cast<OpenMesh::Vec3f> (_be.color( FaceHandle(int(i)) ));
340  material = getMaterial(c);
341  }
342 
343  // if we are ina a new material block, specify in the file which material to use
344  if(lastMat != material) {
345  _out << "usemtl mat" << material << '\n';
346  lastMat = material;
347  }
348  }
349 
350  _out << "f";
351 
352  _be.get_vhandles(FaceHandle(int(i)), vhandles);
353 
354  for (j=0; j< vhandles.size(); ++j)
355  {
356 
357  // Write vertex index
358  idx = vhandles[j].idx() + 1;
359  _out << " " << idx;
360 
361  if (!onlyVertices) {
362  // write separator
363  _out << "/" ;
364 
365  //write texCoords index from halfedge
366  if(_opt.check(Options::FaceTexCoord))
367  {
368  _out << texMap[_be.texcoord(_be.getHeh(FaceHandle(int(i)),vhandles[j]))];
369  }
370 
371  else
372  {
373  // write vertex texture coordinate index
374  if (_opt.check(Options::VertexTexCoord))
375  _out << texMap[_be.texcoord(vhandles[j])];
376  }
377 
378  // write vertex normal index
379  if ( _opt.check(Options::VertexNormal) ) {
380  // write separator
381  _out << "/" ;
382  _out << idx;
383  }
384  }
385  }
386 
387  _out << '\n';
388  }
389 
390  material_.clear();
391  materialA_.clear();
392 
393  return true;
394 }
395 
396 
397 //=============================================================================
398 } // namespace IO
399 } // namespace OpenMesh
400 //=============================================================================
Has (r) / store (w) vertex colors.
Definition: Options.hh:105
Handle for a vertex entity.
Definition: Handles.hh:120
bool register_module(BaseReader *_bl)
Definition: IOManager.hh:217
Has (r) / store (w) face colors.
Definition: Options.hh:109
Has (r) / store (w) face normals.
Definition: Options.hh:108
_IOManager_ & IOManager()
Definition: IOManager.cc:72
osg::Vec3f::ValueType dot(const osg::Vec3f &_v1, const osg::Vec3f &_v2)
Adapter for osg vector member computing a scalar product.
Has (r) / store (w) texture coordinates.
Definition: Options.hh:106
Has (r) / store (w) face texture coordinates.
Definition: Options.hh:110
Handle for a face entity.
Definition: Handles.hh:141
bool write(const std::string &, BaseExporter &, Options, std::streamsize _precision=6) const
Definition: OBJWriter.cc:84
virtual HalfedgeHandle getHeh(FaceHandle _fh, VertexHandle _vh) const =0
getHeh returns the HalfEdgeHandle that belongs to the face specified by _fh and has a toVertexHandle ...
Set options for reader/writer modules.
Definition: Options.hh:90
Set binary mode for r/w.
Definition: Options.hh:100
Has (r) / store (w) vertex normals.
Definition: Options.hh:104
_OBJWriter_ __OBJWriterinstance
Declare the single entity of the OBJ writer.
Definition: OBJWriter.cc:69