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
69 _OBJWriter_ __OBJWriterinstance;
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
83 _OBJWriter_::
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 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
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
205 _OBJWriter_::
206 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 //=============================================================================
OpenMesh::FaceHandle
Handle for a face entity.
Definition: Handles.hh:141
OpenMesh::IO::_OBJWriter_::write
bool write(const std::string &, BaseExporter &, Options, std::streamsize _precision=6) const override
Definition: OBJWriter.cc:84
OpenMesh::IO::Options::VertexColor
Has (r) / store (w) vertex colors.
Definition: Options.hh:105
OpenMesh::IO::Options::FaceColor
Has (r) / store (w) face colors.
Definition: Options.hh:109
OpenMesh::IO::__OBJWriterinstance
_OBJWriter_ __OBJWriterinstance
Declare the single entity of the OBJ writer.
Definition: OBJWriter.cc:69
OpenMesh::VectorT
Definition: Vector11T.hh:83
OpenMesh::IO::BaseExporter::getHeh
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 ...
OpenMesh::VertexHandle
Handle for a vertex entity.
Definition: Handles.hh:120
OpenMesh::IO::_OBJWriter_
Definition: OBJWriter.hh:82
OpenMesh::IO::Options::FaceTexCoord
Has (r) / store (w) face texture coordinates.
Definition: Options.hh:110
OpenMesh::IO::_IOManager_::register_module
bool register_module(BaseReader *_bl)
Definition: IOManager.hh:217
OpenMesh
Definition: MeshItems.hh:59
OpenMesh::IO::BaseExporter
Definition: BaseExporter.hh:84
OpenMesh::IO::Options
Set options for reader/writer modules.
Definition: Options.hh:90
OpenMesh::IO::Options::VertexNormal
Has (r) / store (w) vertex normals.
Definition: Options.hh:104
OpenMesh::IO::IOManager
_IOManager_ & IOManager()
Definition: IOManager.cc:72
OpenMesh::IO::Options::FaceNormal
Has (r) / store (w) face normals.
Definition: Options.hh:108
OpenMesh::IO::Options::Binary
Set binary mode for r/w.
Definition: Options.hh:100
OpenMesh::IO::Options::VertexTexCoord
Has (r) / store (w) texture coordinates.
Definition: Options.hh:106