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