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