Developer Documentation
FileOBJ.cc
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 * $Revision$ *
45 * $LastChangedBy$ *
46 * $Date$ *
47 * *
48 \*===========================================================================*/
49 #include <ACG/GL/GLState.hh>
50 
53 
54 #include <OpenMesh/Core/IO/IOManager.hh>
55 
56 #include <OpenFlipper/Utils/Memory/RAMInfo.hh>
57 
58 #if QT_VERSION >= 0x050000
59  #include <QtWidgets>
60 #else
61  #include <QtGui>
62 #endif
63 
64 #include "FileOBJ.hh"
65 
66 #include <ACG/Utils/SmartPointer.hh>
68 
69 // Defines for the type handling drop down box
70 #define TYPEAUTODETECT 0
71 #define TYPEASK 1
72 #define TYPEPOLY 2
73 #define TYPETRIANGLE 3
74 
75 using namespace Utils;
76 
77 //-----------------------------------------------------------------------------
78 // help functions
79 
80 void remove_duplicated_vertices(VHandles& _indices)
81 {
82  VHandles::iterator endIter = _indices.end();
83  for (VHandles::iterator iter = _indices.begin(); iter != endIter; ++iter)
84  endIter = std::remove(iter+1, endIter, *(iter));
85 
86  _indices.erase(endIter,_indices.end());
87 }
88 
89 //-----------------------------------------------------------------------------
90 
93 : loadOptions_(0),
94  saveOptions_(0),
95  saveBinary_(0),
96  saveVertexColor_(0),
97  saveFaceColor_(0),
98  saveAlpha_(0),
99  saveNormals_(0),
100  saveTexCoords_(0),
101  saveTextures_(0),
102  saveCopyTextures_(0),
103  saveCreateTexFolder_(0),
104  savePrecisionLabel_(0),
105  savePrecision_(0),
106  saveDefaultButton_(0),
107  triMeshHandling_(0),
108  loadVertexColor_(0),
109  loadFaceColor_(0),
110  loadAlpha_(0),
111  loadNormals_(0),
112  loadTexCoords_(0),
113  loadTextures_(0),
114  loadDefaultButton_(0),
115  forceTriangleMesh_(false),
116  forcePolyMesh_(false),
117  textureIndexPropFetched_(false),
118  trimeshOptions_(OBJImporter::NONE)
119 {
120 }
121 
122 //-----------------------------------------------------------------------------------------------------
123 
125 }
126 
127 //-----------------------------------------------------------------------------------------------------
128 
130  return QString( tr("Alias/Wavefront ( *.obj )") );
131 };
132 
133 //-----------------------------------------------------------------------------------------------------
134 
136  return QString( tr("Alias/Wavefront ( *.obj )") );
137 };
138 
139 //-----------------------------------------------------------------------------------------------------
140 
143 
144  #ifdef ENABLE_BSPLINECURVE_SUPPORT
145  type |= DATA_BSPLINE_CURVE;
146  #endif
147 
148  #ifdef ENABLE_BSPLINESURFACE_SUPPORT
149  type |= DATA_BSPLINE_SURFACE;
150  #endif
151 
152  return type;
153 }
154 
155 //-----------------------------------------------------------------------------
156 
157 bool FileOBJPlugin::readMaterial(QString _filename, OBJImporter& _importer)
158 {
159  static QString line;
160  static QString keyWrd;
161  static QString textureName;
162  line.clear();
163  keyWrd.clear();
164  textureName.clear();
165 
166  static QString matName;
167  matName.clear();
168  static Material mat;
169  mat.cleanup();
170  static float f1,f2,f3;
171  f1 = 0;
172  f2 = 0;
173  f3 = 0;
174  static bool insideDefintion;
175  insideDefintion = false;
176  static int textureId;
177  textureId = 1;
178 
179 
180  //open stream
181  QFile matFile(_filename);
182  if (!matFile.open(QFile::ReadOnly))
183  {
184  emit log(LOGERR, tr("readMaterial : cannot open file %1").arg(_filename));
185  return false;
186  }
187 
188  QTextStream matStream(&matFile);
189  if ( matStream.status()!=QTextStream::Ok ){
190  emit log(LOGERR, tr("readMaterial : cannot open stream %1").arg(_filename) );
191  return false;
192  }
193 
194  //clear temp material
195  mat.cleanup();
196 
197  //parse material file
198  while( matStream.status() == QTextStream::Ok && !matStream.atEnd() )
199  {
200  line = matStream.readLine();
201  if ( matStream.status() != QTextStream::Ok ){
202  emit log(LOGERR, tr("readMaterial : Warning! Could not read file properly!"));
203  return false;
204  }
205 
206  if ( line.isEmpty() )
207  continue;
208 
209  QTextStream stream(&line);
210 
211  stream >> keyWrd;
212 
213  if( ( line[0].isSpace() && line[0] != QLatin1Char('\t') ) || line[0] == QLatin1Char('#') )
214  {
215  if (insideDefintion && !matName.isEmpty() && mat.is_valid())
216  {
217  _importer.materials()[matName.toStdString()] = mat;
218  mat.cleanup();
219  }
220  }
221 
222  else if (keyWrd == QLatin1String("newmtl")) // begin new material definition
223  {
224  stream >> matName;
225  insideDefintion = true;
226  }
227 
228  else if (keyWrd == QLatin1String("Kd")) // diffuse color
229  {
230  f1 = getFloat(stream);
231  f2 = getFloat(stream);
232  f3 = getFloat(stream);
233 
234  if( stream.status()==QTextStream::Ok )
235  mat.set_Kd(f1,f2,f3);
236  }
237 
238  else if (keyWrd == QLatin1String("Ka")) // ambient color
239  {
240  f1 = getFloat(stream);
241  f2 = getFloat(stream);
242  f3 = getFloat(stream);
243 
244  if( stream.status()!=QTextStream::Ok )
245  mat.set_Ka(f1,f2,f3);
246  }
247 
248  else if (keyWrd == QLatin1String("Ks")) // specular color
249  {
250  f1 = getFloat(stream);
251  f2 = getFloat(stream);
252  f3 = getFloat(stream);
253 
254  if( stream.status()!=QTextStream::Ok )
255  mat.set_Ks(f1,f2,f3);
256  }
257 #if 0
258  else if (keyWrd == QLatin1String("illum") // diffuse/specular shading model
259  {
260  ; // just skip this
261  }
262 
263  else if (keyWrd == QLatin1String("Ns") // Shininess [0..200]
264  {
265  ; // just skip this
266  }
267 
268  else if (keyWrd == QLatin1String("map_") // map images
269  {
270  // map_Ks, specular map
271  // map_Ka, ambient map
272  // map_Bump, bump map
273  // map_d, opacity map
274  ; // just skip this
275  }
276 #endif
277  else if (keyWrd == QLatin1String("map_Kd") ) {
278  // Get the rest of the line, removing leading or trailing spaces
279  // This will define the filename of the texture
280  textureName = stream.readLine();
281  textureName = textureName.trimmed();
282  if ( ! textureName.isEmpty() )
283  mat.set_map_Kd( textureName.toStdString(), textureId++ );
284  }
285  else if (keyWrd == QLatin1String("Tr")) // transparency value
286  {
287  f1 = getFloat(stream);
288 
289  if( stream.status() == QTextStream::Ok )
290  mat.set_Tr(f1);
291  }
292  else if (keyWrd == QLatin1String("d")) // transparency value
293  {
294  f1 = getFloat(stream);
295 
296  if( stream.status() == QTextStream::Ok )
297  mat.set_Tr(f1);
298  }
299 
300  if ( matStream.status() == QTextStream::Ok && insideDefintion && mat.is_valid() && !matName.isEmpty())
301  _importer.materials()[matName.toStdString()] = mat;
302  }
303 
304  emit log( tr("%1 materials loaded.").arg( _importer.materials().size() ) );
305 
306  return true;
307 }
308 
309 //-----------------------------------------------------------------------------
310 
311 void FileOBJPlugin::createAllGroupObjects(OBJImporter& _importer) {
312 
313  for(unsigned int i = 0; i < _importer.numGroups(); ++i) {
314 
315  // Get group name
316  QString name = _importer.groupName(i);
317  convertToOBJName(name);
318 
319  if ( _importer.isTriangleMesh( i ) ){
320 
321  // add a triangle mesh
322  int id = -1;
323  emit addEmptyObject(DATA_TRIANGLE_MESH, id);
324 
325  BaseObjectData* object(0);
326 
327  if (PluginFunctions::getObject(id, object)) {
328 
329  _importer.setObject(object, i);
330 
331  object->setPath(_importer.path());
332  object->setName(name);
333  }
334 
335  } else if (_importer.isPolyMesh( i )) {
336 
337  int id = -1;
338  emit addEmptyObject(DATA_POLY_MESH, id);
339 
340  BaseObjectData* object(0);
341 
342  if (PluginFunctions::getObject(id, object)) {
343 
344  _importer.setObject(object, i);
345 
346  object->setPath(_importer.path());
347  object->setName(name);
348  }
349  }
350 
351 #ifdef ENABLE_BSPLINECURVE_SUPPORT
352 
353  else if (_importer.isCurve( i )) {
354 
355  int id = -1;
356  emit addEmptyObject(DATA_BSPLINE_CURVE, id);
357 
358  BaseObjectData* object(0);
359 
360  if (PluginFunctions::getObject(id, object)) {
361 
362  _importer.setObject(object, i);
363 
364  object->setPath(_importer.path());
365  object->setName(name);
366  }
367  }
368 
369 #endif
370 
371 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
372 
373  else if (_importer.isSurface( i )) {
374 
375  int id = -1;
376  emit addEmptyObject(DATA_BSPLINE_SURFACE, id);
377 
378  BaseObjectData* object(0);
379 
380  if (PluginFunctions::getObject(id, object)) {
381 
382  _importer.setObject(object, i);
383 
384  object->setPath(_importer.path());
385  object->setName(name);
386  }
387  }
388 
389 #endif
390 
391  //force gui settings
392  if (OpenFlipper::Options::gui() && loadOptions_ != 0) {
393 
394  if (!loadFaceColor_->isChecked())
395  _importer.objectOptions()[ i ] |= OBJImporter::FORCE_NOCOLOR;
396 
397  if (!loadNormals_->isChecked())
398  _importer.objectOptions()[ i ] |= OBJImporter::FORCE_NONORMALS;
399 
400  if (!loadTexCoords_->isChecked() || !loadTextures_->isChecked())
401  _importer.objectOptions()[ i ] |= OBJImporter::FORCE_NOTEXTURES;
402  }
403  }
404 }
405 
406 void FileOBJPlugin::convertToOBJName(QString& _name) {
407 
408  QFileInfo fi(_name);
409 
410  QString n = fi.baseName();
411 
412  _name = n.trimmed() + ".obj";
413 }
414 
416 template <class MeshT>
418 
419  // Create a backup of the original per Vertex texture Coordinates
420  if (_mesh.has_vertex_texcoords2D()) {
421 
423  if (!_mesh.get_property_handle(oldVertexCoords, "Original Per Vertex Texture Coords"))
424  _mesh.add_property(oldVertexCoords, "Original Per Vertex Texture Coords");
425 
426  for (typename MeshT::VertexIter v_it = _mesh.vertices_begin(); v_it != _mesh.vertices_end(); ++v_it)
427  _mesh.property(oldVertexCoords, *v_it) = _mesh.texcoord2D(*v_it);
428 
429  }
430 
431  // Create a backup of the original per Face texture Coordinates
432  if (_mesh.has_halfedge_texcoords2D()) {
433 
435  if (!_mesh.get_property_handle(oldHalfedgeCoords,"Original Per Face Texture Coords"))
436  _mesh.add_property(oldHalfedgeCoords,"Original Per Face Texture Coords");
437 
438  for (typename MeshT::HalfedgeIter he_it = _mesh.halfedges_begin(); he_it != _mesh.halfedges_end(); ++he_it)
439  _mesh.property(oldHalfedgeCoords, *he_it) = _mesh.texcoord2D(*he_it);
440 
441  }
442 }
443 
444 
445 //add textures to the mesh
446 void FileOBJPlugin::addTextures(OBJImporter& _importer, int _objectID ){
447 
448  // TODO : If only one Texture, use single Texturing mode
449  if ( true ) {
450 
451  BaseObject* object = _importer.object(_objectID);
452 
453  if (!object)
454  return;
455 
456  std::map< int,int > newMapping;
457  // zero ( no texture ) always maps to to zero
458  newMapping[0]=0;
459 
460  const std::vector<std::string> matNames = _importer.usedMaterials( _objectID );
461 
462  for (unsigned int i=0; i < matNames.size(); i++){
463 
464  Material& material = _importer.materials()[ matNames[i] ];
465 
466  int textureId = -1;
467 
468  QString textureBlock = QString( material.map_Kd().c_str());
469 
470 
471  QStringList options = textureBlock.split(" ",QString::SkipEmptyParts);
472 
473  while ( options.size() > 1 ) {
474  if ( options[0] == "-blendu" ) {
475  options.pop_front();
476  options.pop_front();
477  } else if ( options[0] == "-blendv" ) {
478  options.pop_front();
479  options.pop_front();
480  } else if ( options[0] == "-cc" ) {
481  options.pop_front();
482  options.pop_front();
483  } else if ( options[0] == "-clamp" ) {
484  options.pop_front();
485  options.pop_front();
486  } else if ( options[0] == "-mm" ) {
487  options.pop_front();
488  options.pop_front();
489  options.pop_front();
490  } else if ( options[0] == "-o" ) {
491  options.pop_front();
492  options.pop_front();
493  options.pop_front();
494  options.pop_front();
495  } else if ( options[0] == "-s" ) {
496  options.pop_front();
497  options.pop_front();
498  options.pop_front();
499  options.pop_front();
500  } else if ( options[0] == "-t" ) {
501  options.pop_front();
502  options.pop_front();
503  options.pop_front();
504  options.pop_front();
505  } else if ( options[0] == "-texres" ) {
506  options.pop_front();
507  options.pop_front();
508  } else {
509  break;
510  }
511  }
512 
513  QString fullName = _importer.path() + QDir::separator() + options.join(" ");
514 
515  QFileInfo info(fullName);
516  if ( info.exists() )
517  emit addMultiTexture("OBJ Data", info.baseName().trimmed(), fullName, object->id(), textureId );
518  else {
519  emit log(LOGWARN, tr("Unable to load texture image %1").arg( QString(material.map_Kd().c_str()) ) );
520  addMultiTexture("OBJ Data","Unknown Texture image " + QString::number(textureId), "unknown.png", object->id(), textureId );
521  }
522 
523  newMapping[ material.map_Kd_index() ] = textureId;
524  }
525 
526  //now map all texture indices to the real texture indices used in OpenFlipper
527 
528  OpenMesh::FPropHandleT< int > indexProperty;
529 
530  //handle PolyMeshes
531  PolyMeshObject* polyMeshObj = dynamic_cast< PolyMeshObject* > (object);
532 
533  if ( polyMeshObj ){
534 
535  PolyMesh& mesh = *(polyMeshObj->mesh());
536 
537  PolyMesh::FaceIter f_it;
538  PolyMesh::FaceIter f_end = mesh.faces_end();
539 
540  if (! mesh.get_property_handle(indexProperty,TEXTUREINDEX) )
541  return;
542 
543  for (f_it = mesh.faces_begin(); f_it != f_end; ++f_it)
544  mesh.property(indexProperty, *f_it) = newMapping[ mesh.property(indexProperty, *f_it) ];
545 
547 
548  return;
549  }
550 
551  //handle new TriMeshes
552  TriMeshObject* triMeshObj = dynamic_cast< TriMeshObject* > (object);
553 
554  if ( triMeshObj ){
555 
556  TriMesh& mesh = *(triMeshObj->mesh());
557 
558  TriMesh::FaceIter f_it;
559  TriMesh::FaceIter f_end = mesh.faces_end();
560 
561  if (! mesh.get_property_handle(indexProperty,TEXTUREINDEX) )
562  return;
563 
564  for (f_it = mesh.faces_begin(); f_it != f_end; ++f_it)
565  mesh.property(indexProperty, *f_it) = newMapping[ mesh.property(indexProperty, *f_it) ];
566 
568 
569  return;
570  }
571  }
572 }
573 
574 void FileOBJPlugin::readOBJFile(QByteArray& _bufferedFile, QString _filename, OBJImporter& _importer)
575 {
576  QString path = QFileInfo(_filename).absolutePath();
577  ptr::shared_ptr<QTextStream> streamPointer;
578  ptr::shared_ptr<QFile> sourceFile;
579 
581  if (_bufferedFile.isNull())
582  {
583  sourceFile.reset(new QFile(_filename) );
584  if(!sourceFile->open(QFile::ReadOnly))
585  {
586  emit log(LOGERR, tr("readOBJFile : cannot open file %1").arg(_filename) );
587  return;
588  }
589  //use the QTextStream and QString objects, since they seem to be more efficient when parsing strings.
590  //especially regarding copy operations.
591  streamPointer.reset( new QTextStream(sourceFile.get()));
592  }
593  else
594  {
595  streamPointer.reset( new QTextStream(&_bufferedFile));
596  }
597  QTextStream input(streamPointer->device());
598  input.seek(0);
599  QTextStream stream;
600  QTextStream lineData;
601  QTextStream tmp;
602  if ( input.status() != QTextStream::Ok){
603  emit log(LOGERR, tr("readOBJFile : cannot read file %1 is the file corrupt?").arg(_filename) );
604  return;
605  }
606 
607  QString currentFileName = QFileInfo(_filename).fileName() ;
608 
609  ReaderMode mode = NONE;
610 
611  QString line;
612  QString keyWrd;
613  QString nextKeyWrd = QLatin1String("");
614 
615 #ifdef ENABLE_BSPLINECURVE_SUPPORT
616  unsigned int curveCount = 0;
617 #endif
618 
619 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
620  unsigned int surfaceCount = 0;
621 #endif
622 
623  float x, y, z, u, v;
624  int deg;
625 
626  std::vector<VertexHandle> vhandles;
627  std::vector<int> face_texcoords;
628  QString matname;
629 
630 #if defined (ENABLE_BSPLINECURVE_SUPPORT) || defined (ENABLE_BSPLINESURFACE_SUPPORT)
631  std::vector< int > cpIndices;
632  std::vector< double > knotsU,knotsV;
633 #endif
634 
635  int faceCount = 0;
636 
637  // We have to keep track of the already read number of vertices to resolve relative (negative indices)
638  int currentVertexCount = 0;
639 
640  // We have to keep track of the already read number of Texture coordinates to resolve relative (negative indices)
641  int currentTextureCoordCount = 0;
642 
643  // We have to keep track of the already read number of normals to resolve relative (negative indices)
644  int currentNormalCount = 0;
645 
646  // keeps track if faces belong to a group or the default group
647  bool inGroup = false;
648  // keeps track if the first face of a mesh has been read yet or not
649  bool firstFace = true;
650 
651  _importer.setPath( path );
652 
653  // Set filename for default mesh
654  _importer.setGroupName(0, currentFileName);
655 
656  // Now add all meshes for every group (if exists)
657  createAllGroupObjects(_importer);
658 
659  while( !input.atEnd() )
660  {
661  line=input.readLine();
662  if ( input.status() == QTextStream::ReadCorruptData ){
663  emit log(LOGERR, tr("readOBJFile : Warning! Could not read file properly!"));
664  return;
665  }
666 
667  // Trim Both leading and trailing spaces
668  line = line.trimmed();
669 
670  // comment
671  if ( line.isEmpty() || line[0] == QLatin1Char('#') || line[0].isSpace() ) {
672  continue;
673  }
674 
675  stream.setString(&line,QIODevice::ReadOnly);
676 
677  //unless the keyWrd for the new line is not determined by the previous line
678  //read it from stream
679  if (nextKeyWrd == QLatin1String(""))
680  stream >> keyWrd;
681  else {
682  keyWrd = nextKeyWrd;
683  nextKeyWrd = QLatin1String("");
684  }
685 
686  // material file
687  if (mode == NONE && keyWrd == QLatin1String("mtllib"))
688  {
689  QString matString;
690 
691  // This will define the filename of the texture
692  matString = stream.readLine();
693 
694  QString matFile = path + QDir::separator() + matString.trimmed();
695 
696  emit log( tr("Loading material file: %1").arg( matFile ) );
697 
698  readMaterial( matFile, _importer );
699  }
700 
701  // usemtl
702  else if (mode == NONE && keyWrd == QLatin1String("usemtl"))
703  {
704  stream >> matname;
705  if ( _importer.materials().find(matname.toStdString())==_importer.materials().end() )
706  {
707  emit log( LOGERR, tr("Warning! Material '%1' not defined in material file").arg( matname ) );
708  matname=QLatin1String("");
709 
710  }else{
711 
712  Material& mat = _importer.materials()[matname.toStdString()];
713 
714  if ( mat.has_Texture() ){
715  //add object if not already there
716  _importer.useMaterial( matname.toStdString() );
717  }
718  }
719  }
720  else if (mode == NONE && keyWrd == QLatin1String("v"))
721  {
722  if (!firstFace)
723  firstFace = true;
724 
725  currentVertexCount++;
726  }
727  // texture coord
728  else if (mode == NONE && keyWrd == QLatin1String("vt"))
729  {
730  if (!firstFace)
731  firstFace = true;
732 
733  // New texture coordinate read so increase counter
734  currentTextureCoordCount++;
735 
736  u = getFloat(stream);
737  v = getFloat(stream);
738 
739  if ( stream.status() == QTextStream::Ok ){
740 
741  _importer.addTexCoord( OpenMesh::Vec2f(u, v) );
742 
743  }else{
744 
745  emit log( LOGERR, tr("Could not add TexCoord. Possible NaN or Inf?\nOnly single 2D texture coordinate per vertex allowed"));
746  }
747  }
748 
749 
750  // normal
751  else if (mode == NONE && keyWrd == QLatin1String("vn"))
752  {
753  if (!firstFace)
754  firstFace = true;
755 
756  // New normal read so increase counter
757  currentNormalCount++;
758 
759  x = getFloat(stream);
760  y = getFloat(stream);
761  z = getFloat(stream);
762 
763  if ( stream.status() == QTextStream::Ok ){
764  _importer.addNormal( OpenMesh::Vec3f(x,y,z) );
765  }else{
766  emit log( LOGERR, tr("Could not read normal. Possible NaN or Inf?"));
767  }
768  }
769 
770  // degree (for curves)
771  else if (mode == NONE && keyWrd == QLatin1String("deg"))
772  {
773  stream >> deg;
774 
775  if ( stream.status() == QTextStream::Ok )
776  _importer.setDegreeU( deg );
777 
778  stream >> deg;
779 
780  if ( stream.status() == QTextStream::Ok )
781  _importer.setDegreeV( deg );
782  }
783 
784  // group
785  else if (mode == NONE && keyWrd == QLatin1String("g")){
786  if (!firstFace)
787  firstFace = true;
788 
789  QString groupName;
790  groupName = stream.readLine();
791 
792  if(faceCount == 0) {
793  currentFileName = groupName;
794  }
795 
796  int id = _importer.groupId(groupName);
797  if(id == -1) {
798  std::cerr << "Error: Group has not been added before!" << std::endl;
799  return;
800  }
801  _importer.setCurrentGroup(id);
802  inGroup = true;
803 
804  faceCount = 0;
805  }
806 
807  // face
808  else if (mode == NONE && keyWrd == QLatin1String("f"))
809  {
810  if (firstFace) {
811  // store faces in the default Group if we aren't in a group already
812  if (!inGroup)
813  _importer.setCurrentGroup(0);
814 
815  firstFace = false;
816  }
817 
818  int component(0), nV(0);
819  int value;
820 
821  vhandles.clear();
822  face_texcoords.clear();
823 
824  // read full line after detecting a face
825  QString faceLine;
826  faceLine = stream.readLine();
827  lineData.setString(&faceLine);
828 
829  // work on the line until nothing left to read
830  while ( !lineData.atEnd() )
831  {
832  // read one block from the line ( vertex/texCoord/normal )
833  QString vertex;
834  lineData >> vertex;
835 
836  do{
837 
838  //get the component (vertex/texCoord/normal)
839  int found=vertex.indexOf(QLatin1String("/"));
840 
841  // parts are seperated by '/' So if no '/' found its the last component
842  if( found != -1 ){
843 
844  // read the index value
845  QString vertexEntry = vertex.left(found);
846  tmp.setString( &vertexEntry );
847 
848  // If we get an empty string this property is undefined in the file
849  if ( vertexEntry.isEmpty() ) {
850  // Switch to next field
851  vertex = vertex.right(vertex.length()-(found+1));
852 
853  // Now we are at the next component
854  ++component;
855 
856  // Skip further processing of this component
857  continue;
858  }
859 
860  // Read current value
861  tmp >> value;
862 
863  // remove the read part from the string
864  vertex = vertex.right(vertex.length()-(found+1));
865 
866  } else {
867 
868  // last component of the vertex, read it.
869  tmp.setString( &vertex );
870  tmp >> value;
871 
872  // Clear vertex after finished reading the line
873  vertex=QLatin1String("");
874 
875  // Nothing to read here ( garbage at end of line )
876  if ( tmp.status() != QTextStream::Ok ) {
877  continue;
878  }
879  }
880 
881  // store the component ( each component is referenced by the index here! )
882  switch (component)
883  {
884  case 0: // vertex
885  if ( value < 0 ) {
886  // Calculation of index :
887  // -1 is the last vertex in the list
888  // As obj counts from 1 and not zero add +1
889  value = currentVertexCount + value + 1;
890  }
891 
892  // Obj counts from 1 and not zero .. array counts from zero therefore -1
893  vhandles.push_back( value-1 );
894  break;
895 
896  case 1: // texture coord
897  if ( value < 0 ) {
898  // Calculation of index :
899  // -1 is the last vertex in the list
900  // As obj counts from 1 and not zero add +1
901  value = currentTextureCoordCount + value + 1;
902  }
903 
904  if (vhandles.empty())
905  {
906  emit log (LOGWARN, tr("Texture coordinates defined, but no vertex coordinates found!"));
907  break;
908  }
909  if ((unsigned int)(value-1) >= _importer.n_texCoords())
910  {
911  emit log (LOGWARN, tr("Too many texcoords defined, skipping the rest"));
912  break;
913  }
914 
915  if ( _importer.n_texCoords() > 0 ) {
916  // Obj counts from 1 and not zero .. array counts from zero therefore -1
917  _importer.setVertexTexCoord( vhandles.back(), value-1 );
918  face_texcoords.push_back( value-1 );
919  } else {
920  emit log( LOGERR, tr("Error setting Texture coordinates") );
921  }
922 
923  break;
924 
925  case 2: // normal
926  if ( value < 0 ) {
927  // Calculation of index :
928  // -1 is the last vertex in the list
929  // As obj counts from 1 and not zero add +1
930  value = currentNormalCount + value + 1;
931  }
932 
933  if (vhandles.empty())
934  {
935  emit log (LOGWARN, tr("Texture coordinates defined, but no vertex coordinates found!"));
936  break;
937  }
938 
939  if ((unsigned int)(value-1) >= _importer.n_normals())
940  {
941  emit log (LOGWARN, tr("Too many normals defined, skipping the rest"));
942  break;
943  }
944 
945  // Obj counts from 1 and not zero .. array counts from zero therefore -1
946  _importer.setNormal(vhandles.back(), value-1);
947  break;
948  }
949 
950  // Prepare for reading next component
951  ++component;
952 
953  // Read until line does not contain any other info
954  } while ( !vertex.isEmpty() );
955 
956  component = 0;
957  nV++;
958  }
959 
960  // remove vertices which can lead to degenerated faces
961  remove_duplicated_vertices(vhandles);
962 
963  // from spec: A minimum of three vertices are required.
964  if( vhandles.size() > 2 ){
965 
966  if ( !face_texcoords.empty() )
967  //if we have texCoords add face+texCoords
968  _importer.addFace(vhandles, face_texcoords );
969  else
970  //otherwise just add the face
971  _importer.addFace(vhandles);
972 
973  faceCount++;
974  }
975 
976 
977  //add material to the last added face(s)
978  //if polygons get triangulated this can be more than one face
979  _importer.addMaterial( matname.toStdString() );
980  }
981 
982 #ifdef ENABLE_BSPLINECURVE_SUPPORT
983  // param
984  else if ( (mode == CURVE && keyWrd == QLatin1String("parm")) || (mode == CURVE && keyWrd == QLatin1String("parm_add")) ){
985 
986  //get curve knots
987  QString paramLine;
988  QString tmp;
989 
990  paramLine = stream.readLine();
991 
992  // value may contain a / as line separator
993  if ( paramLine.endsWith(QLatin1String("\\"))){
994  paramLine = paramLine.left( paramLine.length()-1);
995  nextKeyWrd = QLatin1String("parm_add");
996  }
997 
998  lineData.setString( &paramLine );
999 
1000  if ( keyWrd != QLatin1String("parm_add"))
1001  lineData >> tmp; //push the first u out
1002 
1003  // work on the line until nothing left to read
1004  while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1005  {
1006 
1007  double knot;
1008 
1009  knot = getDouble(lineData);
1010 
1011  if ( lineData.status() == QTextStream::Ok )
1012  knotsU.push_back( knot );
1013  }
1014  }
1015 
1016  // curve
1017  else if ( (mode == NONE && keyWrd == QLatin1String("curv")) || (mode == CURVE && keyWrd == QLatin1String("curv_add")) ){
1018  if (!firstFace)
1019  firstFace = true;
1020 
1021  inGroup = false;
1022 
1023  mode = CURVE;
1024 
1025  if ( keyWrd == QLatin1String("curv") )
1026  {
1027  int id = _importer.getCurveGroupId(curveCount);
1028  if(id == -1) {
1029  std::cerr << "Error: Group has not been added before!" << std::endl;
1030  return;
1031  }
1032  _importer.setCurrentGroup(id);
1033  curveCount++;
1034  }
1035 
1036  //get curve control points
1037  QString curveLine;
1038 
1039  curveLine = stream.readLine();
1040 
1041  // value may contain a / as line separator
1042  if ( curveLine.endsWith(QLatin1String("\\"))){
1043  curveLine = curveLine.left(curveLine.length()-1);
1044  nextKeyWrd = QLatin1String("curv_add");
1045  }
1046 
1047  lineData.setString( &curveLine );
1048 
1049  // Read knots at the beginning before the indices
1050  if ( keyWrd == QLatin1String("curv") ) {
1051  double trash;
1052  trash = getDouble(lineData);
1053  trash = getDouble(lineData);
1054  }
1055 
1056 
1057 
1058  // work on the line until nothing left to read
1059  while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1060  {
1061  int index = 0;
1062 
1063  lineData >> index;
1064 
1065  if ( index < 0 ) {
1066  // Calculation of index :
1067  // -1 is the last vertex in the list
1068  // As obj counts from 1 and not zero add +1
1069  index = currentVertexCount + index + 1;
1070  }
1071 
1072  if ( lineData.status()==QTextStream::Ok )
1073  cpIndices.push_back( index -1 );
1074  }
1075  }
1076 
1077  // end
1078  else if (mode == CURVE && keyWrd == QLatin1String("end")){
1079 
1080  if ( _importer.isCurve( _importer.currentObject() ) ){
1081  // set up the spline curve
1082  _importer.currentCurve()->set_degree( _importer.degreeU() );
1083  _importer.currentCurve()->autocompute_knotvector(false);
1084 
1085  // add the control points
1086  std::vector< ACG::Vec3d > controlPolygon;
1087 
1088  for (unsigned int i = 0; i < cpIndices.size(); ++i)
1089  controlPolygon.push_back( (ACG::Vec3d) _importer.vertex( cpIndices[i] ) );
1090 
1091  _importer.currentCurve()->set_control_polygon( controlPolygon );
1092 
1093  _importer.currentCurve()->set_knots(knotsU);
1094  }
1095 
1096  cpIndices.clear();
1097  knotsU.clear();
1098 
1099  mode = NONE;
1100  }
1101 #endif
1102 
1103 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
1104  // param
1105  else if ( (mode == SURFACE && keyWrd == QLatin1String("parm")) || (mode == SURFACE && keyWrd == QLatin1String("parm_add")) ){
1106 
1107  //get surface knots
1108  QString paramLine;
1109  QString tmp;
1110 
1111  paramLine = stream.readLine();
1112 
1113  // value may contain a / as line separator
1114  if ( paramLine.endsWith(QLatin1String("\\"))){
1115  paramLine = paramLine.left(paramLine.length()-1);
1116  nextKeyWrd = QLatin1String("parm_add");
1117  }
1118 
1119  lineData.setString( &paramLine );
1120 
1121  if ( keyWrd == QLatin1String("parm_add_u"))
1122  tmp = QLatin1String("u");
1123  else if ( keyWrd == QLatin1String("parm_add_v"))
1124  tmp = QLatin1String("v");
1125  else
1126  lineData >> tmp; //get the direction (u or v)
1127 
1128  std::vector< double >* knots;
1129 
1130  //Decide if these are knots in U or V direction
1131  if (tmp == QLatin1String("u"))
1132  knots = &knotsU;
1133  else
1134  knots = &knotsV;
1135 
1136  if (nextKeyWrd != QLatin1String(""))
1137  nextKeyWrd += QLatin1String("_") + tmp;
1138 
1139  // work on the line until nothing left to read
1140  while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1141  {
1142 
1143  double knot;
1144 
1145  knot = getDouble(lineData);
1146 
1147  if ( lineData.status()==QTextStream::Ok ) {
1148  knots->push_back( knot );
1149  }
1150  }
1151  }
1152 
1153  // surface
1154  else if ( (mode == NONE && keyWrd == QLatin1String("surf")) || (mode == SURFACE && keyWrd == QLatin1String("surf_add")) ){
1155  if (!firstFace)
1156  firstFace = true;
1157 
1158  inGroup = false;
1159 
1160  mode = SURFACE;
1161 
1162  if ( keyWrd == QLatin1String("surf") )
1163  {
1164  int id = _importer.getSurfaceGroupId(surfaceCount);
1165  if(id == -1) {
1166  std::cerr << "Error: Group has not been added before!" << std::endl;
1167  return;
1168  }
1169  _importer.setCurrentGroup(id);
1170  surfaceCount++;
1171  }
1172 
1173  //get surface control points
1174  QString surfLine;
1175 
1176  surfLine = stream.readLine();
1177 
1178  // value may contain a / as line separator
1179  if ( surfLine.endsWith(QLatin1String("\\"))){
1180  surfLine = surfLine.left(surfLine.length()-1);
1181  nextKeyWrd = QLatin1String("surf_add");
1182  }
1183 
1184  lineData.setString( &surfLine );
1185 
1186  // work on the line until nothing left to read
1187  while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1188  {
1189  int index = 0;
1190 
1191  lineData >> index;
1192 
1193  if ( index < 0 ) {
1194  // Calculation of index :
1195  // -1 is the last vertex in the list
1196  // As obj counts from 1 and not zero add +1
1197  index = currentVertexCount + index + 1;
1198  }
1199 
1200  if ( lineData.status()==QTextStream::Ok )
1201  cpIndices.push_back( index -1 );
1202  }
1203  }
1204 
1205  // end
1206  else if (mode == SURFACE && keyWrd == QLatin1String("end")){
1207 
1208  if ( _importer.isSurface( _importer.currentObject() ) ){
1209 
1210  // remove first 4 entries since they are the first and last knot (for both direction)
1211  cpIndices.erase(cpIndices.begin());
1212  cpIndices.erase(cpIndices.begin());
1213  cpIndices.erase(cpIndices.begin());
1214  cpIndices.erase(cpIndices.begin());
1215 
1216  // set up the spline surface
1217  _importer.currentSurface()->set_degree( _importer.degreeU(), _importer.degreeV() );
1218 
1219  // compute number of control points in m and in n direction
1220  int dimU = knotsU.size() - _importer.degreeU() - 1;
1221  int dimV = knotsV.size() - _importer.degreeV() - 1;
1222 
1223  // add the control points
1224  std::vector< ACG::Vec3d > controlPolygon;
1225 
1226  for (int i = 0; i < dimU; ++i)
1227  {
1228  controlPolygon.clear();
1229 
1230  for (int j = 0; j < dimV; ++j){
1231  controlPolygon.push_back( (ACG::Vec3d) _importer.vertex( cpIndices[dimU * j + i] ) );
1232  }
1233 
1234  _importer.currentSurface()->add_vector_m(controlPolygon);
1235  }
1236 
1237  _importer.currentSurface()->set_knots_m(knotsU);
1238  _importer.currentSurface()->set_knots_n(knotsV);
1239 
1240 
1241  }
1242 
1243  cpIndices.clear();
1244  knotsU.clear();
1245  knotsV.clear();
1246 
1247  mode = NONE;
1248  }
1249 #endif
1250 
1251  }
1252 
1253 
1254  //checks, if an object with a specified type was added. if not, point cloud was read
1255  bool isType = faceCount != 0;
1256 #ifdef ENABLE_BSPLINECURVE_SUPPORT
1257  isType = isType || curveCount != 0;
1258 #endif
1259 
1260 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
1261  isType = isType || surfaceCount != 0;
1262 #endif
1263 
1264  // we have only read points so far and no faces or modes
1265  // treat them as a polymesh
1266  if (!isType && currentVertexCount != 0 ) {
1267  _importer.forceMeshType( OBJImporter::POLYMESH ); //actually it is a pointcloud
1268  if (!inGroup)
1269  _importer.setCurrentGroup(0);
1270  }
1271 }
1272 
1274 void FileOBJPlugin::checkTypes(QByteArray& _bufferedFile, QString _filename, OBJImporter& _importer, QStringList& _includes)
1275 {
1276  ptr::shared_ptr<QTextStream> streamPointer;
1277  ptr::shared_ptr<QFile> sourceFile;
1278  //setup filestream if not in memory
1279  if (_bufferedFile.isNull() || _bufferedFile.isEmpty())
1280  {
1281 
1282  sourceFile.reset(new QFile(_filename));
1283  if(!sourceFile->open(QFile::ReadOnly))
1284  {
1285  emit log(LOGERR, tr("readOBJFile : cannot open file %1 while checking Types").arg(_filename) );
1286  return;
1287  }
1288  streamPointer.reset(new QTextStream(sourceFile.get()));
1289  }
1290  else
1291  {
1292  streamPointer.reset(new QTextStream(&_bufferedFile));
1293  }
1294  QTextStream input(streamPointer->device());
1295  QTextStream stream;
1296  QTextStream lineData;
1297  QTextStream tmp;
1298 
1299  if ( input.status()!=QTextStream::Ok ){
1300  emit log(LOGERR, tr("readOBJFile : cannot read file %1 while checking Types (is the file corrupt?)").arg(_filename) );
1301  return;
1302  }
1303 
1304  ReaderMode mode = NONE;
1305 
1306  QString line;
1307  QString keyWrd;
1308  QString nextKeyWrd = QLatin1String("");
1309 
1310 #ifdef ENABLE_BSPLINECURVE_SUPPORT
1311  unsigned int curveCount = 0;
1312 #endif
1313 
1314 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
1315  unsigned int surfaceCount = 0;
1316 #endif
1317 
1318  float x, y, z;
1319  int faceCount = 0;
1320 
1321  int PolyMeshCount = 0;
1322  int TriMeshCount = 0;
1323 
1324  OBJImporter::ObjectOptions options = OBJImporter::NONE;
1325 
1326  // keeps track if faces belong to a group or the default group
1327  bool inGroup = false;
1328  // keeps track if the first face of a mesh has been read yet or not
1329  bool firstFace = true;
1330 
1331 #if defined ENABLE_BSPLINECURVE_SUPPORT || defined ENABLE_BSPLINESURFACE_SUPPORT
1332  QString currentGroupName;
1333  int parentId = -1;
1334 #endif
1335 
1336  while( !input.atEnd())
1337  {
1338  line = input.readLine();
1339  if ( input.status()!=QTextStream::Ok ){
1340  emit log(LOGERR, tr("readOBJFile : Warning! Could not read file properly!"));
1341  return;
1342  }
1343 
1344  // Trim Both leading and trailing spaces
1345  line = line.trimmed();
1346 
1347  // comment
1348  if ( line.isEmpty() || line[0] == QLatin1Char('#') || line[0].isSpace() ) {
1349  continue;
1350  }
1351 
1352  stream.setString(&line);
1353 
1354  //unless the keyWrd for the new line is not determined by the previous line
1355  //read it from stream
1356  if (nextKeyWrd == QLatin1String(""))
1357  stream >> keyWrd;
1358  else {
1359  keyWrd = nextKeyWrd;
1360  nextKeyWrd = QLatin1String("");
1361  }
1362 
1363  //call - included obj files
1364  if (mode == NONE && keyWrd == QLatin1String("call")){
1365  firstFace = true;
1366 
1367  QString include;
1368  include =stream.readLine();
1369 
1370  //replace relative path
1371  QString includeStr = include.trimmed();
1372 
1373  if ( !includeStr.isEmpty() ){
1374 
1375  if (includeStr[0] == QLatin1Char('.')){
1376  includeStr = includeStr.right( includeStr.length()-1 );
1377 
1378  QFileInfo fi(_filename);
1379 
1380  includeStr = fi.path() + QDir::separator() + includeStr;
1381  }
1382 
1383  _includes.append( includeStr );
1384  }
1385 
1386  _importer.setObjectOptions(OBJImporter::NONE);
1387  }
1388 
1389  // vertex
1390  else if (mode == NONE && keyWrd == QLatin1String("v"))
1391  {
1392  if (!firstFace)
1393  firstFace = true;
1394 
1395  x = getFloat(stream);
1396  y = getFloat(stream);
1397  z = getFloat(stream);
1398 
1399  if ( stream.status()==QTextStream::Ok )
1400  _importer.addVertex( OpenMesh::Vec3f(x,y,z) );
1401  else
1402  emit log(LOGERR, tr("Could not add Vertex %1. Possible NaN or Inf?").arg(_importer.n_vertices()));
1403  }
1404 
1405  // group
1406  else if (mode == NONE && keyWrd == QLatin1String("g")){
1407  if (!firstFace)
1408  firstFace = true;
1409 
1410  //give options to importer and reinitialize
1411  //for next object
1412 
1413  QString grpName;
1414  grpName = stream.readLine();
1415 
1416  if ( options & OBJImporter::TRIMESH ) TriMeshCount++;
1417  if ( options & OBJImporter::POLYMESH ) PolyMeshCount++;
1418 
1419  int id = _importer.addGroup(grpName);
1420 #if defined ENABLE_BSPLINECURVE_SUPPORT || defined ENABLE_BSPLINESURFACE_SUPPORT
1421  parentId = id;
1422  currentGroupName = grpName;
1423  currentGroupName.remove(QLatin1String(".obj"));
1424 #endif
1425  _importer.setCurrentGroup(id);
1426 
1427  // all following elements are in this group until
1428  // a new group is created
1429  inGroup = true;
1430 
1431  _importer.setObjectOptions( options );
1432  options = OBJImporter::NONE;
1433  faceCount = 0;
1434  }
1435 
1436  // face
1437  else if (mode == NONE && keyWrd == QLatin1String("f")){
1438 
1439  if (firstFace) {
1440  // store faces in the default group if we aren't in a group already
1441  if (!inGroup)
1442  _importer.setCurrentGroup(0);
1443  firstFace = false;
1444  }
1445 
1446  faceCount++;
1447 
1448  int verticesPerFace = 0;
1449  int value;
1450 
1451  // read full line after detecting a face
1452  QString faceLine;
1453  faceLine = stream.readLine();
1454  lineData.setString( &faceLine );
1455 
1456  // work on the line until nothing left to read
1457  while ( !lineData.atEnd() )
1458  {
1459  // read one block from the line ( vertex/texCoord/normal )
1460  QString vertex;
1461  lineData >> vertex;
1462 
1463  verticesPerFace++;
1464 
1465 
1466  //get the vertex component (vertex/texCoord/normal)
1467  int found=vertex.indexOf(QLatin1String("/"));
1468 
1469  // parts are seperated by '/' So if no '/' found its the last component
1470  if( found != -1 ){
1471 
1472  // read the index value
1473  QString vertexEntry = vertex.left(found);
1474  tmp.setString( &vertexEntry );
1475 
1476  // Read current value
1477  tmp >> value;
1478 
1479  if ( tmp.status()!=QTextStream::Ok )
1480  emit log(LOGERR, tr("readOBJFile : Error reading vertex index!"));
1481 
1482  } else {
1483 
1484  // last component of the vertex, read it.
1485  tmp.setString( &vertex );
1486  tmp >> value;
1487 
1488  if ( tmp.status()!=QTextStream::Ok )
1489  emit log(LOGERR, tr("readOBJFile : Error reading vertex index!"));
1490  }
1491 
1492 
1493  if ( value < 0 ) {
1494  // Calculation of index :
1495  // -1 is the last vertex in the list
1496  // As obj counts from 1 and not zero add +1
1497  value = _importer.n_vertices() + value + 1;
1498  }
1499 
1500  // Obj counts from 1 and not zero .. array counts from zero therefore -1
1501  // the importer has to know which vertices are used by the object for correct vertex order
1502  _importer.useVertex( value -1 );
1503  }
1504 
1505 
1506  if( verticesPerFace > 3 ) {
1507  options = OBJImporter::POLYMESH;
1508  _importer.setObjectOptions(options);
1509  } else if ( verticesPerFace == 3 ) {
1510  options = OBJImporter::TRIMESH;
1511  _importer.setObjectOptions(options);
1512  }
1513  }
1514 
1515 #ifdef ENABLE_BSPLINECURVE_SUPPORT
1516 
1517  // curve
1518  if ( (mode == NONE && keyWrd == QLatin1String("curv")) || (mode == CURVE && keyWrd == QLatin1String("curv_add")) ){
1519  if (!firstFace)
1520  firstFace = true;
1521 
1522  inGroup = false;
1523 
1524  mode = CURVE;
1525 
1526  if ( keyWrd == QLatin1String("curv") ) {
1527 
1528  //give options to importer and reinitialize
1529  //for next object
1530  if ( options & OBJImporter::TRIMESH ) TriMeshCount++;
1531  if ( options & OBJImporter::POLYMESH ) PolyMeshCount++;
1532 
1533  QString name = currentGroupName;
1534  if (name.size() == 0)
1535  name = QLatin1String("DefaultGroup");
1536 
1537  name.append(QString("_curve_%1").arg(curveCount));
1538  int id = _importer.addGroup(name);
1539 
1540  if (_importer.numCurves() == 0) {
1541  if (currentGroupName.size() == 0)
1542  _importer.setGroupName(id, QString("DefaultGroup"));
1543  else
1544  _importer.setGroupName(id, currentGroupName);
1545  } else {
1546  if (curveCount == 1) {
1547  int first = _importer.getCurveGroupId(0);
1548  QString tmp = _importer.groupName(first);
1549  tmp.append(QString("_curve_0"));
1550  _importer.setGroupName(first, tmp);
1551  }
1552  _importer.setGroupName(id, name);
1553  }
1554  _importer.setCurveParentId(id, parentId);
1555  _importer.setCurrentGroup(id);
1556  _importer.setCurveGroupId(curveCount, id);
1557  curveCount++;
1558 
1559  _importer.setObjectOptions( options );
1560 
1561  options = OBJImporter::CURVE;
1562  }
1563 
1564  //get curve control points
1565  QString curveLine;
1566 
1567  curveLine=stream.readLine();
1568 
1569  // value may contain a / as line separator
1570  if ( curveLine.endsWith(QLatin1String("\\"))){
1571  curveLine = curveLine.left( curveLine.length()-1);
1572  nextKeyWrd = QLatin1String("curv_add");
1573  }
1574 
1575  lineData.setString( &curveLine );
1576 
1577  // Read knots at the beginning before the indices
1578  if ( keyWrd == QLatin1String("curv") ) {
1579  double trash;
1580  trash = getDouble(lineData);
1581  trash = getDouble(lineData);
1582  }
1583 
1584  // work on the line until nothing left to read
1585  while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1586  {
1587  int index = 0;
1588 
1589  lineData >> index;
1590 
1591  if ( lineData.status()==QTextStream::Ok ){
1592  // the importer has to know which vertices are used by the object for correct vertex order
1593  _importer.useVertex( index -1 );
1594  }
1595  }
1596 
1597  }
1598 
1599  // end
1600  else if (mode == CURVE && keyWrd == QLatin1String("end")){
1601 
1602  mode = NONE;
1603 
1604  _importer.setObjectOptions( options );
1605  options = OBJImporter::TRIMESH;
1606  faceCount = 0;
1607  }
1608 #endif
1609 
1610 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
1611 
1612  // surface
1613  if ( (mode == NONE && keyWrd == QLatin1String("surf")) || (mode == SURFACE && keyWrd == QLatin1String("surf_add")) ){
1614  if (!firstFace)
1615  firstFace = true;
1616 
1617  inGroup = false;
1618 
1619  mode = SURFACE;
1620 
1621  if ( keyWrd == QLatin1String("surf") ){
1622 
1623  //give options to importer and reinitialize
1624  //for next object
1625  if ( options & OBJImporter::TRIMESH ) TriMeshCount++;
1626  if ( options & OBJImporter::POLYMESH ) PolyMeshCount++;
1627 
1628  QString name = currentGroupName;
1629  if (name.size() == 0)
1630  name = QLatin1String("DefaultGroup");
1631 
1632  name.append(QString("_surface_%1").arg(surfaceCount));
1633  int id = _importer.addGroup(name);
1634 
1635  if (_importer.numSurfaces() == 0) {
1636  if (currentGroupName.size() == 0)
1637  _importer.setGroupName(id, "DefaultGroup");
1638  else
1639  _importer.setGroupName(id, currentGroupName);
1640  } else {
1641  if (surfaceCount == 1) {
1642  int first = _importer.getSurfaceGroupId(0);
1643  QString tmp = _importer.groupName(first);
1644  tmp.append(QString("_surface_0"));
1645  _importer.setGroupName(first, tmp);
1646  }
1647  _importer.setGroupName(id, name);
1648  }
1649  _importer.setSurfaceParentId(id, parentId);
1650  _importer.setCurrentGroup(id);
1651  _importer.setSurfaceGroupId(surfaceCount, id);
1652  surfaceCount++;
1653 
1654  _importer.setObjectOptions( options );
1655 
1656  options = OBJImporter::SURFACE;
1657  }
1658 
1659  //get surface control points
1660  QString surfLine;
1661 
1662  surfLine = stream.readLine();
1663 
1664  // value may contain a / as line separator
1665  if ( surfLine.endsWith(QLatin1String("\\"))){
1666  surfLine = surfLine.left(surfLine.length()-1);
1667  nextKeyWrd = QLatin1String("surf_add");
1668  }
1669 
1670  lineData.setString( &surfLine );
1671 
1672  // work on the line until nothing left to read
1673  while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1674  {
1675  int index = 0;
1676 
1677  lineData >> index;
1678 
1679  if ( lineData.status()==QTextStream::Ok ){
1680  // the importer has to know which vertices are used by the object for correct vertex order
1681  _importer.useVertex( index -1 );
1682  }
1683  }
1684  }
1685 
1686  // end
1687  else if (mode == SURFACE && keyWrd == QLatin1String("end")){
1688 
1689  mode = NONE;
1690 
1691  _importer.setObjectOptions( options );
1692  options = OBJImporter::TRIMESH;
1693  faceCount = 0;
1694  }
1695 #endif
1696 
1697  }
1698 
1699  if(faceCount > 0) {
1700  if ( options & OBJImporter::TRIMESH ) TriMeshCount++;
1701  if ( options & OBJImporter::POLYMESH ) PolyMeshCount++;
1702  _importer.setObjectOptions( options );
1703  } else {
1704  // Mesh does not contain any faces
1705  PolyMeshCount++;
1706  if (keyWrd != QLatin1String("call")) {
1707  // we only have vertices and no faces
1708  if (keyWrd == QLatin1String("v") && !inGroup) {
1709  _importer.setCurrentGroup(0);
1710  // treat the file as a polymesh
1711  forceTriangleMesh_ = false;
1712  forcePolyMesh_ = true;
1713  _importer.setObjectOptions(OBJImporter::POLYMESH);
1714  _importer.forceMeshType( OBJImporter::POLYMESH );
1715  for (unsigned int i = 0; i < _importer.n_vertices(); ++i)
1716  _importer.useVertex(i);
1717  } else {
1718  unsigned int currentOptions = _importer.objectOptions()[_importer.currentObject()];
1719  // this is only a triangle mesh if the object is not a curve and not a surface
1720  // also ignore if it is set to NONE
1721  if (!(currentOptions & OBJImporter::CURVE) &&
1722  !(currentOptions & OBJImporter::SURFACE) &&
1723  (currentOptions != OBJImporter::NONE))
1724  _importer.setObjectOptions(OBJImporter::TRIMESH);
1725  }
1726  }
1727  }
1728 
1729  if (TriMeshCount == 0 && PolyMeshCount == 0)
1730  {
1731  return;
1732  }
1733 
1734  if (forceTriangleMesh_){
1735  _importer.forceMeshType( OBJImporter::TRIMESH );
1736  return;
1737  }
1738 
1739  if (forcePolyMesh_){
1740  _importer.forceMeshType( OBJImporter::POLYMESH );
1741  return;
1742  }
1743 
1744  // If we do not have a gui, we will always use the last default
1745  // If we need a gui and the triMeshHandling box is not generated (==0) we also use the last default
1746  if ( OpenFlipper::Options::gui() && triMeshHandling_ != 0 ){
1747 
1748  switch( triMeshHandling_->currentIndex() ){
1749  case TYPEAUTODETECT : //Detect
1750  break;
1751 
1752  case TYPEASK: //ask
1753  QMetaObject::invokeMethod(this,"handleTrimeshDialog",Qt::BlockingQueuedConnection);
1754  if (trimeshOptions_ == OBJImporter::TRIMESH )
1755  _importer.forceMeshType( OBJImporter::TRIMESH );
1756  else if (trimeshOptions_ == OBJImporter::POLYMESH)
1757  _importer.forceMeshType( OBJImporter::POLYMESH );
1758 
1759  break;
1760 
1761  case TYPEPOLY : //polyMesh
1762  _importer.forceMeshType( OBJImporter::POLYMESH ); break;
1763 
1764  case TYPETRIANGLE : //trimesh
1765  _importer.forceMeshType( OBJImporter::TRIMESH ); break;
1766 
1767  default: break;
1768 
1769  }
1770 
1771  }
1772 
1773 }
1774 
1775 void FileOBJPlugin::handleTrimeshDialog()
1776 {
1777  QMessageBox msgBox;
1778  QPushButton *detectButton = msgBox.addButton(tr("Auto-Detect"), QMessageBox::ActionRole);
1779  QPushButton *triButton = msgBox.addButton(tr("Open as triangle mesh"), QMessageBox::ActionRole);
1780  QPushButton *polyButton = msgBox.addButton(tr("Open as poly mesh"), QMessageBox::ActionRole);
1781  msgBox.setWindowTitle( tr("Mesh types in file") );
1782  msgBox.setText( tr("You are about to open a file containing one or more mesh types. \n\n Which mesh type should be used?") );
1783  msgBox.setDefaultButton( detectButton );
1784  msgBox.exec();
1785 
1786 
1787  if (msgBox.clickedButton() == triButton)
1788  trimeshOptions_ = OBJImporter::TRIMESH ;
1789  else if (msgBox.clickedButton() == polyButton)
1790  trimeshOptions_ = OBJImporter::POLYMESH ;
1791 }
1792 
1793 //-----------------------------------------------------------------------------------------------------
1794 
1795 int FileOBJPlugin::loadObject(QString _filename) {
1796 
1797  OBJImporter importer;
1798 
1799  //included filenames
1800  QStringList includes;
1801 
1802  QFile sourceFile(_filename);
1803  if (!sourceFile.open(QFile::ReadOnly))
1804  {
1805  emit log(LOGERR, tr("readOBJFile : cannot open file %1 while checking Types").arg(_filename));
1806  return -1;
1807  }
1808  QByteArray bufferedFile = QByteArray();
1809  //load the entire file to ram if we have at least double the size as free memory.
1810  //otherwise the bytearray stays null and the file is read when it is processed.
1811  unsigned long freeMem = Utils::Memory::queryFreeRAM();
1812  unsigned long fs = sourceFile.size() / 1024 / 1024;
1813  if (freeMem >= 2*fs)
1814  {
1815  bufferedFile = sourceFile.readAll();
1816  }
1817 
1818  //preprocess file and store types in ObjectOptions
1819  checkTypes( bufferedFile, _filename, importer, includes );
1820 
1821  IdList objIDs;
1822 
1823  //load included obj files
1824  for (int i=0; i < includes.size(); i++){
1825 
1826  //int id = loadObject( includes[i], importer );
1827  int id = loadObject( includes[i] );
1828 
1829  if (id != -1)
1830  objIDs.push_back( id );
1831  }
1832 
1833  //add a group if we have includes
1834  if ( ! includes.empty() )
1835  importer.addGroup( QFileInfo(_filename).fileName() );
1836 
1837  //check if something was found
1838  if ( importer.objectOptions().empty() && objIDs.empty() ){
1839 
1840  forceTriangleMesh_ = false;
1841  forcePolyMesh_ = false;
1842 
1843  return -1;
1844  }
1845 
1846  //then parse the obj
1847  readOBJFile( bufferedFile, _filename, importer );
1848 
1849  // finish up
1850  importer.finish();
1851 
1852  int returnID = -1;
1853 
1854  //perhaps add group
1855  if ( importer.numGroups() > 1){
1856 
1857  bool dataControlExists = false;
1858  pluginExists( "datacontrol", dataControlExists );
1859 
1860  if ( dataControlExists ){
1861 
1862  std::vector<OBJImporter::ObjectOptions> options = importer.objectOptions();
1863 #if defined ENABLE_BSPLINECURVE_SUPPORT || defined ENABLE_BSPLINESURFACE_SUPPORT
1864  std::map<int, QString> groupNames;
1865 #endif
1866 
1867 #ifdef ENABLE_BSPLINECURVE_SUPPORT
1868  std::vector< std::vector<int> > curveGroups;
1869  std::vector<int> curveIds;
1870  int lastCurveParent = -2;
1871 #endif
1872 
1873 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
1874  std::vector< std::vector<int> > surfaceGroups;
1875  std::vector<int> surfaceIds;
1876  int lastSurfaceParent = -2;
1877 #endif
1878  for(unsigned int i = 0; i < importer.objectCount(); i++) {
1879  // skip the object if it has no option
1880  // this can happen if the object only included other objects
1881  if (options[i] != NONE) {
1882  BaseObject* obj = importer.object(i);
1883  if(obj) {
1884 #ifdef ENABLE_BSPLINECURVE_SUPPORT
1885  if (options[i] & OBJImporter::CURVE) {
1886  // store the parent group name for later grouping
1887  groupNames[obj->id()] = importer.groupName(importer.getCurveParentId(i));
1888 
1889  // first curve group
1890  if (lastCurveParent == -2) {
1891  lastCurveParent = importer.getCurveParentId(i);
1892  curveIds.push_back(obj->id());
1893  BaseObject* parent = importer.object(lastCurveParent);
1894  if (parent) {
1895  curveIds.push_back(parent->id());
1896  // don't group the parent in the objIDs group
1897  std::vector<int>::iterator pos = std::find(objIDs.begin(), objIDs.end(), parent->id());
1898  if (pos != objIDs.end())
1899  objIDs.erase(pos);
1900  }
1901  // new curve group
1902  } else if (lastCurveParent != importer.getCurveParentId(i)) {
1903  lastCurveParent = importer.getCurveParentId(i);
1904  curveGroups.push_back(curveIds);
1905  curveIds.clear();
1906  curveIds.push_back(obj->id());
1907  BaseObject* parent = importer.object(lastCurveParent);
1908  if (parent) {
1909  curveIds.push_back(parent->id());
1910  // don't group the parent in the objIDs group
1911  std::vector<int>::iterator pos = std::find(objIDs.begin(), objIDs.end(), parent->id());
1912  if (pos != objIDs.end())
1913  objIDs.erase(pos);
1914  }
1915  // add curves to group
1916  } else
1917  curveIds.push_back(obj->id());
1918  }
1919 
1920 #endif
1921 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
1922  if (options[i] & OBJImporter::SURFACE) {
1923  // store the parent group name for later grouping
1924  groupNames[obj->id()] = importer.groupName(importer.getSurfaceParentId(i));
1925 
1926  // first surface group
1927  if (lastSurfaceParent == -2) {
1928  lastSurfaceParent = importer.getSurfaceParentId(i);
1929  surfaceIds.push_back(obj->id());
1930  BaseObject* parent = importer.object(lastSurfaceParent);
1931  if (parent) {
1932  surfaceIds.push_back(parent->id());
1933  std::vector<int>::iterator pos = std::find(objIDs.begin(), objIDs.end(), parent->id());
1934  if (pos != objIDs.end())
1935  objIDs.erase(pos);
1936  }
1937  // new surface group
1938  } else if (lastSurfaceParent != importer.getSurfaceParentId(i)) {
1939  lastSurfaceParent = importer.getSurfaceParentId(i);
1940  surfaceGroups.push_back(surfaceIds);
1941  surfaceIds.clear();
1942  surfaceIds.push_back(obj->id());
1943  BaseObject* parent = importer.object(lastSurfaceParent);
1944  if (parent) {
1945  surfaceIds.push_back(parent->id());
1946  std::vector<int>::iterator pos = std::find(objIDs.begin(), objIDs.end(), parent->id());
1947  if (pos != objIDs.end())
1948  objIDs.erase(pos);
1949  }
1950  // add surfaces to group
1951  } else
1952  surfaceIds.push_back(obj->id());
1953 
1954  }
1955 #endif
1956  if ( (options[i] & OBJImporter::TRIMESH) || (options[i] & OBJImporter::POLYMESH) )
1957  objIDs.push_back( obj->id() );
1958  } else {
1959  std::cerr << "Object is NULL!" << std::endl;
1960  }
1961  }
1962  }
1963 
1964 #ifdef ENABLE_BSPLINECURVE_SUPPORT
1965  // add last group
1966  curveGroups.push_back(curveIds);
1967  std::vector< std::vector<int> >::iterator it = curveGroups.begin();
1968  for (; it != curveGroups.end(); ++it) {
1969  // only group if we have more than one curve
1970  if (it->size() > 2) {
1971  if (groupNames[it->back()].size() == 0)
1972  RPC::callFunctionValue<int>("datacontrol","groupObjects", *it, QFileInfo(_filename).fileName());
1973  else
1974  RPC::callFunctionValue<int>("datacontrol","groupObjects", *it, groupNames[it->back()]);
1975  }
1976  }
1977 #endif
1978 
1979 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
1980  // add last group
1981  surfaceGroups.push_back(surfaceIds);
1982  std::vector< std::vector<int> >::iterator it2 = surfaceGroups.begin();
1983  for (; it2 != surfaceGroups.end(); ++it2) {
1984  // only group if we have more than one surface
1985  if (it2->size() > 2) {
1986  if (groupNames[it2->back()].size() == 0)
1987  RPC::callFunctionValue<int>("datacontrol","groupObjects", *it2, QFileInfo(_filename).fileName());
1988  else
1989  RPC::callFunctionValue<int>("datacontrol","groupObjects", *it2, groupNames[it2->back()]);
1990  }
1991  }
1992 #endif
1993 
1994  // only group if we have more than one object
1995  if (objIDs.size() > 1)
1996  returnID = RPC::callFunctionValue<int>("datacontrol","groupObjects", objIDs, importer.groupName(0));
1997  }
1998  }
1999 
2000  //check all new objects
2001  for(unsigned int i=0; i < importer.objectCount(); i++){
2002 
2003  BaseObject* object = importer.object(i);
2004  if(object == NULL) continue;
2005 
2006  object->setFromFileName(_filename);
2007 
2008  //remember the id of the first opened object
2009  if ( returnID == -1)
2010  returnID = object->id();
2011 
2012  //handle new PolyMeshes
2013  PolyMeshObject* polyMeshObj = dynamic_cast< PolyMeshObject* > (object);
2014 
2015  if ( polyMeshObj ){
2016 
2017  if ( !importer.hasNormals(i) )
2018  polyMeshObj->mesh()->update_normals();
2019  else
2020  polyMeshObj->mesh()->update_face_normals();
2021  }
2022 
2023  //handle new TriMeshes
2024  TriMeshObject* triMeshObj = dynamic_cast< TriMeshObject* > (object);
2025 
2026  if ( triMeshObj ){
2027 
2028  if ( !importer.hasNormals(i) || importer.hasOption( i, OBJImporter::FORCE_NONORMALS ) )
2029  triMeshObj->mesh()->update_normals();
2030  else
2031  triMeshObj->mesh()->update_face_normals();
2032  }
2033 
2034 #ifdef ENABLE_BSPLINECURVE_SUPPORT
2035  //handle new BSplineCurves
2036  BSplineCurveObject* bscObj = dynamic_cast< BSplineCurveObject* > (object);
2037 
2038  if ( bscObj ){
2039  bscObj->splineCurveNode()->updateGeometry();
2040  }
2041 #endif
2042 
2043  //textures
2044  if ( importer.hasTexture(i) && !importer.hasOption( i, OBJImporter::FORCE_NOTEXTURES ) ){
2045 
2046  //add the textures to the object
2047  addTextures( importer, i );
2048 
2049  //set the texture index property to be used
2050  emit setTextureMode("OBJ Data","indexProperty=OriginalTexIndexMapping", object->id() );
2051 
2052  emit switchTexture("OBJ Data", object->id() );
2053 
2055 
2056  }
2057 
2058  //general stuff
2059  emit updatedObject( object->id(), UPDATE_ALL );
2060  emit openedFile( object->id() );
2061  }
2062 
2063  forceTriangleMesh_ = false;
2064  forcePolyMesh_ = false;
2065 
2066 // if ( topLevelObj )
2067 // OpenFlipper::Options::loadingSettings(false);
2068  return returnID;
2069 }
2070 
2071 //-----------------------------------------------------------------------------------------------------
2072 
2074 int FileOBJPlugin::loadObject(QString _filename, DataType _type){
2075 
2076  if ( _type == DATA_TRIANGLE_MESH )
2077  forceTriangleMesh_ = true;
2078  else if ( _type == DATA_POLY_MESH )
2079  forcePolyMesh_ = true;
2080 
2081  return loadObject(_filename);
2082 }
2083 
2084 //-----------------------------------------------------------------------------------------------------
2085 
2086 bool FileOBJPlugin::saveObject(int _id, QString _filename)
2087 {
2088  BaseObjectData* object;
2089  if ( !PluginFunctions::getObject(_id,object) ) {
2090  emit log(LOGERR, tr("saveObject : cannot get object id %1 for save name %2").arg(_id).arg(_filename) );
2091  return false;
2092  }
2093 
2094  //open output stream
2095  std::string filename = std::string( _filename.toUtf8() );
2096 
2097  std::fstream objStream( filename.c_str(), std::ios_base::out );
2098 
2099  if ( !objStream ){
2100 
2101  emit log(LOGERR, tr("saveObject : cannot not open file %1").arg(_filename) );
2102  return false;
2103  }
2104 
2105  //write object
2106  if ( object->dataType( DATA_POLY_MESH ) ) {
2107 
2108  object->setFromFileName(_filename);
2109  object->setName(object->filename());
2110 
2111  PolyMeshObject* polyObj = dynamic_cast<PolyMeshObject* >( object );
2112 
2113 
2114  if ( writeMesh( objStream, _filename, *polyObj->mesh(), polyObj->id() ) ){
2115 
2116  emit log(LOGINFO, tr("Saved object to ") + _filename );
2117  objStream.close();
2118  return true;
2119 
2120  } else {
2121 
2122  emit log(LOGERR, tr("Unable to save ") + _filename);
2123  objStream.close();
2124  return false;
2125  }
2126 
2127  } else if ( object->dataType( DATA_TRIANGLE_MESH ) ) {
2128 
2129  object->setFromFileName(_filename);
2130  object->setName(object->filename());
2131 
2132  TriMeshObject* triObj = dynamic_cast<TriMeshObject* >( object );
2133 
2134 
2135  if ( writeMesh( objStream, _filename, *triObj->mesh(), triObj->id() )) {
2136 
2137  emit log(LOGINFO, tr("Saved object to ") + _filename );
2138  objStream.close();
2139  return true;
2140 
2141  } else {
2142 
2143  emit log(LOGERR, tr("Unable to save ") + _filename );
2144  objStream.close();
2145  return false;
2146  }
2147 
2148 #ifdef ENABLE_BSPLINECURVE_SUPPORT
2149  } else if ( object->dataType( DATA_BSPLINE_CURVE ) ) {
2150 
2151  object->setFromFileName(_filename);
2152  object->setName(object->filename());
2153 
2154  BSplineCurveObject* bscObj = dynamic_cast<BSplineCurveObject* >( object );
2155 
2156 
2157  if ( writeCurve( objStream, _filename, bscObj->splineCurve()) ) {
2158 
2159  emit log(LOGINFO, tr("Saved object to ") + _filename );
2160  objStream.close();
2161  return true;
2162 
2163  } else {
2164 
2165  emit log(LOGERR, tr("Unable to save ") + _filename );
2166  objStream.close();
2167  return false;
2168  }
2169 #endif
2170 
2171 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
2172  } else if ( object->dataType( DATA_BSPLINE_SURFACE ) ) {
2173 
2174  object->setFromFileName(_filename);
2175  object->setName(object->filename());
2176 
2177  BSplineSurfaceObject* bssObj = dynamic_cast<BSplineSurfaceObject* >( object );
2178 
2179 
2180  if ( writeSurface( objStream, _filename, bssObj->splineSurface()) ) {
2181 
2182  emit log(LOGINFO, tr("Saved object to ") + _filename );
2183  objStream.close();
2184  return true;
2185 
2186  } else {
2187 
2188  emit log(LOGERR, tr("Unable to save ") + object->path() + OpenFlipper::Options::dirSeparator() + object->name());
2189  objStream.close();
2190  return false;
2191  }
2192 #endif
2193 
2194  } else {
2195 
2196  emit log(LOGERR, tr("Unable to save (object is not a compatible mesh type)"));
2197  objStream.close();
2198  return false;
2199  }
2200 }
2201 
2202 //-----------------------------------------------------------------------------------------------------
2203 
2204 void FileOBJPlugin::slotHandleCheckBoxes(bool _checked) {
2205 
2206  if(saveCopyTextures_) {
2207  saveCreateTexFolder_->setEnabled(_checked);
2208  saveCreateTexFolder_->setChecked(_checked);
2209  }
2210 }
2211 
2212 //-----------------------------------------------------------------------------------------------------
2213 
2214 QWidget* FileOBJPlugin::saveOptionsWidget(QString /*_currentFilter*/) {
2215 
2216  if (saveOptions_ == 0){
2217  //generate widget
2218  saveOptions_ = new QWidget();
2219  QVBoxLayout* layout = new QVBoxLayout();
2220  layout->setAlignment(Qt::AlignTop);
2221 
2222  saveFaceColor_ = new QCheckBox("Save Face Colors");
2223  layout->addWidget(saveFaceColor_);
2224 
2225  saveAlpha_ = new QCheckBox("Save Color Alpha");
2226  layout->addWidget(saveAlpha_);
2227 
2228  saveNormals_ = new QCheckBox("Save Normals");
2229  layout->addWidget(saveNormals_);
2230 
2231  saveTexCoords_ = new QCheckBox("Save Texture Coordinates");
2232  layout->addWidget(saveTexCoords_);
2233 
2234  saveTextures_ = new QCheckBox("Save Textures");
2235  layout->addWidget(saveTextures_);
2236 
2237  saveCopyTextures_ = new QCheckBox("Copy Texture Files");
2238  layout->addWidget(saveCopyTextures_);
2239 
2240  saveCreateTexFolder_ = new QCheckBox("Create Textures Folder");
2241  layout->addWidget(saveCreateTexFolder_);
2242 
2243  savePrecisionLabel_ = new QLabel("Writer Precision");
2244  layout->addWidget(savePrecisionLabel_);
2245 
2246  savePrecision_ = new QSpinBox();
2247  savePrecision_->setMinimum(1);
2248  savePrecision_->setMaximum(12);
2249  savePrecision_->setValue(6);
2250  layout->addWidget(savePrecision_);
2251 
2252  saveDefaultButton_ = new QPushButton("Make Default");
2253  layout->addWidget(saveDefaultButton_);
2254 
2255  saveOptions_->setLayout(layout);
2256 
2257  connect(saveDefaultButton_, SIGNAL(clicked()), this, SLOT(slotSaveDefault()));
2258  connect(saveCopyTextures_, SIGNAL(toggled(bool)), this, SLOT(slotHandleCheckBoxes(bool)));
2259 
2260  saveFaceColor_->setChecked( OpenFlipperSettings().value("FileObj/Save/FaceColor",true).toBool() );
2261  saveAlpha_->setChecked( OpenFlipperSettings().value("FileObj/Save/Alpha",true).toBool() );
2262  saveNormals_->setChecked( OpenFlipperSettings().value("FileObj/Save/Normals",true).toBool() );
2263  saveTexCoords_->setChecked( OpenFlipperSettings().value("FileObj/Save/TexCoords",true).toBool() );
2264  saveTextures_->setChecked( OpenFlipperSettings().value("FileObj/Save/Textures",true).toBool() );
2265  saveCopyTextures_->setChecked( OpenFlipperSettings().value("FileObj/Save/CopyTextures",true).toBool() );
2266  saveCreateTexFolder_->setChecked( OpenFlipperSettings().value("FileObj/Save/CreateTexFolder",true).toBool() );
2267 
2268  slotHandleCheckBoxes(saveCopyTextures_->isChecked());
2269  }
2270 
2271  return saveOptions_;
2272 }
2273 
2274 //-----------------------------------------------------------------------------------------------------
2275 
2276 QWidget* FileOBJPlugin::loadOptionsWidget(QString /*_currentFilter*/) {
2277 
2278  if (loadOptions_ == 0){
2279  //generate widget
2280  loadOptions_ = new QWidget();
2281  QVBoxLayout* layout = new QVBoxLayout();
2282  layout->setAlignment(Qt::AlignTop);
2283 
2284  QLabel* label = new QLabel(tr("If file contains meshes:"));
2285 
2286  layout->addWidget(label);
2287 
2288  triMeshHandling_ = new QComboBox();
2289  triMeshHandling_->addItem( tr("Detect correct type") );
2290  triMeshHandling_->addItem( tr("Ask") );
2291  triMeshHandling_->addItem( tr("Open as PolyMesh") );
2292  triMeshHandling_->addItem( tr("Open as TriangleMesh") );
2293 
2294  layout->addWidget(triMeshHandling_);
2295 
2296  loadFaceColor_ = new QCheckBox("Load Face Colors");
2297  layout->addWidget(loadFaceColor_);
2298 
2299  loadNormals_ = new QCheckBox("Load Normals");
2300  layout->addWidget(loadNormals_);
2301 
2302  loadTexCoords_ = new QCheckBox("Load Texture Coordinates");
2303  layout->addWidget(loadTexCoords_);
2304 
2305  loadTextures_ = new QCheckBox("Load Textures");
2306  layout->addWidget(loadTextures_);
2307 
2308  loadDefaultButton_ = new QPushButton("Make Default");
2309  layout->addWidget(loadDefaultButton_);
2310 
2311  loadOptions_->setLayout(layout);
2312 
2313  connect(loadDefaultButton_, SIGNAL(clicked()), this, SLOT(slotLoadDefault()));
2314 
2315 
2316  triMeshHandling_->setCurrentIndex(OpenFlipperSettings().value("FileObj/Load/TriMeshHandling",TYPEAUTODETECT).toInt() );
2317 
2318  loadFaceColor_->setChecked( OpenFlipperSettings().value("FileObj/Load/FaceColor",true).toBool() );
2319  loadNormals_->setChecked( OpenFlipperSettings().value("FileObj/Load/Normals",true).toBool() );
2320  loadTexCoords_->setChecked( OpenFlipperSettings().value("FileObj/Load/TexCoords",true).toBool() );
2321  loadTextures_->setChecked( OpenFlipperSettings().value("FileObj/Load/Textures",true).toBool() );
2322  }
2323 
2324  return loadOptions_;
2325 }
2326 
2328  OpenFlipperSettings().setValue( "FileObj/Load/FaceColor", loadFaceColor_->isChecked() );
2329  OpenFlipperSettings().setValue( "FileObj/Load/Normals", loadNormals_->isChecked() );
2330  OpenFlipperSettings().setValue( "FileObj/Load/TexCoords", loadTexCoords_->isChecked() );
2331  OpenFlipperSettings().setValue( "FileObj/Load/Textures", loadTextures_->isChecked() );
2332  OpenFlipperSettings().setValue("FileObj/Load/TriMeshHandling", triMeshHandling_->currentIndex() );
2333 
2334  OpenFlipperSettings().setValue( "Core/File/UseLoadDefaults", true );
2335 }
2336 
2337 
2339  OpenFlipperSettings().setValue( "FileObj/Save/FaceColor", saveFaceColor_->isChecked() );
2340  OpenFlipperSettings().setValue( "FileObj/Save/Normals", saveNormals_->isChecked() );
2341  OpenFlipperSettings().setValue( "FileObj/Save/TexCoords", saveTexCoords_->isChecked() );
2342  OpenFlipperSettings().setValue( "FileObj/Save/Textures", saveTextures_->isChecked() );
2343  OpenFlipperSettings().setValue( "FileObj/Save/CopyTextures", saveCopyTextures_->isChecked() );
2344  OpenFlipperSettings().setValue( "FileObj/Save/CreateTexFolder", saveCreateTexFolder_->isChecked() );
2345 
2346 }
2347 
2348 #if QT_VERSION < 0x050000
2349  Q_EXPORT_PLUGIN2( fileobjplugin , FileOBJPlugin );
2350 #endif
2351 
2352 
int loadObject(QString _filename)
Loads Object and converts it to a triangle mesh if possible.
Definition: FileOBJ.cc:1795
QString getSaveFilters()
Definition: FileOBJ.cc:135
Predefined datatypes.
Definition: DataTypes.hh:96
void setDegreeV(int _degree)
set degree V direction
Definition: OBJImporter.cc:106
void useVertex(int _vertex_index)
used vertices
void slotLoadDefault()
Slot called when user wants to save the given Load options as default.
Definition: FileOBJ.cc:2327
Type for a MeshObject containing a triangle mesh.
Definition: TriangleMesh.hh:73
void setNormal(int _index, int _normalID)
set vertex normal
Definition: OBJImporter.cc:311
QWidget * saveOptionsWidget(QString)
Definition: FileOBJ.cc:2214
int currentObject()
get id of the active object
Definition: OBJImporter.cc:165
void update_normals()
Compute normals for all primitives.
Definition: PolyMeshT.cc:241
QString filename() const
return the filename of the object
Definition: BaseObject.cc:717
bool getObject(int _identifier, BSplineCurveObject *&_object)
bool dataType(DataType _type) const
Definition: BaseObject.cc:232
VertexHandle addVertex(const Vec3f &_point)
add a vertex with coordinate _point
Definition: OBJImporter.cc:63
void forceMeshType(ObjectOptions _meshType)
force all meshes to be opened with specific type
Definition: OBJImporter.cc:699
DLLEXPORT OpenFlipperQSettings & OpenFlipperSettings()
QSettings object containing all program settings of OpenFlipper.
QString path() const
return the path to the object ( defaults to "." if unset )
Definition: BaseObject.cc:745
unsigned int n_vertices()
Global Properties.
Definition: OBJImporter.cc:757
void setObjectOptions(ObjectOptions _options)
Definition: OBJImporter.cc:818
int id() const
Definition: BaseObject.cc:201
void finish()
Definition: OBJImporter.cc:925
DrawMode SOLID_2DTEXTURED_FACE_SHADED
draw per halfedge textured faces
Definition: DrawModes.cc:103
void setFromFileName(const QString &_filename)
Definition: BaseObject.cc:727
QWidget * loadOptionsWidget(QString)
Definition: FileOBJ.cc:2276
MeshT * mesh()
return a pointer to the mesh
Definition: MeshObjectT.cc:351
void initializePlugin()
Initialize Plugin.
Definition: FileOBJ.cc:124
QString path()
Path of the OBJ file.
Definition: OBJImporter.cc:806
void setDegreeU(int _degree)
set degree
Definition: OBJImporter.cc:99
int addTexCoord(const Vec2f &_coord)
add texture coordinates
Definition: OBJImporter.cc:81
int degreeV()
get current degree
Definition: OBJImporter.cc:121
QString getLoadFilters()
Definition: FileOBJ.cc:129
void checkTypes(QByteArray &_bufferedFile, QString _filename, OBJImporter &_importer, QStringList &_includes)
Reader functions.
Definition: FileOBJ.cc:1274
void setVertexTexCoord(VertexHandle _vh, int _texCoordID)
set vertex texture coordinate
Definition: OBJImporter.cc:266
void setValue(const QString &key, const QVariant &value)
Wrapper function which makes it possible to enable Debugging output with -DOPENFLIPPER_SETTINGS_DEBUG...
void backupTextureCoordinates(MeshT &_mesh)
creates a backup of the original per vertex/face texture coordinates
Definition: FileOBJ.cc:417
Vec3f vertex(unsigned int _index)
get vertex with given index
Definition: OBJImporter.cc:70
const DataType DATA_GROUP(1)
Items used for Grouping.
DataType supportedType()
Return your supported object type( e.g. DATA_TRIANGLE_MESH )
Definition: FileOBJ.cc:141
ACG::SceneGraph::BSplineCurveNodeT< BSplineCurve > * splineCurveNode()
Get the scenegraph Node.
bool hasNormals(int _objectID)
Query Object Options.
Definition: OBJImporter.cc:737
BaseObject * object(int _objectID)
return object with given index
Definition: OBJImporter.cc:781
std::vector< int > IdList
Standard Type for id Lists used for scripting.
Definition: DataTypes.hh:192
int degreeU()
get current degree
Definition: OBJImporter.cc:114
FileOBJPlugin()
Constructor.
Definition: FileOBJ.cc:92
#define DATA_BSPLINE_CURVE
Definition: BSplineCurve.hh:73
void convertToOBJName(QString &_name)
Convert non-valid filenames (e.g. of groups that end with .jpg) to valid .objs.
Definition: FileOBJ.cc:406
void update_face_normals()
Update normal vectors for all faces.
Definition: PolyMeshT.cc:259
Type for a Meshobject containing a poly mesh.
Definition: PolyMesh.hh:70
void addFace(const VHandles &_indices)
add a face with indices _indices refering to vertices
Definition: OBJImporter.cc:474
BSplineCurve * splineCurve()
return a pointer to the spline curve
QString name()
Return a name for the plugin.
Definition: FileOBJ.hh:158
void slotSaveDefault()
Slot called when user wants to save the given Save options as default.
Definition: FileOBJ.cc:2338
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
#define DATA_POLY_MESH
Definition: PolyMesh.hh:65
const std::vector< std::string > usedMaterials(unsigned int _objectID)
used materials
#define DATA_BSPLINE_SURFACE
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:66
void setDrawMode(const ACG::SceneGraph::DrawModes::DrawMode &_mode, int _viewer)
Set the draw Mode of a Viewer. .
std::vector< ObjectOptions > & objectOptions()
Object Options for all objects.
Definition: OBJImporter.cc:829
BSplineSurface * splineSurface()
return a pointer to the spline curve
void setObject(BaseObject *_object, int _groupId)
add an object
Definition: OBJImporter.cc:128
void addMaterial(std::string _materialName)
Add a material.
Definition: OBJImporter.cc:572
bool hasOption(unsigned int _id, ObjectOptions _option)
check if object with given id has given option
Definition: OBJImporter.cc:836
int addNormal(const Vec3f &_normal)
add a normal
Definition: OBJImporter.cc:90
MaterialList & materials()
return all loaded materials
Definition: OBJImporter.cc:800