Merge lp:~glmark2-dev/glmark2/obj-update into lp:glmark2/2011.11
- obj-update
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 260 |
Proposed branch: | lp:~glmark2-dev/glmark2/obj-update |
Merge into: | lp:glmark2/2011.11 |
Diff against target: |
730 lines (+296/-180) 8 files modified
src/model.cpp (+262/-168) src/model.h (+17/-4) src/scene-build.cpp (+2/-1) src/scene-bump.cpp (+6/-3) src/scene-refract.cpp (+2/-1) src/scene-shading.cpp (+2/-1) src/scene-shadow.cpp (+2/-1) src/scene-texture.cpp (+3/-1) |
To merge this branch: | bzr merge lp:~glmark2-dev/glmark2/obj-update |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexandros Frantzis | Approve | ||
Review via email: mp+145746@code.launchpad.net |
Commit message
Description of the change
Model: This adds "more complete" handling of the .obj file format for models. In particular, these changes add better support for arbitrary whitespace between attribute elements, and add support for vertex normals and texcoords. These additional attributes also have potentially unique indices in the face descriptions, so the definition of a Face within the Model object needed to change (which caused some changes in the conversion of a model to a mesh). There are many other features of this file format that could be added, but it would be best to visit those upon encountering a compelling enough model.
Jesse Barker (jesse-barker) wrote : | # |
I was conflicted about that, but wanted to be consistent with the texcoord attribute, and also wanted to leave the interface in place to allow for the future possibility of offering different methods of calculating both attributes if they weren't provided with the model (of course, that would require some additional internal changes, so perhaps it's not worth it after all). The interfaces allow the scene to decide which option to use, but I could still see taking it out and worrying about it later.
Preview Diff
1 | === modified file 'src/model.cpp' |
2 | --- src/model.cpp 2012-07-05 17:22:16 +0000 |
3 | +++ src/model.cpp 2013-01-30 23:17:24 +0000 |
4 | @@ -35,6 +35,7 @@ |
5 | |
6 | using std::string; |
7 | using std::vector; |
8 | +using LibMatrix::vec2; |
9 | using LibMatrix::vec3; |
10 | using LibMatrix::uvec3; |
11 | |
12 | @@ -108,50 +109,65 @@ |
13 | int p_pos, int n_pos, int t_pos, |
14 | int nt_pos, int nb_pos) |
15 | { |
16 | - size_t face_count = object.faces.size(); |
17 | - |
18 | - for(size_t i = 0; i < 3 * face_count; i += 3) |
19 | + for (vector<Face>::const_iterator faceIt = object.faces.begin(); |
20 | + faceIt != object.faces.end(); |
21 | + faceIt++) |
22 | { |
23 | - const Face &face = object.faces[i / 3]; |
24 | - const Vertex &a = object.vertices[face.a]; |
25 | - const Vertex &b = object.vertices[face.b]; |
26 | - const Vertex &c = object.vertices[face.c]; |
27 | - |
28 | - mesh.next_vertex(); |
29 | - if (p_pos >= 0) |
30 | - mesh.set_attrib(p_pos, a.v); |
31 | - if (n_pos >= 0) |
32 | - mesh.set_attrib(n_pos, a.n); |
33 | - if (t_pos >= 0) |
34 | - mesh.set_attrib(t_pos, a.t); |
35 | - if (nt_pos >= 0) |
36 | - mesh.set_attrib(nt_pos, a.nt); |
37 | - if (nb_pos >= 0) |
38 | - mesh.set_attrib(nb_pos, a.nb); |
39 | - |
40 | - mesh.next_vertex(); |
41 | - if (p_pos >= 0) |
42 | - mesh.set_attrib(p_pos, b.v); |
43 | - if (n_pos >= 0) |
44 | - mesh.set_attrib(n_pos, b.n); |
45 | - if (t_pos >= 0) |
46 | - mesh.set_attrib(t_pos, b.t); |
47 | - if (nt_pos >= 0) |
48 | - mesh.set_attrib(nt_pos, b.nt); |
49 | - if (nb_pos >= 0) |
50 | - mesh.set_attrib(nb_pos, b.nb); |
51 | - |
52 | - mesh.next_vertex(); |
53 | - if (p_pos >= 0) |
54 | - mesh.set_attrib(p_pos, c.v); |
55 | - if (n_pos >= 0) |
56 | - mesh.set_attrib(n_pos, c.n); |
57 | - if (t_pos >= 0) |
58 | - mesh.set_attrib(t_pos, c.t); |
59 | - if (nt_pos >= 0) |
60 | - mesh.set_attrib(nt_pos, c.nt); |
61 | - if (nb_pos >= 0) |
62 | - mesh.set_attrib(nb_pos, c.nb); |
63 | + // In some model file formats (OBJ in particular), the face description |
64 | + // may contain separate indices per-attribute. So, we need to allow |
65 | + // for this when adding each vertex attribute to the mesh. |
66 | + const Face &face = *faceIt; |
67 | + const Vertex &v1 = object.vertices[face.v.x()]; |
68 | + const Vertex &v2 = object.vertices[face.v.y()]; |
69 | + const Vertex &v3 = object.vertices[face.v.z()]; |
70 | + |
71 | + bool separate_t(face.which & Face::OBJ_FACE_T); |
72 | + |
73 | + const Vertex &t1 = object.vertices[separate_t ? face.t.x() : face.v.x()]; |
74 | + const Vertex &t2 = object.vertices[separate_t ? face.t.y() : face.v.y()]; |
75 | + const Vertex &t3 = object.vertices[separate_t ? face.t.z() : face.v.z()]; |
76 | + |
77 | + bool separate_n(face.which & Face::OBJ_FACE_N); |
78 | + |
79 | + const Vertex &n1 = object.vertices[separate_n ? face.n.x() : face.v.x()]; |
80 | + const Vertex &n2 = object.vertices[separate_n ? face.n.y() : face.v.y()]; |
81 | + const Vertex &n3 = object.vertices[separate_n ? face.n.z() : face.v.z()]; |
82 | + |
83 | + mesh.next_vertex(); |
84 | + if (p_pos >= 0) |
85 | + mesh.set_attrib(p_pos, v1.v); |
86 | + if (n_pos >= 0) |
87 | + mesh.set_attrib(n_pos, n1.n); |
88 | + if (t_pos >= 0) |
89 | + mesh.set_attrib(t_pos, t1.t); |
90 | + if (nt_pos >= 0) |
91 | + mesh.set_attrib(nt_pos, v1.nt); |
92 | + if (nb_pos >= 0) |
93 | + mesh.set_attrib(nb_pos, v1.nb); |
94 | + |
95 | + mesh.next_vertex(); |
96 | + if (p_pos >= 0) |
97 | + mesh.set_attrib(p_pos, v2.v); |
98 | + if (n_pos >= 0) |
99 | + mesh.set_attrib(n_pos, n2.n); |
100 | + if (t_pos >= 0) |
101 | + mesh.set_attrib(t_pos, t2.t); |
102 | + if (nt_pos >= 0) |
103 | + mesh.set_attrib(nt_pos, v2.nt); |
104 | + if (nb_pos >= 0) |
105 | + mesh.set_attrib(nb_pos, v2.nb); |
106 | + |
107 | + mesh.next_vertex(); |
108 | + if (p_pos >= 0) |
109 | + mesh.set_attrib(p_pos, v3.v); |
110 | + if (n_pos >= 0) |
111 | + mesh.set_attrib(n_pos, n3.n); |
112 | + if (t_pos >= 0) |
113 | + mesh.set_attrib(t_pos, t3.t); |
114 | + if (nt_pos >= 0) |
115 | + mesh.set_attrib(nt_pos, v3.nt); |
116 | + if (nb_pos >= 0) |
117 | + mesh.set_attrib(nb_pos, v3.nb); |
118 | } |
119 | } |
120 | |
121 | @@ -225,6 +241,9 @@ |
122 | void |
123 | Model::calculate_texcoords() |
124 | { |
125 | + if (gotTexcoords_) |
126 | + return; |
127 | + |
128 | // Since the model didn't come with texcoords, and we don't actually know |
129 | // if it came with normals, either, we'll use positional spherical mapping |
130 | // to generate texcoords for the model. See: |
131 | @@ -248,6 +267,8 @@ |
132 | curVertex.t.y(asinf(vnorm.y()) / M_PI + 0.5); |
133 | } |
134 | } |
135 | + |
136 | + gotTexcoords_ = true; |
137 | } |
138 | |
139 | /** |
140 | @@ -256,6 +277,9 @@ |
141 | void |
142 | Model::calculate_normals() |
143 | { |
144 | + if (gotNormals_) |
145 | + return; |
146 | + |
147 | LibMatrix::vec3 n; |
148 | |
149 | for (std::vector<Object>::iterator iter = objects_.begin(); |
150 | @@ -269,9 +293,9 @@ |
151 | f_iter++) |
152 | { |
153 | const Face &face = *f_iter; |
154 | - Vertex &a = object.vertices[face.a]; |
155 | - Vertex &b = object.vertices[face.b]; |
156 | - Vertex &c = object.vertices[face.c]; |
157 | + Vertex &a = object.vertices[face.v.x()]; |
158 | + Vertex &b = object.vertices[face.v.y()]; |
159 | + Vertex &c = object.vertices[face.v.z()]; |
160 | |
161 | /* Calculate normal */ |
162 | n = LibMatrix::vec3::cross(b.v - a.v, c.v - a.v); |
163 | @@ -318,8 +342,9 @@ |
164 | v.nt.normalize(); |
165 | v.nb.normalize(); |
166 | } |
167 | - |
168 | } |
169 | + |
170 | + gotNormals_ = true; |
171 | } |
172 | |
173 | /** |
174 | @@ -425,9 +450,10 @@ |
175 | for (uint16_t i = 0; i < qty; i++) { |
176 | float f[3]; |
177 | read_or_fail(input_file, f, sizeof(float) * 3); |
178 | - object->vertices[i].v.x(f[0]); |
179 | - object->vertices[i].v.y(f[1]); |
180 | - object->vertices[i].v.z(f[2]); |
181 | + vec3& vertex = object->vertices[i].v; |
182 | + vertex.x(f[0]); |
183 | + vertex.y(f[1]); |
184 | + vertex.z(f[2]); |
185 | } |
186 | } |
187 | break; |
188 | @@ -445,10 +471,12 @@ |
189 | read_or_fail(input_file, &qty, sizeof(uint16_t)); |
190 | object->faces.resize(qty); |
191 | for (uint16_t i = 0; i < qty; i++) { |
192 | - read_or_fail(input_file, &object->faces[i].a, sizeof(uint16_t)); |
193 | - read_or_fail(input_file, &object->faces[i].b, sizeof(uint16_t)); |
194 | - read_or_fail(input_file, &object->faces[i].c, sizeof(uint16_t)); |
195 | - read_or_fail(input_file, &object->faces[i].face_flags, sizeof(uint16_t)); |
196 | + uint16_t f[4]; |
197 | + read_or_fail(input_file, f, sizeof(uint16_t) * 4); |
198 | + uvec3& face = object->faces[i].v; |
199 | + face.x(f[0]); |
200 | + face.y(f[1]); |
201 | + face.z(f[2]); |
202 | } |
203 | } |
204 | break; |
205 | @@ -467,8 +495,9 @@ |
206 | for (uint16_t i = 0; i < qty; i++) { |
207 | float f[2]; |
208 | read_or_fail(input_file, f, sizeof(float) * 2); |
209 | - object->vertices[i].t.x(f[0]); |
210 | - object->vertices[i].t.y(f[1]); |
211 | + vec2& texcoord = object->vertices[i].t; |
212 | + texcoord.x(f[0]); |
213 | + texcoord.y(f[1]); |
214 | } |
215 | } |
216 | gotTexcoords_ = true; |
217 | @@ -500,118 +529,144 @@ |
218 | return true; |
219 | } |
220 | |
221 | -/** |
222 | - * Parse vec3 values from an OBJ file. |
223 | + |
224 | +const unsigned int Model::Face::OBJ_FACE_V = 0x1; |
225 | +const unsigned int Model::Face::OBJ_FACE_T = 0x2; |
226 | +const unsigned int Model::Face::OBJ_FACE_N = 0x4; |
227 | + |
228 | +/** |
229 | + * Parse 2-element vertex attribute from an OBJ file. |
230 | + * |
231 | + * @param source the source line to parse |
232 | + * @param v the vec2 to populate |
233 | + */ |
234 | +void |
235 | +Model::obj_get_attrib(const string& source, vec2& v) |
236 | +{ |
237 | + // Our attribs are whitespace separated, so use a fuzzy split. |
238 | + vector<string> elements; |
239 | + Util::split(source, ' ', elements, Util::SplitModeFuzzy); |
240 | + |
241 | + // Find the first value... |
242 | + float x = Util::fromString<float>(elements[0]); |
243 | + // And the second value (there might be a third, but we don't care)... |
244 | + float y = Util::fromString<float>(elements[1]); |
245 | + v.x(x); |
246 | + v.y(y); |
247 | +} |
248 | + |
249 | +/** |
250 | + * Parse 3-element vertex attribute from an OBJ file. |
251 | * |
252 | * @param source the source line to parse |
253 | * @param v the vec3 to populate |
254 | */ |
255 | -static void |
256 | -obj_get_values(const string& source, vec3& v) |
257 | +void |
258 | +Model::obj_get_attrib(const string& source, vec3& v) |
259 | { |
260 | - // Skip the definition type... |
261 | - string::size_type endPos = source.find(" "); |
262 | - string::size_type startPos(0); |
263 | - if (endPos == string::npos) |
264 | - { |
265 | - Log::error("Bad element '%s'\n", source.c_str()); |
266 | - return; |
267 | - } |
268 | + // Our attribs are whitespace separated, so use a fuzzy split. |
269 | + vector<string> elements; |
270 | + Util::split(source, ' ', elements, Util::SplitModeFuzzy); |
271 | + |
272 | // Find the first value... |
273 | - startPos = endPos + 1; |
274 | - endPos = source.find(" ", startPos); |
275 | - if (endPos == string::npos) |
276 | - { |
277 | - Log::error("Bad element '%s'\n", source.c_str()); |
278 | - return; |
279 | - } |
280 | - string::size_type numChars(endPos - startPos); |
281 | - string xs(source, startPos, numChars); |
282 | - float x = Util::fromString<float>(xs); |
283 | + float x = Util::fromString<float>(elements[0]); |
284 | // Then the second value... |
285 | - startPos = endPos + 1; |
286 | - endPos = source.find(" ", startPos); |
287 | - if (endPos == string::npos) |
288 | - { |
289 | - Log::error("Bad element '%s'\n", source.c_str()); |
290 | - return; |
291 | - } |
292 | - numChars = endPos - startPos; |
293 | - string ys(source, startPos, numChars); |
294 | - float y = Util::fromString<float>(ys); |
295 | + float y = Util::fromString<float>(elements[1]); |
296 | // And the third value (there might be a fourth, but we don't care)... |
297 | - startPos = endPos + 1; |
298 | - endPos = source.find(" ", startPos); |
299 | - if (endPos == string::npos) |
300 | - { |
301 | - numChars = endPos; |
302 | - } |
303 | - else |
304 | - { |
305 | - numChars = endPos - startPos; |
306 | - } |
307 | - string zs(source, startPos, endPos - startPos); |
308 | - float z = Util::fromString<float>(zs); |
309 | + float z = Util::fromString<float>(elements[2]); |
310 | v.x(x); |
311 | v.y(y); |
312 | v.z(z); |
313 | } |
314 | |
315 | + |
316 | +void |
317 | +Model::obj_face_get_index(const string& tuple, unsigned int& which, |
318 | + unsigned int& v, unsigned int& t, unsigned int& n) |
319 | +{ |
320 | + // We can use a normal split here as syntax requires no spaces around |
321 | + // the '/' delimiter for a face description. |
322 | + vector<string> elements; |
323 | + Util::split(tuple, '/', elements, Util::SplitModeNormal); |
324 | + |
325 | + if (elements.empty()) |
326 | + { |
327 | + which = 0; |
328 | + return; |
329 | + } |
330 | + |
331 | + which = Face::OBJ_FACE_V; |
332 | + v = Util::fromString<unsigned int>(elements[0]); |
333 | + |
334 | + unsigned int num_elements = elements.size(); |
335 | + |
336 | + if (num_elements > 1 && !elements[1].empty()) |
337 | + { |
338 | + which |= Face::OBJ_FACE_T; |
339 | + t = Util::fromString<unsigned int>(elements[1]); |
340 | + } |
341 | + |
342 | + if (num_elements > 2 && !elements[2].empty()) |
343 | + { |
344 | + which |= Face::OBJ_FACE_N; |
345 | + n = Util::fromString<unsigned int>(elements[2]); |
346 | + } |
347 | + |
348 | + return; |
349 | +} |
350 | + |
351 | /** |
352 | - * Parse uvec3 values from an OBJ file. |
353 | + * Parse a face description from an OBJ file. |
354 | + * Faces always specify position, but optionally can also contain separate |
355 | + * indices for texcoords and normals. |
356 | * |
357 | * @param source the source line to parse |
358 | * @param v the uvec3 to populate |
359 | */ |
360 | -static void |
361 | -obj_get_values(const string& source, uvec3& v) |
362 | +void |
363 | +Model::obj_get_face(const string& source, Face& f) |
364 | { |
365 | - // Skip the definition type... |
366 | - string::size_type endPos = source.find(" "); |
367 | - string::size_type startPos(0); |
368 | - if (endPos == string::npos) |
369 | - { |
370 | - Log::error("Bad element '%s'\n", source.c_str()); |
371 | - return; |
372 | - } |
373 | + // Our indices are whitespace separated, so use a fuzzy split. |
374 | + vector<string> elements; |
375 | + Util::split(source, ' ', elements, Util::SplitModeFuzzy); |
376 | + |
377 | // Find the first value... |
378 | - startPos = endPos + 1; |
379 | - endPos = source.find(" ", startPos); |
380 | - if (endPos == string::npos) |
381 | - { |
382 | - Log::error("Bad element '%s'\n", source.c_str()); |
383 | - return; |
384 | - } |
385 | - string::size_type numChars(endPos - startPos); |
386 | - string xs(source, startPos, numChars); |
387 | - unsigned int x = Util::fromString<unsigned int>(xs); |
388 | + unsigned int which(0); |
389 | + unsigned int vx(0); |
390 | + unsigned int tx(0); |
391 | + unsigned int nx(0); |
392 | + obj_face_get_index(elements[0], which, vx, tx, nx); |
393 | + |
394 | // Then the second value... |
395 | - startPos = endPos+1; |
396 | - endPos = source.find(" ", startPos); |
397 | - if (endPos == string::npos) |
398 | - { |
399 | - Log::error("Bad element '%s'\n", source.c_str()); |
400 | - return; |
401 | - } |
402 | - numChars = endPos - startPos; |
403 | - string ys(source, startPos, numChars); |
404 | - unsigned int y = Util::fromString<unsigned int>(ys); |
405 | + unsigned int vy(0); |
406 | + unsigned int ty(0); |
407 | + unsigned int ny(0); |
408 | + obj_face_get_index(elements[1], which, vy, ty, ny); |
409 | + |
410 | // And the third value (there might be a fourth, but we don't care)... |
411 | - startPos = endPos + 1; |
412 | - endPos = source.find(" ", startPos); |
413 | - if (endPos == string::npos) |
414 | - { |
415 | - numChars = endPos; |
416 | - } |
417 | - else |
418 | - { |
419 | - numChars = endPos - startPos; |
420 | - } |
421 | - string zs(source, startPos, numChars); |
422 | - unsigned int z = Util::fromString<unsigned int>(zs); |
423 | - v.x(x); |
424 | - v.y(y); |
425 | - v.z(z); |
426 | + unsigned int vz(0); |
427 | + unsigned int tz(0); |
428 | + unsigned int nz(0); |
429 | + obj_face_get_index(elements[2], which, vz, tz, nz); |
430 | + |
431 | + // OBJ models start absoluted indices at '1', so subtract to re-base to |
432 | + // '0'. We do not handle relative indexing (negative indices). |
433 | + f.which = which; |
434 | + f.v.x(vx - 1); |
435 | + f.v.y(vy - 1); |
436 | + f.v.z(vz - 1); |
437 | + if (which & Face::OBJ_FACE_T) |
438 | + { |
439 | + f.t.x(tx - 1); |
440 | + f.t.y(ty - 1); |
441 | + f.t.z(tz - 1); |
442 | + } |
443 | + if (which & Face::OBJ_FACE_N) |
444 | + { |
445 | + f.n.x(nx - 1); |
446 | + f.n.y(ny - 1); |
447 | + f.n.z(nz - 1); |
448 | + } |
449 | } |
450 | |
451 | /** |
452 | @@ -642,59 +697,98 @@ |
453 | } |
454 | |
455 | // Give ourselves an object to populate. |
456 | - objects_.push_back(Object(filename)); |
457 | + objects_.push_back(Object(string())); |
458 | Object& object(objects_.back()); |
459 | |
460 | + static const string object_definition("o"); |
461 | static const string vertex_definition("v"); |
462 | static const string normal_definition("vn"); |
463 | static const string texcoord_definition("vt"); |
464 | static const string face_definition("f"); |
465 | + vector<vec3> positions; |
466 | + vector<vec3> normals; |
467 | + vector<vec2> texcoords; |
468 | for (vector<string>::const_iterator lineIt = sourceVec.begin(); |
469 | lineIt != sourceVec.end(); |
470 | lineIt++) |
471 | { |
472 | const string& curSrc = *lineIt; |
473 | // Is it a vertex attribute, a face description, comment or other? |
474 | - // We only care about the first two, we ignore comments, object names, |
475 | - // group names, smoothing groups, etc. |
476 | + // We only care about the first two, we ignore comments, group names, |
477 | + // smoothing groups, etc. |
478 | string::size_type startPos(0); |
479 | string::size_type spacePos = curSrc.find(" ", startPos); |
480 | - string definitionType(curSrc, startPos, spacePos - startPos); |
481 | + string::size_type num_chars(string::npos); |
482 | + string definition; |
483 | + if (spacePos != string::npos) |
484 | + { |
485 | + // Could be arbitrary whitespace between description type and |
486 | + // the data |
487 | + string::size_type defPos = curSrc.find_first_not_of(' ', spacePos); |
488 | + definition = string(curSrc, defPos); |
489 | + num_chars = spacePos - startPos; |
490 | + } |
491 | + string definitionType(curSrc, startPos, num_chars); |
492 | + |
493 | if (definitionType == vertex_definition) |
494 | { |
495 | - Vertex v; |
496 | - obj_get_values(curSrc, v.v); |
497 | - object.vertices.push_back(v); |
498 | + vec3 p; |
499 | + obj_get_attrib(definition, p); |
500 | + positions.push_back(p); |
501 | } |
502 | else if (definitionType == normal_definition) |
503 | { |
504 | - // If we encounter an OBJ model with normals, we can update this |
505 | - // to update object.vertices.n directly |
506 | - Log::debug("We got a normal...\n"); |
507 | + vec3 n; |
508 | + obj_get_attrib(definition, n); |
509 | + normals.push_back(n); |
510 | } |
511 | else if (definitionType == texcoord_definition) |
512 | { |
513 | - // If we encounter an OBJ model with normals, we can update this |
514 | - // to update object.vertices.t directly |
515 | - Log::debug("We got a texcoord...\n"); |
516 | + vec2 t; |
517 | + obj_get_attrib(definition, t); |
518 | + texcoords.push_back(t); |
519 | } |
520 | else if (definitionType == face_definition) |
521 | { |
522 | - uvec3 v; |
523 | - obj_get_values(curSrc, v); |
524 | Face f; |
525 | - // OBJ models index from '1'. |
526 | - f.a = v.x() - 1; |
527 | - f.b = v.y() - 1; |
528 | - f.c = v.z() - 1; |
529 | + obj_get_face(definition, f); |
530 | object.faces.push_back(f); |
531 | } |
532 | - } |
533 | + else if (definitionType == object_definition) |
534 | + { |
535 | + object.name = definition; |
536 | + } |
537 | + } |
538 | + |
539 | + if (!texcoords.empty()) |
540 | + { |
541 | + gotTexcoords_ = true; |
542 | + } |
543 | + if (!normals.empty()) |
544 | + { |
545 | + gotNormals_ = true; |
546 | + } |
547 | + unsigned int numVertices = positions.size(); |
548 | + object.vertices.resize(numVertices); |
549 | + for (unsigned int i = 0; i < numVertices; i++) |
550 | + { |
551 | + Vertex& curVertex = object.vertices[i]; |
552 | + curVertex.v = positions[i]; |
553 | + if (gotTexcoords_) |
554 | + { |
555 | + curVertex.t = texcoords[i]; |
556 | + } |
557 | + if (gotNormals_) |
558 | + { |
559 | + curVertex.n = normals[i]; |
560 | + } |
561 | + } |
562 | + |
563 | // Compute bounding box for perspective projection |
564 | compute_bounding_box(object); |
565 | |
566 | - Log::debug("Object populated with %u vertices and %u faces.\n", |
567 | - object.vertices.size(), object.faces.size()); |
568 | + Log::debug("Object name: %s Vertex count: %u Face count: %u\n", |
569 | + object.name.empty() ? "(none)" : object.name.c_str(), object.vertices.size(), object.faces.size()); |
570 | return true; |
571 | } |
572 | |
573 | |
574 | === modified file 'src/model.h' |
575 | --- src/model.h 2012-07-27 09:11:50 +0000 |
576 | +++ src/model.h 2013-01-30 23:17:24 +0000 |
577 | @@ -78,12 +78,13 @@ |
578 | AttribTypeCustom |
579 | } AttribType; |
580 | |
581 | - Model() : gotTexcoords_(false) {} |
582 | + Model() : gotTexcoords_(false), gotNormals_(false) {} |
583 | ~Model() {} |
584 | |
585 | bool load(const std::string& name); |
586 | |
587 | bool needTexcoords() const { return !gotTexcoords_; } |
588 | + bool needNormals() const { return !gotNormals_; } |
589 | void calculate_texcoords(); |
590 | void calculate_normals(); |
591 | void convert_to_mesh(Mesh &mesh); |
592 | @@ -93,11 +94,18 @@ |
593 | const LibMatrix::vec3& maxVec() const { return maxVec_; } |
594 | static const ModelMap& find_models(); |
595 | private: |
596 | - // If the model we loaded contained texcoord data... |
597 | + // If the model we loaded contained texcoord or normal data... |
598 | bool gotTexcoords_; |
599 | + bool gotNormals_; |
600 | + |
601 | struct Face { |
602 | - uint32_t a, b, c; |
603 | - uint16_t face_flags; |
604 | + LibMatrix::uvec3 v; |
605 | + LibMatrix::uvec3 n; |
606 | + LibMatrix::uvec3 t; |
607 | + unsigned int which; // mask of which values are relevant for this face. |
608 | + static const unsigned int OBJ_FACE_V; |
609 | + static const unsigned int OBJ_FACE_T; |
610 | + static const unsigned int OBJ_FACE_N; |
611 | }; |
612 | |
613 | struct Vertex { |
614 | @@ -120,6 +128,11 @@ |
615 | int nt_pos, int nb_pos); |
616 | bool load_3ds(const std::string &filename); |
617 | bool load_obj(const std::string &filename); |
618 | + void obj_get_attrib(const std::string& description, LibMatrix::vec2& v); |
619 | + void obj_get_attrib(const std::string& description, LibMatrix::vec3& v); |
620 | + void obj_face_get_index(const std::string& tuple, unsigned int& which, |
621 | + unsigned int& v, unsigned int& t, unsigned int& n); |
622 | + void obj_get_face(const std::string& description, Face& face); |
623 | |
624 | // For vertices of the bounding box for this model. |
625 | void compute_bounding_box(const Object& object); |
626 | |
627 | === modified file 'src/scene-build.cpp' |
628 | --- src/scene-build.cpp 2012-08-15 09:45:06 +0000 |
629 | +++ src/scene-build.cpp 2013-01-30 23:17:24 +0000 |
630 | @@ -141,7 +141,8 @@ |
631 | orientationVec_ = vec3(0.0, 1.0, 0.0); |
632 | } |
633 | |
634 | - model.calculate_normals(); |
635 | + if (model.needNormals()) |
636 | + model.calculate_normals(); |
637 | |
638 | /* Tell the converter that we only care about position and normal attributes */ |
639 | std::vector<std::pair<Model::AttribType, int> > attribs; |
640 | |
641 | === modified file 'src/scene-bump.cpp' |
642 | --- src/scene-bump.cpp 2012-08-15 09:45:06 +0000 |
643 | +++ src/scene-bump.cpp 2013-01-30 23:17:24 +0000 |
644 | @@ -80,7 +80,8 @@ |
645 | if(!model.load(poly_filename)) |
646 | return false; |
647 | |
648 | - model.calculate_normals(); |
649 | + if (model.needNormals()) |
650 | + model.calculate_normals(); |
651 | |
652 | /* Tell the converter that we only care about position and normal attributes */ |
653 | std::vector<std::pair<Model::AttribType, int> > attribs; |
654 | @@ -177,7 +178,8 @@ |
655 | if(!model.load("asteroid-low")) |
656 | return false; |
657 | |
658 | - model.calculate_normals(); |
659 | + if (model.needNormals()) |
660 | + model.calculate_normals(); |
661 | |
662 | /* Calculate the half vector */ |
663 | LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z()); |
664 | @@ -234,7 +236,8 @@ |
665 | if(!model.load("asteroid-low")) |
666 | return false; |
667 | |
668 | - model.calculate_normals(); |
669 | + if (model.needNormals()) |
670 | + model.calculate_normals(); |
671 | |
672 | /* Calculate the half vector */ |
673 | LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z()); |
674 | |
675 | === modified file 'src/scene-refract.cpp' |
676 | --- src/scene-refract.cpp 2012-12-15 09:46:12 +0000 |
677 | +++ src/scene-refract.cpp 2013-01-30 23:17:24 +0000 |
678 | @@ -322,7 +322,8 @@ |
679 | orientationVec_ = vec3(0.0, 1.0, 0.0); |
680 | } |
681 | |
682 | - model.calculate_normals(); |
683 | + if (model.needNormals()) |
684 | + model.calculate_normals(); |
685 | |
686 | // Mesh setup |
687 | vector<std::pair<Model::AttribType, int> > attribs; |
688 | |
689 | === modified file 'src/scene-shading.cpp' |
690 | --- src/scene-shading.cpp 2012-08-15 09:45:06 +0000 |
691 | +++ src/scene-shading.cpp 2013-01-30 23:17:24 +0000 |
692 | @@ -220,7 +220,8 @@ |
693 | orientationVec_ = vec3(0.0, 1.0, 0.0); |
694 | } |
695 | |
696 | - model.calculate_normals(); |
697 | + if (model.needNormals()) |
698 | + model.calculate_normals(); |
699 | |
700 | /* Tell the converter that we only care about position and normal attributes */ |
701 | std::vector<std::pair<Model::AttribType, int> > attribs; |
702 | |
703 | === modified file 'src/scene-shadow.cpp' |
704 | --- src/scene-shadow.cpp 2012-12-17 16:08:05 +0000 |
705 | +++ src/scene-shadow.cpp 2013-01-30 23:17:24 +0000 |
706 | @@ -324,7 +324,8 @@ |
707 | return false; |
708 | } |
709 | |
710 | - model.calculate_normals(); |
711 | + if (model.needNormals()) |
712 | + model.calculate_normals(); |
713 | |
714 | // Mesh setup |
715 | vector<std::pair<Model::AttribType, int> > attribs; |
716 | |
717 | === modified file 'src/scene-texture.cpp' |
718 | --- src/scene-texture.cpp 2012-08-15 09:45:06 +0000 |
719 | +++ src/scene-texture.cpp 2013-01-30 23:17:24 +0000 |
720 | @@ -208,7 +208,9 @@ |
721 | |
722 | if (model.needTexcoords()) |
723 | model.calculate_texcoords(); |
724 | - model.calculate_normals(); |
725 | + if (model.needNormals()) |
726 | + model.calculate_normals(); |
727 | + |
728 | // Tell the converter which attributes we care about |
729 | std::vector<std::pair<Model::AttribType, int> > attribs; |
730 | attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypePosition, 3)); |
Looks good overall.
A nitpick (not worth blocking on, but nice to have fixed):
I am not sure we need Model:: needNormals( ), since calculate_normals() checks for that already. If we need to make it more explicit that calculate_normals() may or may not actually perform the calculation, it think it would be cleaner to rename it to calculate_ normals_ if_needed( ).