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