Developer Documentation
Loading...
Searching...
No Matches
FileOBJT_impl.hh
1/*===========================================================================*\
2* *
3* OpenFlipper *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openflipper.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenFlipper. *
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#define FILEOBJPLUGIN_C
46
47#include "FileOBJ.hh"
48
49#include <OpenMesh/Core/Utils/color_cast.hh>
50#include <OpenMesh/Core/Geometry/VectorT.hh>
51
52
53//-----------------------------------------------------------------------------------------------------
54
55template< class MeshT >
56bool FileOBJPlugin::writeMaterial(QString _filename, MeshT& _mesh, int _objId )
57{
58 bool optionFaceColors = false;
59 bool optionFaceColorsOverride = false;
60 bool optionColorAlpha = false;
61 bool optionTextures = false;
62 bool optionCopyTextures = false;
63 bool optionCreateTexFolder = false;
64
65 // check options
66 if ( !OpenFlipper::Options::savingSettings() && saveOptions_ != 0) {
67
68 optionColorAlpha = saveAlpha_->isChecked();
69 optionTextures = saveTextures_->isChecked();
70 optionCopyTextures = saveCopyTextures_->isChecked();
71 optionCreateTexFolder = saveCreateTexFolder_->isChecked();
72 }
73 // \TODO Fetch options from ini states if dialog box is not available
74
75 std::fstream matStream( _filename.toStdString().c_str(), std::ios_base::out );
76
77 if ( !matStream ){
78
79 emit log(LOGERR, tr("writeMaterial : cannot not open file %1").arg(_filename) );
80 return false;
81 }
82
83 // \TODO Implement setting of all colors (diffuse, ambient and specular)
84 // There's only diffuse colors so far
86
87 materials_.clear();
88
89 // Prepare materials ( getMaterial handles a list that is set up by this call)
90 for (auto f_it : _mesh.faces()){
91 getMaterial(_mesh, f_it, _objId);
92 }
93
94 //write the materials
95 for(MaterialList::iterator it = materials_.begin(); it != materials_.end(); ++it) {
96 Material& mat = (*it).second;
97 matStream << "newmtl " << mat << '\n';
98 matStream << "Ka 0.5000 0.5000 0.5000" << '\n';
99 ACG::Vec3f c = mat.Kd();
100 matStream << "Kd " << c[0] << " " << c[1] << " " << c[2] << '\n';
101 if(optionColorAlpha) {
102 matStream << "Tr " << mat.Tr() << '\n';
103 }
104 matStream << "illum 1" << '\n';
105
106 // Write out texture info
107 if(optionTextures && mat.has_Texture()) {
108 if(optionCopyTextures) {
109 // Use file path in target folder (relative)
110 QFileInfo file(mat.map_Kd().c_str());
111 if(optionCreateTexFolder) {
112 QFileInfo materialFilename(_filename);
113
114 matStream << "map_Kd " << materialFilename.baseName().toStdString() << "_textures" << QDir::separator().toLatin1()
115 << file.fileName().toStdString() << '\n';
116 } else {
117 matStream << "map_Kd " << file.fileName().toStdString() << '\n';
118 }
119 } else {
120 // Use original file path
121 matStream << "map_Kd " << mat.map_Kd() << '\n';
122 }
123 }
124
125 matStream << '\n';
126 }
127
128 matStream.close();
129
130 return true;
131}
132
133//-----------------------------------------------------------------------------------------------------
134
135template< class MeshT >
136Material& FileOBJPlugin::getMaterial(MeshT& _mesh, const OpenMesh::FaceHandle& _fh, int _objId)
137{
138 bool optionFaceColors = false;
139 bool optionFaceColorsOverride = false;
140 bool optionColorAlpha = false;
141
142 if ( !OpenFlipper::Options::savingSettings() && saveOptions_ != 0) {
143 optionFaceColors = saveFaceColor_->isChecked();
144 optionFaceColorsOverride = saveFaceColorOverride_->isChecked();
145 optionColorAlpha = saveAlpha_->isChecked();
146 }
147 // \TODO Fetch options from ini states if dialog box is not available
148
149 OpenMesh::Vec4f c = _mesh.color( _fh );
150
151 if ( optionFaceColors && !optionFaceColorsOverride ) {
152
153 // If one of the entries is out of range, assume uninitialized color and set to default
154 // Ugly hack to ensure that materials will only be generated if initialized
155 if ( c[0] > 255.0 || c[1] > 255.0 || c[2] > 255.0 || c[0] < 0.0 || c[1] < 0.0 || c[2] < 0.0 || std::isnan(c[0]) || std::isnan(c[1]) || std::isnan(c[2]) ) {
156 c[0] = 155.0;
157 c[1] = 155.0;
158 c[2] = 155.0;
159 c[3] = 1.0;
160
162
163 // If we have too many errors, we always return the first material
164 // Except, if the override option is set.
165 if (materialErrors_ > 10)
166 return (*materials_.begin()).second;
167
168 }
169
170 }
171
172 // First off, try to fetch texture index of current face/object...
173 if(!textureIndexPropFetched_) {
174 emit textureIndexPropertyName(_objId, textureIndexPropertyName_);
175 textureIndexPropFetched_ = true;
176 }
177
178 int texIndex = -1;
179 OpenMesh::FPropHandleT< int > texture_index_property;
180 if ( _mesh.get_property_handle(texture_index_property, textureIndexPropertyName_.toStdString()) ) {
181 texIndex = _mesh.property(texture_index_property, _fh);
182 } else if ( _mesh.get_property_handle(texture_index_property, "f:textureindex") ) {
183 texIndex = _mesh.property(texture_index_property, _fh);
184 } else if(_mesh.has_face_texture_index()) {
185 texIndex = _mesh.texture_index(_fh);
186 } else {
187 QString texName;
188 emit getCurrentTexture(_objId, texName);
189 if(texName != "NONE")
190 emit textureIndex(texName, _objId, texIndex);
191 }
192
193 QString filename;
194 bool hasTexture = false;
195
196 if(texIndex != -1) {
197
198 // Search for texture index in local map
199 std::map<int,QString>::iterator it = texIndexFileMap_.find(texIndex);
200
201 if(it != texIndexFileMap_.end()) {
202 // We already know this file
203 filename = (*it).second;
204 hasTexture = true;
205 } else {
206 // A new texture file has been found
207 QString texName;
208 emit textureName(_objId, texIndex, texName);
209
210 if(texName != "NOT_FOUND") {
211 emit textureFilename( _objId, texName, filename );
212 // => Add to local map
213 texIndexFileMap_.insert(std::pair<int,QString>(texIndex, filename));
214 hasTexture = true;
215 }
216 }
217 }
218
219 for (MaterialList::iterator it = materials_.begin(); it != materials_.end(); ++it) {
220
221 // No texture has been found
222 if(!hasTexture) {
223 // ... just look for diffuse color in materials list
224 if(((*it).second).Kd() == ACG::Vec3f(c[0], c[1], c[2]) &&
225 ((optionColorAlpha && ((*it).second).Tr() == c[3]) || !optionColorAlpha))
226 return (*it).second;
227 } else {
228 // Texture has been found, look for both, matching texture and color
229 QString mKd(((*it).second).map_Kd().c_str());
230 if((((*it).second).Kd() == ACG::Vec3f(c[0], c[1], c[2]) &&
231 ((optionColorAlpha && ((*it).second).Tr() == c[3]) || !optionColorAlpha)) &&
232 (filename == mKd && ((*it).second).map_Kd_index() == texIndex))
233 return (*it).second;
234 }
235 }
236
237 // If not found, add new material(s)
238 Material mat;
239 // Set diffuse color
240 mat.set_Kd(c[0], c[1], c[2]);
241 // Add transparency if available
242 if(optionColorAlpha) mat.set_Tr(c[3]);
243 mat.material_number(materials_.size());
244 // Set texture info
245 if(hasTexture)
246 mat.set_map_Kd(filename.toStdString(), texIndex);
247
248 materials_.insert(std::make_pair(QString("Material%1").arg(mat.material_number()).toStdString(), mat));
249 MaterialList::iterator it = materials_.end();
250 --it;
251 return (*it).second;
252}
253
254
255
256
257//-----------------------------------------------------------------------------------------------------
258
259template< class MeshT >
260bool FileOBJPlugin::writeMesh(std::ostream& _out, QString _filename, MeshT& _mesh, int _objId){
261
262 unsigned int i, nV, idx;
263 ACG::Vec3d v, n;
264 Vec2f t(0.0f,0.0f);
265 typename MeshT::VertexHandle vh;
266 bool useMaterial = false;
268
269 bool optionFaceColors = false;
270 bool optionVertexNormals = false;
271 bool optionVertexTexCoords = true;
272 bool optionTextures = false;
273 bool optionCopyTextures = false;
274 bool optionCreateTexFolder = false;
275
276 QFileInfo fi(_filename);
277
278 // check options
279 if ( !OpenFlipper::Options::savingSettings() && saveOptions_ != 0) {
280 optionFaceColors = saveFaceColor_->isChecked();
281 optionVertexNormals = saveNormals_->isChecked();
282 optionVertexTexCoords = saveTexCoords_->isChecked();
283 optionTextures = saveTextures_->isChecked();
284 optionCopyTextures = saveCopyTextures_->isChecked();
285 optionCreateTexFolder = saveCreateTexFolder_->isChecked();
286 _out.precision(savePrecision_->value());
287 };
288 // \TODO Fetch options from ini states if dialog box is not available
289
290 //create material file if needed
291 if ( optionFaceColors || optionTextures ){
292
293 QString matFile = fi.absolutePath() + QDir::separator() + fi.baseName() + ".mtl";
294
295 useMaterial = writeMaterial(matFile, _mesh, _objId);
296 }
297
298 // Header
299 _out << "# " << _mesh.n_vertices() << " vertices, ";
300 _out << _mesh.n_faces() << " faces" << '\n';
301
302 // Material file
303 if (useMaterial && optionFaceColors )
304 _out << "mtllib " << fi.baseName().toStdString() << ".mtl" << '\n';
305
306 // Store indices of vertices in a map such that
307 // they can easily be referenced for face definitions
308 // later on
309 std::map<typename MeshT::VertexHandle, int> vtMapV;
310
311 int cf = 1;
312 // vertex data (point, normals, texcoords)
313 for (i=0, nV=_mesh.n_vertices(); i<nV; ++i)
314 {
315 vh = typename MeshT::VertexHandle(i);
316 v = _mesh.point(vh);
317 n = _mesh.normal(vh);
318
319 if ( _mesh.has_vertex_texcoords2D() && !_mesh.has_halfedge_texcoords2D() )
320 t = _mesh.texcoord2D(vh);
321
322 // Write out vertex coordinates
323 _out << "v " << v[0] <<" "<< v[1] <<" "<< v[2] << '\n';
324
325 // Write out vertex coordinates
326 if ( optionVertexNormals)
327 _out << "vn " << n[0] <<" "<< n[1] <<" "<< n[2] << '\n';
328
329 // Write out vertex texture coordinates
330 if ( optionVertexTexCoords && _mesh.has_vertex_texcoords2D() && !_mesh.has_halfedge_texcoords2D()) {
331 _out << "vt " << t[0] <<" "<< t[1] << '\n';
332 vtMapV.insert(std::pair<typename MeshT::VertexHandle, int>(vh, cf));
333 cf++;
334 }
335 }
336
337 // Store indices of vertex coordinate (in obj-file)
338 // in map such that the corresponding halfedge
339 // can easily be found later on
340 std::map<typename MeshT::HalfedgeHandle, int> vtMap;
341
342 // If mesh has halfedge tex coords, write them out instead of vertex texcoords
343 if(optionVertexTexCoords && _mesh.has_halfedge_texcoords2D()) {
344 int count = 1;
345 for ( auto f_it : _mesh.faces() ) {
346 for(auto fh_it : f_it.halfedges()) {
347 typename MeshT::TexCoord2D t = _mesh.texcoord2D(fh_it);
348 _out << "vt " << t[0] << " " << t[1] << '\n';
349 vtMap.insert(std::pair<typename MeshT::HalfedgeHandle, int>(fh_it, count));
350 count++;
351 }
352 }
353 }
354
355 Material lastMat;
356
357 // we do not want to write separators if we only write vertex indices
358 bool vertexOnly = !(optionVertexTexCoords && _mesh.has_halfedge_texcoords2D())
359 && !(optionVertexTexCoords && !_mesh.has_halfedge_texcoords2D() && _mesh.has_vertex_texcoords2D())
360 && !(optionVertexNormals);
361
362 for ( auto f_it : _mesh.faces() ){
363
364 if (useMaterial && optionFaceColors) {
365
366 Material& material = getMaterial(_mesh, f_it, _objId);
367
368 // If we are ina a new material block, specify in the file which material to use
369 if(lastMat.material_number() != material.material_number() ) {
370 _out << "usemtl " << material << '\n';
371 lastMat = material;
372 }
373 }
374
375 _out << "f";
376
377 // Write out face information
378 for(auto fh_it : f_it.halfedges()) {
379
380 // Write vertex index
381 idx = fh_it.to().idx() + 1;
382 _out << " " << idx;
383
384 if (!vertexOnly) {
385
386 // Write separator
387 _out << "/" ;
388
389 if ( optionVertexTexCoords ) {
390 // Write vertex texture coordinate index
391 if ( optionVertexTexCoords && _mesh.has_halfedge_texcoords2D()) {
392 // Refer to halfedge texture coordinates
393 typename std::map<typename MeshT::HalfedgeHandle, int>::iterator it = vtMap.find(fh_it);
394 if(it != vtMap.end())
395 _out << (*it).second;
396 } else if (optionVertexTexCoords && !_mesh.has_halfedge_texcoords2D() && _mesh.has_vertex_texcoords2D()) {
397 // Refer to vertex texture coordinates
398 typename std::map<typename MeshT::VertexHandle, int>::iterator it = vtMapV.find(_mesh.to_vertex_handle(fh_it));
399 if(it != vtMapV.end())
400 _out << (*it).second;
401 }
402 }
403
404 // Write vertex normal index
405 if ( optionVertexNormals ) {
406 // Write separator
407 _out << "/" ;
408
409 _out << idx;
410 }
411 }
412 }
413
414 _out << '\n';
415 }
416
417 // Copy texture files (if demanded)
418 if(optionCopyTextures) {
419 // Only test existence of folder once
420 // (for multiple textures)
421 bool testedOnce = false;
422 for(MaterialList::iterator it = materials_.begin(); it != materials_.end(); ++it) {
423 Material& mat = (*it).second;
424
425 if(!mat.has_Texture()) continue;
426
427 QImage img(mat.map_Kd().c_str());
428 QFileInfo img_f(mat.map_Kd().c_str());
429
430 if(img.isNull()) {
431 // Something happened wrong
432 emit log(LOGERR, tr("An error occurred when trying to copy a texture file."));
433 continue;
434 } else {
435 if(optionCreateTexFolder) {
436 // Create folder
437 QDir dir(fi.absolutePath());
438 if(!testedOnce && dir.exists(fi.absolutePath() + QDir::separator() + fi.baseName() + "_textures")) {
439 emit log(LOGERR, tr("The specified target folder already contains a subfolder called textures. Skipping!"));
440 continue;
441 } else {
442 dir.mkdir(fi.baseName() + "_textures");
443 img.save(fi.absolutePath() + QDir::separator() + fi.baseName() + "_textures" + QDir::separator() + img_f.fileName());
444 testedOnce = true;
445 }
446
447 } else {
448 img.save(fi.absolutePath() + QDir::separator() + img_f.fileName());
449 }
450 }
451 }
452 }
453
454 materials_.clear();
455 materialErrors_ = 0;
456 texIndexFileMap_.clear();
457 textureIndexPropFetched_ = false;
458
459 return true;
460}
461
462
463
464
@ LOGERR
unsigned int materialErrors_
number of defect materials encountered during writing
Definition FileOBJ.hh:203
bool writeMaterial(QString _filename, MeshT &_mesh, int _objId)
writer functions
MaterialList materials_
List that contains the material properties.
Definition FileOBJ.hh:200
PointT normal(HalfFaceHandle _hfh) const
size_t n_vertices() const override
Get number of vertices in mesh.
size_t n_faces() const override
Get number of faces in mesh.
VertexHandle to_vertex_handle(HalfEdgeHandle _h) const
Get the vertex the halfedge points to.
VectorT< float, 3 > Vec3f
Definition VectorT.hh:119
Handle for a face entity.
Definition Handles.hh:142