1 /*************************************************************************
2  *                                                                       *
3  * Vega FEM Simulation Library Version 3.1                               *
4  *                                                                       *
5  * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2016 USC        *
6  * All rights reserved.                                                  *
7  *                                                                       *
8  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder,     *
9  *               Yili Zhao, Yijing Li                                    *
10  * http://www.jernejbarbic.com/code                                      *
11  *                                                                       *
12  * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
13  *           Doug L. James, Jovan Popovic                                *
14  *                                                                       *
15  * Funding: National Science Foundation, Link Foundation,                *
16  *          Singapore-MIT GAMBIT Game Lab,                               *
17  *          Zumberge Research and Innovation Fund at USC                 *
18  *                                                                       *
19  * This library is free software; you can redistribute it and/or         *
20  * modify it under the terms of the BSD-style license that is            *
21  * included with this library in the file LICENSE.txt                    *
22  *                                                                       *
23  * This library is distributed in the hope that it will be useful,       *
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
26  * LICENSE.TXT for more details.                                         *
27  *                                                                       *
28  *************************************************************************/
29 
30 #if defined(_WIN32) || defined(WIN32)
31   #pragma warning(disable : 4996)
32   #pragma warning(disable : 4267)
33   #pragma warning(disable : 4244)
34 #endif
35 #include <float.h>
36 #include <math.h>
37 #include <string.h>
38 #include <vector>
39 #include <string>
40 #include <map>
41 #include <set>
42 #include <fstream>
43 #include <sstream>
44 #include <algorithm>
45 #include <functional>
46 #include <cctype>
47 #include <assert.h>
48 #include "macros.h"
49 #include "objMesh-disjointSet.h"
50 #include "objMesh.h"
51 using namespace std;
52 
53 // for faster parallel loading of multimesh binary files, enable the -fopenmp -DUSE_OPENMP macro line in the Makefile-header file (see also documentation)
54 
55 #ifdef USE_OPENMP
56   #include <omp.h>
57 #endif
58 
ObjMesh(const std::string & filename_,fileFormatType fileFormat,int verbose)59 ObjMesh::ObjMesh(const std::string & filename_, fileFormatType fileFormat, int verbose) : filename(filename_)
60 {
61   switch (fileFormat)
62   {
63     case ASCII:
64     {
65       std::string filenameString(filename_);
66       loadFromAscii(filenameString, verbose);
67     }
68     break;
69 
70     case BINARY:
71       loadFromBinary(filename_, verbose);
72     break;
73 
74     default:
75       printf("Error in ObjMesh::ObjMesh: File format %d is unknown.\n", fileFormat);
76       throw 1;
77     break;
78   }
79 
80   computeBoundingBox();
81 
82   // statistics
83   if (verbose)
84   {
85     std::cout << "Parsed obj file '" << filename << "'; statistics:" << std::endl;
86     std::cout << "   " << groups.size() << " groups," << std::endl;
87     std::cout << "   " << getNumFaces() << " faces," << std::endl;
88     std::cout << "   " << vertexPositions.size() << " vertices," << std::endl;
89     std::cout << "   " << normals.size() << " normals, " << std::endl;
90     std::cout << "   " << textureCoordinates.size() << " texture coordinates, " << std::endl;
91   }
92 }
93 
ObjMesh(void * binaryInputStream,streamType stream,int verbose)94 ObjMesh::ObjMesh(void * binaryInputStream, streamType stream, int verbose)
95 {
96   filename = string("");
97   loadFromBinary(binaryInputStream, stream, verbose);
98   computeBoundingBox();
99 }
100 
ObjMesh(int numVertices,const double * vertices,int numTriangles,const int * triangles)101 ObjMesh::ObjMesh(int numVertices, const double * vertices, int numTriangles, const int * triangles)
102 {
103   filename = string("");
104 
105   for(int i=0; i<numVertices; i++)
106     addVertexPosition(Vec3d(vertices[3*i+0], vertices[3*i+1], vertices[3*i+2]));
107 
108   unsigned int materialIndex = 0;
109   addMaterial(string("default"), Vec3d(1,1,1), Vec3d(1,1,1), Vec3d(1,1,1), 0);
110   groups.push_back(Group("defaultGroup", materialIndex));
111   for(int i=0; i<numTriangles; i++)
112   {
113     Face face;
114     face.addVertex(Vertex(triangles[3*i+0]));
115     face.addVertex(Vertex(triangles[3*i+1]));
116     face.addVertex(Vertex(triangles[3*i+2]));
117     addFaceToGroup(face, 0);
118   }
119 
120   computeBoundingBox();
121 }
122 
ObjMesh(int numVertices,const double * vertices,int numFaces,const int * faceVertexCounts,const int * faces)123 ObjMesh::ObjMesh(int numVertices, const double * vertices, int numFaces, const int* faceVertexCounts, const int * faces)
124 {
125   filename = string("");
126 
127   for(int i = 0; i < numVertices; i++)
128     addVertexPosition(Vec3d(vertices[3*i+0], vertices[3*i+1], vertices[3*i+2]));
129 
130   unsigned int materialIndex = 0;
131   addMaterial(string("default"), Vec3d(1,1,1), Vec3d(1,1,1), Vec3d(1,1,1), 0);
132   groups.push_back(Group("defaultGroup", materialIndex));
133   for(int i = 0, k = 0; i < numFaces; i++)
134   {
135     Face face;
136     int faceVertexCount = faceVertexCounts[i];
137     for(int j = 0; j < faceVertexCount; j++)
138     {
139       face.addVertex(Vertex(faces[k]));
140       k++;
141     }
142 
143     addFaceToGroup(face, 0);
144   }
145 
146   computeBoundingBox();
147 }
148 
149 
ObjMesh(const ObjMesh & objMesh_)150 ObjMesh::ObjMesh(const ObjMesh & objMesh_)
151 {
152   // copy materials
153   unsigned int numObjMaterials = objMesh_.getNumMaterials();
154   for (unsigned int materialIndex=0; materialIndex < numObjMaterials; materialIndex++)
155   {
156     std::string materialName = objMesh_.getMaterial(materialIndex).getName();
157     Vec3d Ka = objMesh_.getMaterial(materialIndex).getKa();
158     Vec3d Kd = objMesh_.getMaterial(materialIndex).getKd();
159     Vec3d Ks = objMesh_.getMaterial(materialIndex).getKs();
160     double shininess = objMesh_.getMaterial(materialIndex).getShininess();
161 
162     // add a new material
163     addMaterial(materialName, Ka, Kd, Ks, shininess);
164 
165     if (objMesh_.getMaterial(materialIndex).hasTextureFilename())
166     {
167       std::string textureFilename = objMesh_.getMaterial(materialIndex).getTextureFilename();
168       materials[materialIndex].setTextureFilename(textureFilename);
169     }
170   }  // for materialIndex
171 
172   // copy vertices
173   unsigned int numVertices = objMesh_.getNumVertices();
174   for(unsigned int vertexIndex=0; vertexIndex < numVertices; vertexIndex++)
175     vertexPositions.push_back(objMesh_.vertexPositions[vertexIndex]);
176 
177   // copy texture coordinates
178   unsigned int numTexCoordinates = objMesh_.getNumTextureCoordinates();
179   for(unsigned int textureCoordinateIndex=0; textureCoordinateIndex < numTexCoordinates; textureCoordinateIndex++)
180     textureCoordinates.push_back(objMesh_.textureCoordinates[textureCoordinateIndex]);
181 
182   // copy normals
183   unsigned int numNormals = objMesh_.getNumNormals();
184   for(unsigned int normalIndex=0; normalIndex < numNormals; normalIndex++)
185     normals.push_back(objMesh_.normals[normalIndex]);
186 
187   // copy groups
188   unsigned int numGroups = objMesh_.getNumGroups();
189   for(unsigned int groupIndex=0; groupIndex < numGroups; groupIndex++)
190   {
191     // group name and material index
192     std::string groupName = objMesh_.groups[groupIndex].getName();
193     unsigned int materialIndex = objMesh_.groups[groupIndex].getMaterialIndex();
194     groups.push_back(Group(groupName, materialIndex));
195 
196     // copy faces of current group
197     unsigned int numFaces = objMesh_.groups[groupIndex].getNumFaces();
198     for (unsigned int faceIndex=0; faceIndex < numFaces; faceIndex++)
199     {
200       const Face * objMeshFace = objMesh_.groups[groupIndex].getFaceHandle(faceIndex);
201       unsigned int numFaceVertices = objMeshFace->getNumVertices();
202       Face currentFace;
203       for (unsigned int vertexIndex=0; vertexIndex < numFaceVertices; vertexIndex++)
204         currentFace.addVertex(objMeshFace->getVertex(vertexIndex));
205 
206       groups[groupIndex].addFace(currentFace);
207     }
208   }  // for groupIndex
209 
210   // copy filename
211   filename = objMesh_.filename;
212 
213   computeBoundingBox();
214 }
215 
loadFromAscii(const string & filename,int verbose)216 int ObjMesh::loadFromAscii(const string & filename, int verbose)
217 {
218   unsigned int numFaces = 0;
219 
220   const int maxline = 4096;
221   std::ifstream ifs(filename.c_str());
222   char line[maxline];
223 
224   unsigned int currentGroup=0;
225   unsigned int ignoreCounter=0;
226 
227   unsigned int currentMaterialIndex = 0;
228 
229   // Note: the default material will be added when encountered in the obj file, or at the end if necessary. One cannot simply add it here at the beginning because a material read from the .mtl file could also be called "default".
230 
231   if (verbose)
232     std::cout << "Parsing .obj file '" << filename << "'." << std::endl;
233 
234   if (!ifs)
235   {
236     std::string message = "Could not open .obj file '";
237     message.append(filename);
238     message.append( "'" );
239     throw ObjMeshException( message );
240   }
241 
242   int lineNum = 0;
243   int numGroupFaces = 0;
244   int groupCloneIndex = 0;
245   std::string groupSourceName;
246 
247   while(ifs)
248   {
249     lineNum++;
250     ifs.getline(line, maxline);
251     if (strlen(line) > 0)
252     {
253       // if ending in '\\', the next line should be concatenated to the current line
254       int lastCharPos = (int)strlen(line)-1;
255       while(line[lastCharPos] == '\\')
256       {
257         line[lastCharPos] = ' ';  // first turn '\' to ' '
258         char nextline[maxline];
259         ifs.getline(nextline, maxline);
260         strcat(line, nextline);
261         lastCharPos = (int)strlen(line)-1;
262       }
263     }
264 
265     std::string lineString(line);
266     // trim white space ahead
267     lineString.erase(lineString.begin(), std::find_if(lineString.begin(), lineString.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
268     // trim white space in the end
269     lineString.erase(std::find_if(lineString.rbegin(), lineString.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), lineString.end());
270 
271 
272     memset(line, 0, maxline);
273     strcpy(line, lineString.c_str());
274 
275     convertWhitespaceToSingleBlanks(line);
276 
277     char command = line[0];
278 
279     if (strncmp(line,"v ",2) == 0) // vertex
280     {
281       //std::cout << "v " ;
282       Vec3d pos;
283       double x,y,z;
284       if (sscanf(line, "v %lf %lf %lf\n", &x, &y, &z) < 3)
285       {
286         throw ObjMeshException("Invalid vertex", filename, lineNum);
287       }
288       pos = Vec3d(x,y,z);
289       vertexPositions.push_back( pos );
290     }
291     else if (strncmp(line, "vn ", 3) == 0)
292     {
293       //std::cout << "vn " ;
294       Vec3d normal;
295       double x,y,z;
296       if (sscanf(line,"vn %lf %lf %lf\n", &x, &y, &z) < 3)
297       {
298         throw ObjMeshException("Invalid normal", filename, lineNum);
299       }
300       normal = Vec3d(x,y,z);
301       normals.push_back(normal);
302     }
303     else if (strncmp(line, "vt ", 3) == 0 )
304     {
305       //std::cout << "vt " ;
306       Vec3d tex(0.0);
307       double x,y;
308       if (sscanf(line, "vt %lf %lf\n", &x, &y) < 2)
309       {
310         throw ObjMeshException("Invalid texture coordinate", filename, lineNum);
311       }
312       tex = Vec3d(x,y,0);
313       textureCoordinates.push_back(tex);
314     }
315     else if (strncmp(line, "g ", 2) == 0)
316     {
317       // remove last newline
318       if (strlen(line) > 0)
319       {
320         if (line[strlen(line)-1] == '\n')
321           line[strlen(line)-1] = 0;
322       }
323 
324       // remove last carriage return
325       if (strlen(line) > 0)
326       {
327         if (line[strlen(line)-1] == '\r')
328           line[strlen(line)-1] = 0;
329       }
330 
331       std::string name;
332       if (strlen(line) < 2)
333       {
334         if (verbose)
335           cout << "Warning:  Empty group name encountered: " << filename << " " << lineNum << endl;
336         name = string("");
337       }
338       else
339         name = string(&line[2]);
340 
341       //printf("Detected group: %s\n", &line[2]);
342 
343       // check if this group already exists
344       bool groupFound = false;
345       unsigned int counter = 0;
346       for(std::vector< Group >::const_iterator itr = groups.begin(); itr != groups.end(); itr++)
347       {
348         if (itr->getName() == name)
349         {
350           currentGroup = counter;
351           groupFound = true;
352           break;
353         }
354         counter++;
355       }
356       if (!groupFound)
357       {
358         groups.push_back(Group(name, currentMaterialIndex));
359         currentGroup = groups.size() - 1;
360         numGroupFaces = 0;
361         groupCloneIndex = 0;
362         groupSourceName = name;
363       }
364     }
365     else if ((strncmp(line, "f ", 2) == 0) || (strncmp(line, "fo ", 3) == 0))
366     {
367       char * faceLine = &line[2];
368       if (strncmp(line, "fo", 2) == 0)
369         faceLine = &line[3];
370 
371       //std::cout << "f " ;
372       if (groups.empty())
373       {
374         groups.push_back(Group("default"));
375         currentGroup = 0;
376       }
377 
378       Face face;
379 
380       // the faceLine string now looks like the following:
381       //   vertex1 vertex2 ... vertexn
382       // where vertexi is v/t/n, v//n, v/t, or v
383 
384       char * curPos = faceLine;
385       while( *curPos != '\0' )
386       {
387         // seek for next whitespace or eof
388         char * tokenEnd = curPos;
389         while ((*tokenEnd != ' ') && (*tokenEnd != '\0'))
390           tokenEnd++;
391 
392         bool whiteSpace = false;
393         if (*tokenEnd == ' ')
394         {
395           *tokenEnd = '\0';
396           whiteSpace = true;
397         }
398 
399         int pos;
400         int nor;
401         int tex;
402         std::pair< bool, unsigned int > texPos;
403         std::pair< bool, unsigned int > normal;
404 
405         // now, parse curPos
406         if (strstr(curPos,"//") != NULL)
407         {
408           if (sscanf(curPos, "%d//%d", &pos, &nor) < 2)
409           {
410             throw ObjMeshException( "Invalid face", filename, lineNum);
411           }
412 
413           // v//n
414           if (pos < 0)
415             pos = (int)vertexPositions.size() + pos + 1;
416           if (nor < 0)
417             nor = (int)normals.size() + nor + 1;
418 
419           texPos = make_pair(false, 0);
420           normal = make_pair(true, (unsigned int)nor);
421         }
422         else
423         {
424           if (sscanf(curPos, "%d/%d/%d", &pos, &tex, &nor) != 3)
425           {
426             if (strstr(curPos, "/") != NULL)
427             {
428               if (sscanf(curPos, "%d/%d", &pos, &tex) == 2)
429               {
430                 // v/t
431                 if (pos < 0)
432                   pos = (int)vertexPositions.size() + pos + 1;
433                 if (tex < 0)
434                   tex = (int)textureCoordinates.size() + tex + 1;
435 
436                 texPos = make_pair(true, (unsigned int)tex);
437                 normal = make_pair(false, 0);
438               }
439               else
440               {
441                 throw ObjMeshException("Invalid face", filename, lineNum);
442               }
443             }
444             else
445             {
446               if (sscanf(curPos, "%d", &pos) == 1)
447               {
448                 // v
449                 if (pos < 0)
450                   pos = (int)vertexPositions.size() + pos + 1;
451 
452                 texPos = make_pair(false, 0);
453                 normal = make_pair(false, 0);
454               }
455               else
456               {
457                 throw ObjMeshException("Invalid face", filename, lineNum);
458               }
459             }
460           }
461           else
462           {
463             // v/t/n
464             if (pos < 0)
465               pos = (int)vertexPositions.size() + pos + 1;
466             if (tex < 0)
467               tex = (int)textureCoordinates.size() + tex + 1;
468             if (nor < 0)
469               nor = (int)normals.size() + nor + 1;
470 
471             texPos = make_pair(true, (unsigned int)tex);
472             normal = make_pair(true, (unsigned int)nor);
473           }
474         }
475 
476         // sanity check
477         if ((pos < 1) || (pos > (int)vertexPositions.size()))
478         {
479           printf("Error: vertex %d is out of bounds.\n", pos);
480           throw 51;
481         }
482 
483         if (texPos.first && ((tex < 1) || (tex > (int)textureCoordinates.size())))
484         {
485           printf("Error: texture %d is out of bounds.\n", tex);
486           throw 53;
487         }
488 
489         if (normal.first && ((nor < 1) || (nor > (int)normals.size())))
490         {
491           printf("Error: normal %d is out of bounds.\n", nor);
492           throw 52;
493         }
494 
495         // decrease indices to make them 0-indexed
496         pos--;
497         if (texPos.first)
498           texPos.second--;
499         if (normal.first)
500           normal.second--;
501 
502         face.addVertex(Vertex((unsigned int)pos, texPos, normal));
503 
504         if (whiteSpace)
505         {
506           *tokenEnd = ' ';
507           curPos = tokenEnd + 1;
508         }
509         else
510           curPos = tokenEnd;
511       }
512 
513       numFaces++;
514       groups[currentGroup].addFace(face);
515       numGroupFaces++;
516     }
517     else if ((strncmp(line, "#", 1) == 0 ) || (strncmp(line, "\0", 1) == 0))
518     {
519       // ignore comment lines and empty lines
520     }
521     else if (strncmp(line, "usemtl", 6) == 0)
522     {
523       // switch to a new material
524       if (numGroupFaces > 0)
525       {
526         // usemtl without a "g" statement; must create a new group
527         // first, create unique name
528         char newNameC[4096];
529         sprintf(newNameC, "%s.%d", groupSourceName.c_str(), groupCloneIndex);
530         //printf("Splitting group...\n");
531         //printf("New name=%s\n", newNameC);
532         std::string newName(newNameC);
533         groups.push_back(Group(newName, currentMaterialIndex));
534         currentGroup = groups.size()-1;
535         numGroupFaces = 0;
536         groupCloneIndex++;
537       }
538 
539       materialSearch:
540       bool materialFound = false;
541       unsigned int counter = 0;
542       char * materialName = &line[7];
543       for(std::vector< Material >::const_iterator itr = materials.begin(); itr != materials.end(); itr++)
544       {
545         if (itr->getName() == string(materialName))
546         {
547           currentMaterialIndex = counter;
548 
549           // update current group
550           if (groups.empty())
551           {
552             groups.push_back(Group("default"));
553             currentGroup = 0;
554           }
555 
556           groups[currentGroup].setMaterialIndex(currentMaterialIndex);
557           materialFound = true;
558           break;
559         }
560         counter++;
561       }
562 
563       if (!materialFound)
564       {
565         if (strcmp(materialName, "default") == 0)
566         {
567           addDefaultMaterial();
568           goto materialSearch;
569         }
570 
571         char msg[4096];
572         sprintf(msg, "Obj mesh material %s does not exist.\n", materialName);
573         throw ObjMeshException(msg);
574       }
575     }
576     else if (strncmp(line, "mtllib", 6) == 0)
577     {
578       char mtlFilename[4096];
579       strcpy(mtlFilename, filename.c_str());
580       parseMaterials(mtlFilename, &line[7], verbose);
581     }
582     else if ((strncmp(line, "s ", 2) == 0 ) || (strncmp(line, "o ", 2) == 0))
583     {
584       // ignore lines beginning with s and o
585       //std::cout << command << " ";
586       if (ignoreCounter < 5)
587       {
588         if (verbose)
589           std::cout << "Warning: ignoring '" << command << "' line" << std::endl;
590         ignoreCounter++;
591       }
592       if (ignoreCounter == 5)
593       {
594         if (verbose)
595           std::cout << "(suppressing further output of ignored lines)" << std::endl;
596         ignoreCounter++;
597       }
598     }
599     else
600     {
601       //std::cout << "invalid ";
602       std::ostringstream msg;
603       msg << "Invalid line in .obj file '" << filename << "': " << line;
604       throw ObjMeshException(msg.str(), filename, lineNum);
605     }
606   }
607 
608   // add the "default" material if it doesn't already exist
609   addDefaultMaterial();
610 
611   return 0;
612 }
613 
addDefaultMaterial()614 void ObjMesh::addDefaultMaterial()
615 {
616   // search if there already is the "default" material
617   bool addDefaultMaterial = true;
618   unsigned int numObjMaterials = getNumMaterials();
619   for (unsigned int materialIndex=0; materialIndex<numObjMaterials; materialIndex++)
620   {
621     std::string materialNameString = materials[materialIndex].getName();
622     char * materialName = (char *)(materialNameString.c_str());
623 
624     if(strcmp(materialName, "default") == 0)
625     {
626       addDefaultMaterial = false;
627       break;
628     }
629   }
630 
631   if (addDefaultMaterial)
632   {
633     addMaterial(string("default"), Vec3d(0.2,0.2,0.2), Vec3d(0.6,0.6,0.6), Vec3d(0.0,0.0,0.0), 65);
634   }
635 }
636 
getGroupNames() const637 std::vector<std::string> ObjMesh::getGroupNames() const
638 {
639   std::vector<std::string> result;
640   result.reserve(groups.size());
641   for(std::vector<Group>::const_iterator groupItr = groups.begin(); groupItr != groups.end(); groupItr++)
642     result.push_back(groupItr->getName());
643 
644   return result;
645 }
646 
getGroup(const std::string name) const647 ObjMesh::Group ObjMesh::getGroup(const std::string name) const
648 {
649   for(std::vector<Group>::const_iterator itr = groups.begin(); itr != groups.end(); itr++)
650   {
651     if (itr->getName() == name)
652       return *itr;
653   }
654 
655   std::ostringstream oss;
656   oss << "Invalid group name: '" << name << "'.";
657   throw ObjMeshException(oss.str());
658 }
659 
printInfo() const660 void ObjMesh::printInfo() const
661 {
662   typedef std::vector<std::string> SVec;
663   SVec groupNames1 = getGroupNames();
664   for(SVec::const_iterator nameItr = groupNames1.begin(); nameItr != groupNames1.end(); nameItr++)
665   {
666     std::cout << "Found obj group '" << *nameItr << std::endl;
667     ObjMesh::Group group1 = getGroup(*nameItr); // retrieve group named *nameItr, and store it into "group"
668     std::cout << "Iterating through group faces..." << std::endl;
669     for( unsigned int iFace = 0; iFace < group1.getNumFaces(); iFace++ )
670     {
671       ObjMesh::Face face = group1.getFace(iFace); // get face number iFace
672       if (face.getNumVertices() == 3)
673         std::cout << "  found triangle ";
674       else if (face.getNumVertices() == 4)
675         std::cout << "  found quadrilateral ";
676       else
677         std::cout << "  found " << face.getNumVertices() << "-gon ";
678 
679       // Since the vertex positions are unique within the files, we can
680       // use these to cross-index the polygons.
681       for( unsigned int iVertex = 0; iVertex < face.getNumVertices(); iVertex++ )
682       {
683         if ( iVertex != 0 )
684           std::cout << " -> ";
685         std::cout << face.getVertex(iVertex).getPositionIndex(); // print out integer indices of the vertices
686       }
687       std::cout << std::endl;
688 
689       // Now we will retrieve positions, normals, and texture coordinates of the
690       // files by indexing into the global vertex namespace.
691       for( unsigned int iVertex = 0; iVertex < face.getNumVertices(); iVertex++ )
692       {
693         ObjMesh::Vertex vertex = face.getVertex(iVertex);
694         std::cout << "    vertex " << iVertex << "; " << std::endl;
695         std::cout << "      position = " << getPosition(vertex.getPositionIndex()) << ";" << std::endl;
696         if (vertex.hasNormalIndex())
697           std::cout << "      normal = " << getNormal(vertex.getNormalIndex()) << ";" << std::endl;
698         if (vertex.hasTextureCoordinateIndex())
699           std::cout << "      texture coordinate = " << getTextureCoordinate(vertex.getTextureCoordinateIndex()) << ";" << std::endl;
700       }
701     }
702   }
703 }
704 
isTriangularMesh() const705 bool ObjMesh::isTriangularMesh() const
706 {
707   for(unsigned int i=0; i < groups.size(); i++) // over all groups
708     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
709     {
710       if (groups[i].getFace(j).getNumVertices() != 3)
711         return false;
712     }
713   return true;
714 }
715 
isQuadrilateralMesh() const716 bool ObjMesh::isQuadrilateralMesh() const
717 {
718   for(unsigned int i=0; i < groups.size(); i++) // over all groups
719     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
720     {
721       if (groups[i].getFace(j).getNumVertices() != 4)
722         return false;
723     }
724   return true;
725 }
726 
computeMaxFaceDegree() const727 unsigned int ObjMesh::computeMaxFaceDegree() const
728 {
729   unsigned int maxDegree = 0;
730   for(unsigned int i=0; i < groups.size(); i++) // over all groups
731     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
732     {
733       const Face * face = groups[i].getFaceHandle(j);
734       if (face->getNumVertices() > maxDegree)
735         maxDegree = face->getNumVertices();
736     }
737 
738   return maxDegree;
739 }
740 
triangulate()741 void ObjMesh::triangulate()
742 {
743   for(unsigned int i=0; i < groups.size(); i++) // over all groups
744     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
745     {
746       Face * face = (Face*) groups[i].getFaceHandle(j);
747       if (face->getNumVertices() < 3)
748       {
749         printf("Warning: encountered a face with fewer than 3 vertices.\n");
750       }
751 
752       unsigned int faceDegree = face->getNumVertices();
753 
754       if (faceDegree > 3)
755       {
756         // triangulate the face
757 
758         // get the vertices:
759         vector<Vertex> vertices;
760         for(unsigned int k=0; k<face->getNumVertices(); k++)
761           vertices.push_back(face->getVertex(k));
762 
763         Face newFace;
764         newFace.addVertex(vertices[0]);
765         newFace.addVertex(vertices[1]);
766         newFace.addVertex(vertices[2]);
767 
768         // overwrite old face
769         *face = newFace;
770 
771         for(unsigned int k=2; k<faceDegree-1; k++)
772         {
773           // tesselate the remainder of the old face
774           Face newFace;
775           newFace.addVertex(vertices[0]);
776           newFace.addVertex(vertices[k]);
777           newFace.addVertex(vertices[k+1]);
778           groups[i].addFace(newFace);
779         }
780       }
781     }
782 }
783 
computeBoundingBox()784 void ObjMesh::computeBoundingBox()
785 {
786   bmin = Vec3d(DBL_MAX, DBL_MAX, DBL_MAX);
787   bmax = Vec3d(-DBL_MAX, -DBL_MAX, -DBL_MAX);
788 
789   for(unsigned int i=0; i < vertexPositions.size(); i++) // over all vertices
790   {
791     Vec3d p = vertexPositions[i];
792 
793     if (p[0] < bmin[0])
794       bmin[0] = p[0];
795     if (p[0] > bmax[0])
796       bmax[0] = p[0];
797 
798     if (p[1] < bmin[1])
799       bmin[1] = p[1];
800     if (p[1] > bmax[1])
801       bmax[1] = p[1];
802 
803     if (p[2] < bmin[2])
804       bmin[2] = p[2];
805     if (p[2] > bmax[2])
806       bmax[2] = p[2];
807   }
808 
809   center = 0.5 * (bmin + bmax);
810   cubeHalf = 0.5 * (bmax - bmin);
811   diameter = len(bmax - bmin);
812 }
813 
getBoundingBox(double expansionRatio,Vec3d * bmin_,Vec3d * bmax_) const814 void ObjMesh::getBoundingBox(double expansionRatio, Vec3d * bmin_, Vec3d * bmax_) const
815 {
816   *bmin_ = center - expansionRatio * cubeHalf;
817   *bmax_ = center + expansionRatio * cubeHalf;
818 }
819 
getCubicBoundingBox(double expansionRatio,Vec3d * bmin_,Vec3d * bmax_) const820 void ObjMesh::getCubicBoundingBox(double expansionRatio, Vec3d * bmin_, Vec3d * bmax_) const
821 {
822   double maxHalf = cubeHalf[0];
823 
824   if (cubeHalf[1] > maxHalf)
825     maxHalf = cubeHalf[1];
826 
827   if (cubeHalf[2] > maxHalf)
828     maxHalf = cubeHalf[2];
829 
830   Vec3d cubeHalfCube = Vec3d(maxHalf, maxHalf, maxHalf);
831 
832   *bmin_ = center - expansionRatio * cubeHalfCube;
833   *bmax_ = center + expansionRatio * cubeHalfCube;
834 }
835 
getDiameter() const836 double ObjMesh::getDiameter() const
837 {
838   return diameter;
839 }
840 
saveObjMeshesToBinary(const std::string & filename,int numObjMeshes,ObjMesh ** objMeshes,int * saveObjMeshesFlag,int outputMaterials,int verbose)841 int ObjMesh::saveObjMeshesToBinary(const std::string & filename, int numObjMeshes, ObjMesh ** objMeshes, int * saveObjMeshesFlag, int outputMaterials, int verbose)
842 {
843   FILE * output = fopen(filename.c_str(), "wb");
844   if (output == NULL)
845   {
846     printf("Error in ObjMesh::saveToBinary: cannot open %s to write.\n", filename.c_str());
847     return 1;
848   }
849 
850   unsigned int * bytesWritten = (unsigned int*) calloc (numObjMeshes, sizeof(unsigned int));
851 
852   // count the number of bytes written to the file for every obj mesh
853   for(int i=0; i<numObjMeshes; i++)
854   {
855     if (saveObjMeshesFlag[i] == 0)
856       continue;
857 
858     bool countBytesOnly = true;
859     objMeshes[i]->saveToBinary(NULL, outputMaterials, &bytesWritten[i], countBytesOnly);
860   }
861 
862   if (verbose)
863   {
864     printf("number of bytes for each obj mesh: \n");
865     for(int i=0; i<numObjMeshes; i++)
866       printf("%u, ", bytesWritten[i]);
867     printf("\n");
868   }
869 
870   // write the header to the file
871   fwrite(&numObjMeshes, sizeof(int), 1, output);
872   fwrite(bytesWritten, sizeof(unsigned int), numObjMeshes, output);
873 
874   // write the obj meshes to the file
875   for(int i=0; i<numObjMeshes; i++)
876   {
877     if (saveObjMeshesFlag[i] == 0)
878       continue;
879 
880     bool countBytesOnly = false;
881     objMeshes[i]->saveToBinary(output, outputMaterials, &bytesWritten[i], countBytesOnly, verbose);
882   }
883 
884   free(bytesWritten);
885   fclose(output);
886 
887   return 0;
888 }
889 
saveToBinary(const std::string & filename_,int outputMaterials,int verbose) const890 int ObjMesh::saveToBinary(const std::string & filename_, int outputMaterials, int verbose) const
891 {
892   FILE * fout = fopen(filename_.c_str(), "wb");
893   if (fout == NULL)
894   {
895     printf("Error in ObjMesh::saveToBinary: cannot open file %s to write.\n", filename_.c_str());
896   }
897   bool countBytesOnly = false;
898   int code = saveToBinary(fout, outputMaterials, NULL, countBytesOnly, verbose);
899   fclose(fout);
900   return code;
901 }
902 
903 // return:
904 // 0 = succeeded
905 // 1 = failed
saveToBinary(FILE * binaryOutputStream,int outputMaterials,unsigned int * bytesWritten,bool countBytesOnly,int verbose) const906 int ObjMesh::saveToBinary(FILE * binaryOutputStream, int outputMaterials, unsigned int * bytesWritten, bool countBytesOnly, int verbose) const
907 {
908   // first pass: count the total number of bytes to be written to the file
909   // second pass: do the actual writing
910   enum {COUNT_BYTES, WRITE_TO_DISK, NUM_PASSES};
911   int totalPasses = NUM_PASSES;
912   if (countBytesOnly)
913     totalPasses = WRITE_TO_DISK;
914 
915   unsigned int totalBytes = 0;
916   for(int pass = 0; pass < totalPasses; pass++)
917   {
918     unsigned int bytes = 0;
919     unsigned int items;
920 
921     // the header will be the number of bytes (including the totalbytes itself)
922     items = 1;
923     if (pass == WRITE_TO_DISK)
924       items = fwrite(&totalBytes, sizeof(unsigned int), 1, binaryOutputStream);
925     if (items != 1)
926       return 1;
927     bytes += items * sizeof(unsigned int);
928 
929     // save the flag that determines whether to output materials or not
930     items = 1;
931     if (pass == WRITE_TO_DISK)
932       items = fwrite(&outputMaterials, sizeof(int), 1, binaryOutputStream);
933     if (items != 1)
934       return 1;
935     bytes += items * sizeof(int);
936 
937     // save materials, if necessary
938     if (outputMaterials)
939     {
940       unsigned int numObjMaterials = getNumMaterials();
941 
942       // save the number of materials
943       items = 1;
944       if (pass == WRITE_TO_DISK)
945         items = fwrite(&numObjMaterials, sizeof(unsigned int), 1, binaryOutputStream);
946       if (items != 1)
947         return 1;
948       bytes += items * sizeof(unsigned int);
949 
950       // save the material names
951       for (unsigned int materialIndex=0; materialIndex < numObjMaterials; materialIndex++)
952       {
953         std::string materialNameString = materials[materialIndex].getName();
954         char * materialName = (char *)(materialNameString.c_str());
955         unsigned int strLength = strlen(materialName);
956         items = 1;
957         if (pass == WRITE_TO_DISK)
958           items = fwrite(&strLength, sizeof(unsigned int), 1, binaryOutputStream);
959         if (items != 1)
960           return 1;
961         bytes += items * sizeof(unsigned int);
962 
963         items = strLength;
964         if (pass == WRITE_TO_DISK)
965           items = fwrite(materialName, sizeof(char), strLength, binaryOutputStream);
966         if (items != strLength)
967           return 1;
968         bytes += items * sizeof(char);
969       }
970 
971       // Ka, Kd, Ks, each of which has 3 doubles, plus Ns, a double
972       // So there are 10 doubles for every material
973       enum {KA_0, KA_1, KA_3, KD_0, KD_1, KD_2, KS_0, KS_1, KS_2, NS, NUM_MATERIAL_PROPERTIES};
974       double * materialProperties = (double *) malloc(sizeof(double) * NUM_MATERIAL_PROPERTIES * numObjMaterials);
975 
976       std::vector<unsigned int> materialHasTextureImageKd;
977 
978       for (unsigned int materialIndex=0; materialIndex < numObjMaterials; materialIndex++)
979       {
980         unsigned int offset = materialIndex * NUM_MATERIAL_PROPERTIES;
981 
982         Vec3d Ka = materials[materialIndex].getKa();
983         Ka.convertToArray(&materialProperties[offset]);
984 
985         Vec3d Kd = materials[materialIndex].getKd();
986         Kd.convertToArray(&materialProperties[offset + 3]);
987 
988         Vec3d Ks = materials[materialIndex].getKs();
989         Ks.convertToArray(&materialProperties[offset + 6]);
990 
991         materialProperties[offset + 9] = materials[materialIndex].getShininess() * 1000.0 / 128.0;
992 
993         if (materials[materialIndex].hasTextureFilename())
994           materialHasTextureImageKd.push_back(materialIndex);
995       }  // for materialIndex
996 
997       // save the material properties
998       items = NUM_MATERIAL_PROPERTIES * numObjMaterials;
999       if (pass == WRITE_TO_DISK)
1000         items = fwrite(materialProperties, sizeof(double), NUM_MATERIAL_PROPERTIES * numObjMaterials, binaryOutputStream);
1001       if (items !=  NUM_MATERIAL_PROPERTIES * numObjMaterials)
1002         return 1;
1003       bytes += items * sizeof(double);
1004 
1005       free(materialProperties);
1006 
1007       // save the number of materials which have map_Kd texture images
1008       unsigned int vectorSize = materialHasTextureImageKd.size();
1009       items = 1;
1010       if (pass == WRITE_TO_DISK)
1011         items = fwrite(&vectorSize, sizeof(unsigned int), 1, binaryOutputStream);
1012       if (items != 1)
1013         return 1;
1014       bytes += items * sizeof(unsigned int);
1015 
1016       for(unsigned int materialIndex=0; materialIndex < vectorSize; materialIndex++)
1017       {
1018         // save the material ID
1019         unsigned int materialID = materialHasTextureImageKd[materialIndex];
1020         items = 1;
1021         if (pass == WRITE_TO_DISK)
1022           items = fwrite(&materialID, sizeof(unsigned int), 1, binaryOutputStream);
1023         if (items != 1)
1024           return 1;
1025         bytes += items * sizeof(unsigned int);
1026 
1027         // save the material image
1028         std::string textureFilenameString = materials[materialID].getTextureFilename();
1029         char * textureFilename = (char *)(textureFilenameString.c_str());
1030         unsigned int strLength = strlen(textureFilename);
1031         items = 1;
1032         if (pass == WRITE_TO_DISK)
1033           items = fwrite(&strLength, sizeof(unsigned int), 1, binaryOutputStream);
1034         if (items != 1)
1035           return 1;
1036         bytes += items * sizeof(unsigned int);
1037 
1038         items = strLength;
1039         if (pass == WRITE_TO_DISK)
1040           items = fwrite(textureFilename, sizeof(char), strLength, binaryOutputStream);
1041         if (items != strLength)
1042           return 1;
1043         bytes += items * sizeof(char);
1044       }  // for materialIndex
1045     }  // if outputMaterials
1046 
1047     // save the number of vertices
1048     unsigned int numVertices = vertexPositions.size();
1049     items = 1;
1050     if (pass == WRITE_TO_DISK)
1051       items = fwrite(&numVertices, sizeof(unsigned int), 1, binaryOutputStream);
1052     if (items != 1)
1053       return 1;
1054     bytes += items * sizeof(unsigned int);
1055 
1056     // save vertices
1057     for (unsigned int vertexIndex=0; vertexIndex < numVertices; vertexIndex++)
1058     {
1059       Vec3d pos = getPosition(vertexIndex);
1060       double temp[3];
1061       pos.convertToArray(temp);
1062 
1063       items = 3;
1064       if (pass == WRITE_TO_DISK)
1065         items = fwrite(temp, sizeof(double), 3, binaryOutputStream);
1066       if (items != 3)
1067         return 1;
1068       bytes += items * sizeof(double);
1069     }
1070 
1071     // save the number of texture coordinates
1072     unsigned int numTexCoordinates = textureCoordinates.size();
1073     items = 1;
1074     if (pass == WRITE_TO_DISK)
1075       items = fwrite(&numTexCoordinates, sizeof(unsigned int), 1, binaryOutputStream);
1076     if (items != 1)
1077       return 1;
1078     bytes += items * sizeof(unsigned int);
1079 
1080     // save texture coordinates
1081     for (unsigned int texCoordinateIndex=0; texCoordinateIndex < numTexCoordinates; texCoordinateIndex++)
1082     {
1083       Vec3d texCoord_ = getTextureCoordinate(texCoordinateIndex);
1084       double temp[3];
1085       texCoord_.convertToArray(temp);
1086 
1087       items = 3;
1088       if (pass == WRITE_TO_DISK)
1089         items = fwrite(temp, sizeof(double), 3, binaryOutputStream);
1090       if (items != 3)
1091         return 1;
1092       bytes += items * sizeof(double);
1093     }
1094 
1095     // save the number of normals
1096     unsigned int numNormals = normals.size();
1097     items = 1;
1098     if (pass == WRITE_TO_DISK)
1099       items = fwrite(&numNormals, sizeof(unsigned int), 1, binaryOutputStream);
1100     if (items != 1)
1101       return 1;
1102     bytes += items * sizeof(unsigned int);
1103 
1104     // save normals
1105     for (unsigned int normalIndex=0; normalIndex < numNormals; normalIndex++)
1106     {
1107       Vec3d normal_ = getNormal(normalIndex);
1108       double temp[3];
1109       normal_.convertToArray(temp);
1110       items = 3;
1111       if (pass == WRITE_TO_DISK)
1112         items = fwrite(temp, sizeof(double), 3, binaryOutputStream);
1113       if (items != 3)
1114         return 1;
1115       bytes += items * sizeof(double);
1116     }
1117 
1118     // save the number of groups
1119     unsigned int numGroups = groups.size();
1120     items = 1;
1121     if (pass == WRITE_TO_DISK)
1122       items = fwrite(&numGroups, sizeof(unsigned int), 1, binaryOutputStream);
1123     if (items != 1)
1124       return 1;
1125     bytes += items * sizeof(unsigned int);
1126 
1127     // save groups and faces
1128     for(unsigned int groupIndex=0; groupIndex < groups.size(); groupIndex++)
1129     {
1130       // save group name
1131       std::string groupNameString = groups[groupIndex].getName();
1132       char * groupNameStr = (char *)(groupNameString.c_str());
1133       unsigned int strLength = strlen(groupNameStr);
1134       items = 1;
1135       if (pass == WRITE_TO_DISK)
1136         items = fwrite(&strLength, sizeof(unsigned int), 1, binaryOutputStream);
1137       if (items != 1)
1138         return 1;
1139       bytes += items * sizeof(unsigned int);
1140 
1141 
1142       items = strLength;
1143       if (pass == WRITE_TO_DISK)
1144         items = fwrite(groupNameStr, sizeof(char), strLength, binaryOutputStream);
1145       if (items != strLength)
1146         return 1;
1147       bytes += items * sizeof(char);
1148 
1149       // save the material index of the current group
1150       if (outputMaterials)
1151       {
1152         unsigned int materialIndex = groups[groupIndex].getMaterialIndex();
1153         items = 1;
1154         if (pass == WRITE_TO_DISK)
1155           items = fwrite(&materialIndex, sizeof(unsigned int), 1, binaryOutputStream);
1156         if (items != 1)
1157           return 1;
1158         bytes += items * sizeof(unsigned int);
1159       }
1160 
1161       // save the number of faces of the current group
1162       unsigned int numFaces = groups[groupIndex].getNumFaces();
1163       items = 1;
1164       if (pass == WRITE_TO_DISK)
1165         items = fwrite(&numFaces, sizeof(unsigned int), 1, binaryOutputStream);
1166       if (items != 1)
1167         return 1;
1168       bytes += items * sizeof(unsigned int);
1169 
1170       // save the number of vertices of each face in current group
1171       unsigned int totalFaceVertices = 0;
1172       unsigned int * numFaceVerticesArray = (unsigned int *) malloc (sizeof(unsigned int) * numFaces);
1173       for (unsigned int faceIndex=0; faceIndex < numFaces; faceIndex++)
1174       {
1175         Face face = groups[groupIndex].getFace(faceIndex); // get face whose number is faceIndex
1176         numFaceVerticesArray[faceIndex] = face.getNumVertices();
1177         totalFaceVertices += numFaceVerticesArray[faceIndex];
1178       }
1179       items = numFaces;
1180       if (pass == WRITE_TO_DISK)
1181         items = fwrite(numFaceVerticesArray, sizeof(unsigned int), numFaces, binaryOutputStream);
1182       if (items != numFaces)
1183         return 1;
1184       bytes += items * sizeof(unsigned int);
1185       free(numFaceVerticesArray);
1186 
1187       unsigned int * verticesArray = (unsigned int *) malloc (sizeof(unsigned int) * totalFaceVertices);
1188       // because the output is 1-indexed, we can use 0 to represent "no such property"
1189       unsigned int * textureCoordinateIndexArray = (unsigned *) malloc (sizeof(unsigned int) * totalFaceVertices);
1190       unsigned int * normalIndexArray = (unsigned *) malloc (sizeof(unsigned int) * totalFaceVertices);
1191       memset(textureCoordinateIndexArray, 0, sizeof(unsigned int) * totalFaceVertices);
1192       memset(normalIndexArray, 0, sizeof(unsigned int) * totalFaceVertices);
1193       unsigned int vertexCount = 0;
1194       for (unsigned int faceIndex=0; faceIndex < numFaces; faceIndex++)
1195       {
1196         Face face = groups[groupIndex].getFace(faceIndex); // get the face whose number is faceIndex
1197         unsigned int numFaceVertices = face.getNumVertices();
1198 
1199         // current face
1200         for (unsigned int vertexIndex=0; vertexIndex < numFaceVertices; vertexIndex++)
1201         {
1202           Vertex vertex = face.getVertex(vertexIndex);
1203           verticesArray[vertexCount] = vertex.getPositionIndex() + 1; // 1-indexed
1204 
1205           if (vertex.hasTextureCoordinateIndex())
1206             textureCoordinateIndexArray[vertexCount] = vertex.getTextureCoordinateIndex() + 1; // 1-indexed
1207 
1208           if (vertex.hasNormalIndex())
1209             normalIndexArray[vertexCount] = vertex.getNormalIndex() + 1; // 1-indexed
1210 
1211           vertexCount++;
1212         }  // for vertexIndex
1213       }  // for faceIndex
1214 
1215       // save the vertices of each face
1216       items = totalFaceVertices;
1217       if (pass == WRITE_TO_DISK)
1218         items = fwrite(verticesArray, sizeof(unsigned int), totalFaceVertices, binaryOutputStream);
1219       if (items != totalFaceVertices)
1220         return 1;
1221       bytes += items * sizeof(unsigned int);
1222 
1223       items = totalFaceVertices;
1224       if (pass == WRITE_TO_DISK)
1225         items = fwrite(textureCoordinateIndexArray, sizeof(unsigned int), totalFaceVertices, binaryOutputStream);
1226       if (items != totalFaceVertices)
1227         return 1;
1228       bytes += items * sizeof(unsigned int);
1229 
1230       items = totalFaceVertices;
1231       if (pass == WRITE_TO_DISK)
1232         items = fwrite(normalIndexArray, sizeof(unsigned int), totalFaceVertices, binaryOutputStream);
1233       if (items != totalFaceVertices)
1234         return 1;
1235       bytes += items * sizeof(unsigned int);
1236 
1237       free(verticesArray);
1238       free(textureCoordinateIndexArray);
1239       free(normalIndexArray);
1240 
1241     }  // for groupIndex
1242 
1243     if (pass == COUNT_BYTES)
1244       totalBytes = bytes;
1245   } // for pass
1246 
1247   if (bytesWritten != NULL)
1248     *bytesWritten = totalBytes;
1249   else
1250     if (countBytesOnly)
1251     {
1252       printf("Warning in ObjMesh::saveToBinary: 'bytesWritten' is set to NULL while 'countBytesOnly' is set true.\n");
1253       return 2;
1254     }
1255 
1256   return 0;
1257 }
1258 
save(const string & filename,int outputMaterials,fileFormatType fileFormat,int verbose) const1259 void ObjMesh::save(const string & filename, int outputMaterials, fileFormatType fileFormat, int verbose) const
1260 {
1261   switch(fileFormat)
1262   {
1263     case ASCII:
1264       saveToAscii(filename, outputMaterials, verbose);
1265       break;
1266 
1267     case BINARY:
1268         saveToBinary(filename, outputMaterials, verbose);
1269       break;
1270 
1271     default:
1272       printf("Error in ObjMesh::save: file format is unknown.\n");
1273       break;
1274   }
1275 }
1276 
saveToAscii(const string & filename,int outputMaterials,int verbose) const1277 void ObjMesh::saveToAscii(const string & filename, int outputMaterials, int verbose) const
1278 {
1279   string materialFilename;
1280   string materialFilenameLocal;
1281 
1282   if (outputMaterials && (getNumMaterials() == 0))
1283     outputMaterials = 0;
1284 
1285   if (outputMaterials)
1286   {
1287     materialFilename = filename + ".mtl";
1288     // remove directory part from materialFilename
1289     char * materialFilenameTempC = (char*)materialFilename.c_str();
1290     char * beginString = materialFilenameTempC;
1291     // seek for last '/'
1292     for(unsigned int i=0; i< strlen(materialFilenameTempC); i++)
1293       if ((materialFilenameTempC[i] == '/') || (materialFilenameTempC[i] == '\\'))
1294         beginString = &materialFilenameTempC[i+1];
1295 
1296     materialFilenameLocal = string(beginString);
1297   }
1298 
1299   if (verbose >= 1)
1300   {
1301     cout << "Writing obj to file " << filename << " ." << endl;
1302     if (outputMaterials)
1303       cout << "Writing materials to " << materialFilename << " ." << endl;
1304     else
1305       cout << "No material output." << endl;
1306   }
1307 
1308   // open file
1309   ofstream fout(filename.c_str());
1310 
1311   if (!fout)
1312   {
1313     cout << "Error: could not write to file " << filename << endl;
1314     return;
1315   }
1316 
1317   // count total number of triangles
1318   int numTriangles = 0;
1319   for(unsigned int i = 0; i < groups.size(); i++ )
1320     numTriangles += groups[i].getNumFaces();
1321 
1322   fout << "# Generated by the ObjMesh class" << endl;
1323   fout << "# Number of vertices: " << vertexPositions.size() << endl;
1324   fout << "# Number of texture coordinates: " << textureCoordinates.size() << endl;
1325   fout << "# Number of normals: " << normals.size() << endl;
1326   fout << "# Number of faces: " << numTriangles << endl;
1327   fout << "# Number of groups: " << groups.size() << endl;
1328 
1329   if (outputMaterials)
1330     fout << endl << "mtllib " << materialFilenameLocal << endl << endl;
1331 
1332   // vertices...
1333   for (unsigned int i=0; i < vertexPositions.size(); i++)
1334   {
1335     Vec3d pos = getPosition(i);
1336     fout << "v " << pos[0] << " " << pos[1] << " " << pos[2] << endl;
1337   }
1338 
1339   // texture coordinates...
1340   for (unsigned int i=0; i < textureCoordinates.size(); i++)
1341   {
1342     Vec3d texCoord_ = getTextureCoordinate(i);
1343     fout << "vt " << texCoord_[0] << " " << texCoord_[1] << endl;
1344   }
1345 
1346   // normals...
1347   for (unsigned int i=0; i < normals.size(); i++)
1348   {
1349     Vec3d normal_ = getNormal(i);
1350     fout << "vn " << normal_[0] << " " << normal_[1] << " " << normal_[2] << endl;
1351   }
1352 
1353   // groups and faces...
1354   for(unsigned int i = 0; i < groups.size(); i++ )
1355   {
1356     fout << "g " << groups[i].getName() << endl;
1357     if (outputMaterials)
1358       fout << "usemtl " << materials[groups[i].getMaterialIndex()].getName() << endl;
1359 
1360     for( unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
1361     {
1362       Face face = groups[i].getFace(iFace); // get face whose number is iFace
1363 
1364       fout << "f";
1365 
1366       if (face.getNumVertices() < 3)
1367         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
1368 
1369       for ( unsigned int iVertex = 0; iVertex < face.getNumVertices(); iVertex++ )
1370       {
1371         Vertex vertex = face.getVertex(iVertex);
1372         fout << " " << int(vertex.getPositionIndex() + 1);
1373 
1374         if (vertex.hasTextureCoordinateIndex() || vertex.hasNormalIndex())
1375         {
1376           fout << "/";
1377 
1378           if (vertex.hasTextureCoordinateIndex())
1379             fout << int(vertex.getTextureCoordinateIndex() + 1);
1380 
1381           if (vertex.hasNormalIndex())
1382           {
1383             fout << "/";
1384 
1385             if (vertex.hasNormalIndex())
1386               fout << int(vertex.getNormalIndex() + 1);
1387           }
1388         }
1389       }
1390 
1391       fout << endl;
1392     }
1393   }
1394 
1395   fout.close();
1396 
1397   if (outputMaterials)
1398   {
1399     ofstream fout(materialFilename.c_str());
1400 
1401     if (!fout)
1402     {
1403       cout << "Error: could not write to file " << materialFilename << endl;
1404       return;
1405     }
1406 
1407     for(unsigned int i=0; i< getNumMaterials(); i++)
1408     {
1409       fout << "newmtl " << materials[i].getName() << endl;
1410       fout << "illum 4" << endl;
1411 
1412       Vec3d Ka = materials[i].getKa();
1413       Vec3d Kd = materials[i].getKd();
1414       Vec3d Ks = materials[i].getKs();
1415       double shininess = materials[i].getShininess() * 1000.0 / 128.0;
1416 
1417       fout << "Ka " << Ka[0] << " " << Ka[1] << " " << Ka[2] << endl;
1418       fout << "Kd " << Kd[0] << " " << Kd[1] << " " << Kd[2] << endl;
1419       fout << "Ks " << Ks[0] << " " << Ks[1] << " " << Ks[2] << endl;
1420       fout << "Ns " << shininess << endl;
1421       if (materials[i].hasTextureFilename())
1422       {
1423         std::string textureFilename = materials[i].getTextureFilename();
1424         fout << "map_Kd " << textureFilename << endl;
1425       }
1426       fout << endl;
1427     }
1428 
1429     fout.close();
1430   }
1431 }
1432 
saveToAbq(const string & filename) const1433 void ObjMesh::saveToAbq(const string & filename) const
1434 {
1435   cout << "Writing obj to abq file " << filename << " ." << endl;
1436 
1437   if (computeMaxFaceDegree() > 4)
1438   {
1439     cout << "Error: mesh has faces with more than 4 vertices." << endl;
1440     return;
1441   }
1442 
1443   vector<double> surfaceAreas ;
1444   computeSurfaceAreaPerGroup(surfaceAreas);
1445   for(unsigned int i=0; i<surfaceAreas.size(); i++)
1446   {
1447     printf("Surface area of group %d: %G\n",i,surfaceAreas[i]);
1448   }
1449 
1450   // open file
1451   FILE * fout = fopen(filename.c_str(),"w");
1452 
1453   if (!fout)
1454   {
1455     cout << "Error: could not write to file " << filename << endl;
1456     return;
1457   }
1458 
1459   // vertices...
1460   fprintf(fout, "*NODE\n");
1461   for (unsigned int i=0; i < vertexPositions.size(); i++)
1462   {
1463     Vec3d pos = getPosition(i);
1464     fprintf(fout,"   %d,   %.15f,   %.15f,   %.15f\n",i+1,pos[0],pos[1],pos[2]);
1465   }
1466 
1467   // groups and faces...
1468   int faceCount=0;
1469   vector<int> startIndex; // for generation of element sets
1470   vector<int> endIndex;
1471   vector<std::string> groupNames;
1472   for(unsigned int i = 0; i < groups.size(); i++ )
1473   {
1474     printf("Num faces in group %d: %d\n",i+1,(int)(groups[i].getNumFaces()));
1475 
1476     if (groups[i].getNumFaces() == 0)
1477       continue;
1478 
1479     startIndex.push_back(faceCount+1);
1480     groupNames.push_back(groups[i].getName());
1481 
1482     // two passes: triangles and quads
1483     for(unsigned int numFaceVertices=3; numFaceVertices<=4; numFaceVertices++)
1484     {
1485       bool firstElement = true;
1486       for( unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
1487       {
1488         Face face = groups[i].getFace(iFace); // get face whose number is iFace
1489 
1490         if (face.getNumVertices() != numFaceVertices)
1491           continue;
1492 
1493         if (firstElement)
1494         {
1495           fprintf(fout,"*ELEMENT, TYPE=S%d\n",numFaceVertices);
1496           firstElement = false;
1497         }
1498 
1499         faceCount++;
1500         fprintf(fout,"%d   ",faceCount);
1501 
1502         for ( unsigned int iVertex = 0; iVertex < face.getNumVertices(); iVertex++ )
1503         {
1504           Vertex vertex = face.getVertex(iVertex);
1505           fprintf(fout,",%d",vertex.getPositionIndex() + 1);
1506         }
1507 
1508         fprintf(fout,"\n");
1509       }
1510     }
1511 
1512     endIndex.push_back(faceCount);
1513   }
1514 
1515   for(unsigned int i=0; i<startIndex.size(); i++)
1516   {
1517     fprintf(fout,"*ELSET,ELSET=%s,GENERATE\n",groupNames[i].c_str());
1518     fprintf(fout,"  %d,%d\n",startIndex[i],endIndex[i]);
1519   }
1520 
1521   fprintf(fout,"*ELSET,ELSET=EALL,GENERATE\n");
1522   fprintf(fout,"  1,%d\n",faceCount);
1523 
1524   fclose(fout);
1525 }
1526 
saveToStl(const string & filename) const1527 void ObjMesh::saveToStl(const string & filename) const
1528 {
1529   cout << "Writing obj to STL file " << filename << " ." << endl;
1530 
1531   // open file
1532   ofstream fout(filename.c_str());
1533 
1534   if (!fout)
1535   {
1536     cout << "Error: could not write to file " << filename << endl;
1537     return;
1538   }
1539 
1540   // check if mesh is triangular
1541   if (!isTriangularMesh())
1542   {
1543     cout << "Error: input mesh is not triangular. " << endl;
1544     return;
1545   }
1546 
1547   // count total number of triangles
1548   int numTriangles = 0;
1549   for(unsigned int i = 0; i < groups.size(); i++ )
1550     numTriangles += groups[i].getNumFaces();
1551 
1552   fout << "# Generated automatically by the ObjMesh class" << endl;
1553   fout << "# Number of vertices: " << vertexPositions.size() << endl;
1554   fout << "# Number of faces: " << numTriangles << endl;
1555 
1556 
1557   fout << "solid" << endl;
1558 
1559   // groups and faces...
1560   for(unsigned int i = 0; i < groups.size(); i++ )
1561   {
1562     for( unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
1563     {
1564       Face face = groups[i].getFace(iFace); // get face whose number is iFace
1565 
1566       if (face.getNumVertices() < 3)
1567         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
1568 
1569       fout << "  facet normal ";
1570 
1571       // get the face data
1572       Vertex v0 = face.getVertex(0);
1573       Vertex v1 = face.getVertex(1);
1574       Vertex v2 = face.getVertex(2);
1575 
1576       Vec3d p0 = getPosition(v0);
1577       Vec3d p1 = getPosition(v1);
1578       Vec3d p2 = getPosition(v2);
1579 
1580       // compute the face normal
1581       Vec3d normal = norm(cross(p1-p0,p2-p0));
1582 
1583       fout << normal[0] << " " << normal[1] << " " << normal[2] << endl;
1584 
1585       fout << "    outer loop" << endl;
1586 
1587       fout << "      vertex " << p0[0] << " " << p0[1] << " " << p0[2] << endl;
1588       fout << "      vertex " << p1[0] << " " << p1[1] << " " << p1[2] << endl;
1589       fout << "      vertex " << p2[0] << " " << p2[1] << " " << p2[2] << endl;
1590 
1591       fout << "    endloop" << endl;
1592       fout << "  endfacet" << endl;
1593 
1594     }
1595   }
1596 
1597   fout << "endsolid" << endl;
1598 
1599   fout.close();
1600 }
1601 
saveToSmesh(const std::string & filename) const1602 void ObjMesh::saveToSmesh(const std::string & filename) const
1603 {
1604   cout << "Writing obj to smesh file " << filename << " ." << endl;
1605 
1606   // open file
1607   ofstream fout(filename.c_str());
1608 
1609   if (!fout)
1610   {
1611     cout << "Error: could not write to file " << filename << endl;
1612     return;
1613   }
1614 
1615   fout << getNumVertices() << " 3 0 0" << endl;
1616 
1617   // write out vertices
1618   for(unsigned int i=0; i< getNumVertices(); i++)
1619   {
1620     Vec3d p = getPosition(i);
1621     fout << i+1 << " " << p[0] << " " << p[1] << " " << p[2] << endl;
1622   }
1623 
1624   // count total number of faces
1625   int numFaces = 0;
1626   for(unsigned int i = 0; i < groups.size(); i++ )
1627     numFaces += groups[i].getNumFaces();
1628 
1629   fout << endl;
1630   fout << numFaces << " 0" << endl;
1631 
1632   // groups and faces...
1633   for(unsigned int i = 0; i < groups.size(); i++ )
1634     for( unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
1635     {
1636       Face face = groups[i].getFace(iFace); // get face whose number is iFace
1637       fout << face.getNumVertices();
1638       for(unsigned int j=0; j<face.getNumVertices(); j++)
1639       {
1640         // get the face data
1641         Vertex v = face.getVertex(j);
1642         fout << " " << v.getPositionIndex() + 1;
1643       }
1644       fout << endl;
1645     }
1646 
1647   fout << endl;
1648   fout << "0" << endl;
1649   fout << "0" << endl;
1650   fout.close();
1651 }
1652 
splitIntoConnectedComponents(int withinGroupsOnly,int verbose) const1653 ObjMesh * ObjMesh::splitIntoConnectedComponents(int withinGroupsOnly, int verbose) const
1654 {
1655   // withinGroupsOnly:
1656   // 0: off (global split; may fuse texture coordinates)
1657   // 1: intersect global connected components with groups
1658   // 2: break each group into connected components, regardless of the rest of the mesh
1659 
1660   vector<DisjointSet*> dset;
1661   if (withinGroupsOnly == 2)
1662   {
1663     for(unsigned int i=0; i < groups.size(); i++) // over all groups
1664     {
1665       DisjointSet * groupDisjointSet = new DisjointSet(getNumVertices());
1666       groupDisjointSet->MakeSet();
1667       dset.push_back(groupDisjointSet);
1668     }
1669   }
1670   else
1671   {
1672     DisjointSet * globalDisjointSet = new DisjointSet(getNumVertices());
1673     globalDisjointSet->MakeSet();
1674     for(unsigned int i=0; i < groups.size(); i++) // over all groups
1675       dset.push_back(globalDisjointSet);
1676   }
1677 
1678   // build vertex connections
1679   for(unsigned int i=0; i < groups.size(); i++) // over all groups
1680   {
1681     // if (verbose == 0)
1682     // {
1683     //   printf("%d ", i);
1684     //   fflush(NULL);
1685     // }
1686 
1687     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
1688     {
1689       if (verbose)
1690       {
1691         if (j % 100 == 1)
1692           printf("Processing group %d / %d, face %d / %d.\n", i, (int)groups.size(), j, (int)groups[i].getNumFaces());
1693       }
1694       const Face * face = groups[i].getFaceHandle(j);
1695       int faceDegree = (int)face->getNumVertices();
1696       for(int vtx=0; vtx<faceDegree-1; vtx++)
1697       {
1698         int vertex = face->getVertex(vtx).getPositionIndex();
1699         int vertexNext = face->getVertex(vtx+1).getPositionIndex();
1700         dset[i]->UnionSet(vertex, vertexNext);
1701       }
1702     }
1703   }
1704   // if (verbose == 0)
1705   //   printf("\n");
1706 
1707   // determine group for every face
1708   int numOutputGroups = 0;
1709   vector<map<int, int> *> representatives;
1710   if (withinGroupsOnly == 2)
1711   {
1712     for(unsigned int i=0; i < groups.size(); i++) // over all groups
1713     {
1714       map<int, int> * groupMap = new map<int,int>();
1715       representatives.push_back(groupMap);
1716     }
1717   }
1718   else
1719   {
1720     map<int, int> * globalMap = new map<int,int>();
1721     for(unsigned int i=0; i < groups.size(); i++) // over all groups
1722       representatives.push_back(globalMap);
1723   }
1724 
1725   vector<vector<int> > faceGroup;
1726   for(unsigned int i=0; i < groups.size(); i++) // over all groups
1727   {
1728     // if (verbose == 0)
1729     // {
1730     //   printf("%d ", i);
1731     //   fflush(NULL);
1732     // }
1733 
1734     faceGroup.push_back(vector<int>());
1735     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
1736     {
1737       if (verbose)
1738       {
1739         if (j % 100 == 1)
1740           printf("Processing group %d / %d, face %d / %d.\n", i, (int)groups.size(), j, (int)groups[i].getNumFaces());
1741       }
1742       const Face * face = groups[i].getFaceHandle(j);
1743       int rep = dset[i]->FindSet(face->getVertex(0).getPositionIndex());
1744 
1745       map<int,int> :: iterator iter = representatives[i]->find(rep);
1746       int groupID;
1747       if (iter == representatives[i]->end())
1748       {
1749         groupID = numOutputGroups;
1750         representatives[i]->insert(make_pair(rep, numOutputGroups));
1751         numOutputGroups++;
1752       }
1753       else
1754         groupID = iter->second;
1755 
1756       faceGroup[i].push_back(groupID);
1757     }
1758   }
1759 
1760   // if (verbose == 0)
1761   //   printf("\n");
1762 
1763   // build output mesh
1764   ObjMesh * output = new ObjMesh();
1765 
1766   // output vertices
1767   for(unsigned int i=0; i<getNumVertices(); i++)
1768     output->addVertexPosition(getPosition(i));
1769 
1770   // output normals
1771   for(unsigned int i=0; i<getNumNormals(); i++)
1772     output->addVertexNormal(getNormal(i));
1773 
1774   // output texture coordinates
1775   for(unsigned int i=0; i<getNumTextureCoordinates(); i++)
1776     output->addTextureCoordinate(getTextureCoordinate(i));
1777 
1778   // output materials
1779   for(unsigned int i=0; i<getNumMaterials(); i++)
1780     output->addMaterial(getMaterial(i));
1781 
1782   // create output groups, taking into account potential splitting in case: withinGroupsOnly == 1
1783   int groupCount = 0;
1784   map <pair<int, int>, int> outputGroup;
1785   for(unsigned int i=0; i < groups.size(); i++) // over all groups
1786   {
1787     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
1788     {
1789       int groupID = faceGroup[i][j];
1790       if (withinGroupsOnly == 1)
1791       {
1792         if (outputGroup.find(make_pair(i, groupID)) == outputGroup.end())
1793         {
1794           outputGroup.insert(make_pair(make_pair(i, groupID), groupCount));
1795           groupCount++;
1796         }
1797       }
1798       else
1799       {
1800         outputGroup.insert(make_pair(make_pair(i, groupID), groupID));
1801       }
1802     }
1803   }
1804 
1805   if (withinGroupsOnly == 1)
1806     numOutputGroups = groupCount;
1807 
1808   // create groups
1809   for(int i=0; i<numOutputGroups; i++)
1810   {
1811     char s[96];
1812     sprintf(s, "group%05d", i);
1813     output->addGroup(string(s));
1814   }
1815 
1816   // add faces to groups
1817   for(unsigned int i=0; i < groups.size(); i++) // over all groups
1818   {
1819     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
1820     {
1821       const Face * face = groups[i].getFaceHandle(j);
1822       int connectedGroupID = faceGroup[i][j];
1823 
1824       map<pair<int,int>, int> :: iterator iter = outputGroup.find(make_pair(i, connectedGroupID));
1825       if (iter == outputGroup.end())
1826       {
1827         printf("Error: encountered unhandled (input group, connected group) case.\n");
1828       }
1829       int groupID = iter->second;
1830 
1831       output->addFaceToGroup(*face, groupID);
1832 
1833       // set the material for this group
1834       Group * outputGroupHandle = (Group*) output->getGroupHandle(groupID);
1835       outputGroupHandle->setMaterialIndex(groups[i].getMaterialIndex());
1836     }
1837   }
1838 
1839   output->computeBoundingBox();
1840 
1841   // de-allocate
1842   for(unsigned int i=0; i < groups.size(); i++) // over all groups
1843   {
1844     delete(representatives[i]);
1845     representatives[i] = NULL;
1846     delete(dset[i]);
1847     dset[i] = NULL;
1848 
1849     if (withinGroupsOnly != 2)
1850       break;
1851   }
1852 
1853   return output;
1854 }
1855 
clone(const std::vector<std::pair<int,int>> & groupsAndFaces,int removeIsolatedVertices) const1856 ObjMesh * ObjMesh::clone(const std::vector<std::pair<int, int> > & groupsAndFaces, int removeIsolatedVertices) const
1857 {
1858   ObjMesh * output = new ObjMesh();
1859   output->materials = materials;
1860   output->vertexPositions = vertexPositions;
1861   output->textureCoordinates = textureCoordinates;
1862   output->normals = normals;
1863 
1864   for(int i=0; i<(int)getNumGroups(); i++)
1865   {
1866     Group group(groups[i].getName(), groups[i].getMaterialIndex());
1867     output->addGroup(group);
1868   }
1869 
1870   for(std::vector<std::pair<int, int> > :: const_iterator iter = groupsAndFaces.begin(); iter != groupsAndFaces.end(); iter++)
1871     output->groups[iter->first].addFace(groups[iter->first].getFace(iter->second));
1872 
1873   if (removeIsolatedVertices)
1874     output->removeIsolatedVertices();
1875 
1876   return output;
1877 }
1878 
1879 // extracts the given group
extractGroup(unsigned int groupID,int keepOnlyUsedNormals,int keepOnlyUsedTextureCoordinates) const1880 ObjMesh * ObjMesh::extractGroup(unsigned int groupID, int keepOnlyUsedNormals, int keepOnlyUsedTextureCoordinates) const
1881 {
1882   map<int,int> oldToNewVertices;
1883   map<int,int> newToOldVertices;
1884 
1885   map<int,int> oldToNewNormals;
1886   map<int,int> newToOldNormals;
1887 
1888   map<int,int> oldToNewTextureCoordinates;
1889   map<int,int> newToOldTextureCoordinates;
1890 
1891   int numGroupVertices = 0;
1892   int numGroupNormals = 0;
1893   int numGroupTextureCoordinates = 0;
1894 
1895   // establish new vertices, normals and texture coordinates
1896   for (unsigned int j=0; j < groups[groupID].getNumFaces(); j++) // over all faces
1897   {
1898     const Face * face = groups[groupID].getFaceHandle(j);
1899     int faceDegree = (int)face->getNumVertices();
1900     for(int vtx=0; vtx<faceDegree; vtx++)
1901     {
1902       int vertex = face->getVertex(vtx).getPositionIndex();
1903       if (oldToNewVertices.find(vertex) == oldToNewVertices.end())
1904       {
1905         oldToNewVertices.insert(make_pair(vertex, numGroupVertices));
1906         newToOldVertices.insert(make_pair(numGroupVertices, vertex));
1907         numGroupVertices++;
1908       }
1909 
1910       if (face->getVertex(vtx).hasNormalIndex())
1911       {
1912         int normalIndex = face->getVertex(vtx).getNormalIndex();
1913         if (oldToNewNormals.find(normalIndex) == oldToNewNormals.end())
1914         {
1915           oldToNewNormals.insert(make_pair(normalIndex, numGroupNormals));
1916           newToOldNormals.insert(make_pair(numGroupNormals, normalIndex));
1917           numGroupNormals++;
1918         }
1919       }
1920 
1921       if (face->getVertex(vtx).hasTextureCoordinateIndex())
1922       {
1923         int textureCoordinateIndex = face->getVertex(vtx).getTextureCoordinateIndex();
1924         if (oldToNewTextureCoordinates.find(textureCoordinateIndex) == oldToNewTextureCoordinates.end())
1925         {
1926           oldToNewTextureCoordinates.insert(make_pair(textureCoordinateIndex, numGroupTextureCoordinates));
1927           newToOldTextureCoordinates.insert(make_pair(numGroupTextureCoordinates, textureCoordinateIndex));
1928           numGroupTextureCoordinates++;
1929         }
1930       }
1931     }
1932   }
1933 
1934   ObjMesh * output = new ObjMesh();
1935 
1936   // output vertices
1937   for(int i=0; i<numGroupVertices; i++)
1938     output->addVertexPosition(getPosition(newToOldVertices[i]));
1939 
1940   // output normals
1941   if (keepOnlyUsedNormals)
1942   {
1943     for(int i=0; i<numGroupNormals; i++)
1944       output->addVertexNormal(getNormal(newToOldNormals[i]));
1945   }
1946   else
1947   {
1948     for(unsigned int i=0; i<getNumNormals(); i++)
1949       output->addVertexNormal(getNormal(i));
1950   }
1951 
1952   // output texture coordinates
1953   if (keepOnlyUsedTextureCoordinates)
1954   {
1955     for(int i=0; i<numGroupTextureCoordinates; i++)
1956       output->addTextureCoordinate(getTextureCoordinate(newToOldTextureCoordinates[i]));
1957   }
1958   else
1959   {
1960     for(unsigned int i=0; i<getNumTextureCoordinates(); i++)
1961       output->addTextureCoordinate(getTextureCoordinate(i));
1962   }
1963 
1964   // output materials
1965   for(unsigned int i=0; i<getNumMaterials(); i++)
1966     output->addMaterial(getMaterial(i));
1967 
1968   // add a single group
1969   char s[4096];
1970   //sprintf(s, "group%05d", groupID);
1971   sprintf(s, "%s", groups[groupID].getName().c_str());
1972   output->addGroup(s);
1973 
1974   // set material for the extracted group
1975   //unsigned int newGroupIndex = output->groups.size() - 1;
1976   //if (newGroupIndex < 0)
1977   //{
1978   //  printf("Error: failed to add a new group to the mesh.\n");
1979   //  exit(0);
1980   //}
1981   if (output->groups.size() == 0)
1982   {
1983     printf("Error: failed to add a new group to the mesh.\n");
1984     exit(0);
1985   }
1986   unsigned int newGroupIndex = output->groups.size() - 1;
1987 
1988   output->groups[newGroupIndex].setMaterialIndex(groups[groupID].getMaterialIndex());
1989 
1990   // add faces to the group
1991   for (unsigned int j=0; j < groups[groupID].getNumFaces(); j++) // over all faces
1992   {
1993     const Face * face = groups[groupID].getFaceHandle(j);
1994     Face newFace;
1995     int faceDegree = (int)face->getNumVertices();
1996     for(int vtx=0; vtx<faceDegree; vtx++)
1997     {
1998       int oldVertexIndex = face->getVertex(vtx).getPositionIndex();
1999       int newVertexIndex = oldToNewVertices[oldVertexIndex];
2000       Vertex newVertex = face->getVertex(vtx);
2001       newVertex.setPositionIndex(newVertexIndex);
2002 
2003       if (newVertex.hasNormalIndex() && keepOnlyUsedNormals)
2004       {
2005         int oldNormalIndex = face->getVertex(vtx).getNormalIndex();
2006         int newNormalIndex = oldToNewNormals[oldNormalIndex];
2007         newVertex.setNormalIndex(newNormalIndex);
2008       }
2009 
2010       if (newVertex.hasTextureCoordinateIndex() && keepOnlyUsedTextureCoordinates)
2011       {
2012         int oldTextureCoordinateIndex = face->getVertex(vtx).getTextureCoordinateIndex();
2013         int newTextureCoordinateIndex = oldToNewTextureCoordinates[oldTextureCoordinateIndex];
2014         newVertex.setTextureCoordinateIndex(newTextureCoordinateIndex);
2015       }
2016 
2017       newFace.addVertex(newVertex);
2018     }
2019     output->addFaceToGroup(newFace, 0);
2020   }
2021 
2022   output->computeBoundingBox();
2023   return output;
2024 }
2025 
computeTriangleSurfaceArea(Vec3d & p0,Vec3d & p1,Vec3d & p2)2026 double ObjMesh::computeTriangleSurfaceArea(Vec3d & p0, Vec3d & p1, Vec3d & p2)
2027 {
2028   return 0.5 * (len(cross(p1-p0, p2-p0)));
2029 }
2030 
computeCentroids(std::vector<Vec3d> & centroids) const2031 void ObjMesh::computeCentroids(std::vector<Vec3d> & centroids) const
2032 {
2033   interpolateToCentroids(vertexPositions, centroids);
2034 }
2035 
interpolateToCentroids(const std::vector<double> & nodalData,std::vector<double> & centroidData) const2036 void ObjMesh::interpolateToCentroids(const std::vector<double> & nodalData, std::vector<double> & centroidData) const
2037 {
2038   int faceIndex = 0;
2039   // over all faces
2040   for(unsigned int i = 0; i < groups.size(); i++ )
2041   {
2042     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2043     {
2044       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2045 
2046       double data = 0;
2047       for (unsigned int iVertex = 0; iVertex < face.getNumVertices(); iVertex++)
2048       {
2049         unsigned int index = face.getVertex(iVertex).getPositionIndex();
2050         data += nodalData[index];
2051       }
2052 
2053       data /= face.getNumVertices();
2054       centroidData[faceIndex] = data;
2055 
2056       faceIndex++;
2057     }
2058   }
2059 }
2060 
interpolateToCentroids(const std::vector<Vec3d> & nodalData,std::vector<Vec3d> & centroidData) const2061 void ObjMesh::interpolateToCentroids(const std::vector<Vec3d> & nodalData, std::vector<Vec3d> & centroidData) const
2062 {
2063   int faceIndex = 0;
2064   // over all faces
2065   for(unsigned int i = 0; i < groups.size(); i++ )
2066   {
2067     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
2068     {
2069       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2070 
2071       Vec3d data(0,0,0);
2072       for (unsigned int iVertex = 0; iVertex < face.getNumVertices(); iVertex++)
2073       {
2074         unsigned int index = face.getVertex(iVertex).getPositionIndex();
2075         data += nodalData[index];
2076       }
2077 
2078       data /= face.getNumVertices();
2079       centroidData[faceIndex] = data;
2080 
2081       faceIndex++;
2082     }
2083   }
2084 }
2085 
computeSurfaceAreaPerVertex()2086 void ObjMesh::computeSurfaceAreaPerVertex()
2087 {
2088   for (unsigned int i=0; i < getNumVertices(); i++)
2089     surfaceAreaPerVertex.push_back(0);
2090 
2091   // over all faces
2092   for(unsigned int i = 0; i < groups.size(); i++)
2093   {
2094     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
2095     {
2096       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2097 
2098       if (face.getNumVertices() < 3)
2099         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2100 
2101       double faceSurfaceArea = computeFaceSurfaceArea(face);
2102 
2103       for (unsigned int iVertex = 0; iVertex < face.getNumVertices(); iVertex++)
2104       {
2105         unsigned int index = face.getVertex(iVertex).getPositionIndex();
2106         surfaceAreaPerVertex[index] += faceSurfaceArea / face.getNumVertices(); // each vertex owns an equal share of the face
2107       }
2108     }
2109   }
2110 }
2111 
scaleUniformly(const Vec3d & center,double factor)2112 void ObjMesh::scaleUniformly(const Vec3d & center, double factor)
2113 {
2114   for (unsigned int i=0; i < vertexPositions.size(); i++) // over all vertices
2115     vertexPositions[i] = center + factor * (vertexPositions[i] - center);
2116 
2117   computeBoundingBox();
2118 }
2119 
transformRigidly(const Vec3d & translation,const Mat3d & rotation)2120 void ObjMesh::transformRigidly(const Vec3d & translation, const Mat3d & rotation)
2121 {
2122   for (unsigned int i=0; i < vertexPositions.size(); i++) // over all vertices
2123   {
2124     Vec3d rotatedPosition = rotation * vertexPositions[i];
2125     vertexPositions[i] = rotatedPosition + translation;
2126   }
2127 
2128   for (unsigned int i=0; i < normals.size(); i++) // over all normals
2129   {
2130     Vec3d rotatedNormal = rotation * normals[i];
2131     normals[i] = rotatedNormal;
2132   }
2133 
2134   computeBoundingBox();
2135 }
2136 
deform(double * u)2137 void ObjMesh::deform(double * u)
2138 {
2139   for (unsigned int i=0; i < vertexPositions.size(); i++) // over all vertices
2140     vertexPositions[i] += Vec3d(&u[3*i]);
2141 
2142   computeBoundingBox();
2143 }
2144 
computeVolume() const2145 double ObjMesh::computeVolume() const
2146 {
2147   double volume = 0;
2148 
2149   // over all faces
2150   for(unsigned int i = 0; i < groups.size(); i++ )
2151   {
2152     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2153     {
2154       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2155 
2156       for (unsigned int iVertex = 0; iVertex < face.getNumVertices() - 2; iVertex++)
2157       {
2158         // base vertex
2159         Vec3d v0 = getPosition(face.getVertex(0));
2160         Vec3d v1 = getPosition(face.getVertex(iVertex+1));
2161         Vec3d v2 = getPosition(face.getVertex(iVertex+2));
2162 
2163         Vec3d normal = cross(v1-v0, v2-v0);
2164         Vec3d center = 1.0 / 3 * (v0 + v1 + v2);
2165 
2166         volume += dot(normal, center);
2167       }
2168     }
2169   }
2170 
2171   volume /= 6.0;
2172 
2173   return volume;
2174 }
2175 
computeCenterOfMass_Vertices() const2176 Vec3d ObjMesh::computeCenterOfMass_Vertices() const
2177 {
2178   Vec3d center(0,0,0);
2179   for (unsigned int i=0; i < vertexPositions.size(); i++) // over all vertices
2180     center += vertexPositions[i];
2181   center /= vertexPositions.size();
2182   return center;
2183 }
2184 
computeCenterOfMass_Triangles(const vector<double> & groupDensities) const2185 Vec3d ObjMesh::computeCenterOfMass_Triangles(const vector<double> & groupDensities) const
2186 {
2187   Vec3d centerOfMass = 0.0;
2188   double totalMass=0.0;
2189   // over all faces
2190   for(unsigned int i = 0; i < groups.size(); i++ )
2191   {
2192     double density = groupDensities[i];
2193     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2194     {
2195       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2196 
2197       double area = computeFaceSurfaceArea(face);
2198       double mass = density * area;
2199       totalMass += mass;
2200       Vec3d centroid = computeFaceCentroid(face);
2201       centerOfMass += mass * centroid;
2202     }
2203   }
2204   centerOfMass /= totalMass;
2205   return centerOfMass;
2206 }
2207 
computeInertiaTensor_Triangles(double IT[6]) const2208 void ObjMesh::computeInertiaTensor_Triangles(double IT[6]) const
2209 {
2210   double surfaceMassDensity = 1.0;
2211   vector<double> groupDensities;
2212   for(unsigned int i=0; i<groups.size(); i++)
2213     groupDensities.push_back(surfaceMassDensity);
2214   computeInertiaTensor_Triangles(groupDensities, IT);
2215 }
2216 
computeInertiaTensor_Triangles(double mass,double IT[6]) const2217 void ObjMesh::computeInertiaTensor_Triangles(double mass, double IT[6]) const
2218 {
2219   double surface = computeSurfaceArea();
2220   double surfaceMassDensity = mass / surface;
2221   vector<double> groupDensities;
2222   for(unsigned int i=0; i<groups.size(); i++)
2223     groupDensities.push_back(surfaceMassDensity);
2224   computeInertiaTensor_Triangles(groupDensities, IT);
2225 }
2226 
computeMass(const vector<double> & groupDensities) const2227 double ObjMesh::computeMass(const vector<double> & groupDensities) const
2228 {
2229   double totalMass = 0.0;
2230   // over all faces
2231   for(unsigned int i=0; i < groups.size(); i++)
2232   {
2233     double density = groupDensities[i];
2234     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2235     {
2236       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2237 
2238       if (face.getNumVertices() < 3)
2239       {
2240         printf("Warning: encountered a face with fewer than three vertices.\n");
2241         continue;
2242       }
2243 
2244       double area = computeFaceSurfaceArea(face);
2245       double mass = density * area;
2246       totalMass += mass;
2247     }
2248   }
2249 
2250   return totalMass;
2251 }
2252 
computeInertiaTensor_Triangles(const vector<double> & groupDensities,double IT[6]) const2253 void ObjMesh::computeInertiaTensor_Triangles(const vector<double> & groupDensities, double IT[6]) const
2254 {
2255   Vec3d centerOfMass = 0.0;
2256   memset(IT, 0, sizeof(double) * 6);
2257   double totalMass=0.0;
2258 
2259   // over all faces
2260   for(unsigned int i = 0; i < groups.size(); i++)
2261   {
2262     double density = groupDensities[i];
2263     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
2264     {
2265       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2266 
2267       if (face.getNumVertices() < 3)
2268       {
2269         printf("Warning: encountered a face with fewer than three vertices.\n");
2270         continue;
2271       }
2272 
2273       double area = computeFaceSurfaceArea(face);
2274       double mass = density * area;
2275       totalMass += mass;
2276       Vec3d centroid = computeFaceCentroid(face);
2277       centerOfMass += mass * centroid;
2278 
2279       Vec3d v0 = getPosition(face.getVertex(0));
2280       for (unsigned int iVertex = 1; iVertex < face.getNumVertices()-1; iVertex++ )
2281       {
2282         Vec3d v1 = getPosition(face.getVertex(iVertex));
2283         Vec3d v2 = getPosition(face.getVertex(iVertex + 1));
2284         double ITTriangle[6];
2285         computeSpecificInertiaTensor(v0, v1, v2, ITTriangle);
2286 
2287         double triangleArea = 0.5 * len(cross(v1-v0, v2-v0));
2288         for(int j=0; j<6; j++)
2289           IT[j] += triangleArea * density * ITTriangle[j];
2290       }
2291     }
2292   }
2293 
2294   centerOfMass /= totalMass;
2295 
2296   // IT is now the center around the origin
2297   // transfer tensor to the center of mass
2298   double a = centerOfMass[0];
2299   double b = centerOfMass[1];
2300   double c = centerOfMass[2];
2301 
2302   double correction[6] =
2303        { b*b + c*c, -a*b, -a*c,
2304                a*a + c*c, -b*c,
2305                      a*a + b*b };
2306 
2307   for(int i=0; i<6; i++)
2308     IT[i] -= totalMass * correction[i];
2309 
2310 }
2311 
computeCenterOfMass_Triangles() const2312 Vec3d ObjMesh::computeCenterOfMass_Triangles() const
2313 {
2314   Vec3d centerOfMass = 0.0;
2315 
2316   double totalArea=0.0;
2317   // over all faces
2318   for(unsigned int i = 0; i < groups.size(); i++ )
2319   {
2320     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2321     {
2322       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2323 
2324       double area = computeFaceSurfaceArea(face);
2325       totalArea += area;
2326       Vec3d centroid = computeFaceCentroid(face);
2327       centerOfMass += area * centroid;
2328     }
2329   }
2330 
2331   centerOfMass /= totalArea;
2332 
2333   return centerOfMass;
2334 }
2335 
computeFaceSurfaceAreas(vector<double> & surfaceAreas) const2336 void ObjMesh::computeFaceSurfaceAreas(vector<double> & surfaceAreas) const
2337 {
2338   int faceIndex = 0;
2339 
2340   // over all faces
2341   for(unsigned int i = 0; i < groups.size(); i++ )
2342   {
2343     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2344     {
2345       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2346       surfaceAreas[faceIndex] = computeFaceSurfaceArea(face);
2347       faceIndex++;
2348     }
2349   }
2350 }
2351 
computeSurfaceArea() const2352 double ObjMesh::computeSurfaceArea() const
2353 {
2354   double area = 0;
2355 
2356   // over all faces
2357   for(unsigned int i = 0; i < groups.size(); i++ )
2358   {
2359     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2360     {
2361       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2362       area += computeFaceSurfaceArea(face);
2363     }
2364   }
2365 
2366   return area;
2367 }
2368 
computeSurfaceAreaPerGroup(vector<double> & surfaceAreas) const2369 void ObjMesh::computeSurfaceAreaPerGroup(vector<double> & surfaceAreas) const
2370 {
2371   surfaceAreas.clear();
2372 
2373   for(unsigned int i = 0; i < groups.size(); i++ )
2374   {
2375     double area = 0;
2376     // over all faces
2377     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2378     {
2379       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2380       area += computeFaceSurfaceArea(face);
2381     }
2382 
2383     surfaceAreas.push_back(area);
2384   }
2385 
2386 }
2387 
computeMassPerVertex(const vector<double> & groupSurfaceMassDensity,vector<double> & masses) const2388 void ObjMesh::computeMassPerVertex(const vector<double> & groupSurfaceMassDensity, vector<double> & masses) const
2389 {
2390   masses.clear();
2391   for(unsigned int i=0; i<getNumVertices(); i++)
2392     masses.push_back(0.0);
2393 
2394   for(unsigned int i = 0; i < groups.size(); i++)
2395   {
2396     // over all faces
2397     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
2398     {
2399       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2400       double faceSurfaceArea = computeFaceSurfaceArea(face);
2401       for ( unsigned int iVertex = 0; iVertex < face.getNumVertices(); iVertex++ )
2402         masses[face.getVertex(iVertex).getPositionIndex()] += groupSurfaceMassDensity[i] * faceSurfaceArea / face.getNumVertices();
2403     }
2404   }
2405 }
2406 
2407 // warning: normal is computed using the first three face vertices (assumes planar face)
computeFaceNormal(const Face & face) const2408 Vec3d ObjMesh::computeFaceNormal(const Face & face) const
2409 {
2410   // the three vertices
2411   Vec3d pos0 = getPosition(face.getVertex(0));
2412   Vec3d pos1 = getPosition(face.getVertex(1));
2413   Vec3d pos2 = getPosition(face.getVertex(2));
2414   Vec3d normal = norm(cross(pos1 - pos0, pos2 - pos0));
2415 
2416   if (isNaN(normal[0]) || isNaN(normal[1]) || isNaN(normal[2]))
2417   {
2418     //degenerate geometry; return an arbitrary normal
2419     normal = Vec3d(1.0, 0.0, 0.0);
2420   }
2421 
2422   return normal;
2423 }
2424 
buildFaceNormals()2425 void ObjMesh::buildFaceNormals()
2426 {
2427   for(unsigned int i = 0; i < groups.size(); i++)
2428   {
2429     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
2430     {
2431       ObjMesh::Face * faceHandle = (Face*) groups[i].getFaceHandle(iFace); // get face whose number is iFace
2432 
2433       if (faceHandle->getNumVertices() < 3)
2434         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2435 
2436       Vec3d normal = computeFaceNormal(*faceHandle);
2437       faceHandle->setFaceNormal(normal);
2438     }
2439   }
2440 }
2441 
setNormalsToFaceNormals()2442 void ObjMesh::setNormalsToFaceNormals()
2443 {
2444   // over all faces
2445   normals.clear();
2446   for(unsigned int i=0; i < groups.size(); i++)
2447   {
2448     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
2449     {
2450       const ObjMesh::Face * faceHandle = groups[i].getFaceHandle(iFace); // get face whose number is iFace
2451 
2452       if (faceHandle->getNumVertices() < 3)
2453         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2454 
2455       if (faceHandle->hasFaceNormal())
2456         addVertexNormal(faceHandle->getFaceNormal());
2457       else
2458         addVertexNormal(computeFaceNormal(*faceHandle));
2459 
2460       // over all vertices of the face
2461       for (unsigned k=0; k<faceHandle->getNumVertices(); k++)
2462       {
2463         Vertex * vertex = (Vertex*) faceHandle->getVertexHandle(k);
2464         vertex->setNormalIndex(getNumNormals() - 1);
2465       }
2466     }
2467   }
2468 }
2469 
setNormalsToAverageFaceNormals()2470 void ObjMesh::setNormalsToAverageFaceNormals()
2471 {
2472   vector<Vec3d> normalBuffer(getNumVertices(),Vec3d(0,0,0));
2473   vector<unsigned int> normalCount(getNumVertices(),0);
2474 
2475   // over all faces
2476   for(unsigned int i = 0; i < groups.size(); i++ )
2477   {
2478     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2479     {
2480       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2481 
2482       if (face.getNumVertices() < 3)
2483         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2484 
2485       // the three vertices
2486       unsigned int index0 = face.getVertex(0).getPositionIndex();
2487       unsigned int index1 = face.getVertex(1).getPositionIndex();
2488       unsigned int index2 = face.getVertex(2).getPositionIndex();
2489 
2490       Vec3d pos0 = getPosition(index0);
2491       Vec3d pos1 = getPosition(index1);
2492       Vec3d pos2 = getPosition(index2);
2493 
2494       Vec3d normal = norm(cross(pos1-pos0,pos2-pos0));
2495       // this works even for non-triangle meshes
2496 
2497       normalBuffer[index0] += normal;
2498       normalBuffer[index1] += normal;
2499       normalBuffer[index2] += normal;
2500 
2501       normalCount[index0]++;
2502       normalCount[index1]++;
2503       normalCount[index2]++;
2504 
2505     }
2506   }
2507 
2508   bool errorMessageSeen=false;
2509   // normalize the normals
2510   for (unsigned int i=0; i < getNumVertices(); i++)
2511   {
2512     if (normalCount[i] == 0)
2513     {
2514       if (!errorMessageSeen)
2515         cout << "Warning: encountered a vertex not belonging to any triangle (suppressing further warnings)" << endl;
2516       errorMessageSeen = true;
2517       normalBuffer[i] = Vec3d(1,0,0); // assign some bogus normal
2518     }
2519     else
2520       normalBuffer[i] = norm(normalBuffer[i]);
2521   }
2522 
2523   // register new normals with the objMesh data structure
2524   normals.clear();
2525   for (unsigned int i=0; i < getNumVertices(); i++)
2526     addVertexNormal(normalBuffer[i]);
2527 
2528   for(unsigned int i = 0; i < groups.size(); i++ )
2529   {
2530     Group * group = &(groups[i]);
2531     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2532     {
2533       const Face * face = group->getFaceHandle(iFace);
2534 
2535       if (face->getNumVertices() < 3)
2536         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2537 
2538       for (unsigned k=0; k<face->getNumVertices(); k++)
2539       {
2540         Vertex * vertex = (Vertex*) face->getVertexHandle(k);
2541         vertex->setNormalIndex(vertex->getPositionIndex());
2542       }
2543     }
2544   }
2545 }
2546 
getClosestVertex(const Vec3d & queryPos,double * distance) const2547 unsigned int ObjMesh::getClosestVertex(const Vec3d & queryPos, double * distance) const
2548 {
2549   double closestDist2 = DBL_MAX;
2550   double candidateDist2;
2551   unsigned int indexClosest = 0;
2552   for(unsigned int i=0; i< getNumVertices(); i++)
2553   {
2554     Vec3d relPos = getPosition(i) - queryPos;
2555     if ((candidateDist2 = dot(relPos,relPos)) < closestDist2)
2556     {
2557       closestDist2 = candidateDist2;
2558       indexClosest = i;
2559     }
2560   }
2561 
2562   if (distance != NULL)
2563     *distance = sqrt(closestDist2);
2564   return indexClosest;
2565 }
2566 
computeMinEdgeLength() const2567 double ObjMesh::computeMinEdgeLength() const
2568 {
2569   double minLength = -1;
2570 
2571   // over all faces
2572   for(unsigned int i = 0; i < groups.size(); i++ )
2573   {
2574     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2575     {
2576       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2577 
2578       if (face.getNumVertices() < 3)
2579 	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2580 
2581       for (unsigned k=0; k<face.getNumVertices(); k++)
2582       {
2583         Vec3d pos0 = getPosition(face.getVertex(k));
2584         Vec3d pos1 = getPosition(face.getVertex((k+1) % face.getNumVertices()));
2585         double length = len(pos1-pos0);
2586 
2587         if (minLength < 0) // only the first time
2588           minLength = length;
2589         else if (length < minLength)
2590           minLength = length;
2591       }
2592     }
2593   }
2594 
2595   return minLength;
2596 
2597 }
2598 
computeAverageEdgeLength() const2599 double ObjMesh::computeAverageEdgeLength() const
2600 {
2601   double totalLength = 0.0;
2602   int numEdges = 0;
2603 
2604   // over all faces
2605   for(unsigned int i = 0; i < groups.size(); i++ )
2606   {
2607     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2608     {
2609       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2610 
2611       if (face.getNumVertices() < 3)
2612 	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2613 
2614       for (unsigned k=0; k<face.getNumVertices(); k++)
2615       {
2616         Vec3d pos0 = getPosition(face.getVertex(k));
2617         Vec3d pos1 = getPosition(face.getVertex((k+1) % face.getNumVertices()));
2618         double length = len(pos1-pos0);
2619         totalLength += length;
2620         numEdges++;
2621       }
2622     }
2623   }
2624 
2625   return totalLength/numEdges;
2626 }
2627 
computeMedianEdgeLength() const2628 double ObjMesh::computeMedianEdgeLength() const
2629 {
2630   vector<double> lengths;
2631 
2632   // over all faces
2633   for(unsigned int i = 0; i < groups.size(); i++ )
2634   {
2635     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2636     {
2637       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2638 
2639       if (face.getNumVertices() < 3)
2640 	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2641 
2642       for (unsigned k=0; k<face.getNumVertices(); k++)
2643       {
2644         Vec3d pos0 = getPosition(face.getVertex(k));
2645         Vec3d pos1 = getPosition(face.getVertex((k+1) % face.getNumVertices()));
2646         double length = len(pos1-pos0);
2647         lengths.push_back(length);
2648       }
2649     }
2650   }
2651 
2652   sort(lengths.begin(), lengths.end());
2653 
2654   return lengths[lengths.size() / 2];
2655 }
2656 
computeMaxEdgeLength() const2657 double ObjMesh::computeMaxEdgeLength() const
2658 {
2659   double maxLength = 0;
2660 
2661   // over all faces
2662   for(unsigned int i = 0; i < groups.size(); i++ )
2663   {
2664     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2665     {
2666       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2667 
2668       if (face.getNumVertices() < 3)
2669 	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2670 
2671       for (unsigned k=0; k<face.getNumVertices(); k++)
2672       {
2673         Vec3d pos0 = getPosition(face.getVertex(k));
2674         Vec3d pos1 = getPosition(face.getVertex((k+1) % face.getNumVertices()));
2675         double length = len(pos1-pos0);
2676         if (length > maxLength)
2677           maxLength = length;
2678       }
2679     }
2680   }
2681 
2682   return maxLength;
2683 }
2684 
computeMinEdgeLength(int * vtxa,int * vtxb) const2685 double ObjMesh::computeMinEdgeLength(int * vtxa, int * vtxb) const
2686 {
2687   *vtxa = *vtxb = -1;
2688 
2689   double minLength = -1;
2690 
2691   // over all faces
2692   for(unsigned int i = 0; i < groups.size(); i++ )
2693   {
2694     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2695     {
2696       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2697 
2698       if (face.getNumVertices() < 3)
2699 	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2700 
2701       for (unsigned k=0; k<face.getNumVertices(); k++)
2702       {
2703         Vec3d pos0 = getPosition(face.getVertex(k));
2704         Vec3d pos1 = getPosition(face.getVertex((k+1) % face.getNumVertices()));
2705 
2706         double length = len(pos1-pos0);
2707 
2708         if (minLength < 0) // only the first time
2709           minLength = length;
2710         else if (length < minLength)
2711           minLength = length;
2712 
2713         *vtxa = face.getVertex(k).getPositionIndex();
2714         *vtxb = face.getVertex((k+1) % face.getNumVertices()).getPositionIndex();
2715       }
2716     }
2717   }
2718 
2719   return minLength;
2720 }
2721 
computeMaxEdgeLength(int * vtxa,int * vtxb) const2722 double ObjMesh::computeMaxEdgeLength(int * vtxa, int * vtxb) const
2723 {
2724   *vtxa = *vtxb = -1;
2725 
2726   double maxLength = 0;
2727 
2728   // over all faces
2729   for(unsigned int i = 0; i < groups.size(); i++ )
2730   {
2731     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2732     {
2733       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2734 
2735       if (face.getNumVertices() < 3)
2736 	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2737 
2738       for (unsigned k=0; k<face.getNumVertices(); k++)
2739       {
2740         Vec3d pos0 = getPosition(face.getVertex(k));
2741         Vec3d pos1 = getPosition(face.getVertex((k+1) % face.getNumVertices()));
2742         double length = len(pos1-pos0);
2743 
2744         if (length > maxLength)
2745           maxLength = length;
2746 
2747         *vtxa = face.getVertex(k).getPositionIndex();
2748         *vtxb = face.getVertex((k+1) % face.getNumVertices()).getPositionIndex();
2749       }
2750     }
2751   }
2752 
2753   return maxLength;
2754 }
2755 
getNumFaces() const2756 unsigned int ObjMesh::getNumFaces() const
2757 {
2758   unsigned int counter = 0;
2759   for (unsigned int i=0; i < groups.size(); i++)
2760     counter += groups[i].getNumFaces();
2761 
2762   return counter;
2763 }
2764 
setNormalsToPseudoNormals()2765 void ObjMesh::setNormalsToPseudoNormals()
2766 {
2767   // nuke any previous normals
2768   normals.clear();
2769 
2770   // registers pseudonormals as the new normals
2771   for(unsigned int i=0; i < getNumVertices(); i++)
2772   {
2773     normals.push_back(pseudoNormals[i]);
2774   }
2775 
2776   // over all faces
2777   for(unsigned int i = 0; i < groups.size(); i++ )
2778   {
2779     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2780     {
2781       const ObjMesh::Face * face = groups[i].getFaceHandle(iFace); // get face whose number is iFace
2782 
2783       // over all vertices of the face
2784       for (unsigned k=0; k<face->getNumVertices(); k++)
2785       {
2786         Vertex * vertex = (Vertex*) face->getVertexHandle(k);
2787         vertex->setNormalIndex(vertex->getPositionIndex());
2788       }
2789     }
2790   }
2791 }
2792 
buildVertexNormals(double angle)2793 void ObjMesh::buildVertexNormals(double angle)
2794 {
2795   if (vertexFaceNeighbors.size() == 0)
2796   {
2797     printf("Error: buildVertexNormals() failed because vertex face neighbors were not built prior to calling buildVertexNormals(). Call \"buildVertexFaceNeighbors\".\n");
2798     return;
2799   }
2800 
2801   double cosang = cos(angle * M_PI / 180.0);
2802 
2803   normals.clear();
2804   int averageIndex = 0;
2805 
2806   for(unsigned int i = 0; i < getNumVertices(); i++)
2807   {
2808     if (vertexFaceNeighbors[i].size() == 0)
2809     {
2810       //silly lonely vertex
2811       continue;
2812     }
2813     const Face * firstFace = getGroupHandle(vertexFaceNeighbors[i].begin()->getGroupIndex())->getFaceHandle(vertexFaceNeighbors[i].begin()->getFaceIndex());
2814     if (!firstFace->hasFaceNormal())
2815     {
2816       printf("Warning: face normals not computed\n");
2817       return;
2818     }
2819     Vec3d firstNorm = firstFace->getFaceNormal();
2820 
2821     Vec3d average = Vec3d(0.0);
2822     bool averagedAnything = false;
2823 
2824     //find which faces contribute
2825     for(std::list<VertexFaceNeighbor>::iterator iter = vertexFaceNeighbors[i].begin(); iter != vertexFaceNeighbors[i].end(); iter++)
2826     {
2827       //get angle
2828       const Face * currentFace = getGroupHandle(iter->getGroupIndex())->getFaceHandle(iter->getFaceIndex());
2829       if (!currentFace->hasFaceNormal())
2830       {
2831         printf("Warning: face normals not computed\n");
2832         return;
2833       }
2834       //dot product
2835       if (dot(firstNorm, currentFace->getFaceNormal()) > cosang)
2836       {
2837         //is good, so contribute to average
2838         average += currentFace->getFaceNormal();
2839         iter->setAveraged(true);
2840         averagedAnything = true;
2841       }
2842       else
2843         iter->setAveraged(false);
2844     }
2845 
2846     if (averagedAnything)
2847     {
2848       normals.push_back(norm(average));
2849       averageIndex = normals.size() - 1;
2850     }
2851 
2852     //determine consequences for associated vertices in each face
2853     for(std::list<VertexFaceNeighbor>::iterator iter = vertexFaceNeighbors[i].begin(); iter != vertexFaceNeighbors[i].end(); iter++)
2854     {
2855       const Face * currentFace = getGroupHandle(iter->getGroupIndex())->getFaceHandle(iter->getFaceIndex());
2856       if (iter->getAveraged())
2857       {
2858         //use average for normal
2859         Vertex * vertex = (Vertex*) currentFace->getVertexHandle(iter->getFaceVertexIndex());
2860         vertex->setNormalIndex(averageIndex);
2861       }
2862       else
2863       {
2864         //use face normal for normal
2865         normals.push_back(currentFace->getFaceNormal());
2866         Vertex * vertex = (Vertex*) currentFace->getVertexHandle(iter->getFaceVertexIndex());
2867         vertex->setNormalIndex(normals.size() - 1);
2868       }
2869     }
2870   }
2871 }
2872 
buildVertexNormalsFancy(double angle)2873 void ObjMesh::buildVertexNormalsFancy(double angle)
2874 {
2875   if (vertexFaceNeighbors.size() == 0)
2876   {
2877     printf("Error: buildVertexNormalsFancy() failed because vertex face neighbors were not built prior to calling buildVertexNormalsFancy(). Call \"buildVertexFaceNeighbors\".\n");
2878     return;
2879   }
2880 
2881   double cosang = cos(angle * M_PI / 180.0);
2882 
2883   normals.clear();
2884 
2885   for(unsigned int i = 0; i < groups.size(); i++ )
2886   {
2887     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2888     {
2889       const ObjMesh::Face * faceHandle = groups[i].getFaceHandle(iFace); // get face whose number is iFace
2890 
2891       if (faceHandle->getNumVertices() < 3)
2892         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
2893 
2894       if (!faceHandle->hasFaceNormal())
2895         printf("Warning: face has no face normal\n");
2896 
2897       Vec3d faceNorm = faceHandle->getFaceNormal();
2898 
2899       for(unsigned int j = 0; j < faceHandle->getNumVertices(); j++)
2900       {
2901         //process the neighbors of this vertex
2902         int vertexIndex = faceHandle->getVertexHandle(j)->getPositionIndex();
2903         Vec3d newNorm(0.0);
2904         bool averagedAnything = false;
2905 
2906         for(std::list<VertexFaceNeighbor>::iterator iter = vertexFaceNeighbors[vertexIndex].begin(); iter != vertexFaceNeighbors[vertexIndex].end(); iter++)
2907         {
2908           const Face * neighborFaceHandle = getGroupHandle(iter->getGroupIndex())->getFaceHandle(iter->getFaceIndex());
2909           if (!neighborFaceHandle->hasFaceNormal())
2910             printf("Warning: face has no face normal\n");
2911 
2912           if (dot(faceNorm, neighborFaceHandle->getFaceNormal()) > cosang)
2913           {
2914             newNorm += neighborFaceHandle->getFaceNormal();
2915             averagedAnything = true;
2916           }
2917         }
2918         if (!averagedAnything)
2919         {
2920           printf("error in mesh neighbor structure\n");
2921           newNorm = Vec3d(1.0);
2922         }
2923         normals.push_back(norm(newNorm));
2924         Vertex * vertex = (Vertex*) faceHandle->getVertexHandle(j);
2925         vertex->setNormalIndex(normals.size() - 1);
2926       }
2927     }
2928   }
2929 }
2930 
setMaterialAlpha(double alpha)2931 void ObjMesh::setMaterialAlpha(double alpha)
2932 {
2933   for(unsigned int i = 0; i < materials.size(); i++)
2934     materials[i].setAlpha(alpha);
2935 }
2936 
setSingleMaterial(const Material & material)2937 void ObjMesh::setSingleMaterial(const Material & material)
2938 {
2939   materials.clear();
2940   materials.push_back(material);
2941   for(int groupNo=0; groupNo<(int)groups.size(); groupNo++)
2942     groups[groupNo].setMaterialIndex(0);
2943 }
2944 
computePseudoNormals()2945 void ObjMesh::computePseudoNormals()
2946 {
2947   vector<int> vertexDegree(getNumVertices());
2948   for(unsigned int i=0; i<getNumVertices(); i++)
2949     pseudoNormals.push_back(0.0);
2950 
2951   // over all faces
2952   for(unsigned int i = 0; i < groups.size(); i++ )
2953   {
2954     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
2955     {
2956       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
2957 
2958       // over all vertices
2959       for (unsigned k=0; k<face.getNumVertices(); k++)
2960       {
2961         // compute angle at that vertex in radians
2962         Vec3d pos = getPosition(face.getVertex(k));
2963         Vec3d posNext, posPrev;
2964         if (k != face.getNumVertices() - 1)
2965           posNext = getPosition(face.getVertex(k + 1));
2966         else
2967           posNext = getPosition(face.getVertex(0));
2968 
2969         if (k != 0)
2970           posPrev = getPosition(face.getVertex(k - 1));
2971         else
2972           posPrev = getPosition(face.getVertex(face.getNumVertices() - 1));
2973 
2974         double lenNext = len(posNext-pos);
2975         double lenPrev = len(posPrev-pos);
2976 
2977         double angle = acos(dot(posNext-pos,posPrev-pos)/lenNext/lenPrev);
2978         Vec3d normal = norm(cross(posNext-pos, posPrev-pos));
2979 
2980         if (isNaN(normal[0]) || isNaN(normal[1]) || isNaN(normal[2]))
2981         {
2982           cout << "Error (when computing vertex pseudonormals): NaN encountered (face with zero surface area)." << endl;
2983           cout << "Group: " << i << " Face: " << iFace << " " << endl;
2984           normal[0] = 0; normal[1] = 0; normal[2] = 0;
2985           //cout << "  vtx0: " << index0 << " vtx1: " << index1 << " vtx2: " << index2 << endl;
2986           //cout << "  "  << p0 << endl;
2987           //cout << "  "  << p1 << endl;
2988           //cout << "  "  << p2 << endl;
2989           //cout << "Feature: " << normali << endl;
2990         }
2991         else
2992         {
2993           if ((lenNext == 0) || (lenPrev == 0) || isNaN(angle))
2994           {
2995             cout << "Warning (when computing vertex pseudonormals): encountered zero-length edge" << endl;
2996             cout << "  lenNext: " << lenNext << " lenPrev: " << lenPrev << " angle: " << angle << endl;
2997           }
2998           else
2999           {
3000             pseudoNormals[face.getVertex(k).getPositionIndex()] += angle * normal;
3001             vertexDegree[face.getVertex(k).getPositionIndex()]++;
3002           }
3003         }
3004       }
3005     }
3006   }
3007 
3008   for(unsigned int i=0; i<getNumVertices(); i++)
3009   {
3010     if (vertexDegree[i] != 0)
3011     {
3012       Vec3d pseudoNormalRaw = pseudoNormals[i];
3013       pseudoNormals[i] = norm(pseudoNormalRaw);
3014       if (isNaN(pseudoNormals[i][0]) || isNaN(pseudoNormals[i][1]) || isNaN(pseudoNormals[i][2]))
3015       {
3016         cout << "Error (when computing vertex pseudonormals): NaN encountered." << endl;
3017         cout << "Vertex: " << i << " pseudoNormal=" << pseudoNormals[i][0] << " " << pseudoNormals[i][1] << " " << pseudoNormals[i][2] << endl;
3018         cout << "  Pseudonormal before normalization=";
3019         printf("%G %G %G\n", pseudoNormalRaw[0], pseudoNormalRaw[1], pseudoNormalRaw[2]);
3020         cout << "  Vertex degree=" << vertexDegree[i] << endl;
3021         pseudoNormals[i] = 0.0;
3022       }
3023     }
3024     else
3025       pseudoNormals[i] = 0.0;
3026   }
3027 }
3028 
computeEdgePseudoNormals()3029 void ObjMesh::computeEdgePseudoNormals()
3030 {
3031   edgePseudoNormals.clear();
3032 
3033   // over all faces
3034   for(unsigned int i = 0; i < groups.size(); i++ )
3035   {
3036     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
3037     {
3038       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
3039       Vec3d pos0 = getPosition(face.getVertex(0));
3040       Vec3d pos1 = getPosition(face.getVertex(1));
3041       Vec3d pos2 = getPosition(face.getVertex(2));
3042       Vec3d normal = norm(cross(pos1-pos0, pos2-pos0));
3043 
3044       if (isNaN(normal[0]) || isNaN(normal[1]) || isNaN(normal[2]))
3045       {
3046         cout << "Error: nan encountered (face with zero surface area)." << endl;
3047         cout << "Group: " << i << " Face: " << iFace << " " << endl;
3048         exit(1);
3049       }
3050 
3051       // over all edges at the face
3052       for (unsigned k=0; k<face.getNumVertices(); k++)
3053       {
3054 
3055         unsigned int startVertex = face.getVertex(k).getPositionIndex();
3056         unsigned int endVertex = face.getVertex( (k+1) % face.getNumVertices()).getPositionIndex();
3057 
3058         pair<unsigned int, unsigned int> edge;
3059         if (startVertex < endVertex)
3060         {
3061           edge.first = startVertex;
3062           edge.second = endVertex;
3063         }
3064         else
3065         {
3066           edge.first = endVertex;
3067           edge.second = startVertex;
3068         }
3069 
3070         map< pair<unsigned int, unsigned int>, Vec3d > :: iterator iter = edgePseudoNormals.find(edge);
3071 
3072         if (iter == edgePseudoNormals.end())
3073         {
3074           edgePseudoNormals.insert(make_pair(edge,normal));
3075         }
3076         else
3077         {
3078           iter->second += normal;
3079         }
3080       }
3081     }
3082   }
3083 
3084   // normalize normals
3085   map< pair<unsigned int, unsigned int>, Vec3d > :: iterator iter;
3086   for(iter = edgePseudoNormals.begin(); iter != edgePseudoNormals.end(); ++iter)
3087   {
3088     Vec3d normal = norm(iter->second);
3089     if (isNaN(normal[0]) || isNaN(normal[1]) || isNaN(normal[2]))
3090     {
3091       cout << "Warning (while computing edge pseudonormals): NaN encountered (face with zero surface area)." << endl;
3092       normal[0] = 1; normal[1] = 0; normal[2] = 0;
3093     }
3094     iter->second = normal;
3095   }
3096 }
3097 
removeZeroAreaFaces(int verbose)3098 int ObjMesh::removeZeroAreaFaces(int verbose)
3099 {
3100   int numZeroAreaFaces = 0;
3101 
3102   // over all faces
3103   for(unsigned int i = 0; i < groups.size(); i++ )
3104   {
3105     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
3106     {
3107       if ((verbose == 1) && (iFace % 100 == 0))
3108       {
3109         printf("Processing face %d in group %d...\n", iFace, i);
3110         fflush(NULL);
3111       }
3112 
3113       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
3114       Vec3d pos0 = getPosition(face.getVertex(0));
3115       Vec3d pos1 = getPosition(face.getVertex(1));
3116       Vec3d pos2 = getPosition(face.getVertex(2));
3117       Vec3d normal = norm(cross(pos1-pos0, pos2-pos0));
3118 
3119       bool identicalVertex = false;
3120       for(unsigned int jj=0; jj< face.getNumVertices(); jj++)
3121         for(unsigned int kk=jj+1; kk< face.getNumVertices(); kk++)
3122         {
3123           if (face.getVertex(jj).getPositionIndex() == face.getVertex(kk).getPositionIndex())
3124             identicalVertex = true;
3125         }
3126 
3127       if (isNaN(normal[0]) || isNaN(normal[1]) || isNaN(normal[2]) || identicalVertex)
3128       {
3129         groups[i].removeFace(iFace);
3130         iFace--;
3131         numZeroAreaFaces++;
3132       }
3133     }
3134   }
3135 
3136   return numZeroAreaFaces;
3137 }
3138 
removeHangingFaces()3139 int ObjMesh::removeHangingFaces()
3140 {
3141   map< pair<unsigned int,unsigned int>, vector<pair<unsigned int, unsigned int> > > facesAdjacentToEdge;
3142 
3143   // build facesAdjacentToEdge
3144   // over all faces
3145   for(unsigned int iGroup = 0; iGroup < groups.size(); iGroup++ )
3146   {
3147     for(unsigned int iFace = 0; iFace < groups[iGroup].getNumFaces(); iFace++ )
3148     {
3149       ObjMesh::Face face = groups[iGroup].getFace(iFace); // get face whose number is iFace
3150       for(unsigned int j=0; j<face.getNumVertices(); j++) // over all vertices of this face
3151       {
3152         unsigned int vtxIndexA = face.getVertex(j).getPositionIndex();
3153         unsigned int vtxIndexB = face.getVertex((j + 1) % face.getNumVertices()).getPositionIndex();
3154         if (vtxIndexA > vtxIndexB)
3155           std::swap(vtxIndexA, vtxIndexB);
3156 
3157         std::pair<unsigned int, unsigned int> myPair(vtxIndexA, vtxIndexB);
3158         if (facesAdjacentToEdge.find(myPair) == facesAdjacentToEdge.end())
3159           facesAdjacentToEdge.insert(make_pair(myPair, vector<pair<unsigned int, unsigned int> >()));
3160         facesAdjacentToEdge[myPair].push_back(make_pair(iGroup, iFace));
3161       }
3162     }
3163   }
3164 
3165   set<pair<unsigned int, unsigned int> > eraseList;
3166 
3167   // check the map for edges with more than two neighbors
3168   for(map< pair<unsigned int,unsigned int>, vector<pair<unsigned int, unsigned int> > > :: iterator iter = facesAdjacentToEdge.begin(); iter != facesAdjacentToEdge.end(); iter++)
3169   {
3170     if ((iter->second).size() > 2)
3171     {
3172       // edge has more than two neighboring faces
3173 
3174       // check all adjacent faces, to see if any of them has an edge that has no other neighbor
3175       for(unsigned int i=0; i<(iter->second).size(); i++)
3176       {
3177         unsigned int iGroup = (iter->second)[i].first;
3178         unsigned int iFace = (iter->second)[i].second;
3179 
3180         ObjMesh::Face face = groups[iGroup].getFace(iFace); // get face whose number is iFace
3181         for(unsigned int j=0; j<face.getNumVertices(); j++) // over all vertices
3182         {
3183           unsigned int vtxIndexA = face.getVertex(j).getPositionIndex();
3184           unsigned int vtxIndexB = face.getVertex((j + 1) % face.getNumVertices()).getPositionIndex();
3185           if (vtxIndexA > vtxIndexB)
3186             std::swap(vtxIndexA, vtxIndexB);
3187 
3188           std::pair<unsigned int, unsigned int> myPair(vtxIndexA, vtxIndexB);
3189           if (facesAdjacentToEdge[myPair].size() == 1)
3190           {
3191             // found an edge with only one neighboring face (this face)
3192             // erase the face
3193             eraseList.insert((iter->second)[i]);
3194             break;
3195           }
3196         }
3197       }
3198     }
3199   }
3200 
3201   // erase faces whose all three edges are not shared by any other face
3202   // over all faces
3203   for(unsigned int iGroup = 0; iGroup < groups.size(); iGroup++ )
3204   {
3205     for(unsigned int iFace = 0; iFace < groups[iGroup].getNumFaces(); iFace++ )
3206     {
3207       int eraseFace = 1;
3208       ObjMesh::Face face = groups[iGroup].getFace(iFace); // get face whose number is iFace
3209       for(unsigned int j=0; j<face.getNumVertices(); j++) // over all vertices of this face
3210       {
3211         unsigned int vtxIndexA = face.getVertex(j).getPositionIndex();
3212         unsigned int vtxIndexB = face.getVertex((j + 1) % face.getNumVertices()).getPositionIndex();
3213         if (vtxIndexA > vtxIndexB)
3214           std::swap(vtxIndexA, vtxIndexB);
3215 
3216         std::pair<unsigned int, unsigned int> myPair(vtxIndexA, vtxIndexB);
3217         if (facesAdjacentToEdge[myPair].size() > 1)
3218         {
3219           eraseFace = 0;
3220           break;
3221         }
3222       }
3223 
3224       if (eraseFace)
3225         eraseList.insert(make_pair(iGroup, iFace));
3226     }
3227   }
3228 
3229   //printf("Erase list size is: %d\n", (int)eraseList.size());
3230 
3231   // erase the scheduled faces
3232   // must iterate from the back to front, to have correct indexing
3233   for(set<pair<unsigned int, unsigned int> > :: reverse_iterator iter = eraseList.rbegin(); iter != eraseList.rend(); iter++)
3234   {
3235     unsigned int iGroup = iter->first;
3236     unsigned int iFace = iter->second;
3237     Group * groupHandle = (Group*) getGroupHandle(iGroup);
3238     groupHandle->removeFace(iFace);
3239   }
3240 
3241   return (int) eraseList.size();
3242 }
3243 
removeNonManifoldEdges()3244 int ObjMesh::removeNonManifoldEdges()
3245 {
3246   map< pair<unsigned int,unsigned int>, vector<pair<unsigned int, unsigned int> > > facesAdjacentToEdge;
3247 
3248   // build facesAdjacentToEdge
3249   // over all faces
3250   for(unsigned int iGroup = 0; iGroup < groups.size(); iGroup++ )
3251   {
3252     for(unsigned int iFace = 0; iFace < groups[iGroup].getNumFaces(); iFace++ )
3253     {
3254       ObjMesh::Face face = groups[iGroup].getFace(iFace); // get face whose number is iFace
3255       for(unsigned int j=0; j<face.getNumVertices(); j++) // over all vertices of this face
3256       {
3257         unsigned int vtxIndexA = face.getVertex(j).getPositionIndex();
3258         unsigned int vtxIndexB = face.getVertex((j + 1) % face.getNumVertices()).getPositionIndex();
3259         if (vtxIndexA > vtxIndexB)
3260           std::swap(vtxIndexA, vtxIndexB);
3261 
3262         std::pair<unsigned int, unsigned int> myPair(vtxIndexA, vtxIndexB);
3263         if (facesAdjacentToEdge.find(myPair) == facesAdjacentToEdge.end())
3264           facesAdjacentToEdge.insert(make_pair(myPair, vector<pair<unsigned int, unsigned int> >()));
3265         facesAdjacentToEdge[myPair].push_back(make_pair(iGroup, iFace));
3266       }
3267     }
3268   }
3269 
3270   vector<pair<unsigned int, unsigned int> > eraseList;
3271 
3272   // check the map for edges with more than two neighbors
3273   for(map<pair<unsigned int,unsigned int>, vector<pair<unsigned int, unsigned int> > > :: iterator iter = facesAdjacentToEdge.begin(); iter != facesAdjacentToEdge.end(); iter++)
3274   {
3275     if ((iter->second).size() > 2)
3276       eraseList.push_back(iter->first);
3277   }
3278 
3279   sort(eraseList.begin(), eraseList.end());
3280   //printf("Erase list size: %d\n", eraseList.size());
3281 
3282   int removedEdges = 0;
3283 
3284   for(unsigned int i=0; i<eraseList.size(); i++)
3285   {
3286     if (eraseList[i].first == eraseList[i].second)
3287       continue;
3288 
3289     //printf("Removing edge: %d to %d.\n", eraseList[i].first, eraseList[i].second);
3290 
3291     int removeIsolatedVertices_ = 0;
3292     collapseEdge(eraseList[i].first, eraseList[i].second, removeIsolatedVertices_);
3293     removedEdges++;
3294 
3295     // renumber all future pairs
3296     for(unsigned int j=i+1; j<eraseList.size(); j++)
3297     {
3298       if (eraseList[j].first == eraseList[i].second)
3299         eraseList[j].first = eraseList[i].first;
3300       if (eraseList[j].second == eraseList[i].second)
3301         eraseList[j].second = eraseList[i].first;
3302 
3303       if (eraseList[j].first > eraseList[j].second)
3304         std::swap(eraseList[j].first, eraseList[j].second);
3305     }
3306   }
3307 
3308   removeIsolatedVertices();
3309 
3310   return removedEdges;
3311 }
3312 
collapseEdge(unsigned int vertexA,unsigned int vertexB,int removeIsolatedVertices_)3313 void ObjMesh::collapseEdge(unsigned int vertexA, unsigned int vertexB, int removeIsolatedVertices_)
3314 {
3315   if (vertexA > vertexB)
3316     std::swap(vertexA, vertexB);
3317 
3318   // over all faces
3319   for(unsigned int iGroup = 0; iGroup < groups.size(); iGroup++)
3320   {
3321     Group * group = (Group*) getGroupHandle(iGroup);
3322     vector<unsigned int> eraseList;
3323     for(unsigned int iFace = 0; iFace < groups[iGroup].getNumFaces(); iFace++)
3324     {
3325       int eraseFace = 0;
3326       Face * face = (Face*) group->getFaceHandle(iFace); // get face whose number is iFace
3327       for(unsigned int j=0; j<face->getNumVertices(); j++) // over all vertices of this face
3328       {
3329         unsigned int vtxIndex = face->getVertex(j).getPositionIndex();
3330         if (vtxIndex == vertexB)
3331         {
3332           Vertex * vertex = (Vertex*) face->getVertexHandle(j);
3333           vertex->setPositionIndex(vertexA);
3334         }
3335       }
3336 
3337       // remove consecutive vertices
3338       for(unsigned int j=0; j<face->getNumVertices(); j++) // over all vertices of this face
3339       {
3340         unsigned int vtxIndexA = face->getVertex(j).getPositionIndex();
3341         unsigned int vtxIndexB = face->getVertex((j + 1) % face->getNumVertices()).getPositionIndex();
3342         if (vtxIndexA == vtxIndexB)
3343         {
3344           if (face->getNumVertices() <= 3)
3345             eraseFace = 1;
3346           else
3347             face->removeVertex(j);
3348           break;
3349         }
3350       }
3351 
3352       if (eraseFace)
3353         eraseList.push_back(iFace);
3354     }
3355 
3356     for(int i=(int)eraseList.size()-1; i>=0; i--)
3357     {
3358       //printf("Erasing face %d\n", eraseList[i]);
3359       group->removeFace(eraseList[i]);
3360     }
3361   }
3362 
3363   if (removeIsolatedVertices_)
3364     removeIsolatedVertices();
3365 }
3366 
3367 // returns 1 on success, 0 otherwise
getEdgePseudoNormal(unsigned int i,unsigned int j,Vec3d * pseudoNormal) const3368 int ObjMesh::getEdgePseudoNormal(unsigned int i, unsigned int j, Vec3d * pseudoNormal) const
3369 {
3370   pair<unsigned int,unsigned int> edge;
3371 
3372   if (i < j)
3373   {
3374     edge.first = i;
3375     edge.second = j;
3376   }
3377   else
3378   {
3379     edge.first = j;
3380     edge.second = i;
3381   }
3382 
3383   map< pair<unsigned int, unsigned int> , Vec3d > :: const_iterator iter = edgePseudoNormals.find(edge);
3384 
3385   if (iter != edgePseudoNormals.end())
3386   {
3387     *pseudoNormal = iter->second;
3388     return 0;
3389   }
3390 
3391   return 1;
3392 }
3393 
computeFaceSurfaceArea(const Face & face) const3394 double ObjMesh::computeFaceSurfaceArea(const Face & face) const
3395 {
3396   double faceSurfaceArea = 0;
3397 
3398   // base vertex
3399   Vec3d basePos = getPosition(face.getVertex(0));
3400 
3401   for ( unsigned int iVertex = 1; iVertex < face.getNumVertices()-1; iVertex++ )
3402   {
3403      Vec3d pos1 = getPosition(face.getVertex(iVertex));
3404      Vec3d pos2 = getPosition(face.getVertex(iVertex + 1));
3405      faceSurfaceArea += fabs(computeTriangleSurfaceArea(basePos, pos1, pos2));
3406   }
3407 
3408   return faceSurfaceArea;
3409 }
3410 
computeFaceCentroid(const Face & face) const3411 Vec3d ObjMesh::computeFaceCentroid(const Face & face) const
3412 {
3413   Vec3d centroid = 0.0;
3414   for ( unsigned int iVertex = 0; iVertex < face.getNumVertices(); iVertex++ )
3415      centroid += getPosition(face.getVertex(iVertex));
3416   centroid /= face.getNumVertices();
3417 
3418   return centroid;
3419 }
3420 
computeSpecificInertiaTensor(Vec3d & v0,Vec3d & v1,Vec3d & v2,double t[6]) const3421 void ObjMesh::computeSpecificInertiaTensor(Vec3d & v0, Vec3d & v1, Vec3d & v2, double t[6]) const
3422 {
3423 
3424   t[0] = (v0[1]*v0[1] + v0[2]*v0[2] + v1[1]*v1[1] + v1[2]*v1[2] +
3425     v1[1]*v2[1] + v2[1]*v2[1] + v0[1]*(v1[1] + v2[1]) +
3426     v1[2]*v2[2] + v2[2]*v2[2] + v0[2]*(v1[2] + v2[2]))/6;
3427 
3428   t[1] = (-2*v1[0]*v1[1] - v1[1]*v2[0] - v0[1]*(v1[0] + v2[0])
3429      - v1[0]*v2[1] - 2*v2[0]*v2[1] -
3430      v0[0]*(2*v0[1] + v1[1] + v2[1]))/12;
3431 
3432   t[2] = (-2*v1[0]*v1[2] - v1[2]*v2[0] - v0[2]*(v1[0] + v2[0]) -
3433     v1[0]*v2[2] - 2*v2[0]*v2[2] -
3434     v0[0]*(2*v0[2] + v1[2] + v2[2]))/12;
3435 
3436   t[3] =  (v0[0]*v0[0] + v0[2]*v0[2] + v1[0]*v1[0] + v1[2]*v1[2] +
3437     v1[0]*v2[0] + v2[0]*v2[0] + v0[0]*(v1[0] + v2[0]) +
3438     v1[2]*v2[2] + v2[2]*v2[2] + v0[2]*(v1[2] + v2[2]))/6;
3439 
3440   t[4] = (-2*v1[1]*v1[2] - v1[2]*v2[1] -
3441     v0[2]*(v1[1] + v2[1]) - v1[1]*v2[2] - 2*v2[1]*v2[2] -
3442     v0[1]*(2*v0[2] + v1[2] + v2[2]))/12;
3443 
3444   t[5] = (v0[0]*v0[0] + v0[1]*v0[1] + v1[0]*v1[0] + v1[1]*v1[1] +
3445     v1[0]*v2[0] + v2[0]*v2[0] + v0[0]*(v1[0] + v2[0]) +
3446     v1[1]*v2[1] + v2[1]*v2[1] + v0[1]*(v1[1] + v2[1]))/6;
3447 }
3448 
dirname(const char * path,char * result)3449 void ObjMesh::dirname(const char * path, char * result)
3450 {
3451   // seek for last '/' or '\'
3452   const char * ch = path;
3453   int lastPos = -1;
3454   int pos=0;
3455 
3456   while (*ch != 0)
3457   {
3458     if (*ch == '\\')
3459 	  lastPos = pos;
3460 
3461     if (*ch == '/')
3462 	  lastPos = pos;
3463 
3464 	ch++;
3465 	pos++;
3466   }
3467 
3468   if (lastPos != -1)
3469   {
3470     memcpy(result,path,sizeof(char)*lastPos);
3471     result[lastPos] = 0;
3472   }
3473   else
3474   {
3475 	result[0] = '.';
3476 	result[1] = 0;
3477   }
3478 }
3479 
parseMaterials(const std::string & objMeshFilename,const std::string & materialFilename,int verbose)3480 void ObjMesh::parseMaterials(const std::string & objMeshFilename, const std::string & materialFilename, int verbose)
3481 {
3482   FILE * file;
3483   //char buf[128];
3484   //unsigned int numMaterials;
3485 
3486   char objMeshFilenameCopy[4096];
3487   strcpy(objMeshFilenameCopy, objMeshFilename.c_str());
3488 
3489   char dir[4096];
3490   dirname(objMeshFilenameCopy,dir);
3491   char filename[4096];
3492   strcpy(filename, dir);
3493   strcat(filename, "/");
3494   strcat(filename, materialFilename.c_str());
3495 
3496   file = fopen(filename, "r");
3497   if (!file)
3498   {
3499     fprintf(stderr, " parseMaterials() failed: can't open material file %s.\n", filename);
3500     std::string message = "Failed to open material file '";
3501     message.append(filename);
3502     message.append("'");
3503     throw ObjMeshException(message);
3504   }
3505 
3506   double Ka[3];
3507   double Kd[3];
3508   double Ks[3];
3509   double shininess;
3510   string matName;
3511   string textureFile = string();
3512 
3513   // now, read in the data
3514   char buf[4096];
3515   unsigned int numMaterials = 0;
3516   while(fscanf(file, "%s", buf) != EOF)
3517   {
3518     switch(buf[0])
3519     {
3520       case '#':
3521         // comment
3522         // ignore the rest of line
3523         fgets_(buf, sizeof(buf), file);
3524       break;
3525 
3526       case 'n':
3527         // newmtl
3528         if (numMaterials >= 1) // flush previous material
3529           addMaterial(matName, Vec3d(Ka[0], Ka[1], Ka[2]), Vec3d(Kd[0], Kd[1], Kd[2]), Vec3d(Ks[0], Ks[1], Ks[2]), shininess, textureFile);
3530 
3531         // reset to default
3532         Ka[0] = 0.1; Ka[1] = 0.1; Ka[2] = 0.1;
3533         Kd[0] = 0.5; Kd[1] = 0.5; Kd[2] = 0.5;
3534         Ks[0] = 0.0; Ks[1] = 0.0; Ks[2] = 0.0;
3535         shininess = 65;
3536         textureFile = string();
3537 
3538         fgets_(buf, sizeof(buf), file);
3539         sscanf(buf, "%s %s", buf, buf);
3540         numMaterials++;
3541         matName = string(buf);
3542       break;
3543 
3544       case 'N':
3545         if (buf[1] == 's')
3546         {
3547           if (fscanf(file, "%lf", &shininess) < 1)
3548             printf("Warning: incorect mtl file syntax. Unable to read shininess.\n");
3549           // wavefront shininess is from [0, 1000], so scale for OpenGL
3550           shininess *= 128.0 / 1000.0;
3551         }
3552         else
3553           fgets_(buf, sizeof(buf), file); // ignore the rest of the line
3554       break;
3555 
3556       case 'K':
3557         switch(buf[1])
3558         {
3559           case 'd':
3560             if (fscanf(file, "%lf %lf %lf", &Kd[0], &Kd[1], &Kd[2]) < 3)
3561               printf("Warning: incorect mtl file syntax. Unable to read Kd.\n");
3562           break;
3563 
3564           case 's':
3565             if (fscanf(file, "%lf %lf %lf", &Ks[0], &Ks[1], &Ks[2]) < 3)
3566               printf("Warning: incorect mtl file syntax. Unable to read Ks.\n");
3567            break;
3568 
3569           case 'a':
3570             if (fscanf(file, "%lf %lf %lf", &Ka[0], &Ka[1], &Ka[2]) < 3)
3571               printf("Warning: incorect mtl file syntax. Unable to read Ka.\n");
3572           break;
3573 
3574           default:
3575             // ignore the rest of the line
3576             fgets_(buf, sizeof(buf), file);
3577           break;
3578         }
3579       break;
3580 
3581       case 'm':
3582         if (strcmp(buf, "map_Kd") == 0)
3583         {
3584           fgets_(buf, sizeof(buf), file);
3585           sscanf(buf, "%s %s", buf, buf);
3586           textureFile = string(buf);
3587           if (verbose)
3588             printf("Noticed texture %s.\n", textureFile.c_str());
3589         }
3590       break;
3591 
3592       default:
3593         // ignore the rest of the line
3594         fgets_(buf, sizeof(buf), file);
3595       break;
3596     }
3597   }
3598 
3599   if (numMaterials >= 1) // flush last material
3600     addMaterial(matName, Vec3d(Ka[0], Ka[1], Ka[2]), Vec3d(Kd[0], Kd[1], Kd[2]), Vec3d(Ks[0], Ks[1], Ks[2]), shininess, textureFile);
3601 
3602   fclose(file);
3603 }
3604 
initSurfaceSampling()3605 void ObjMesh::initSurfaceSampling()
3606 {
3607   if (!isTriangularMesh())
3608   {
3609     throw ObjMeshException("Error in init surface sampling: surface not triangular.");
3610   }
3611 
3612   double totalSurfaceArea = computeSurfaceArea();
3613   double area = 0;
3614 
3615   // over all faces
3616   for(unsigned int i = 0; i < groups.size(); i++ )
3617   {
3618     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
3619     {
3620       surfaceSamplingAreas.push_back(make_pair(area, groups[i].getFaceHandle(iFace)));
3621       ObjMesh::Face face = groups[i].getFace(iFace);
3622       area += computeFaceSurfaceArea(face) / totalSurfaceArea;
3623     }
3624   }
3625 
3626 }
3627 
getSurfaceSamplePosition(double sample) const3628 Vec3d ObjMesh::getSurfaceSamplePosition(double sample) const
3629 {
3630   unsigned int facePosition;
3631   for(facePosition=0; facePosition< surfaceSamplingAreas.size()-1; facePosition++)
3632   {
3633     if ((surfaceSamplingAreas[facePosition].first <= sample) &&
3634         (surfaceSamplingAreas[facePosition+1].first > sample))
3635       break;
3636   }
3637 
3638   // facePosition now contains the index of the face to sample from
3639   const Face * face = surfaceSamplingAreas[facePosition].second;
3640 
3641   // sample at random on the face
3642   double alpha, beta;
3643   do
3644   {
3645     alpha = 1.0 * rand() / RAND_MAX;
3646     beta = 1.0 * rand() / RAND_MAX;
3647   }
3648   while (alpha + beta > 1);
3649 
3650   double gamma = 1 - alpha - beta;
3651 
3652   Vec3d v0 = getPosition(face->getVertex(0));
3653   Vec3d v1 = getPosition(face->getVertex(1));
3654   Vec3d v2 = getPosition(face->getVertex(2));
3655 
3656   Vec3d sampledPos = alpha * v0 + beta * v1 + gamma * v2;
3657   return sampledPos;
3658 }
3659 
getMeshRadius(const Vec3d & centroid,double * radius) const3660 void ObjMesh::getMeshRadius(const Vec3d & centroid, double * radius) const
3661 {
3662   double radiusSquared = 0.0;
3663   for(std::vector<Vec3d>::const_iterator iter = vertexPositions.begin(); iter != vertexPositions.end(); iter++)
3664   {
3665     double newSquared = len2(*iter - centroid);
3666     if (newSquared > radiusSquared)
3667       radiusSquared = newSquared;
3668   }
3669   *radius = sqrt(radiusSquared);
3670 }
3671 
getMeshGeometricParameters(Vec3d * centroid,double * radius) const3672 void ObjMesh::getMeshGeometricParameters(Vec3d * centroid, double * radius) const
3673 {
3674   //find the centroid
3675   *centroid = Vec3d(0, 0, 0);
3676   for(std::vector<Vec3d>::const_iterator iter = vertexPositions.begin(); iter != vertexPositions.end(); iter++)
3677     *centroid += *iter;
3678   *centroid /= getNumVertices();
3679 
3680   // find the radius
3681   getMeshRadius(*centroid, radius);
3682 }
3683 
buildVertexFaceNeighbors()3684 void ObjMesh::buildVertexFaceNeighbors()
3685 {
3686   vertexFaceNeighbors.clear();
3687   for(unsigned int i=0; i<getNumVertices(); i++)
3688     vertexFaceNeighbors.push_back(std::list<VertexFaceNeighbor>());
3689 
3690   //go through each of the faces
3691   for(unsigned int i = 0; i < groups.size(); i++ )
3692   {
3693     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
3694     {
3695       const ObjMesh::Face * faceHandle = groups[i].getFaceHandle(iFace); // get face whose number is iFace
3696 
3697       if (faceHandle->getNumVertices() < 3)
3698         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
3699 
3700       for(unsigned int j = 0; j < faceHandle->getNumVertices(); j++)
3701       {
3702         const ObjMesh::Vertex * vertexHandle = faceHandle->getVertexHandle(j);
3703         vertexFaceNeighbors[vertexHandle->getPositionIndex()].push_back(ObjMesh::VertexFaceNeighbor(i, iFace, j));
3704       }
3705     }
3706   }
3707 }
3708 
clearVertexFaceNeighbors()3709 void ObjMesh::clearVertexFaceNeighbors()
3710 {
3711   vertexFaceNeighbors.clear();
3712 }
3713 
removeFace(unsigned int i)3714 void ObjMesh::Group::removeFace(unsigned int i)
3715 {
3716   faces.erase(faces.begin() + i);
3717 }
3718 
ObjMeshException(const std::string & reason)3719 ObjMeshException::ObjMeshException(const std::string & reason)
3720 {
3721   std::ostringstream oss;
3722   oss << "Error:  " << reason;
3723   std::cout << std::endl << oss.str() << std::endl;
3724 }
3725 
ObjMeshException(const std::string & reason,const std::string & filename,unsigned int line)3726 ObjMeshException::ObjMeshException(const std::string & reason, const std::string & filename, unsigned int line)
3727 {
3728   std::ostringstream oss;
3729   oss << "Error in file '" << filename << "', line " << line << ": " << reason;
3730   std::cout << std::endl << oss.str() << std::endl;
3731 }
3732 
operator ==(const Material & mat2) const3733 bool ObjMesh::Material::operator==(const Material & mat2) const
3734 {
3735   if (len(Ka - mat2.Ka) > 1e-7)
3736     return false;
3737 
3738   if (len(Kd - mat2.Kd) > 1e-7)
3739     return false;
3740 
3741   if (len(Ks - mat2.Ks) > 1e-7)
3742     return false;
3743 
3744   if (fabs(shininess - mat2.shininess) > 1e-7)
3745     return false;
3746 
3747   return true;
3748 }
3749 
removeDuplicatedMaterials()3750 int ObjMesh::removeDuplicatedMaterials()
3751 {
3752   unsigned int numMaterials = getNumMaterials();
3753   vector<int> reNumberVector(numMaterials);
3754 
3755   vector<Material> newMaterials;
3756 
3757   // detected duplicated materials
3758   for(unsigned int i=0; i<numMaterials; i++)
3759   {
3760     bool newMaterial = true;
3761     for(unsigned int j=0; j<newMaterials.size(); j++)
3762     {
3763       if (newMaterials[j] == materials[i])
3764       {
3765         newMaterial = false;
3766         reNumberVector[i] = j;
3767         break;
3768       }
3769     }
3770 
3771     if (newMaterial)
3772     {
3773       newMaterials.push_back(materials[i]);
3774       reNumberVector[i] = newMaterials.size() - 1;
3775     }
3776   }
3777 
3778   materials = newMaterials;
3779 
3780   // correct the groups
3781   for(unsigned int i=0; i<getNumGroups(); i++)
3782     groups[i].setMaterialIndex(reNumberVector[groups[i].getMaterialIndex()]);
3783 
3784   return materials.size();
3785 }
3786 
exportGeometry(int * numVertices,double ** vertices,int * numTriangles,int ** triangles,int * numGroups,int ** triangleGroups) const3787 void ObjMesh::exportGeometry(int * numVertices, double ** vertices, int * numTriangles , int ** triangles, int * numGroups, int ** triangleGroups) const
3788 {
3789   // set vertices
3790   if (numVertices != NULL)
3791     *numVertices = vertexPositions.size();
3792   if (vertices != NULL)
3793   {
3794     *vertices = (double*) malloc (sizeof(double) * 3 * vertexPositions.size());
3795     for(size_t i=0; i< vertexPositions.size(); i++)
3796     {
3797       Vec3d vtx = getPosition(i);
3798       (*vertices)[3*i+0] = vtx[0];
3799       (*vertices)[3*i+1] = vtx[1];
3800       (*vertices)[3*i+2] = vtx[2];
3801     }
3802   }
3803 
3804   if (numGroups != NULL)
3805     *numGroups = getNumGroups();
3806 
3807   if (numTriangles == NULL && triangles == NULL && triangleGroups == NULL)
3808     return;
3809 
3810   // count #triangles
3811   int nt = 0;
3812   for(unsigned int i=0; i < groups.size(); i++) // over all groups
3813     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
3814     {
3815       const Face * face = groups[i].getFaceHandle(j);
3816       if (face->getNumVertices() < 3)
3817         continue;
3818       nt += face->getNumVertices() - 2;
3819     }
3820 
3821   if(numTriangles != NULL)
3822     *numTriangles = nt;
3823   if (triangles == NULL && triangleGroups == NULL)
3824     return;
3825 
3826   // set triangles
3827   if(triangles != NULL)
3828     *triangles = (int*) malloc (sizeof(int) * 3 * nt);
3829 
3830   // set triangle groups while setting triangle positions (easy addition)
3831   if (triangleGroups != NULL)
3832     *triangleGroups = (int*) calloc (nt, sizeof(int)); // set all groups to 0 just in case
3833 
3834   for(unsigned int i=0, tri=0; i < groups.size(); i++) // over all groups
3835   {
3836     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
3837     {
3838       const Face * face = groups[i].getFaceHandle(j);
3839       if (face->getNumVertices() < 3)
3840       {
3841         printf("Warning: encountered a face with fewer than 3 vertices.\n");
3842         continue;
3843       }
3844 
3845       unsigned int faceDegree = face->getNumVertices();
3846 
3847       // triangulate the face
3848 
3849       // get the vertices:
3850       vector<Vertex> vertices;
3851       for(unsigned int k=0; k<face->getNumVertices(); k++)
3852         vertices.push_back(face->getVertex(k));
3853 
3854       // set triangle vertex positions
3855       if(triangles != NULL)
3856       {
3857         (*triangles)[3*tri+0] = vertices[0].getPositionIndex();
3858         (*triangles)[3*tri+1] = vertices[1].getPositionIndex();
3859         (*triangles)[3*tri+2] = vertices[2].getPositionIndex();
3860       }
3861 
3862       // set triangle group
3863       if (triangleGroups != NULL)
3864         (*triangleGroups)[tri] = i;
3865 
3866       // increment triangle counter
3867       tri++;
3868 
3869       for(unsigned int k=2; k<faceDegree-1; k++)
3870       {
3871         if(triangles != NULL)
3872         {
3873           (*triangles)[3*tri+0] = vertices[0].getPositionIndex();
3874           (*triangles)[3*tri+1] = vertices[k].getPositionIndex();
3875           (*triangles)[3*tri+2] = vertices[k+1].getPositionIndex();
3876         }
3877 
3878         // set triangle group
3879         if (triangleGroups != NULL)
3880           (*triangleGroups)[tri] = i;
3881 
3882         // increment triangle counter
3883         tri++;
3884       }
3885     }
3886   }
3887   //printf("Exported %d vertices and %d triangles.\n", vertexPositions.size(), nt);
3888 }
3889 
exportFaceGeometry(int * numVertices,double ** vertices,int * numFaces,int ** faceCardinality,int ** faces,int * numGroups,int ** faceGroups) const3890 void ObjMesh::exportFaceGeometry(int * numVertices, double ** vertices, int * numFaces, int ** faceCardinality, int ** faces, int * numGroups, int ** faceGroups) const
3891 {
3892   // set vertices
3893   if (numVertices != NULL)
3894     *numVertices = vertexPositions.size();
3895   if (vertices != NULL)
3896   {
3897     *vertices = (double*) malloc (sizeof(double) * 3 * *numVertices);
3898     for(int i=0; i< *numVertices; i++)
3899     {
3900       Vec3d vtx = getPosition(i);
3901       (*vertices)[3*i+0] = vtx[0];
3902       (*vertices)[3*i+1] = vtx[1];
3903       (*vertices)[3*i+2] = vtx[2];
3904     }
3905   }
3906 
3907   if(numGroups != NULL)
3908     *numGroups = getNumGroups();
3909 
3910   if (numFaces == NULL && faceCardinality == NULL && faces == NULL && faceGroups == NULL)
3911     return;
3912 
3913   // set faces
3914   int nf = 0, totalCardinality = 0;
3915   for(unsigned int i=0; i < groups.size(); i++) // over all groups
3916     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
3917     {
3918       const Face * face = groups[i].getFaceHandle(j);
3919       if (face->getNumVertices() < 3)
3920         continue;
3921       nf++;
3922       totalCardinality += face->getNumVertices();
3923     }
3924 
3925   if(numFaces != NULL)
3926     (*numFaces) = nf;
3927   if (faceCardinality == NULL && faces == NULL && faceGroups == NULL)
3928     return;
3929 
3930   if(faceCardinality != NULL)
3931     *faceCardinality = (int*) malloc (sizeof(int) * nf);
3932 
3933   if(faces != NULL)
3934     *faces = (int*) malloc (sizeof(int) * totalCardinality);
3935 
3936   if(faceGroups != NULL)
3937     *faceGroups = (int*) calloc (*numFaces, sizeof(int));
3938 
3939   int faceCounter = 0;
3940   int tc = 0;
3941   for(unsigned int i=0; i < groups.size(); i++) // over all groups
3942     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
3943     {
3944       const Face * face = groups[i].getFaceHandle(j);
3945       if (face->getNumVertices() < 3)
3946       {
3947         printf("Warning: encountered a face with fewer than 3 vertices.\n");
3948         continue;
3949       }
3950 
3951       int faceDegree = (int)face->getNumVertices();
3952       if(faceCardinality != NULL)
3953         (*faceCardinality)[faceCounter] = faceDegree;
3954       if(faceGroups != NULL)
3955         (*faceGroups)[faceCounter] = i;
3956       if(faces != NULL)
3957         for(unsigned int k=0; k<face->getNumVertices(); k++)
3958           (*faces)[tc + k] = face->getVertex(k).getPositionIndex();
3959 
3960       faceCounter++;
3961       tc += faceDegree;
3962     }
3963   //printf("Exported %d vertices and %d faces. Average number of vertices: %G\n", *numVertices, *numFaces, 1.0 * totalCardinality / (*numFaces));
3964 }
3965 
exportUVGeometry(int * numUVVertices,double ** UVvertices,int * numUVTriangles,int ** UVTriangles) const3966 void ObjMesh::exportUVGeometry(int * numUVVertices, double ** UVvertices, int * numUVTriangles, int ** UVTriangles) const // exports the geometry in the texture coordinate space
3967 {
3968   // count num UV triangles
3969   *numUVTriangles = 0;
3970   for(unsigned int i=0; i < groups.size(); i++) // over all groups
3971     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
3972     {
3973       const Face * face = groups[i].getFaceHandle(j);
3974       if (face->getNumVertices() < 3)
3975         continue;
3976       *numUVTriangles += face->getNumVertices() - 2;
3977     }
3978 
3979   *numUVVertices = 3 * *numUVTriangles;
3980   *UVvertices = (double*) malloc (sizeof(double) * 3 * *numUVVertices);
3981   *UVTriangles = (int*) malloc (sizeof(int) * 3 * *numUVTriangles);
3982 
3983   int tri = 0;
3984   for(unsigned int i=0; i < groups.size(); i++) // over all groups
3985   {
3986     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
3987     {
3988       const Face * face = groups[i].getFaceHandle(j);
3989       if (face->getNumVertices() < 3)
3990       {
3991         printf("Warning: encountered a face with fewer than 3 vertices.\n");
3992         continue;
3993       }
3994 
3995       // get the vertices:
3996       vector<Vertex> vertices;
3997       for(unsigned int k=0; k<face->getNumVertices(); k++)
3998         vertices.push_back(face->getVertex(k));
3999 
4000       // triangulate the face
4001       for(int i=0; i<(int) face->getNumVertices()-2; i++)
4002       {
4003         Vec3d uv0 = getTextureCoordinate(vertices[0].getTextureCoordinateIndex());;
4004         Vec3d uv1 = getTextureCoordinate(vertices[i+1].getTextureCoordinateIndex());;
4005         Vec3d uv2 = getTextureCoordinate(vertices[i+2].getTextureCoordinateIndex());;
4006         (*UVvertices)[9*tri+0] = uv0[0];
4007         (*UVvertices)[9*tri+1] = uv0[1];
4008         (*UVvertices)[9*tri+2] = uv0[2];
4009         (*UVvertices)[9*tri+3] = uv1[0];
4010         (*UVvertices)[9*tri+4] = uv1[1];
4011         (*UVvertices)[9*tri+5] = uv1[2];
4012         (*UVvertices)[9*tri+6] = uv2[0];
4013         (*UVvertices)[9*tri+7] = uv2[1];
4014         (*UVvertices)[9*tri+8] = uv2[2];
4015 
4016         (*UVTriangles)[3*tri+0] = 3*tri+0;
4017         (*UVTriangles)[3*tri+1] = 3*tri+1;
4018         (*UVTriangles)[3*tri+2] = 3*tri+2;
4019 
4020         // increment triangle counter
4021         tri++;
4022       }
4023     }
4024   }
4025 }
4026 
4027 // allows one to query the vertex indices of each triangle
4028 // order of triangles is same as in "exportGeometry": for every group, traverse all faces, and tesselate each face into triangles
initTriangleLookup()4029 void ObjMesh::initTriangleLookup()
4030 {
4031   int numVertices;
4032   double * vertices;
4033   int numTriangles;
4034   int * trianglesV;
4035   exportGeometry(&numVertices, &vertices, &numTriangles , &trianglesV);
4036 
4037   triangles.clear();
4038   for(int i=0; i<3*numTriangles; i++)
4039     triangles.push_back(trianglesV[i]);
4040 
4041   free(trianglesV);
4042   free(vertices);
4043 }
4044 
clearTriangleLookup()4045 void ObjMesh::clearTriangleLookup()
4046 {
4047   triangles.clear();
4048 }
4049 
getTriangle(int triangleIndex,int * vtxA,int * vtxB,int * vtxC)4050 void ObjMesh::getTriangle(int triangleIndex, int * vtxA, int * vtxB, int * vtxC) // must call "initTriangleLookup" first
4051 {
4052   *vtxA = triangles[3*triangleIndex+0];
4053   *vtxB = triangles[3*triangleIndex+1];
4054   *vtxC = triangles[3*triangleIndex+2];
4055 }
4056 
renumberVertices(const vector<int> & permutation)4057 void ObjMesh::renumberVertices(const vector<int> & permutation)
4058 {
4059   unsigned int numVertices = getNumVertices();
4060 
4061   // renumber vertices
4062   if (permutation.size() != numVertices)
4063   {
4064     printf("Error: permutation size mismatch.\n");
4065   }
4066 
4067   vector<Vec3d> vertexPositionsBuffer(numVertices);
4068   for(unsigned int i=0; i < numVertices; i++)
4069     vertexPositionsBuffer[permutation[i]] = vertexPositions[i];
4070 
4071   vertexPositions = vertexPositionsBuffer;
4072 
4073   // renumber faces
4074   for(unsigned int i=0; i < groups.size(); i++) // over all groups
4075     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
4076     {
4077       const Face * face = groups[i].getFaceHandle(j);
4078       if (face->getNumVertices() < 3)
4079       {
4080         printf("Warning: encountered a face with fewer than 3 vertices.\n");
4081         continue;
4082       }
4083 
4084       for(unsigned int k=0; k<face->getNumVertices(); k++)
4085       {
4086         Vertex * vtx = (Vertex*) face->getVertexHandle(k);
4087         vtx->setPositionIndex(permutation[vtx->getPositionIndex()]);
4088       }
4089     }
4090 }
4091 
computeNumIsolatedVertices() const4092 int ObjMesh::computeNumIsolatedVertices() const
4093 {
4094   vector<int> counter(getNumVertices(), 0);
4095 
4096   for(unsigned int i=0; i < groups.size(); i++) // over all groups
4097     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
4098     {
4099       const Face * face = groups[i].getFaceHandle(j);
4100       for(unsigned int k=0; k<face->getNumVertices(); k++)
4101         counter[face->getVertex(k).getPositionIndex()]++;
4102     }
4103 
4104   int numIsolatedVertices = 0;
4105   for(unsigned int i=0; i<getNumVertices(); i++)
4106     if (counter[i] == 0)
4107       numIsolatedVertices++;
4108 
4109   return numIsolatedVertices;
4110 }
4111 
removeIsolatedVertices()4112 int ObjMesh::removeIsolatedVertices()
4113 {
4114   vector<int> counter(getNumVertices(), 0);
4115 
4116   for(unsigned int i=0; i < groups.size(); i++) // over all groups
4117     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
4118     {
4119       const Face * face = groups[i].getFaceHandle(j);
4120       for(unsigned int k=0; k<face->getNumVertices(); k++)
4121         counter[face->getVertex(k).getPositionIndex()]++;
4122     }
4123 
4124   map<int,int> oldToNew;
4125   map<int,int> newToOld;
4126 
4127   int numConnectedVertices = 0;
4128   for(unsigned int i=0; i<getNumVertices(); i++)
4129   {
4130     if (counter[i] != 0)
4131     {
4132       oldToNew.insert(make_pair(i, numConnectedVertices));
4133       newToOld.insert(make_pair(numConnectedVertices, i));
4134       numConnectedVertices++;
4135     }
4136   }
4137 
4138   int numOriginalVertices = getNumVertices();
4139   int numIsolatedVertices = numOriginalVertices - numConnectedVertices;
4140 
4141   // relink vertices, remove old vertices
4142   for(int i=0; i<numConnectedVertices; i++)
4143     vertexPositions[i] = vertexPositions[newToOld[i]];
4144 
4145   for(int i=numConnectedVertices; i<numOriginalVertices; i++)
4146     vertexPositions.pop_back();
4147 
4148   // renumber vertices inside faces
4149   for(unsigned int i=0; i < groups.size(); i++) // over all groups
4150     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
4151     {
4152       const Face * face = groups[i].getFaceHandle(j);
4153       for(unsigned int k=0; k<face->getNumVertices(); k++)
4154       {
4155         Vertex * vertex = (Vertex*) face->getVertexHandle(k);
4156         int oldPositionIndex = vertex->getPositionIndex();
4157         vertex->setPositionIndex(oldToNew[oldPositionIndex]);
4158       }
4159     }
4160 
4161   return numIsolatedVertices;
4162 }
4163 
removeIsolatedNormals()4164 int ObjMesh::removeIsolatedNormals()
4165 {
4166   vector<int> counter(getNumNormals(), 0);
4167 
4168   for(unsigned int i=0; i < groups.size(); i++) // over all groups
4169     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
4170     {
4171       const Face * face = groups[i].getFaceHandle(j);
4172       for(unsigned int k=0; k<face->getNumVertices(); k++)
4173       {
4174         if (face->getVertex(k).hasNormalIndex())
4175           counter[face->getVertex(k).getNormalIndex()]++;
4176       }
4177     }
4178 
4179   map<int,int> oldToNew;
4180   map<int,int> newToOld;
4181 
4182   int numConnectedNormals = 0;
4183   for(unsigned int i=0; i<getNumNormals(); i++)
4184   {
4185     if (counter[i] != 0)
4186     {
4187       oldToNew.insert(make_pair(i, numConnectedNormals));
4188       newToOld.insert(make_pair(numConnectedNormals, i));
4189       numConnectedNormals++;
4190     }
4191   }
4192 
4193   int numOriginalNormals = getNumNormals();
4194   int numIsolatedNormals = numOriginalNormals - numConnectedNormals;
4195 
4196   // relink normals, remove old normals
4197   for(int i=0; i<numConnectedNormals; i++)
4198     normals[i] = normals[newToOld[i]];
4199 
4200   for(int i=numConnectedNormals; i<numOriginalNormals; i++)
4201     normals.pop_back();
4202 
4203   // renumber normals inside faces
4204   for(unsigned int i=0; i < groups.size(); i++) // over all groups
4205     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
4206     {
4207       const Face * face = groups[i].getFaceHandle(j);
4208       for(unsigned int k=0; k<face->getNumVertices(); k++)
4209       {
4210         Vertex * vertex = (Vertex*) face->getVertexHandle(k);
4211         if (vertex->hasNormalIndex())
4212         {
4213           int oldNormalIndex = vertex->getNormalIndex();
4214           vertex->setNormalIndex(oldToNew[oldNormalIndex]);
4215         }
4216       }
4217     }
4218 
4219   return numIsolatedNormals;
4220 }
4221 
removeIsolatedTextureCoordinates()4222 int ObjMesh::removeIsolatedTextureCoordinates()
4223 {
4224   vector<int> counter(getNumTextureCoordinates(), 0);
4225 
4226   for(unsigned int i=0; i < groups.size(); i++) // over all groups
4227     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
4228     {
4229       const Face * face = groups[i].getFaceHandle(j);
4230       for(unsigned int k=0; k<face->getNumVertices(); k++)
4231       {
4232         if (face->getVertex(k).hasTextureCoordinateIndex())
4233           counter[face->getVertex(k).getTextureCoordinateIndex()]++;
4234       }
4235     }
4236 
4237   map<int,int> oldToNew;
4238   map<int,int> newToOld;
4239 
4240   int numConnectedTextureCoordinates = 0;
4241   for(unsigned int i=0; i<getNumTextureCoordinates(); i++)
4242   {
4243     if (counter[i] != 0)
4244     {
4245       oldToNew.insert(make_pair(i, numConnectedTextureCoordinates));
4246       newToOld.insert(make_pair(numConnectedTextureCoordinates, i));
4247       numConnectedTextureCoordinates++;
4248     }
4249   }
4250 
4251   int numOriginalTextureCoordinates = getNumTextureCoordinates();
4252   int numIsolatedTextureCoordinates = numOriginalTextureCoordinates - numConnectedTextureCoordinates;
4253 
4254   // relink textureCoordinates, remove old textureCoordinates
4255   for(int i=0; i<numConnectedTextureCoordinates; i++)
4256     textureCoordinates[i] = textureCoordinates[newToOld[i]];
4257 
4258   for(int i=numConnectedTextureCoordinates; i<numOriginalTextureCoordinates; i++)
4259     textureCoordinates.pop_back();
4260 
4261   // renumber textureCoordinates inside faces
4262   for(unsigned int i=0; i < groups.size(); i++) // over all groups
4263     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
4264     {
4265       const Face * face = groups[i].getFaceHandle(j);
4266       for(unsigned int k=0; k<face->getNumVertices(); k++)
4267       {
4268         Vertex * vertex = (Vertex*) face->getVertexHandle(k);
4269         if (vertex->hasTextureCoordinateIndex())
4270         {
4271           int oldTextureCoordinateIndex = vertex->getTextureCoordinateIndex();
4272           vertex->setTextureCoordinateIndex(oldToNew[oldTextureCoordinateIndex]);
4273         }
4274       }
4275     }
4276 
4277   return numIsolatedTextureCoordinates;
4278 }
4279 
mergeGroups(const vector<int> & groupIndicesIn)4280 void ObjMesh::mergeGroups(const vector<int> & groupIndicesIn)
4281 {
4282   if (groupIndicesIn.size() < 1)
4283     return;
4284 
4285   // sort group indices
4286   vector<int> groupIndices(groupIndicesIn);
4287   sort(groupIndices.begin(), groupIndices.end());
4288 
4289   // the groups will be copied into the group with the smallest index
4290   Group * groupDest = (Group*) getGroupHandle(*(groupIndices.begin()));
4291 
4292   int count = 0;
4293   for(vector<int> :: reverse_iterator iter = groupIndices.rbegin(); iter != groupIndices.rend(); iter++)
4294   {
4295     if (count != (int)groupIndices.size() - 1) // no need to process the last group (with smallest index)
4296     {
4297       int groupIndex = *iter;
4298       Group * groupSource = (Group*) getGroupHandle(groupIndex);
4299       for(int i=0; i<(int)groupSource->getNumFaces(); i++)
4300         groupDest->addFace(groupSource->getFace(i));
4301       //printf("Removing group %d. Num groups = %d\n", groupIndex, (int)groups.size());
4302       groups[groupIndex] = groups.back();
4303       groups.pop_back();
4304     }
4305     count++;
4306   }
4307 }
4308 
removeEmptyGroups()4309 void ObjMesh::removeEmptyGroups()
4310 {
4311   for(vector<Group> :: iterator iter = groups.begin(); iter != groups.end(); iter++)
4312   {
4313     if (iter->getNumFaces() == 0)
4314     {
4315       *iter = groups.back();
4316       groups.pop_back();
4317     }
4318   }
4319 }
4320 
appendMesh(ObjMesh * mesh)4321 void ObjMesh::appendMesh(ObjMesh * mesh)
4322 {
4323   // add vertices
4324   int numVerticesCurrent = getNumVertices();
4325   for(unsigned int i=0; i<mesh->getNumVertices(); i++)
4326     addVertexPosition(mesh->getPosition(i));
4327 
4328   // add normals
4329   int numNormalsCurrent = getNumNormals();
4330   for(unsigned int i=0; i<mesh->getNumNormals(); i++)
4331     addVertexNormal(mesh->getNormal(i));
4332 
4333   // add texture coordinates
4334   int numTextureCoordinatesCurrent = getNumTextureCoordinates();
4335   for(unsigned int i=0; i<mesh->getNumTextureCoordinates(); i++)
4336     addTextureCoordinate(mesh->getTextureCoordinate(i));
4337 
4338   // add materials
4339   int numMaterialsCurrent = getNumMaterials();
4340   for(unsigned int i=0; i<mesh->getNumMaterials(); i++)
4341     addMaterial(mesh->getMaterial(i));
4342 
4343   for(unsigned int i=0; i<mesh->getNumGroups(); i++)
4344   {
4345     const ObjMesh::Group * group = mesh->getGroupHandle(i);
4346     addGroup(group->getName());
4347     unsigned int newGroupID = getNumGroups() - 1;
4348     ObjMesh::Group * newGroup = (ObjMesh::Group*) getGroupHandle(newGroupID);
4349     newGroup->setMaterialIndex(numMaterialsCurrent + group->getMaterialIndex());
4350 
4351     // over all faces in the group of the current obj file
4352     for(unsigned int j=0; j<group->getNumFaces(); j++)
4353     {
4354       const ObjMesh::Face * face = group->getFaceHandle(j);
4355       for(unsigned int k=0; k<face->getNumVertices(); k++)
4356       {
4357         ObjMesh::Vertex * vertex = (ObjMesh::Vertex*) face->getVertexHandle(k);
4358         vertex->setPositionIndex(vertex->getPositionIndex() + numVerticesCurrent);
4359         if (vertex->hasNormalIndex())
4360           vertex->setNormalIndex(vertex->getNormalIndex() + numNormalsCurrent);
4361         if (vertex->hasTextureCoordinateIndex())
4362           vertex->setTextureCoordinateIndex(vertex->getTextureCoordinateIndex() + numTextureCoordinatesCurrent);
4363       }
4364       addFaceToGroup(*face,newGroupID);
4365     }
4366   }
4367 }
4368 
4369 // removes all whitespace characters from string s
removeWhitespace(char * s)4370 void ObjMesh::removeWhitespace(char * s)
4371 {
4372   char * p = s;
4373   while (*p != 0)
4374   {
4375     // erase empty space
4376     while (*p == ' ')
4377     {
4378       char * q = p;
4379       while (*q != 0) // move characters to the left, by one character
4380       {
4381         *q = *(q+1);
4382         q++;
4383       }
4384     }
4385     p++;
4386   }
4387 }
4388 
4389 // shrinks all whitespace to a single space character; also removes any whitespace before end of string
convertWhitespaceToSingleBlanks(char * s)4390 void ObjMesh::convertWhitespaceToSingleBlanks(char * s)
4391 {
4392   char * p = s;
4393   while (*p != 0)
4394   {
4395     // erase consecutive empty space characters, or end-of-string spaces
4396     while ((*p == ' ') && ((*(p+1) == 0) || (*(p+1) == ' ')))
4397     {
4398       char * q = p;
4399       while (*q != 0) // move characters to the left, by one character
4400       {
4401         *q = *(q+1);
4402         q++;
4403       }
4404     }
4405     p++;
4406   }
4407 }
4408 
fgets_(char * s,int n,FILE * stream)4409 void ObjMesh::fgets_(char * s, int n, FILE * stream)
4410 {
4411   char * result = fgets(s, n, stream);
4412   if (result == NULL)
4413     printf("Warning: bad input file syntax. fgets_ returned NULL.\n");
4414   return;
4415 }
4416 
getGroupIndex(const std::string name) const4417 unsigned int ObjMesh::getGroupIndex(const std::string name) const
4418 {
4419   int count = 0;
4420   for(std::vector<Group>::const_iterator itr = groups.begin(); itr != groups.end(); itr++)
4421   {
4422     if (itr->getName() == name)
4423       return count;
4424     count++;
4425   }
4426 
4427   std::ostringstream oss;
4428   oss << "Invalid group name: '" << name << "'.";
4429   throw ObjMeshException(oss.str());
4430 
4431   return 0;
4432 }
4433 
getMaterialIndex(const std::string name) const4434 unsigned int ObjMesh::getMaterialIndex(const std::string name) const
4435 {
4436   int count = 0;
4437   for(std::vector<Material>::const_iterator itr = materials.begin(); itr != materials.end(); itr++)
4438   {
4439     if (itr->getName() == name)
4440       return count;
4441     count++;
4442   }
4443 
4444   std::ostringstream oss;
4445   oss << "Invalid material name: '" << name << "'.";
4446   throw ObjMeshException(oss.str());
4447 
4448   return 0;
4449 }
4450 
removeGroup(const int groupIndex)4451 void ObjMesh::removeGroup(const int groupIndex)
4452 {
4453   if ((groupIndex >= (int) groups.size()) || (groupIndex < 0))
4454   {
4455     printf("Warning: cannot remove group %d. Invalid group number.\n", groupIndex);
4456     return;
4457   }
4458   groups[groupIndex] = groups[groups.size() - 1];
4459   groups.pop_back();
4460   computeBoundingBox();
4461 }
4462 
removeGroup(const std::string name)4463 void ObjMesh::removeGroup(const std::string name)
4464 {
4465   int groupIndex = getGroupIndex(name);
4466   removeGroup(groupIndex);
4467 }
4468 
removeAllGroups()4469 void ObjMesh::removeAllGroups()
4470 {
4471   groups.clear();
4472   computeBoundingBox();
4473 }
4474 
4475 // 0 = no group uses a material that references a texture image, 1 = otherwise
usesTextureMapping()4476 int ObjMesh::usesTextureMapping()
4477 {
4478   int result = 0;
4479   for(std::vector<Group>::const_iterator itr = groups.begin(); itr != groups.end(); itr++)
4480   {
4481     const Material * material = getMaterialHandle(itr->getMaterialIndex());
4482     result = result | ((int)material->hasTextureFilename());
4483   }
4484   return result;
4485 }
4486 
loadObjMeshesFromBinary(const std::string & binaryFilename,int * numObjMeshes,ObjMesh *** objMeshes,int verbose)4487 int ObjMesh::loadObjMeshesFromBinary(const std::string & binaryFilename, int * numObjMeshes, ObjMesh *** objMeshes, int verbose)
4488 {
4489   FILE * fin = fopen(binaryFilename.c_str(), "rb");
4490   if (fin == NULL)
4491   {
4492     if (verbose)
4493       printf("Error in ObjMesh::loadObjMeshesFromBinary: cannot open %s to load.\n", binaryFilename.c_str());
4494     return 1;
4495   }
4496 
4497   int code = loadObjMeshesFromBinary(fin, numObjMeshes, objMeshes, verbose);
4498 
4499   for(int objMeshIndex = 0; objMeshIndex < *numObjMeshes; objMeshIndex++)
4500     if ((*objMeshes)[objMeshIndex])
4501       (*objMeshes)[objMeshIndex]->filename = binaryFilename;
4502 
4503   return code;
4504 }
4505 
loadObjMeshesFromBinary(FILE * fin,int * numObjMeshes,ObjMesh *** objMeshes,int verbose)4506 int ObjMesh::loadObjMeshesFromBinary(FILE * fin, int * numObjMeshes, ObjMesh *** objMeshes, int verbose)
4507 {
4508   // read the number of obj meshes
4509   unsigned int items = fread(numObjMeshes, sizeof(int), 1, fin);
4510   if (items != 1)
4511     return 1;
4512 
4513   int numMeshes = *numObjMeshes;
4514 
4515   if (verbose)
4516     printf("number of obj meshes to be read from binary: %d\n", numMeshes);
4517 
4518   // read how many bytes are stored for every obj mesh
4519   unsigned int * bytesWritten = (unsigned int *) calloc (numMeshes, sizeof(unsigned int));
4520   items = fread(bytesWritten, sizeof(unsigned int), numMeshes, fin);
4521   if ((int)items != numMeshes)
4522     return 1;
4523 
4524   if (verbose)
4525   {
4526     printf("number of bytes for each obj mesh: \n");
4527     for(int i=0; i<numMeshes; i++)
4528       printf("%u, ", bytesWritten[i]);
4529     printf("\n");
4530   }
4531 
4532   // compute the total bytes
4533   unsigned int totalBytes = 0;
4534   for(int i=0; i<numMeshes; i++)
4535     totalBytes += bytesWritten[i];
4536 
4537   // allocate memory for obj meshes
4538   (*objMeshes) = (ObjMesh **) malloc (sizeof(ObjMesh *) * numMeshes);
4539   for (int i=0; i<numMeshes; i++)
4540     (*objMeshes)[i] = NULL;
4541 
4542   // read entire block from the memory
4543   unsigned char * memory = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes);
4544   items = fread(memory, sizeof(unsigned char), totalBytes, fin);
4545 
4546   if (verbose)
4547     printf("total bytes excluding header: %u\n", totalBytes);
4548 
4549   // compute the offset for every obj mesh
4550   unsigned int * offset = (unsigned int *) calloc (numMeshes, sizeof(unsigned int));
4551   for(int i=1; i<numMeshes; i++)
4552     offset[i] = offset[i-1] + bytesWritten[i-1];
4553 
4554   // load every obj mesh from memory
4555   #ifdef USE_OPENMP
4556     #pragma omp parallel for
4557   #endif
4558   for(int i=0; i<numMeshes; i++)
4559   {
4560     if (bytesWritten[i] != 0)
4561     {
4562       unsigned char * location = memory + offset[i];
4563       streamType stream = MEMORY_STREAM;
4564       int verbose = 0;
4565       (*objMeshes)[i] = new ObjMesh((void *)location, stream, verbose);
4566     }
4567   }
4568 
4569   free(bytesWritten);
4570   free(memory);
4571   free(offset);
4572 
4573   return 0;
4574 }
4575 
loadFromBinary(const std::string & filename_,int verbose)4576 int ObjMesh::loadFromBinary(const std::string & filename_, int verbose)
4577 {
4578   filename = filename_;
4579   FILE * binaryInputStream = fopen(filename_.c_str(), "rb");
4580   if (binaryInputStream == NULL)
4581   {
4582     printf("Error in ObjMesh::loadFromBinary: cannot load obj from binary file %s.\n", filename_.c_str());
4583     return 1;
4584   }
4585 
4586   if (verbose)
4587     std::cout << "Parsing .obj file '" << filename << "'." << std::endl;
4588 
4589   streamType stream = FILE_STREAM;
4590   int code = loadFromBinary(binaryInputStream, stream, verbose);
4591   fclose(binaryInputStream);
4592 
4593   // statistics
4594   if (code == 0 && verbose)
4595   {
4596     std::cout << "Parsed obj file '" << filename << "'; statistics:" << std::endl;
4597     std::cout << "   " << groups.size() << " groups," << std::endl;
4598     std::cout << "   " << getNumFaces() << " faces," << std::endl;
4599     std::cout << "   " << vertexPositions.size() << " vertices," << std::endl;
4600     std::cout << "   " << normals.size() << " normals, " << std::endl;
4601     std::cout << "   " << textureCoordinates.size() << " texture coordinates, " << std::endl;
4602   }
4603   return code;
4604 }
4605 
loadFromBinary(void * binaryInputStream_,streamType stream,int verbose)4606 int ObjMesh::loadFromBinary(void * binaryInputStream_, streamType stream, int verbose)
4607 {
4608   unsigned int (*genericRead)(void *, unsigned int, unsigned int, void *);
4609   void * binaryInputStream;
4610   if (stream == MEMORY_STREAM)
4611   {
4612     genericRead = &ObjMesh::readFromMemory;
4613     binaryInputStream = &binaryInputStream_; // a wrapper for input stream
4614   }
4615   else
4616   {
4617     genericRead = &ObjMesh::readFromFile;
4618     binaryInputStream = binaryInputStream_;
4619   }
4620 
4621   const unsigned int defaultMaterialIndex = 0;
4622   Vec3d defaultKa(0.2,0.2,0.2);
4623   Vec3d defaultKd(0.6,0.6,0.6);
4624   Vec3d defaultKs(0.0,0.0,0.0);
4625   double defaultShininess = 65.0;
4626 
4627   unsigned int totalBytes;
4628   unsigned int items = genericRead(&totalBytes, sizeof(unsigned int), 1, binaryInputStream);
4629   if (items != 1)
4630   {
4631     if (verbose)
4632       printf("Error in ObjMesh::loadFromBinary: cannot read the number of total bytes.\n");
4633     return 1;
4634   }
4635 
4636   // important: subtract the bytes used to save totalBytes
4637   totalBytes -= sizeof(unsigned int);
4638 
4639   vector<unsigned char> objMeshBuffer;
4640   unsigned char * objMeshBufferPtr = NULL;
4641   if (stream == FILE_STREAM)
4642   {
4643     try
4644     {
4645       objMeshBuffer.resize(totalBytes);
4646     }
4647     catch (std::bad_alloc const &)
4648     {
4649       printf("Error in ObjMesh::loadFromBinary: cannot allocate buffer to read entire obj mesh.\n");
4650       return 1;
4651     }
4652     objMeshBufferPtr = &objMeshBuffer[0];
4653 
4654     items = genericRead(objMeshBufferPtr, sizeof(unsigned char), totalBytes, binaryInputStream);
4655     if (items != totalBytes)
4656     {
4657       if (verbose)
4658         printf("Error in ObjMesh::loadFromBinary: cannot read from the binary file.\n");
4659       return 1;
4660     }
4661   }
4662   else
4663     objMeshBufferPtr = (unsigned char *) binaryInputStream_; // use current input stream directly, NOT the binaryInputStream wrapper
4664 
4665   void * binaryInputBuffer = &objMeshBufferPtr;
4666 
4667   // read whether the mesh has materials or not
4668   int hasMaterials = 0;
4669   readFromMemory(&hasMaterials, sizeof(int), 1, binaryInputBuffer);
4670   if (!hasMaterials)
4671   {
4672     // add default material
4673     addMaterial(string("default"), defaultKa, defaultKd, defaultKs, defaultShininess);
4674   }
4675 
4676   vector<double> doubleVec;
4677   if (hasMaterials)
4678   {
4679     // number of materials
4680     unsigned int numObjMaterials;
4681     readFromMemory(&numObjMaterials, sizeof(unsigned int), 1, binaryInputBuffer);
4682     if (numObjMaterials == 0)
4683     {
4684       // add default materials
4685       addMaterial(string("default"), defaultKa, defaultKd, defaultKs, defaultShininess);
4686     }
4687 
4688     // name of the materials
4689     for (unsigned int materialIndex=0; materialIndex < numObjMaterials; materialIndex++)
4690     {
4691       // the length of current material name
4692       unsigned int strLength;
4693       readFromMemory(&strLength, sizeof(unsigned int), 1, binaryInputBuffer);
4694 
4695       // material name
4696       char * materialName = (char *) malloc (sizeof(char) * (strLength + 1));
4697       readFromMemory(materialName, sizeof(char), strLength, binaryInputBuffer);
4698       materialName[strLength] = '\0';
4699 
4700       // add a new material
4701       addMaterial(materialName, defaultKa, defaultKd, defaultKs, defaultShininess);
4702     }
4703 
4704     // material properties
4705     // Ka, Kd, Ks, each of which has 3 doubles, plus Ns, a double
4706     // So there are 10 doubles for every material
4707     const int numDoubles = 10;
4708     doubleVec.resize(numDoubles * numObjMaterials);
4709     readFromMemory(&doubleVec[0], sizeof(double), numDoubles * numObjMaterials, binaryInputBuffer);
4710     for (unsigned int materialIndex=0; materialIndex < numObjMaterials; materialIndex++)
4711     {
4712       unsigned int offset = materialIndex * numDoubles;
4713       Vec3d Ka(&doubleVec[offset]);
4714       Vec3d Kd(&doubleVec[offset+3]);
4715       Vec3d Ks(&doubleVec[offset+6]);
4716       double shininess = doubleVec[offset+9] * 128.0 / 1000.0;
4717       materials[materialIndex].setKa(Ka);
4718       materials[materialIndex].setKd(Kd);
4719       materials[materialIndex].setKs(Ks);
4720       materials[materialIndex].setShininess(shininess);
4721     }
4722 
4723     // number of materials which have map_Kd images
4724     unsigned int numMaterialsHasKdImages;
4725     readFromMemory(&numMaterialsHasKdImages, sizeof(unsigned int), 1, binaryInputBuffer);
4726     for (unsigned int materialIndex=0; materialIndex < numMaterialsHasKdImages; materialIndex++)
4727     {
4728       // the material ID
4729       unsigned int materialID;
4730       readFromMemory(&materialID, sizeof(unsigned int), 1, binaryInputBuffer);
4731 
4732       // material image
4733       unsigned int strLength;
4734       readFromMemory(&strLength, sizeof(unsigned int), 1, binaryInputBuffer);
4735       char textureFilename[4096];
4736       readFromMemory(textureFilename, sizeof(char), strLength, binaryInputBuffer);
4737       textureFilename[strLength] = '\0';
4738 
4739       // set the image
4740       materials[materialID].setTextureFilename(textureFilename);
4741     }  // for materialIndex
4742   }  // if (hasMaterials)
4743 
4744   // the number of vertices
4745   unsigned int numVertices;
4746   readFromMemory(&numVertices, sizeof(unsigned int), 1, binaryInputBuffer);
4747 
4748   // vertices
4749   doubleVec.resize(numVertices * 3);
4750   readFromMemory(&doubleVec[0], sizeof(double), numVertices * 3, binaryInputBuffer);
4751   for(unsigned int vertexIndex=0; vertexIndex < numVertices; vertexIndex++)
4752   {
4753     unsigned int offset = vertexIndex * 3;
4754     Vec3d pos(doubleVec[offset], doubleVec[offset+1], doubleVec[offset+2]);
4755     vertexPositions.push_back(pos);
4756   }
4757 
4758   // the number of texture coordinates
4759   unsigned int numTexCoordinates;
4760   readFromMemory(&numTexCoordinates, sizeof(unsigned int), 1, binaryInputBuffer);
4761 
4762   // texture coordinates
4763   if (numTexCoordinates > 0)
4764   {
4765     doubleVec.resize(numTexCoordinates * 3);
4766     readFromMemory(&doubleVec[0], sizeof(double), numTexCoordinates * 3, binaryInputBuffer);
4767     for(unsigned int textureCoordinateIndex=0; textureCoordinateIndex < numTexCoordinates; textureCoordinateIndex++)
4768     {
4769       unsigned int offset = textureCoordinateIndex * 3;
4770       Vec3d tex(doubleVec[offset], doubleVec[offset+1], doubleVec[offset+2]);
4771       textureCoordinates.push_back(tex);
4772     }
4773   }
4774 
4775   // the number of normals
4776   unsigned int numNormals;
4777   readFromMemory(&numNormals, sizeof(unsigned int), 1, binaryInputBuffer);
4778 
4779   // normals
4780   if (numNormals > 0)
4781   {
4782     doubleVec.resize(numNormals * 3);
4783     readFromMemory(&doubleVec[0], sizeof(double), numNormals * 3, binaryInputBuffer);
4784     for(unsigned int normalIndex=0; normalIndex < numNormals; normalIndex++)
4785     {
4786       unsigned int offset = normalIndex * 3;
4787       Vec3d normal(doubleVec[offset], doubleVec[offset+1], doubleVec[offset+2]);
4788       normals.push_back(normal);
4789     }
4790   }
4791 
4792   // the number of groups
4793   unsigned int numGroups;
4794   readFromMemory(&numGroups, sizeof(unsigned int), 1, binaryInputBuffer);
4795 
4796   // groups and faces
4797   for(unsigned int groupIndex=0; groupIndex < numGroups; groupIndex++)
4798   {
4799     // group name
4800     unsigned int strLength;
4801     readFromMemory(&strLength, sizeof(unsigned int), 1, binaryInputBuffer);
4802     char groupName[4096];
4803     readFromMemory(groupName, sizeof(char), strLength, binaryInputBuffer);
4804     groupName[strLength] = '\0';
4805 
4806     // group material index
4807     if (hasMaterials)
4808     {
4809       // material index
4810       unsigned int materialIndex;
4811       readFromMemory(&materialIndex, sizeof(unsigned int), 1, binaryInputBuffer);
4812       groups.push_back(Group(groupName, materialIndex));
4813     }
4814     else
4815     {
4816       // use default material
4817       groups.push_back(Group(groupName, defaultMaterialIndex));
4818     }
4819 
4820     // the number of faces of the current group
4821     unsigned int numFaces;
4822     readFromMemory(&numFaces, sizeof(unsigned int), 1, binaryInputBuffer);
4823 
4824     // the number of vertices in every face
4825     unsigned int * numFaceVerticesArray = (unsigned int *) malloc (sizeof(unsigned int) * numFaces);
4826     readFromMemory(numFaceVerticesArray, sizeof(unsigned int), numFaces, binaryInputBuffer);
4827 
4828     unsigned int totalFaceVertices = 0;
4829     for (unsigned int faceIndex=0; faceIndex < numFaces; faceIndex++)
4830       totalFaceVertices += numFaceVerticesArray[faceIndex];
4831 
4832     // vertex indices of every face in the current group
4833     unsigned int * vertexArray = (unsigned int *) malloc (sizeof(unsigned int) * totalFaceVertices);
4834     readFromMemory(vertexArray, sizeof(unsigned int), totalFaceVertices, binaryInputBuffer);
4835 
4836     // texture coordinate indices of every face in the current group
4837     unsigned int * texCoordinateArray = (unsigned int *) malloc (sizeof(unsigned int) * totalFaceVertices);
4838     readFromMemory(texCoordinateArray, sizeof(unsigned int), totalFaceVertices, binaryInputBuffer);
4839 
4840     // normal indices of every face in the current group
4841     unsigned int * normalArray = (unsigned int *) malloc (sizeof(unsigned int) * totalFaceVertices);
4842     readFromMemory(normalArray, sizeof(unsigned int), totalFaceVertices, binaryInputBuffer);
4843 
4844     unsigned int vertexCount = 0;
4845     for (unsigned int faceIndex=0; faceIndex < numFaces; faceIndex++)
4846     {
4847       Face currentFace;
4848       for (unsigned int vertexIndex=0; vertexIndex < numFaceVerticesArray[faceIndex]; vertexIndex++)
4849       {
4850         unsigned int pos = vertexArray[vertexCount] - 1; // 0-indexed
4851         std::pair< bool, unsigned int > texPos;
4852         std::pair< bool, unsigned int > normal;
4853 
4854         if (texCoordinateArray[vertexCount] == 0)  // no texture coordinate index
4855         {
4856           texPos.first = false;
4857           texPos.second = 0;
4858         }
4859         else
4860         {
4861           texPos.first = true;
4862           texPos.second = texCoordinateArray[vertexCount] - 1;
4863         }
4864 
4865         if (normalArray[vertexCount] == 0)  // no normal index
4866         {
4867           normal.first = false;
4868           normal.second = 0;
4869         }
4870         else
4871         {
4872           normal.first = true;
4873           normal.second = normalArray[vertexCount] - 1;
4874         }
4875         currentFace.addVertex(Vertex(pos, texPos, normal));
4876 
4877         vertexCount++;
4878       }  // for vertexIndex (current face)
4879 
4880       groups[groupIndex].addFace(currentFace);
4881     }
4882 
4883     free(numFaceVerticesArray);
4884     free(vertexArray);
4885     free(texCoordinateArray);
4886     free(normalArray);
4887   }  // for groupIndex
4888 
4889   // search if there is a "default" material, if not, add it
4890   addDefaultMaterial();
4891 
4892   return 0;
4893 }
4894 
readFromMemory(void * buf,unsigned int elementSize,unsigned int numElements,void * memoryLocation_)4895 unsigned int ObjMesh::readFromMemory(void * buf, unsigned int elementSize, unsigned int numElements, void * memoryLocation_)
4896 {
4897   unsigned char * memoryLocation = (unsigned char *)(*(void **)(memoryLocation_));
4898   unsigned int numBytes = elementSize * numElements;
4899   memcpy(buf, memoryLocation, numBytes);
4900   (*(void **)(memoryLocation_)) = (void *)((unsigned char *)(*(void **)(memoryLocation_)) + numBytes);
4901   return numElements;
4902 }
4903 
readFromFile(void * buf,unsigned int elementSize,unsigned int numElements,void * fin)4904 unsigned int ObjMesh::readFromFile(void * buf, unsigned int elementSize, unsigned int numElements, void * fin)
4905 {
4906   return fread(buf, elementSize, numElements, (FILE*)fin);
4907 }
4908 
4909