1 /*
2  * Copyright © 2008 Ben Smith
3  * Copyright © 2010-2011 Linaro Limited
4  *
5  * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
6  *
7  * glmark2 is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * glmark2.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  * Authors:
21  *  Ben Smith (original glmark benchmark)
22  *  Alexandros Frantzis (glmark2)
23  */
24 #include "mesh.h"
25 #include "model.h"
26 #include "vec.h"
27 #include "log.h"
28 #include "options.h"
29 #include "util.h"
30 #include "float.h"
31 #include "math.h"
32 #include <algorithm>
33 #include <fstream>
34 #include <sstream>
35 #include <memory>
36 
37 using std::string;
38 using std::vector;
39 using LibMatrix::vec2;
40 using LibMatrix::vec3;
41 using LibMatrix::uvec3;
42 
read_from_file(std::istream & file,void * dst,size_t nmemb,size_t size)43 static inline void read_from_file(std::istream& file, void *dst, size_t nmemb, size_t size) {
44     file.read(reinterpret_cast<char *>(dst), nmemb * size);
45 #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
46     char *p = reinterpret_cast<char *>(dst);
47     for (size_t i = 0; i < nmemb; ++i) {
48         if (size == 2) {
49             std::swap(p[size*i], p[size*i+1]);
50         } else if (size == 4) {
51             std::swap(p[size*i], p[size*i+3]);
52             std::swap(p[size*i+1], p[size*i+2]);
53         }
54     }
55 #endif
56 }
57 
58 #define read_or_fail(file, dst, nmemb, size) do { \
59     read_from_file((file), (dst), (nmemb), (size)); \
60     if (file.gcount() < (std::streamsize)((nmemb)*(size))) { \
61         Log::error("%s: %d: Failed to read %zd bytes from 3ds file (read %zd)\n", \
62                    __FUNCTION__, __LINE__, \
63                    (size_t)((nmemb)*(size)), file.gcount()); \
64         return false; \
65     } \
66 } while(0);
67 
68 /**
69  * Computes the bounding box for a Model::Object.
70  *
71  * @param object the Model object
72  */
73 void
compute_bounding_box(const Object & object)74 Model::compute_bounding_box(const Object& object)
75 {
76     float minX(FLT_MAX);
77     float maxX(FLT_MIN);
78     float minY(FLT_MAX);
79     float maxY(FLT_MIN);
80     float minZ(FLT_MAX);
81     float maxZ(FLT_MIN);
82     for (vector<Vertex>::const_iterator vIt = object.vertices.begin(); vIt != object.vertices.end(); vIt++)
83     {
84         const vec3& curVtx = vIt->v;
85         if (curVtx.x() < minX)
86         {
87             minX = curVtx.x();
88         }
89         if (curVtx.x() > maxX)
90         {
91             maxX = curVtx.x();
92         }
93         if (curVtx.y() < minY)
94         {
95             minY = curVtx.y();
96         }
97         if (curVtx.y() > maxY)
98         {
99             maxY = curVtx.y();
100         }
101         if (curVtx.z() < minZ)
102         {
103             minZ = curVtx.z();
104         }
105         if (curVtx.z() > maxZ)
106         {
107             maxZ = curVtx.z();
108         }
109     }
110     maxVec_ = vec3(maxX, maxY, maxZ);
111     minVec_ = vec3(minX, minY, minZ);
112 }
113 
114 /**
115  * Appends the vertices of a Model::Object to a Mesh.
116  *
117  * @param object the object to append
118  * @param mesh the mesh to append to
119  * @param p_pos the attribute position to use for the 'position' attribute
120  * @param n_pos the attribute position to use for the 'normal' attribute
121  * @param t_pos the attribute position to use for the 'texcoord' attribute
122  */
123 void
append_object_to_mesh(const Object & object,Mesh & mesh,int p_pos,int n_pos,int t_pos,int nt_pos,int nb_pos)124 Model::append_object_to_mesh(const Object &object, Mesh &mesh,
125                              int p_pos, int n_pos, int t_pos,
126                              int nt_pos, int nb_pos)
127 {
128     for (vector<Face>::const_iterator faceIt = object.faces.begin();
129          faceIt != object.faces.end();
130          faceIt++)
131     {
132         // In some model file formats (OBJ in particular), the face description
133         // may contain separate indices per-attribute.  So, we need to allow
134         // for this when adding each vertex attribute to the mesh.
135         const Face &face = *faceIt;
136         const Vertex &v1 = object.vertices[face.v.x()];
137         const Vertex &v2 = object.vertices[face.v.y()];
138         const Vertex &v3 = object.vertices[face.v.z()];
139 
140         bool separate_t(face.which & Face::OBJ_FACE_T);
141 
142         const Vertex &t1 = object.vertices[separate_t ? face.t.x() : face.v.x()];
143         const Vertex &t2 = object.vertices[separate_t ? face.t.y() : face.v.y()];
144         const Vertex &t3 = object.vertices[separate_t ? face.t.z() : face.v.z()];
145 
146         bool separate_n(face.which & Face::OBJ_FACE_N);
147 
148         const Vertex &n1 = object.vertices[separate_n ? face.n.x() : face.v.x()];
149         const Vertex &n2 = object.vertices[separate_n ? face.n.y() : face.v.y()];
150         const Vertex &n3 = object.vertices[separate_n ? face.n.z() : face.v.z()];
151 
152         mesh.next_vertex();
153         if (p_pos >= 0)
154             mesh.set_attrib(p_pos, v1.v);
155         if (n_pos >= 0)
156             mesh.set_attrib(n_pos, n1.n);
157         if (t_pos >= 0)
158             mesh.set_attrib(t_pos, t1.t);
159         if (nt_pos >= 0)
160             mesh.set_attrib(nt_pos, v1.nt);
161         if (nb_pos >= 0)
162             mesh.set_attrib(nb_pos, v1.nb);
163 
164         mesh.next_vertex();
165         if (p_pos >= 0)
166             mesh.set_attrib(p_pos, v2.v);
167         if (n_pos >= 0)
168             mesh.set_attrib(n_pos, n2.n);
169         if (t_pos >= 0)
170             mesh.set_attrib(t_pos, t2.t);
171         if (nt_pos >= 0)
172             mesh.set_attrib(nt_pos, v2.nt);
173         if (nb_pos >= 0)
174             mesh.set_attrib(nb_pos, v2.nb);
175 
176         mesh.next_vertex();
177         if (p_pos >= 0)
178             mesh.set_attrib(p_pos, v3.v);
179         if (n_pos >= 0)
180             mesh.set_attrib(n_pos, n3.n);
181         if (t_pos >= 0)
182             mesh.set_attrib(t_pos, t3.t);
183         if (nt_pos >= 0)
184             mesh.set_attrib(nt_pos, v3.nt);
185         if (nb_pos >= 0)
186             mesh.set_attrib(nb_pos, v3.nb);
187     }
188 }
189 
190 /**
191  * Converts a model to a mesh using the default attributes bindings.
192  *
193  * The default attributes and their order is: Position, Normal, Texcoord
194  *
195  * @param mesh the mesh to populate
196  */
197 void
convert_to_mesh(Mesh & mesh)198 Model::convert_to_mesh(Mesh &mesh)
199 {
200     std::vector<std::pair<AttribType, int> > attribs;
201 
202     attribs.push_back(std::pair<AttribType, int>(AttribTypePosition, 3));
203     attribs.push_back(std::pair<AttribType, int>(AttribTypeNormal, 3));
204     attribs.push_back(std::pair<AttribType, int>(AttribTypeTexcoord, 2));
205 
206     convert_to_mesh(mesh, attribs);
207 }
208 
209 /**
210  * Converts a model to a mesh using custom attribute bindings.
211  *
212  * The attribute bindings are pairs of <AttribType, dimensionality>.
213  *
214  * @param mesh the mesh to populate
215  * @param attribs the attribute bindings to use
216  */
217 void
convert_to_mesh(Mesh & mesh,const std::vector<std::pair<AttribType,int>> & attribs)218 Model::convert_to_mesh(Mesh &mesh,
219                        const std::vector<std::pair<AttribType, int> > &attribs)
220 {
221     std::vector<int> format;
222     int p_pos = -1;
223     int n_pos = -1;
224     int t_pos = -1;
225     int nt_pos = -1;
226     int nb_pos = -1;
227 
228     mesh.reset();
229 
230     for (std::vector<std::pair<AttribType, int> >::const_iterator ai = attribs.begin();
231          ai != attribs.end();
232          ai++)
233     {
234         format.push_back(ai->second);
235         if (ai->first == AttribTypePosition)
236             p_pos = ai - attribs.begin();
237         else if (ai->first == AttribTypeNormal)
238             n_pos = ai - attribs.begin();
239         else if (ai->first == AttribTypeTexcoord)
240             t_pos = ai - attribs.begin();
241         else if (ai->first == AttribTypeTangent)
242             nt_pos = ai - attribs.begin();
243         else if (ai->first == AttribTypeBitangent)
244             nb_pos = ai - attribs.begin();
245     }
246 
247     mesh.set_vertex_format(format);
248 
249     for (std::vector<Object>::const_iterator iter = objects_.begin();
250          iter != objects_.end();
251          iter++)
252     {
253         append_object_to_mesh(*iter, mesh, p_pos, n_pos, t_pos, nt_pos, nb_pos);
254     }
255 }
256 
257 void
calculate_texcoords()258 Model::calculate_texcoords()
259 {
260     if (gotTexcoords_)
261         return;
262 
263     // Since the model didn't come with texcoords, and we don't actually know
264     // if it came with normals, either, we'll use positional spherical mapping
265     // to generate texcoords for the model.  See:
266     // http://www.mvps.org/directx/articles/spheremap.htm for more details.
267     vec3 centerVec = maxVec_ + minVec_;
268     centerVec *= 0.5;
269 
270     for (std::vector<Object>::iterator iter = objects_.begin();
271          iter != objects_.end();
272          iter++)
273     {
274         Object &object = *iter;
275         for (vector<Vertex>::iterator vertexIt = object.vertices.begin();
276              vertexIt != object.vertices.end();
277              vertexIt++)
278         {
279             Vertex& curVertex = *vertexIt;
280             vec3 vnorm(curVertex.v - centerVec);
281             vnorm.normalize();
282             curVertex.t.x(asinf(vnorm.x()) / M_PI + 0.5);
283             curVertex.t.y(asinf(vnorm.y()) / M_PI + 0.5);
284         }
285     }
286 
287     gotTexcoords_ = true;
288 }
289 
290 /**
291  * Calculates the normal vectors of the model vertices.
292  */
293 void
calculate_normals()294 Model::calculate_normals()
295 {
296     if (gotNormals_)
297         return;
298 
299     LibMatrix::vec3 n;
300 
301     for (std::vector<Object>::iterator iter = objects_.begin();
302          iter != objects_.end();
303          iter++)
304     {
305         Object &object = *iter;
306 
307         for (vector<Face>::const_iterator f_iter = object.faces.begin();
308              f_iter != object.faces.end();
309              f_iter++)
310         {
311             const Face &face = *f_iter;
312             Vertex &a = object.vertices[face.v.x()];
313             Vertex &b = object.vertices[face.v.y()];
314             Vertex &c = object.vertices[face.v.z()];
315 
316             /* Calculate normal */
317             n = LibMatrix::vec3::cross(b.v - a.v, c.v - a.v);
318             n.normalize();
319             a.n += n;
320             b.n += n;
321             c.n += n;
322 
323             LibMatrix::vec3 q1(b.v - a.v);
324             LibMatrix::vec3 q2(c.v - a.v);
325             LibMatrix::vec2 u1(b.t - a.t);
326             LibMatrix::vec2 u2(c.t - a.t);
327             float det = (u1.x() * u2.y() - u2.x() * u1.y());
328 
329             /* Calculate tangent */
330             LibMatrix::vec3 nt;
331             nt.x(det * (u2.y() * q1.x() - u1.y() * q2.x()));
332             nt.y(det * (u2.y() * q1.y() - u1.y() * q2.y()));
333             nt.z(det * (u2.y() * q1.z() - u1.y() * q2.z()));
334             nt.normalize();
335             a.nt += nt;
336             b.nt += nt;
337             c.nt += nt;
338 
339             /* Calculate bitangent */
340             LibMatrix::vec3 nb;
341             nb.x(det * (u1.x() * q2.x() - u2.x() * q1.x()));
342             nb.y(det * (u1.x() * q2.y() - u2.x() * q1.y()));
343             nb.z(det * (u1.x() * q2.z() - u2.x() * q1.z()));
344             nb.normalize();
345             a.nb += nb;
346             b.nb += nb;
347             c.nb += nb;
348         }
349 
350         for (vector<Vertex>::iterator v_iter = object.vertices.begin();
351              v_iter != object.vertices.end();
352              v_iter++)
353         {
354             Vertex &v = *v_iter;
355             /* Orthogonalize */
356             v.nt = (v.nt - v.n * LibMatrix::vec3::dot(v.nt, v.n));
357             v.n.normalize();
358             v.nt.normalize();
359             v.nb.normalize();
360         }
361     }
362 
363     gotNormals_ = true;
364 }
365 
366 /**
367  * Load a model from a 3DS file.
368  *
369  * @param filename the name of the file
370  *
371  * @return whether loading succeeded
372  */
373 bool
load_3ds(const std::string & filename)374 Model::load_3ds(const std::string &filename)
375 {
376     Object *object(0);
377 
378     Log::debug("Loading model from 3ds file '%s'\n", filename.c_str());
379 
380     const std::unique_ptr<std::istream> input_file_ptr(Util::get_resource(filename));
381     std::istream& input_file(*input_file_ptr);
382 
383     if (!input_file) {
384         Log::error("Could not open 3ds file '%s'\n", filename.c_str());
385         return false;
386     }
387 
388     // Loop to scan the whole file
389     while (!input_file.eof()) {
390         uint16_t chunk_id;
391         uint32_t chunk_length;
392 
393         // Read the chunk header
394         read_from_file(input_file, &chunk_id, 1, 2);
395         if (input_file.gcount() == 0) {
396             continue;
397         }
398         else if (input_file.gcount() < 2) {
399             Log::error("%s: %d: Failed to read %zd bytes from 3ds file (read %zd)\n",
400                        __FUNCTION__, __LINE__, 2, input_file.gcount());
401             return false;
402         }
403 
404         //Read the length of the chunk
405         read_or_fail(input_file, &chunk_length, 1, 4);
406 
407         switch (chunk_id)
408         {
409             //----------------- MAIN3DS -----------------
410             // Description: Main chunk, contains all the other chunks
411             // Chunk ID: 4d4d
412             // Chunk Length: 0 + sub chunks
413             //-------------------------------------------
414             case 0x4d4d:
415                 break;
416 
417             //----------------- EDIT3DS -----------------
418             // Description: 3D Editor chunk, objects layout info
419             // Chunk ID: 3d3d (hex)
420             // Chunk Length: 0 + sub chunks
421             //-------------------------------------------
422             case 0x3d3d:
423                 break;
424 
425             //--------------- EDIT_OBJECT ---------------
426             // Description: Object block, info for each object
427             // Chunk ID: 4000 (hex)
428             // Chunk Length: len(object name) + sub chunks
429             //-------------------------------------------
430             case 0x4000:
431                 {
432                 std::stringstream ss;
433                 unsigned char c = 1;
434 
435                 for (int i = 0; i < 20 && c != '\0'; i++) {
436                     read_or_fail(input_file, &c, 1, 1);
437                     ss << c;
438                 }
439 
440                 objects_.push_back(Object(ss.str()));
441                 object = &objects_.back();
442                 }
443                 break;
444 
445             //--------------- OBJ_TRIMESH ---------------
446             // Description: Triangular mesh, contains chunks for 3d mesh info
447             // Chunk ID: 4100 (hex)
448             // Chunk Length: 0 + sub chunks
449             //-------------------------------------------
450             case 0x4100:
451                 break;
452 
453             //--------------- TRI_VERTEXL ---------------
454             // Description: Vertices list
455             // Chunk ID: 4110 (hex)
456             // Chunk Length: 1 x unsigned short (number of vertices)
457             //             + 3 x float (vertex coordinates) x (number of vertices)
458             //             + sub chunks
459             //-------------------------------------------
460             case 0x4110:
461                 {
462                 uint16_t qty;
463                 read_or_fail(input_file, &qty, 1, sizeof(uint16_t));
464                 object->vertices.resize(qty);
465 
466                 for (uint16_t i = 0; i < qty; i++) {
467                     float f[3];
468                     read_or_fail(input_file, f, 3, sizeof(float));
469                     vec3& vertex = object->vertices[i].v;
470                     vertex.x(f[0]);
471                     vertex.y(f[1]);
472                     vertex.z(f[2]);
473                 }
474                 }
475                 break;
476 
477             //--------------- TRI_FACEL1 ----------------
478             // Description: Polygons (faces) list
479             // Chunk ID: 4120 (hex)
480             // Chunk Length: 1 x unsigned short (number of polygons)
481             //             + 3 x unsigned short (polygon points) x (number of polygons)
482             //             + sub chunks
483             //-------------------------------------------
484             case 0x4120:
485                 {
486                 uint16_t qty;
487                 read_or_fail(input_file, &qty, 1, sizeof(uint16_t));
488                 object->faces.resize(qty);
489                 for (uint16_t i = 0; i < qty; i++) {
490                     uint16_t f[4];
491                     read_or_fail(input_file, f, 4, sizeof(uint16_t));
492                     uvec3& face = object->faces[i].v;
493                     face.x(f[0]);
494                     face.y(f[1]);
495                     face.z(f[2]);
496                 }
497                 }
498                 break;
499 
500             //------------- TRI_MAPPINGCOORS ------------
501             // Description: Vertices list
502             // Chunk ID: 4140 (hex)
503             // Chunk Length: 1 x unsigned short (number of mapping points)
504             //             + 2 x float (mapping coordinates) x (number of mapping points)
505             //             + sub chunks
506             //-------------------------------------------
507             case 0x4140:
508                 {
509                 uint16_t qty;
510                 read_or_fail(input_file, &qty, 1, sizeof(uint16_t));
511                 for (uint16_t i = 0; i < qty; i++) {
512                     float f[2];
513                     read_or_fail(input_file, f, 2, sizeof(float));
514                     vec2& texcoord = object->vertices[i].t;
515                     texcoord.x(f[0]);
516                     texcoord.y(f[1]);
517                 }
518                 }
519                 gotTexcoords_ = true;
520                 break;
521 
522             //----------- Skip unknow chunks ------------
523             //We need to skip all the chunks that currently we don't use
524             //We use the chunk length information to set the file pointer
525             //to the same level next chunk
526             //-------------------------------------------
527             default:
528                 input_file.seekg(chunk_length - 6, std::ios::cur);
529         }
530     }
531 
532     // Compute bounding box for perspective projection
533     compute_bounding_box(*object);
534 
535     if (Options::show_debug) {
536         for (std::vector<Object>::const_iterator iter = objects_.begin();
537              iter != objects_.end();
538              iter++)
539         {
540             Log::debug("    Object name: %s Vertex count: %d Face count: %d\n",
541                        iter->name.c_str(), iter->vertices.size(), iter->faces.size());
542         }
543     }
544 
545     return true;
546 }
547 
548 
549 const unsigned int Model::Face::OBJ_FACE_V = 0x1;
550 const unsigned int Model::Face::OBJ_FACE_T = 0x2;
551 const unsigned int Model::Face::OBJ_FACE_N = 0x4;
552 
553 /**
554  * Parse 2-element vertex attribute from an OBJ file.
555  *
556  * @param source the source line to parse
557  * @param v the vec2 to populate
558  */
559 void
obj_get_attrib(const string & source,vec2 & v)560 Model::obj_get_attrib(const string& source, vec2& v)
561 {
562     // Our attribs are whitespace separated, so use a fuzzy split.
563     vector<string> elements;
564     Util::split(source, ' ', elements, Util::SplitModeFuzzy);
565 
566     // Find the first value...
567     float x = Util::fromString<float>(elements[0]);
568     // And the second value (there might be a third, but we don't care)...
569     float y = Util::fromString<float>(elements[1]);
570     v.x(x);
571     v.y(y);
572 }
573 
574 /**
575  * Parse 3-element vertex attribute from an OBJ file.
576  *
577  * @param source the source line to parse
578  * @param v the vec3 to populate
579  */
580 void
obj_get_attrib(const string & source,vec3 & v)581 Model::obj_get_attrib(const string& source, vec3& v)
582 {
583     // Our attribs are whitespace separated, so use a fuzzy split.
584     vector<string> elements;
585     Util::split(source, ' ', elements, Util::SplitModeFuzzy);
586 
587     // Find the first value...
588     float x = Util::fromString<float>(elements[0]);
589     // Then the second value...
590     float y = Util::fromString<float>(elements[1]);
591     // And the third value (there might be a fourth, but we don't care)...
592     float z = Util::fromString<float>(elements[2]);
593     v.x(x);
594     v.y(y);
595     v.z(z);
596 }
597 
598 
599 void
obj_face_get_index(const string & tuple,unsigned int & which,unsigned int & v,unsigned int & t,unsigned int & n)600 Model::obj_face_get_index(const string& tuple, unsigned int& which,
601     unsigned int& v, unsigned int& t, unsigned int& n)
602 {
603     // We can use a normal split here as syntax requires no spaces around
604     // the '/' delimiter for a face description.
605     vector<string> elements;
606     Util::split(tuple, '/', elements, Util::SplitModeNormal);
607 
608     if (elements.empty())
609     {
610         which = 0;
611         return;
612     }
613 
614     which = Face::OBJ_FACE_V;
615     v = Util::fromString<unsigned int>(elements[0]);
616 
617     unsigned int num_elements = elements.size();
618 
619     if (num_elements > 1 && !elements[1].empty())
620     {
621         which |= Face::OBJ_FACE_T;
622         t = Util::fromString<unsigned int>(elements[1]);
623     }
624 
625     if (num_elements > 2 && !elements[2].empty())
626     {
627         which |= Face::OBJ_FACE_N;
628         n = Util::fromString<unsigned int>(elements[2]);
629     }
630 
631     return;
632 }
633 
634 /**
635  * Parse a face description from an OBJ file.
636  * Faces always specify position, but optionally can also contain separate
637  * indices for texcoords and normals.
638  *
639  * @param source the source line to parse
640  * @param v the uvec3 to populate
641  */
642 void
obj_get_face(const string & source,Face & f)643 Model::obj_get_face(const string& source, Face& f)
644 {
645     // Our indices are whitespace separated, so use a fuzzy split.
646     vector<string> elements;
647     Util::split(source, ' ', elements, Util::SplitModeFuzzy);
648 
649     // Find the first value...
650     unsigned int which(0);
651     unsigned int vx(0);
652     unsigned int tx(0);
653     unsigned int nx(0);
654     obj_face_get_index(elements[0], which, vx, tx, nx);
655 
656     // Then the second value...
657     unsigned int vy(0);
658     unsigned int ty(0);
659     unsigned int ny(0);
660     obj_face_get_index(elements[1], which, vy, ty, ny);
661 
662     // And the third value (there might be a fourth, but we don't care)...
663     unsigned int vz(0);
664     unsigned int tz(0);
665     unsigned int nz(0);
666     obj_face_get_index(elements[2], which, vz, tz, nz);
667 
668     // OBJ models start absoluted indices at '1', so subtract to re-base to
669     // '0'.  We do not handle relative indexing (negative indices).
670     f.which = which;
671     f.v.x(vx - 1);
672     f.v.y(vy - 1);
673     f.v.z(vz - 1);
674     if (which & Face::OBJ_FACE_T)
675     {
676         f.t.x(tx - 1);
677         f.t.y(ty - 1);
678         f.t.z(tz - 1);
679     }
680     if (which & Face::OBJ_FACE_N)
681     {
682         f.n.x(nx - 1);
683         f.n.y(ny - 1);
684         f.n.z(nz - 1);
685     }
686 }
687 
688 /**
689  * Load a model from an OBJ file.
690  *
691  * @param filename the name of the file
692  *
693  * @return whether loading succeeded
694  */
695 bool
load_obj(const std::string & filename)696 Model::load_obj(const std::string &filename)
697 {
698     Log::debug("Loading model from obj file '%s'\n", filename.c_str());
699 
700     const std::unique_ptr<std::istream> input_file_ptr(Util::get_resource(filename));
701     std::istream& inputFile(*input_file_ptr);
702     if (!inputFile)
703     {
704         Log::error("Failed to open '%s'\n", filename.c_str());
705         return false;
706     }
707 
708     vector<string> sourceVec;
709     string curLine;
710     while (getline(inputFile, curLine))
711     {
712         sourceVec.push_back(curLine);
713     }
714 
715     // Give ourselves an object to populate.
716     objects_.push_back(Object(string()));
717     Object& object(objects_.back());
718 
719     static const string object_definition("o");
720     static const string vertex_definition("v");
721     static const string normal_definition("vn");
722     static const string texcoord_definition("vt");
723     static const string face_definition("f");
724     vector<vec3> positions;
725     vector<vec3> normals;
726     vector<vec2> texcoords;
727     for (vector<string>::const_iterator lineIt = sourceVec.begin();
728          lineIt != sourceVec.end();
729          lineIt++)
730     {
731         const string& curSrc = *lineIt;
732         // Is it a vertex attribute, a face description, comment or other?
733         // We only care about the first two, we ignore comments, group names,
734         // smoothing groups, etc.
735         string::size_type startPos(0);
736         string::size_type spacePos = curSrc.find(" ", startPos);
737         string::size_type num_chars(string::npos);
738         string definition;
739         if (spacePos != string::npos)
740         {
741             // Could be arbitrary whitespace between description type and
742             // the data
743             string::size_type defPos = curSrc.find_first_not_of(' ', spacePos);
744             definition = string(curSrc, defPos);
745             num_chars = spacePos - startPos;
746         }
747         string definitionType(curSrc, startPos, num_chars);
748 
749         if (definitionType == vertex_definition)
750         {
751             vec3 p;
752             obj_get_attrib(definition, p);
753             positions.push_back(p);
754         }
755         else if (definitionType == normal_definition)
756         {
757             vec3 n;
758             obj_get_attrib(definition, n);
759             normals.push_back(n);
760         }
761         else if (definitionType == texcoord_definition)
762         {
763             vec2 t;
764             obj_get_attrib(definition, t);
765             texcoords.push_back(t);
766         }
767         else if (definitionType == face_definition)
768         {
769             Face f;
770             obj_get_face(definition, f);
771             object.faces.push_back(f);
772         }
773         else if (definitionType == object_definition)
774         {
775             object.name = definition;
776         }
777     }
778 
779     if (!texcoords.empty())
780     {
781         gotTexcoords_ = true;
782     }
783     if (!normals.empty())
784     {
785         gotNormals_ = true;
786     }
787     unsigned int numVertices = positions.size();
788     object.vertices.resize(numVertices);
789     for (unsigned int i = 0; i < numVertices; i++)
790     {
791         Vertex& curVertex = object.vertices[i];
792         curVertex.v = positions[i];
793         if (gotTexcoords_)
794         {
795             curVertex.t = texcoords[i];
796         }
797         if (gotNormals_)
798         {
799             curVertex.n = normals[i];
800         }
801     }
802 
803     // Compute bounding box for perspective projection
804     compute_bounding_box(object);
805 
806     Log::debug("Object name: %s Vertex count: %u Face count: %u\n",
807         object.name.empty() ? "(none)" : object.name.c_str(), object.vertices.size(), object.faces.size());
808     return true;
809 }
810 
811 namespace ModelPrivate
812 {
813 ModelMap modelMap;
814 }
815 
816 /**
817  * Locate all available models.
818  *
819  * This method scans the built-in data paths and build a database of usable
820  * models available to scenes.  Map is available on a read-only basis to scenes
821  * that might find it useful for listing models, etc.
822  *
823  * @return a map containing information about the located models
824  */
825 const ModelMap&
find_models()826 Model::find_models()
827 {
828     if (!ModelPrivate::modelMap.empty())
829     {
830         return ModelPrivate::modelMap;
831     }
832     vector<string> pathVec;
833     string dataDir(Options::data_path + "/models");
834     Util::list_files(dataDir, pathVec);
835 #ifdef GLMARK_EXTRAS_PATH
836     string extrasDir(GLMARK_EXTRAS_PATH"/models");
837     Util::list_files(extrasDir, pathVec);
838 #endif
839 
840     // Now that we have a list of all of the model files available to us,
841     // let's go through and pull out the names and what format they're in
842     // so the scene can decide which ones to use.
843     for(vector<string>::const_iterator pathIt = pathVec.begin();
844         pathIt != pathVec.end();
845         pathIt++)
846     {
847         const string& curPath = *pathIt;
848         string::size_type namePos(0);
849         string::size_type slashPos = curPath.rfind("/");
850         if (slashPos != string::npos)
851         {
852             // Advance to the first character after the last slash
853             namePos = slashPos + 1;
854         }
855 
856         ModelFormat format(MODEL_INVALID);
857         string::size_type extPos = curPath.rfind(".3ds");
858         if (extPos == string::npos)
859         {
860             // It's not a 3ds model
861             extPos = curPath.rfind(".obj");
862             if (extPos == string::npos)
863             {
864                 // It's not an obj model either, so skip it.
865                 continue;
866             }
867             format = MODEL_OBJ;
868         }
869         else
870         {
871             // It's a 3ds model
872             format = MODEL_3DS;
873         }
874 
875         string name(curPath, namePos, extPos - namePos);
876         ModelDescriptor* desc = new ModelDescriptor(name, format, curPath);
877         ModelPrivate::modelMap.insert(std::make_pair(name, desc));
878     }
879 
880     return ModelPrivate::modelMap;
881 }
882 
883 /**
884  * Load a model by name.
885  *
886  * You must initialize the available model collection using
887  * Model::find_models() before using this method.
888  *
889  * @param modelName the model name
890  *
891  * @return whether the operation succeeded
892  */
893 bool
load(const string & modelName)894 Model::load(const string& modelName)
895 {
896     bool retVal(false);
897     ModelMap::const_iterator modelIt = ModelPrivate::modelMap.find(modelName);
898     if (modelIt == ModelPrivate::modelMap.end())
899     {
900         return retVal;
901     }
902 
903     ModelDescriptor* desc = modelIt->second;
904     switch (desc->format())
905     {
906         case MODEL_INVALID:
907             break;
908         case MODEL_3DS:
909             retVal = load_3ds(desc->pathname());
910             break;
911         case MODEL_OBJ:
912             retVal = load_obj(desc->pathname());
913             break;
914     }
915 
916     return retVal;
917 }
918