1 // cmodfix.cpp
2 //
3 // Copyright (C) 2004, Chris Laurel <claurel@shatters.net>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // Perform various adjustments to a cmod file
11 
12 #include <celengine/modelfile.h>
13 #include <celengine/tokenizer.h>
14 #include <celengine/texmanager.h>
15 #include <cel3ds/3dsread.h>
16 #include <celmath/mathlib.h>
17 #include <cstring>
18 #include <cassert>
19 #include <cmath>
20 #include <cstdio>
21 #include <algorithm>
22 #include <vector>
23 #ifdef TRISTRIP
24 #include <NvTriStrip.h>
25 #endif
26 
27 using namespace std;
28 
29 string inputFilename;
30 string outputFilename;
31 bool outputBinary = false;
32 bool uniquify = false;
33 bool genNormals = false;
34 bool genTangents = false;
35 bool weldVertices = false;
36 bool mergeMeshes = false;
37 bool stripify = false;
38 unsigned int vertexCacheSize = 16;
39 float smoothAngle = 60.0f;
40 
41 
usage()42 void usage()
43 {
44     cerr << "Usage: cmodfix [options] [input cmod file [output cmod file]]\n";
45     cerr << "   --binary (or -b)      : output a binary .cmod file\n";
46     cerr << "   --ascii (or -a)       : output an ASCII .cmod file\n";
47     cerr << "   --uniquify (or -u)    : eliminate duplicate vertices\n";
48     cerr << "   --tangents (or -t)    : generate tangents\n";
49     cerr << "   --normals (or -n)     : generate normals\n";
50     cerr << "   --smooth (or -s) <angle> : smoothing angle for normal generation\n";
51     cerr << "   --weld (or -w)        : join identical vertices before normal generation\n";
52     cerr << "   --merge (or -m)       : merge submeshes to improve rendering performance\n";
53 #ifdef TRISTRIP
54     cerr << "   --optimize (or -o)    : optimize by converting triangle lists to strips\n";
55 #endif
56 }
57 
58 
59 struct Vertex
60 {
VertexVertex61     Vertex() :
62         index(0), attributes(NULL) {};
63 
VertexVertex64     Vertex(uint32 _index, const void* _attributes) :
65         index(_index), attributes(_attributes) {};
66 
67     uint32 index;
68     const void* attributes;
69 };
70 
71 
72 struct Face
73 {
74     Vec3f normal;
75     uint32 i[3];    // vertex attribute indices
76     uint32 vi[3];   // vertex point indices -- same as above unless welding
77 };
78 
79 
80 typedef std::binary_function<const Vertex&, const Vertex&, bool> VertexComparator;
81 
82 
83 class FullComparator : public VertexComparator
84 {
85 public:
FullComparator(int _vertexSize)86     FullComparator(int _vertexSize) :
87         vertexSize(_vertexSize)
88     {
89     }
90 
operator ()(const Vertex & a,const Vertex & b) const91     bool operator()(const Vertex& a, const Vertex& b) const
92     {
93         const char* s0 = reinterpret_cast<const char*>(a.attributes);
94         const char* s1 = reinterpret_cast<const char*>(b.attributes);
95         for (int i = 0; i < vertexSize; i++)
96         {
97             if (s0[i] < s1[i])
98                 return true;
99             else if (s0[i] > s1[i])
100                 return false;
101         }
102 
103         return false;
104     }
105 
106 private:
107     int vertexSize;
108 };
109 
110 
111 class PointComparator : public VertexComparator
112 {
113 public:
PointComparator()114     PointComparator()
115     {
116     }
117 
operator ()(const Vertex & a,const Vertex & b) const118     bool operator()(const Vertex& a, const Vertex& b) const
119     {
120         const Point3f* p0 = reinterpret_cast<const Point3f*>(a.attributes);
121         const Point3f* p1 = reinterpret_cast<const Point3f*>(b.attributes);
122 
123         if (p0->x < p1->x)
124         {
125             return true;
126         }
127         else if (p0->x > p1->x)
128         {
129             return false;
130         }
131         else
132         {
133             if (p0->y < p1->y)
134                 return true;
135             else if (p0->y > p1->y)
136                 return false;
137             else
138                 return p0->z < p1->z;
139         }
140     }
141 
142 private:
143     int ignore;
144 };
145 
146 
147 class PointTexCoordComparator : public VertexComparator
148 {
149 public:
PointTexCoordComparator(uint32 _posOffset,uint32 _texCoordOffset,bool _wrap)150     PointTexCoordComparator(uint32 _posOffset,
151                             uint32 _texCoordOffset,
152                             bool _wrap) :
153         posOffset(_posOffset),
154         texCoordOffset(_texCoordOffset),
155         wrap(_wrap)
156     {
157     }
158 
operator ()(const Vertex & a,const Vertex & b) const159     bool operator()(const Vertex& a, const Vertex& b) const
160     {
161         const char* adata = reinterpret_cast<const char*>(a.attributes);
162         const char* bdata = reinterpret_cast<const char*>(b.attributes);
163         const Point3f* p0 = reinterpret_cast<const Point3f*>(adata + posOffset);
164         const Point3f* p1 = reinterpret_cast<const Point3f*>(bdata + posOffset);
165         const Point2f* tc0 = reinterpret_cast<const Point2f*>(adata + posOffset);
166         const Point2f* tc1 = reinterpret_cast<const Point2f*>(bdata + posOffset);
167         if (p0->x < p1->x)
168         {
169             return true;
170         }
171         else if (p0->x > p1->x)
172         {
173             return false;
174         }
175         else
176         {
177             if (p0->y < p1->y)
178             {
179                 return true;
180             }
181             else if (p0->y > p1->y)
182             {
183                 return false;
184             }
185             else
186             {
187                 if (p0->z < p1->z)
188                 {
189                     return true;
190                 }
191                 else if (p0->z > p1->z)
192                 {
193                     return false;
194                 }
195                 else
196                 {
197                     if (tc0->x < tc1->x)
198                         return true;
199                     else if (tc0->x > tc1->x)
200                         return false;
201                     else
202                         return tc0->y < tc1->y;
203                 }
204             }
205         }
206     }
207 
208 private:
209     uint32 posOffset;
210     uint32 texCoordOffset;
211     bool wrap;
212 };
213 
214 
equal(const Vertex & a,const Vertex & b,uint32 vertexSize)215 bool equal(const Vertex& a, const Vertex& b, uint32 vertexSize)
216 {
217     const char* s0 = reinterpret_cast<const char*>(a.attributes);
218     const char* s1 = reinterpret_cast<const char*>(b.attributes);
219 
220     for (uint32 i = 0; i < vertexSize; i++)
221     {
222         if (s0[i] != s1[i])
223             return false;
224     }
225 
226     return true;
227 }
228 
229 
equalPoint(const Vertex & a,const Vertex & b)230 bool equalPoint(const Vertex& a, const Vertex& b)
231 {
232     const Point3f* p0 = reinterpret_cast<const Point3f*>(a.attributes);
233     const Point3f* p1 = reinterpret_cast<const Point3f*>(b.attributes);
234 
235     return *p0 == *p1;
236 }
237 
238 
operator ==(const Mesh::VertexAttribute & a,const Mesh::VertexAttribute & b)239 bool operator==(const Mesh::VertexAttribute& a,
240                 const Mesh::VertexAttribute& b)
241 {
242     return (a.semantic == b.semantic &&
243             a.format   == b.format &&
244             a.offset   == b.offset);
245 }
246 
247 
operator <(const Mesh::VertexAttribute & a,const Mesh::VertexAttribute & b)248 bool operator<(const Mesh::VertexAttribute& a,
249                const Mesh::VertexAttribute& b)
250 {
251     if (a.semantic < b.semantic)
252     {
253         return true;
254     }
255     else if (b.semantic < a.semantic)
256     {
257         return false;
258     }
259     else
260     {
261         if (a.format < b.format)
262             return true;
263         else if (b.format < a.format)
264             return false;
265         else
266             return a.offset < b.offset;
267     }
268 }
269 
270 
operator ==(const Mesh::VertexDescription & a,const Mesh::VertexDescription & b)271 bool operator==(const Mesh::VertexDescription& a,
272                 const Mesh::VertexDescription& b)
273 {
274     if (a.stride != b.stride || a.nAttributes != b.nAttributes)
275         return false;
276 
277     for (uint32 i = 0; i < a.nAttributes; i++)
278     {
279         if (!(a.attributes[i] == b.attributes[i]))
280             return false;
281     }
282 
283     return true;
284 }
285 
286 
operator <(const Mesh::VertexDescription & a,const Mesh::VertexDescription & b)287 bool operator<(const Mesh::VertexDescription& a,
288                const Mesh::VertexDescription& b)
289 {
290     if (a.stride < b.stride)
291         return true;
292     else if (b.stride < a.stride)
293         return false;
294 
295     if (a.nAttributes < b.nAttributes)
296         return true;
297     else if (b.nAttributes < b.nAttributes)
298         return false;
299 
300     for (uint32 i = 0; i < a.nAttributes; i++)
301     {
302         if (a.attributes[i] < b.attributes[i])
303             return true;
304         else if (b.attributes[i] < a.attributes[i])
305             return false;
306     }
307 
308     return false;
309 }
310 
311 
312 class MeshVertexDescComparator :
313     public std::binary_function<const Mesh*, const Mesh*, bool>
314 {
315 public:
MeshVertexDescComparator()316     MeshVertexDescComparator()
317     {
318     }
319 
operator ()(const Mesh * a,const Mesh * b) const320     bool operator()(const Mesh* a, const Mesh* b) const
321     {
322         return a->getVertexDescription() < b->getVertexDescription();
323     }
324 
325 private:
326     int ignore;
327 };
328 
329 
330 
uniquifyVertices(Mesh & mesh)331 bool uniquifyVertices(Mesh& mesh)
332 {
333     uint32 nVertices = mesh.getVertexCount();
334     const Mesh::VertexDescription& desc = mesh.getVertexDescription();
335 
336     if (nVertices == 0)
337         return false;
338 
339     const char* vertexData = reinterpret_cast<const char*>(mesh.getVertexData());
340     if (vertexData == NULL)
341         return false;
342 
343     // Initialize the array of vertices
344     vector<Vertex> vertices(nVertices);
345     uint32 i;
346     for (i = 0; i < nVertices; i++)
347     {
348         vertices[i] = Vertex(i, vertexData + i * desc.stride);
349     }
350 
351     // Sort the vertices so that identical ones will be ordered consecutively
352     sort(vertices.begin(), vertices.end(), FullComparator(desc.stride));
353 
354     // Count the number of unique vertices
355     uint32 uniqueVertexCount = 0;
356     for (i = 0; i < nVertices; i++)
357     {
358         if (i == 0 || !equal(vertices[i - 1], vertices[i], desc.stride))
359             uniqueVertexCount++;
360     }
361 
362     // No work left to do if we couldn't eliminate any vertices
363     if (uniqueVertexCount == nVertices)
364         return true;
365 
366     // Build the vertex map and the uniquified vertex data
367     vector<uint32> vertexMap(nVertices);
368     char* newVertexData = new char[uniqueVertexCount * desc.stride];
369     const char* oldVertexData = reinterpret_cast<const char*>(mesh.getVertexData());
370     uint32 j = 0;
371     for (i = 0; i < nVertices; i++)
372     {
373         if (i == 0 || !equal(vertices[i - 1], vertices[i], desc.stride))
374         {
375             if (i != 0)
376                 j++;
377             assert(j < uniqueVertexCount);
378             memcpy(newVertexData + j * desc.stride,
379                    oldVertexData + vertices[i].index * desc.stride,
380                    desc.stride);
381         }
382         vertexMap[vertices[i].index] = j;
383     }
384 
385     // Replace the vertex data with the compacted data
386     delete mesh.getVertexData();
387     mesh.setVertices(uniqueVertexCount, newVertexData);
388 
389     mesh.remapIndices(vertexMap);
390 
391     return true;
392 }
393 
394 
395 Point3f
getVertex(const void * vertexData,int positionOffset,uint32 stride,uint32 index)396 getVertex(const void* vertexData,
397           int positionOffset,
398           uint32 stride,
399           uint32 index)
400 {
401     const float* fdata = reinterpret_cast<const float*>(reinterpret_cast<const char*>(vertexData) + stride * index + positionOffset);
402 
403     return Point3f(fdata[0], fdata[1], fdata[2]);
404 }
405 
406 
407 Point2f
getTexCoord(const void * vertexData,int texCoordOffset,uint32 stride,uint32 index)408 getTexCoord(const void* vertexData,
409             int texCoordOffset,
410             uint32 stride,
411             uint32 index)
412 {
413     const float* fdata = reinterpret_cast<const float*>(reinterpret_cast<const char*>(vertexData) + stride * index + texCoordOffset);
414 
415     return Point2f(fdata[0], fdata[1]);
416 }
417 
418 
419 Vec3f
averageFaceVectors(const vector<Face> & faces,uint32 thisFace,uint32 * vertexFaces,uint32 vertexFaceCount,float cosSmoothingAngle)420 averageFaceVectors(const vector<Face>& faces,
421                    uint32 thisFace,
422                    uint32* vertexFaces,
423                    uint32 vertexFaceCount,
424                    float cosSmoothingAngle)
425 {
426     const Face& face = faces[thisFace];
427 
428     Vec3f v = Vec3f(0, 0, 0);
429     for (uint32 i = 0; i < vertexFaceCount; i++)
430     {
431         uint32 f = vertexFaces[i];
432         float cosAngle = face.normal * faces[f].normal;
433         if (f == thisFace || cosAngle > cosSmoothingAngle)
434             v += faces[f].normal;
435     }
436 
437     if (v * v == 0.0f)
438         v = Vec3f(1.0f, 0.0f, 0.0f);
439     else
440         v.normalize();
441 
442     return v;
443 }
444 
445 
446 void
copyVertex(void * newVertexData,const Mesh::VertexDescription & newDesc,const void * oldVertexData,const Mesh::VertexDescription & oldDesc,uint32 oldIndex,const uint32 fromOffsets[])447 copyVertex(void* newVertexData,
448            const Mesh::VertexDescription& newDesc,
449            const void* oldVertexData,
450            const Mesh::VertexDescription& oldDesc,
451            uint32 oldIndex,
452            const uint32 fromOffsets[])
453 {
454     const char* oldVertex = reinterpret_cast<const char*>(oldVertexData) +
455         oldDesc.stride * oldIndex;
456     char* newVertex = reinterpret_cast<char*>(newVertexData);
457 
458     for (uint32 i = 0; i < newDesc.nAttributes; i++)
459     {
460         if (fromOffsets[i] != ~0)
461         {
462             memcpy(newVertex + newDesc.attributes[i].offset,
463                    oldVertex + fromOffsets[i],
464                    Mesh::getVertexAttributeSize(newDesc.attributes[i].format));
465         }
466     }
467 }
468 
469 
470 void
augmentVertexDescription(Mesh::VertexDescription & desc,Mesh::VertexAttributeSemantic semantic,Mesh::VertexAttributeFormat format)471 augmentVertexDescription(Mesh::VertexDescription& desc,
472                          Mesh::VertexAttributeSemantic semantic,
473                          Mesh::VertexAttributeFormat format)
474 {
475     Mesh::VertexAttribute* attributes = new Mesh::VertexAttribute[desc.nAttributes + 1];
476     uint32 stride = 0;
477     uint32 nAttributes = 0;
478     bool foundMatch = false;
479 
480     for (uint32 i = 0; i < desc.nAttributes; i++)
481     {
482         if (semantic == desc.attributes[i].semantic &&
483             format != desc.attributes[i].format)
484         {
485             // The semantic matches, but the format does not; skip this
486             // item.
487         }
488         else
489         {
490             if (semantic == desc.attributes[i].semantic)
491                 foundMatch = true;
492 
493             attributes[nAttributes] = desc.attributes[i];
494             attributes[nAttributes].offset = stride;
495             stride += Mesh::getVertexAttributeSize(desc.attributes[i].format);
496             nAttributes++;
497         }
498     }
499 
500     if (!foundMatch)
501     {
502         attributes[nAttributes++] = Mesh::VertexAttribute(semantic,
503                                                           format,
504                                                           stride);
505         stride += Mesh::getVertexAttributeSize(format);
506     }
507 
508     delete[] desc.attributes;
509     desc.attributes = attributes;
510     desc.nAttributes = nAttributes;
511     desc.stride = stride;
512 }
513 
514 
515 template <typename T> void
joinVertices(vector<Face> & faces,const void * vertexData,const Mesh::VertexDescription & desc,const T & comparator)516 joinVertices(vector<Face>& faces,
517              const void* vertexData,
518              const Mesh::VertexDescription& desc,
519              const T& comparator)
520 {
521     // Don't do anything if we're given no data
522     if (faces.size() == 0)
523         return;
524 
525     // Must have a position
526     assert(desc.getAttribute(Mesh::Position).format == Mesh::Float3);
527 
528     uint32 posOffset = desc.getAttribute(Mesh::Position).offset;
529     const char* vertexPoints = reinterpret_cast<const char*>(vertexData) +
530         posOffset;
531     uint32 nVertices = faces.size() * 3;
532 
533     // Initialize the array of vertices
534     vector<Vertex> vertices(nVertices);
535     uint32 f;
536     for (f = 0; f < faces.size(); f++)
537     {
538         for (uint32 j = 0; j < 3; j++)
539         {
540             uint32 index = faces[f].i[j];
541             vertices[f * 3 + j] = Vertex(index,
542                                          vertexPoints + desc.stride * index);
543 
544         }
545     }
546 
547     // Sort the vertices so that identical ones will be ordered consecutively
548     sort(vertices.begin(), vertices.end(), comparator);
549 
550     // Build the vertex merge map
551     vector<uint32> mergeMap(nVertices);
552     uint32 lastUnique = 0;
553     for (uint32 i = 0; i < nVertices; i++)
554     {
555         if (i == 0 ||
556             comparator.operator()(vertices[i - 1], vertices[i]) ||
557             comparator.operator()(vertices[i], vertices[i - 1]))
558         {
559             lastUnique = i;
560         }
561         mergeMap[vertices[i].index] = vertices[lastUnique].index;
562     }
563 
564     // Remap the vertex indices
565     for (f = 0; f < faces.size(); f++)
566     {
567         for (uint32 k= 0; k < 3; k++)
568             faces[f].vi[k] = mergeMap[faces[f].i[k]];
569     }
570 }
571 
572 
573 Mesh*
generateNormals(Mesh & mesh,float smoothAngle,bool weld)574 generateNormals(Mesh& mesh,
575                 float smoothAngle,
576                 bool weld)
577 {
578     uint32 nVertices = mesh.getVertexCount();
579     float cosSmoothAngle = (float) cos(smoothAngle);
580 
581     const Mesh::VertexDescription& desc = mesh.getVertexDescription();
582 
583     if (desc.getAttribute(Mesh::Position).format != Mesh::Float3)
584     {
585         cerr << "Vertex position must be a float3\n";
586         return NULL;
587     }
588     uint32 posOffset = desc.getAttribute(Mesh::Position).offset;
589 
590     uint32 nFaces = 0;
591     uint32 i;
592     for (i = 0; mesh.getGroup(i) != NULL; i++)
593     {
594         const Mesh::PrimitiveGroup* group = mesh.getGroup(i);
595 
596         switch (group->prim)
597         {
598         case Mesh::TriList:
599             if (group->nIndices < 3 || group->nIndices % 3 != 0)
600             {
601                 cerr << "Triangle list has invalid number of indices\n";
602                 return NULL;
603             }
604             nFaces += group->nIndices / 3;
605             break;
606 
607         case Mesh::TriStrip:
608         case Mesh::TriFan:
609             if (group->nIndices < 3)
610             {
611                 cerr << "Error: tri strip or fan has less than three indices\n";
612                 return NULL;
613             }
614             nFaces += group->nIndices - 2;
615             break;
616 
617         default:
618             cerr << "Cannot generate normals for non-triangle primitives\n";
619             return NULL;
620         }
621     }
622 
623     // Build the array of faces; this may require decomposing triangle strips
624     // and fans into triangle lists.
625     vector<Face> faces(nFaces);
626 
627     uint32 f = 0;
628     for (i = 0; mesh.getGroup(i) != NULL; i++)
629     {
630         const Mesh::PrimitiveGroup* group = mesh.getGroup(i);
631 
632         switch (group->prim)
633         {
634         case Mesh::TriList:
635             {
636                 for (uint32 j = 0; j < group->nIndices / 3; j++)
637                 {
638                     assert(f < nFaces);
639                     faces[f].i[0] = group->indices[j * 3];
640                     faces[f].i[1] = group->indices[j * 3 + 1];
641                     faces[f].i[2] = group->indices[j * 3 + 2];
642                     f++;
643                 }
644             }
645             break;
646 
647         case Mesh::TriStrip:
648             {
649                 for (uint32 j = 2; j < group->nIndices; j++)
650                 {
651                     assert(f < nFaces);
652                     if (j % 2 == 0)
653                     {
654                         faces[f].i[0] = group->indices[j - 2];
655                         faces[f].i[1] = group->indices[j - 1];
656                         faces[f].i[2] = group->indices[j];
657                     }
658                     else
659                     {
660                         faces[f].i[0] = group->indices[j - 1];
661                         faces[f].i[1] = group->indices[j - 2];
662                         faces[f].i[2] = group->indices[j];
663                     }
664                     f++;
665                 }
666             }
667             break;
668 
669         case Mesh::TriFan:
670             {
671                 for (uint32 j = 2; j < group->nIndices; j++)
672                 {
673                     assert(f < nFaces);
674                     faces[f].i[0] = group->indices[0];
675                     faces[f].i[1] = group->indices[j - 1];
676                     faces[f].i[2] = group->indices[j];
677                     f++;
678                 }
679             }
680             break;
681 
682         default:
683             assert(0);
684             break;
685         }
686     }
687     assert(f == nFaces);
688 
689     const void* vertexData = mesh.getVertexData();
690 
691     // Compute normals for the faces
692     for (f = 0; f < nFaces; f++)
693     {
694         Face& face = faces[f];
695         Point3f p0 = getVertex(vertexData, posOffset, desc.stride, face.i[0]);
696         Point3f p1 = getVertex(vertexData, posOffset, desc.stride, face.i[1]);
697         Point3f p2 = getVertex(vertexData, posOffset, desc.stride, face.i[2]);
698         face.normal = cross(p1 - p0, p2 - p1);
699         if (face.normal * face.normal > 0.0f)
700             face.normal.normalize();
701     }
702 
703     // For each vertex, create a list of faces that contain it
704     uint32* faceCounts = new uint32[nVertices];
705     uint32** vertexFaces = new uint32*[nVertices];
706 
707     // Initialize the lists
708     for (i = 0; i < nVertices; i++)
709     {
710         faceCounts[i] = 0;
711         vertexFaces[i] = NULL;
712     }
713 
714     // If we're welding vertices before generating normals, find identical
715     // points and merge them.  Otherwise, the point indices will be the same
716     // as the attribute indices.
717     if (weld)
718     {
719         joinVertices(faces, vertexData, desc, PointComparator());
720     }
721     else
722     {
723         for (f = 0; f < nFaces; f++)
724         {
725             faces[f].vi[0] = faces[f].i[0];
726             faces[f].vi[1] = faces[f].i[1];
727             faces[f].vi[2] = faces[f].i[2];
728         }
729     }
730 
731     // Count the number of faces in which each vertex appears
732     for (f = 0; f < nFaces; f++)
733     {
734         Face& face = faces[f];
735         faceCounts[face.vi[0]]++;
736         faceCounts[face.vi[1]]++;
737         faceCounts[face.vi[2]]++;
738     }
739 
740     // Allocate space for the per-vertex face lists
741     for (i = 0; i < nVertices; i++)
742     {
743         if (faceCounts[i] > 0)
744         {
745             vertexFaces[i] = new uint32[faceCounts[i] + 1];
746             vertexFaces[i][0] = faceCounts[i];
747         }
748     }
749 
750     // Fill in the vertex/face lists
751     for (f = 0; f < nFaces; f++)
752     {
753         Face& face = faces[f];
754         vertexFaces[face.vi[0]][faceCounts[face.vi[0]]--] = f;
755         vertexFaces[face.vi[1]][faceCounts[face.vi[1]]--] = f;
756         vertexFaces[face.vi[2]][faceCounts[face.vi[2]]--] = f;
757     }
758 
759     // Compute the vertex normals by averaging
760     vector<Vec3f> vertexNormals(nFaces * 3);
761     for (f = 0; f < nFaces; f++)
762     {
763         Face& face = faces[f];
764         for (uint32 j = 0; j < 3; j++)
765         {
766             vertexNormals[f * 3 + j] =
767                 averageFaceVectors(faces, f,
768                                    &vertexFaces[face.vi[j]][1],
769                                    vertexFaces[face.vi[j]][0],
770                                    cosSmoothAngle);
771         }
772     }
773 
774     // Finally, create a new mesh with normals included
775 
776     // Create the new vertex description
777     Mesh::VertexDescription newDesc(desc);
778     augmentVertexDescription(newDesc, Mesh::Normal, Mesh::Float3);
779 
780     // We need to convert the copy the old vertex attributes to the new
781     // mesh.  In order to do this, we need the old offset of each attribute
782     // in the new vertex description.  The fromOffsets array will contain
783     // this mapping.
784     uint32 normalOffset = 0;
785     uint32 fromOffsets[16];
786     for (i = 0; i < newDesc.nAttributes; i++)
787     {
788         fromOffsets[i] = ~0;
789 
790         if (newDesc.attributes[i].semantic == Mesh::Normal)
791         {
792             normalOffset = newDesc.attributes[i].offset;
793         }
794         else
795         {
796             for (uint32 j = 0; j < desc.nAttributes; j++)
797             {
798                 if (desc.attributes[j].semantic == newDesc.attributes[i].semantic)
799                 {
800                     assert(desc.attributes[j].format == newDesc.attributes[i].format);
801                     fromOffsets[i] = desc.attributes[j].offset;
802                     break;
803                 }
804             }
805         }
806     }
807 
808     // Copy the old vertex data along with the generated normals to the
809     // new vertex data buffer.
810     void* newVertexData = new char[newDesc.stride * nFaces * 3];
811     for (f = 0; f < nFaces; f++)
812     {
813         Face& face = faces[f];
814 
815         for (uint32 j = 0; j < 3; j++)
816         {
817             char* newVertex = reinterpret_cast<char*>(newVertexData) +
818                 (f * 3 + j) * newDesc.stride;
819             copyVertex(newVertex, newDesc,
820                        vertexData, desc,
821                        face.i[j],
822                        fromOffsets);
823             memcpy(newVertex + normalOffset, &vertexNormals[f * 3 + j],
824                    Mesh::getVertexAttributeSize(Mesh::Float3));
825         }
826     }
827 
828     // Create the Celestia mesh
829     Mesh* newMesh = new Mesh();
830     newMesh->setVertexDescription(newDesc);
831     newMesh->setVertices(nFaces * 3, newVertexData);
832 
833     // Create a trivial index list
834     uint32* indices = new uint32[nFaces * 3];
835     for (i = 0; i < nFaces * 3; i++)
836         indices[i] = i;
837 
838     // TODO: This assumes that the mesh uses only one material.  Normal
839     // generation should really be done one primitive group at a time.
840     uint32 materialIndex = mesh.getGroup(0)->materialIndex;
841     newMesh->addGroup(Mesh::TriList, materialIndex, nFaces * 3, indices);
842 
843     // Clean up
844     delete[] faceCounts;
845     for (i = 0; i < nVertices; i++)
846     {
847         if (vertexFaces[i] != NULL)
848             delete[] vertexFaces[i];
849     }
850     delete[] vertexFaces;
851 
852     return newMesh;
853 }
854 
855 
856 Mesh*
generateTangents(Mesh & mesh,bool weld)857 generateTangents(Mesh& mesh,
858                  bool weld)
859 {
860     uint32 nVertices = mesh.getVertexCount();
861 
862     // In order to generate tangents, we require positions, normals, and
863     // 2D texture coordinates in the vertex description.
864     const Mesh::VertexDescription& desc = mesh.getVertexDescription();
865     if (desc.getAttribute(Mesh::Position).format != Mesh::Float3)
866     {
867         cerr << "Vertex position must be a float3\n";
868         return NULL;
869     }
870 
871     if (desc.getAttribute(Mesh::Normal).format != Mesh::Float3)
872     {
873         cerr << "float3 format vertex normal required\n";
874         return NULL;
875     }
876 
877     if (desc.getAttribute(Mesh::Texture0).format == Mesh::InvalidFormat)
878     {
879         cerr << "Texture coordinates must be present in mesh to generate tangents\n";
880         return NULL;
881     }
882 
883     if (desc.getAttribute(Mesh::Texture0).format != Mesh::Float2)
884     {
885         cerr << "Texture coordinate must be a float2\n";
886         return NULL;
887     }
888 
889     // Count the number of faces in the mesh.
890     // (All geometry should already converted to triangle lists)
891     uint32 i;
892     uint32 nFaces = 0;
893     for (i = 0; mesh.getGroup(i) != NULL; i++)
894     {
895         const Mesh::PrimitiveGroup* group = mesh.getGroup(i);
896         if (group->prim == Mesh::TriList)
897         {
898             assert(group->nIndices % 3 == 0);
899             nFaces += group->nIndices / 3;
900         }
901         else
902         {
903             cerr << "Mesh should contain just triangle lists\n";
904             return NULL;
905         }
906     }
907 
908     // Build the array of faces; this may require decomposing triangle strips
909     // and fans into triangle lists.
910     vector<Face> faces(nFaces);
911 
912     uint32 f = 0;
913     for (i = 0; mesh.getGroup(i) != NULL; i++)
914     {
915         const Mesh::PrimitiveGroup* group = mesh.getGroup(i);
916 
917         switch (group->prim)
918         {
919         case Mesh::TriList:
920             {
921                 for (uint32 j = 0; j < group->nIndices / 3; j++)
922                 {
923                     assert(f < nFaces);
924                     faces[f].i[0] = group->indices[j * 3];
925                     faces[f].i[1] = group->indices[j * 3 + 1];
926                     faces[f].i[2] = group->indices[j * 3 + 2];
927                     f++;
928                 }
929             }
930             break;
931         }
932     }
933 
934     uint32 posOffset = desc.getAttribute(Mesh::Position).offset;
935     uint32 normOffset = desc.getAttribute(Mesh::Normal).offset;
936     uint32 texCoordOffset = desc.getAttribute(Mesh::Texture0).offset;
937 
938     const void* vertexData = mesh.getVertexData();
939 
940     // Compute tangents for faces
941     for (f = 0; f < nFaces; f++)
942     {
943         Face& face = faces[f];
944         Point3f p0 = getVertex(vertexData, posOffset, desc.stride, face.i[0]);
945         Point3f p1 = getVertex(vertexData, posOffset, desc.stride, face.i[1]);
946         Point3f p2 = getVertex(vertexData, posOffset, desc.stride, face.i[2]);
947         Point2f tc0 = getTexCoord(vertexData, texCoordOffset, desc.stride, face.i[0]);
948         Point2f tc1 = getTexCoord(vertexData, texCoordOffset, desc.stride, face.i[1]);
949         Point2f tc2 = getTexCoord(vertexData, texCoordOffset, desc.stride, face.i[2]);
950         float s1 = tc1.x - tc0.x;
951         float s2 = tc2.x - tc0.x;
952         float t1 = tc1.y - tc0.y;
953         float t2 = tc2.y - tc0.y;
954         float a = s1 * t2 - s2 * t1;
955         if (a != 0.0f)
956             face.normal = (t2 * (p1 - p0) - t1 * (p2 - p0)) * (1.0f / a);
957         else
958             face.normal = Vec3f(0.0f, 0.0f, 0.0f);
959     }
960 
961     // For each vertex, create a list of faces that contain it
962     uint32* faceCounts = new uint32[nVertices];
963     uint32** vertexFaces = new uint32*[nVertices];
964 
965     // Initialize the lists
966     for (i = 0; i < nVertices; i++)
967     {
968         faceCounts[i] = 0;
969         vertexFaces[i] = NULL;
970     }
971 
972     // If we're welding vertices before generating normals, find identical
973     // points and merge them.  Otherwise, the point indices will be the same
974     // as the attribute indices.
975     if (weld)
976     {
977         joinVertices(faces, vertexData, desc, PointTexCoordComparator(0, 0, true));
978     }
979     else
980     {
981         for (f = 0; f < nFaces; f++)
982         {
983             faces[f].vi[0] = faces[f].i[0];
984             faces[f].vi[1] = faces[f].i[1];
985             faces[f].vi[2] = faces[f].i[2];
986         }
987     }
988 
989     // Count the number of faces in which each vertex appears
990     for (f = 0; f < nFaces; f++)
991     {
992         Face& face = faces[f];
993         faceCounts[face.vi[0]]++;
994         faceCounts[face.vi[1]]++;
995         faceCounts[face.vi[2]]++;
996     }
997 
998     // Allocate space for the per-vertex face lists
999     for (i = 0; i < nVertices; i++)
1000     {
1001         if (faceCounts[i] > 0)
1002         {
1003             vertexFaces[i] = new uint32[faceCounts[i] + 1];
1004             vertexFaces[i][0] = faceCounts[i];
1005         }
1006     }
1007 
1008     // Fill in the vertex/face lists
1009     for (f = 0; f < nFaces; f++)
1010     {
1011         Face& face = faces[f];
1012         vertexFaces[face.vi[0]][faceCounts[face.vi[0]]--] = f;
1013         vertexFaces[face.vi[1]][faceCounts[face.vi[1]]--] = f;
1014         vertexFaces[face.vi[2]][faceCounts[face.vi[2]]--] = f;
1015     }
1016 
1017     // Compute the vertex tangents by averaging
1018     vector<Vec3f> vertexTangents(nFaces * 3);
1019     for (f = 0; f < nFaces; f++)
1020     {
1021         Face& face = faces[f];
1022         for (uint32 j = 0; j < 3; j++)
1023         {
1024             vertexTangents[f * 3 + j] =
1025                 averageFaceVectors(faces, f,
1026                                    &vertexFaces[face.vi[j]][1],
1027                                    vertexFaces[face.vi[j]][0],
1028                                    0.0f);
1029         }
1030     }
1031 
1032     // Create the new vertex description
1033     Mesh::VertexDescription newDesc(desc);
1034     augmentVertexDescription(newDesc, Mesh::Tangent, Mesh::Float3);
1035 
1036     // We need to convert the copy the old vertex attributes to the new
1037     // mesh.  In order to do this, we need the old offset of each attribute
1038     // in the new vertex description.  The fromOffsets array will contain
1039     // this mapping.
1040     uint32 tangentOffset = 0;
1041     uint32 fromOffsets[16];
1042     for (i = 0; i < newDesc.nAttributes; i++)
1043     {
1044         fromOffsets[i] = ~0;
1045 
1046         if (newDesc.attributes[i].semantic == Mesh::Tangent)
1047         {
1048             tangentOffset = newDesc.attributes[i].offset;
1049         }
1050         else
1051         {
1052             for (uint32 j = 0; j < desc.nAttributes; j++)
1053             {
1054                 if (desc.attributes[j].semantic == newDesc.attributes[i].semantic)
1055                 {
1056                     assert(desc.attributes[j].format == newDesc.attributes[i].format);
1057                     fromOffsets[i] = desc.attributes[j].offset;
1058                     break;
1059                 }
1060             }
1061         }
1062     }
1063 
1064     // Copy the old vertex data along with the generated tangents to the
1065     // new vertex data buffer.
1066     void* newVertexData = new char[newDesc.stride * nFaces * 3];
1067     for (f = 0; f < nFaces; f++)
1068     {
1069         Face& face = faces[f];
1070 
1071         for (uint32 j = 0; j < 3; j++)
1072         {
1073             char* newVertex = reinterpret_cast<char*>(newVertexData) +
1074                 (f * 3 + j) * newDesc.stride;
1075             copyVertex(newVertex, newDesc,
1076                        vertexData, desc,
1077                        face.i[j],
1078                        fromOffsets);
1079             memcpy(newVertex + tangentOffset, &vertexTangents[f * 3 + j],
1080                    Mesh::getVertexAttributeSize(Mesh::Float3));
1081         }
1082     }
1083 
1084     // Create the Celestia mesh
1085     Mesh* newMesh = new Mesh();
1086     newMesh->setVertexDescription(newDesc);
1087     newMesh->setVertices(nFaces * 3, newVertexData);
1088 
1089     // Create a trivial index list
1090     uint32* indices = new uint32[nFaces * 3];
1091     for (i = 0; i < nFaces * 3; i++)
1092         indices[i] = i;
1093 
1094     // TODO: This assumes that the mesh uses only one material.  Tangent
1095     // generation should really be done one primitive group at a time.
1096     uint32 materialIndex = mesh.getGroup(0)->materialIndex;
1097     newMesh->addGroup(Mesh::TriList, materialIndex, nFaces * 3, indices);
1098 
1099     // Clean up
1100     delete[] faceCounts;
1101     for (i = 0; i < nVertices; i++)
1102     {
1103         if (vertexFaces[i] != NULL)
1104             delete[] vertexFaces[i];
1105     }
1106     delete[] vertexFaces;
1107 
1108     return newMesh;
1109 }
1110 
1111 
1112 void
addGroupWithOffset(Mesh & mesh,const Mesh::PrimitiveGroup & group,uint32 offset)1113 addGroupWithOffset(Mesh& mesh,
1114                    const Mesh::PrimitiveGroup& group,
1115                    uint32 offset)
1116 {
1117     if (group.nIndices == 0)
1118         return;
1119 
1120     uint32* newIndices = new uint32[group.nIndices];
1121     for (uint32 i = 0; i < group.nIndices; i++)
1122         newIndices[i] = group.indices[i] + offset;
1123 
1124     mesh.addGroup(group.prim, group.materialIndex,
1125                   group.nIndices, newIndices);
1126 }
1127 
1128 
1129 // Merge all meshes that share the same vertex description
1130 Model*
mergeModelMeshes(const Model & model)1131 mergeModelMeshes(const Model& model)
1132 {
1133     vector<Mesh*> meshes;
1134 
1135     uint32 i;
1136     for (i = 0; model.getMesh(i) != NULL; i++)
1137     {
1138         meshes.push_back(model.getMesh(i));
1139     }
1140 
1141     // Sort the meshes by vertex description
1142     sort(meshes.begin(), meshes.end(), MeshVertexDescComparator());
1143 
1144     Model* newModel = new Model();
1145 
1146     // Copy materials into the new model
1147     for (i = 0; model.getMaterial(i) != NULL; i++)
1148     {
1149         newModel->addMaterial(model.getMaterial(i));
1150     }
1151 
1152     uint32 meshIndex = 0;
1153     while (meshIndex < meshes.size())
1154     {
1155         const Mesh::VertexDescription& desc =
1156             meshes[meshIndex]->getVertexDescription();
1157 
1158         // Count the number of matching meshes
1159         uint32 nMatchingMeshes;
1160         for (nMatchingMeshes = 1;
1161              meshIndex + nMatchingMeshes < meshes.size();
1162              nMatchingMeshes++)
1163         {
1164             if (!(meshes[meshIndex + nMatchingMeshes]->getVertexDescription() == desc))
1165             {
1166                 break;
1167             }
1168         }
1169 
1170         // Count the number of vertices in all matching meshes
1171         uint32 totalVertices = 0;
1172         uint32 j;
1173         for (j = meshIndex; j < meshIndex + nMatchingMeshes; j++)
1174         {
1175             totalVertices += meshes[j]->getVertexCount();
1176         }
1177 
1178         char* vertexData = new char[totalVertices * desc.stride];
1179 
1180         // Create the new empty mesh
1181         Mesh* mergedMesh = new Mesh();
1182         mergedMesh->setVertexDescription(desc);
1183         mergedMesh->setVertices(totalVertices, vertexData);
1184 
1185         // Copy the vertex data and reindex and add primitive groups
1186         uint32 vertexCount = 0;
1187         for (j = meshIndex; j < meshIndex + nMatchingMeshes; j++)
1188         {
1189             const Mesh* mesh = meshes[j];
1190             memcpy(vertexData + vertexCount * desc.stride,
1191                    mesh->getVertexData(),
1192                    mesh->getVertexCount() * desc.stride);
1193 
1194             for (uint32 k = 0; mesh->getGroup(k) != NULL; k++)
1195             {
1196                 addGroupWithOffset(*mergedMesh, *mesh->getGroup(k),
1197                                    vertexCount);
1198             }
1199 
1200             vertexCount += mesh->getVertexCount();
1201         }
1202         assert(vertexCount == totalVertices);
1203 
1204         newModel->addMesh(mergedMesh);
1205 
1206         meshIndex += nMatchingMeshes;
1207     }
1208 
1209     return newModel;
1210 }
1211 
1212 
1213 #ifdef TRISTRIP
1214 bool
convertToStrips(Mesh & mesh)1215 convertToStrips(Mesh& mesh)
1216 {
1217     vector<Mesh::PrimitiveGroup*> groups;
1218 
1219     // NvTriStrip library can only handle 16-bit indices
1220     if (mesh.getVertexCount() >= 0x10000)
1221     {
1222         return true;
1223     }
1224 
1225     // Verify that the mesh contains just tri strips
1226     uint32 i;
1227     for (i = 0; mesh.getGroup(i) != NULL; i++)
1228     {
1229         if (mesh.getGroup(i)->prim != Mesh::TriList)
1230             return true;
1231     }
1232 
1233     // Convert the existing groups to triangle strips
1234     for (i = 0; mesh.getGroup(i) != NULL; i++)
1235     {
1236         const Mesh::PrimitiveGroup* group = mesh.getGroup(i);
1237 
1238         // Convert the vertex indices to shorts for the TriStrip library
1239         unsigned short* indices = new unsigned short[group->nIndices];
1240         uint32 j;
1241         for (j = 0; j < group->nIndices; j++)
1242         {
1243             indices[j] = (unsigned short) group->indices[j];
1244         }
1245 
1246         PrimitiveGroup* strips = NULL;
1247         unsigned short nGroups;
1248         bool r = GenerateStrips(indices,
1249                                 group->nIndices,
1250                                 &strips,
1251                                 &nGroups,
1252                                 false);
1253         if (!r || strips == NULL)
1254         {
1255             cerr << "Generate tri strips failed\n";
1256             return false;
1257         }
1258 
1259         // Call the tristrip library to convert the lists to strips.  Then,
1260         // convert from the NvTriStrip's primitive group structure to the
1261         // CMOD one and add it to the collection that will be added once
1262         // the mesh's original primitive groups are cleared.
1263         for (j = 0; j < nGroups; j++)
1264         {
1265             Mesh::PrimitiveGroupType prim = Mesh::InvalidPrimitiveGroupType;
1266             switch (strips[j].type)
1267             {
1268             case PT_LIST:
1269                 prim = Mesh::TriList;
1270                 break;
1271             case PT_STRIP:
1272                 prim = Mesh::TriStrip;
1273                 break;
1274             case PT_FAN:
1275                 prim = Mesh::TriFan;
1276                 break;
1277             }
1278 
1279             if (prim != Mesh::InvalidPrimitiveGroupType &&
1280                 strips[j].numIndices != 0)
1281             {
1282                 Mesh::PrimitiveGroup* newGroup = new Mesh::PrimitiveGroup();
1283                 newGroup->prim = prim;
1284                 newGroup->materialIndex = group->materialIndex;
1285                 newGroup->nIndices = strips[j].numIndices;
1286                 newGroup->indices = new uint32[newGroup->nIndices];
1287                 for (uint32 k = 0; k < newGroup->nIndices; k++)
1288                     newGroup->indices[k] = strips[j].indices[k];
1289 
1290                 groups.push_back(newGroup);
1291             }
1292         }
1293 
1294         delete[] strips;
1295     }
1296 
1297     mesh.clearGroups();
1298 
1299     // Add the stripified groups to the mesh
1300     for (vector<Mesh::PrimitiveGroup*>::const_iterator iter = groups.begin();
1301          iter != groups.end(); iter++)
1302     {
1303         mesh.addGroup(*iter);
1304     }
1305 
1306     return true;
1307 }
1308 #endif
1309 
1310 
parseCommandLine(int argc,char * argv[])1311 bool parseCommandLine(int argc, char* argv[])
1312 {
1313     int i = 1;
1314     int fileCount = 0;
1315 
1316     while (i < argc)
1317     {
1318         if (argv[i][0] == '-')
1319         {
1320             if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--binary"))
1321             {
1322                 outputBinary = true;
1323             }
1324             else if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--ascii"))
1325             {
1326                 outputBinary = false;
1327             }
1328             else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--uniquify"))
1329             {
1330                 uniquify = true;
1331             }
1332             else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--normals"))
1333             {
1334                 genNormals = true;
1335             }
1336             else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--tangents"))
1337             {
1338                 genTangents = true;
1339             }
1340             else if (!strcmp(argv[i], "-w") || !strcmp(argv[i], "--weld"))
1341             {
1342                 weldVertices = true;
1343             }
1344             else if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--merge"))
1345             {
1346                 mergeMeshes = true;
1347             }
1348             else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--optimize"))
1349             {
1350                 stripify = true;
1351             }
1352             else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--smooth"))
1353             {
1354                 if (i == argc - 1)
1355                 {
1356                     return false;
1357                 }
1358                 else
1359                 {
1360                     if (sscanf(argv[i + 1], " %f", &smoothAngle) != 1)
1361                         return false;
1362                     i++;
1363                 }
1364             }
1365             else
1366             {
1367                 return false;
1368             }
1369             i++;
1370         }
1371         else
1372         {
1373             if (fileCount == 0)
1374             {
1375                 // input filename first
1376                 inputFilename = string(argv[i]);
1377                 fileCount++;
1378             }
1379             else if (fileCount == 1)
1380             {
1381                 // output filename second
1382                 outputFilename = string(argv[i]);
1383                 fileCount++;
1384             }
1385             else
1386             {
1387                 // more than two filenames on the command line is an error
1388                 return false;
1389             }
1390             i++;
1391         }
1392     }
1393 
1394     return true;
1395 }
1396 
1397 
main(int argc,char * argv[])1398 int main(int argc, char* argv[])
1399 {
1400     if (!parseCommandLine(argc, argv))
1401     {
1402         usage();
1403         return 1;
1404     }
1405 
1406     Model* model = NULL;
1407     if (!inputFilename.empty())
1408     {
1409         ifstream in(inputFilename.c_str(), ios::in | ios::binary);
1410         if (!in.good())
1411         {
1412             cerr << "Error opening " << inputFilename << "\n";
1413             return 1;
1414         }
1415         model = LoadModel(in);
1416     }
1417     else
1418     {
1419         model = LoadModel(cin);
1420     }
1421 
1422     if (model == NULL)
1423         return 1;
1424 
1425     if (genNormals || genTangents)
1426     {
1427         Model* newModel = new Model();
1428         uint32 i;
1429 
1430         // Copy materials
1431         for (i = 0; model->getMaterial(i) != NULL; i++)
1432         {
1433             newModel->addMaterial(model->getMaterial(i));
1434         }
1435 
1436         // Generate normals and/or tangents for each model in the mesh
1437         for (i = 0; model->getMesh(i) != NULL; i++)
1438         {
1439             Mesh* mesh = model->getMesh(i);
1440             Mesh* newMesh = NULL;
1441 
1442             if (genNormals)
1443             {
1444                 newMesh = generateNormals(*mesh,
1445                                           degToRad(smoothAngle),
1446                                           weldVertices);
1447                 if (newMesh == NULL)
1448                 {
1449                     cerr << "Error generating normals!\n";
1450                     return 1;
1451                 }
1452                 // TODO: clean up old mesh
1453                 mesh = newMesh;
1454             }
1455 
1456             if (genTangents)
1457             {
1458                 newMesh = generateTangents(*mesh, weldVertices);
1459                 if (newMesh == NULL)
1460                 {
1461                     cerr << "Error generating tangents!\n";
1462                     return 1;
1463                 }
1464                 // TODO: clean up old mesh
1465                 mesh = newMesh;
1466             }
1467 
1468             newModel->addMesh(mesh);
1469         }
1470 
1471         // delete model;
1472         model = newModel;
1473     }
1474 
1475     if (mergeMeshes)
1476     {
1477         model = mergeModelMeshes(*model);
1478     }
1479 
1480     if (uniquify)
1481     {
1482         for (uint32 i = 0; model->getMesh(i) != NULL; i++)
1483         {
1484             Mesh* mesh = model->getMesh(i);
1485             uniquifyVertices(*mesh);
1486         }
1487     }
1488 
1489 #ifdef TRISTRIP
1490     if (stripify)
1491     {
1492         SetCacheSize(vertexCacheSize);
1493         for (uint32 i = 0; model->getMesh(i) != NULL; i++)
1494         {
1495             Mesh* mesh = model->getMesh(i);
1496             convertToStrips(*mesh);
1497         }
1498     }
1499 #endif
1500 
1501     if (outputFilename.empty())
1502     {
1503         if (outputBinary)
1504             SaveModelBinary(model, cout);
1505         else
1506             SaveModelAscii(model, cout);
1507     }
1508     else
1509     {
1510         ofstream out(outputFilename.c_str(),
1511                      outputBinary ? (ios::binary|ios::out) : ios::out);
1512         if (!out.good())
1513         {
1514             cerr << "Error opening output file " << outputFilename << "\n";
1515             return 1;
1516         }
1517 
1518         if (outputBinary)
1519             SaveModelBinary(model, out);
1520         else
1521             SaveModelAscii(model, out);
1522     }
1523 
1524     return 0;
1525 }
1526