From patchwork Fri Feb 1 17:27:11 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jesse Barker X-Patchwork-Id: 14404 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 8D16323E57 for ; Fri, 1 Feb 2013 17:27:15 +0000 (UTC) Received: from mail-ve0-f182.google.com (mail-ve0-f182.google.com [209.85.128.182]) by fiordland.canonical.com (Postfix) with ESMTP id 27440A19A2E for ; Fri, 1 Feb 2013 17:27:15 +0000 (UTC) Received: by mail-ve0-f182.google.com with SMTP id ox1so2294086veb.27 for ; Fri, 01 Feb 2013 09:27:14 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:x-forwarded-to:x-forwarded-for:delivered-to:x-received :received-spf:content-type:mime-version:x-launchpad-project :x-launchpad-branch:x-launchpad-message-rationale :x-launchpad-branch-revision-number:x-launchpad-notification-type:to :from:subject:message-id:date:reply-to:sender:errors-to:precedence :x-generated-by:x-launchpad-hash:x-gm-message-state; bh=kZELcHlhYg0WXVtRLUBVrnx6YZgku3BhfEfumcsXjUY=; b=GZgq7Vb46KCqZhGB1/2ZGzgZIWUAw7gBX0dhS7MUYb3KcxArjRWK/1NsCErOr9+IUe 1IZ10qlxyofS7aiFQkYhVxNUEvnAE2KAcP47nBqjRkYH/IXgGc3QUg5w11XTWwcHPY7g /Lm50FpLALykmmRHmQt3Si108rGa5koGlDKda+YqFxsRW+urzhc1AZbHHEv4xMZGBW57 CWKF/vAbItyqefRoRgytRSPNb+tKrHawM54hudgupnmySqFCXH3/xP9cCxfK6eUlukNN wMTqBuaDAnfqNTHaSJ5GzxJySpFa47K2efmZ8z8G8PJZMcnWWJGrc+iecwDCyI+HEpvN wg4Q== X-Received: by 10.220.151.141 with SMTP id c13mr12007438vcw.64.1359739634559; Fri, 01 Feb 2013 09:27:14 -0800 (PST) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.58.252.8 with SMTP id zo8csp127812vec; Fri, 1 Feb 2013 09:27:13 -0800 (PST) X-Received: by 10.180.85.8 with SMTP id d8mr4241649wiz.4.1359739632082; Fri, 01 Feb 2013 09:27:12 -0800 (PST) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id bj4si897363wib.33.2013.02.01.09.27.11 (version=TLSv1 cipher=RC4-SHA bits=128/128); Fri, 01 Feb 2013 09:27:12 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1U1KON-0007KE-6C for ; Fri, 01 Feb 2013 17:27:11 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 16130E00F4 for ; Fri, 1 Feb 2013 17:27:11 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: glmark2 X-Launchpad-Branch: ~glmark2-dev/glmark2/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 260 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~glmark2-dev/glmark2/trunk] Rev 260: Merge of lp:~glmark2-dev/glmark2/obj-update Message-Id: <20130201172711.3242.68696.launchpad@ackee.canonical.com> Date: Fri, 01 Feb 2013 17:27:11 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="16462"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: bfbd6d260c1401eeaeb60a2ac7ca493541c98541 X-Gm-Message-State: ALoCoQla9SFOyYLgCqPwazL1hmgI7IgBwudkYqlTbYddSKLNycAUv1lQf05BEFOpbBcMzQ4Ba9Im Merge authors: Jesse Barker (jesse-barker) Related merge proposals: https://code.launchpad.net/~glmark2-dev/glmark2/obj-update/+merge/145746 proposed by: Jesse Barker (jesse-barker) review: Approve - Alexandros Frantzis (afrantzis) ------------------------------------------------------------ revno: 260 [merge] committer: Jesse Barker branch nick: trunk timestamp: Fri 2013-02-01 09:21:16 -0800 message: Merge of lp:~glmark2-dev/glmark2/obj-update Model: Adds more general support of the Wavefront .obj file format. We still only support triangular faces (the format itself allows for arbitrary polygonal faces), however, we now support vertex normals and texcoords supplied with the object, and the format actually allows for separate indexing into each of those attribute lists, so those must be tracked and represented separately by Model (both loading and when converting to Mesh). We do not support any of the more complex features of the file format like curves, free-form surfaces, and the various material description options (separate file load and file format). modified: src/model.cpp src/model.h src/scene-build.cpp src/scene-bump.cpp src/scene-refract.cpp src/scene-shading.cpp src/scene-shadow.cpp src/scene-texture.cpp --- lp:glmark2 https://code.launchpad.net/~glmark2-dev/glmark2/trunk You are subscribed to branch lp:glmark2. To unsubscribe from this branch go to https://code.launchpad.net/~glmark2-dev/glmark2/trunk/+edit-subscription === modified file 'src/model.cpp' --- src/model.cpp 2012-07-05 17:22:16 +0000 +++ src/model.cpp 2013-01-30 23:07:13 +0000 @@ -35,6 +35,7 @@ using std::string; using std::vector; +using LibMatrix::vec2; using LibMatrix::vec3; using LibMatrix::uvec3; @@ -108,50 +109,65 @@ int p_pos, int n_pos, int t_pos, int nt_pos, int nb_pos) { - size_t face_count = object.faces.size(); - - for(size_t i = 0; i < 3 * face_count; i += 3) + for (vector::const_iterator faceIt = object.faces.begin(); + faceIt != object.faces.end(); + faceIt++) { - const Face &face = object.faces[i / 3]; - const Vertex &a = object.vertices[face.a]; - const Vertex &b = object.vertices[face.b]; - const Vertex &c = object.vertices[face.c]; - - mesh.next_vertex(); - if (p_pos >= 0) - mesh.set_attrib(p_pos, a.v); - if (n_pos >= 0) - mesh.set_attrib(n_pos, a.n); - if (t_pos >= 0) - mesh.set_attrib(t_pos, a.t); - if (nt_pos >= 0) - mesh.set_attrib(nt_pos, a.nt); - if (nb_pos >= 0) - mesh.set_attrib(nb_pos, a.nb); - - mesh.next_vertex(); - if (p_pos >= 0) - mesh.set_attrib(p_pos, b.v); - if (n_pos >= 0) - mesh.set_attrib(n_pos, b.n); - if (t_pos >= 0) - mesh.set_attrib(t_pos, b.t); - if (nt_pos >= 0) - mesh.set_attrib(nt_pos, b.nt); - if (nb_pos >= 0) - mesh.set_attrib(nb_pos, b.nb); - - mesh.next_vertex(); - if (p_pos >= 0) - mesh.set_attrib(p_pos, c.v); - if (n_pos >= 0) - mesh.set_attrib(n_pos, c.n); - if (t_pos >= 0) - mesh.set_attrib(t_pos, c.t); - if (nt_pos >= 0) - mesh.set_attrib(nt_pos, c.nt); - if (nb_pos >= 0) - mesh.set_attrib(nb_pos, c.nb); + // In some model file formats (OBJ in particular), the face description + // may contain separate indices per-attribute. So, we need to allow + // for this when adding each vertex attribute to the mesh. + const Face &face = *faceIt; + const Vertex &v1 = object.vertices[face.v.x()]; + const Vertex &v2 = object.vertices[face.v.y()]; + const Vertex &v3 = object.vertices[face.v.z()]; + + bool separate_t(face.which & Face::OBJ_FACE_T); + + const Vertex &t1 = object.vertices[separate_t ? face.t.x() : face.v.x()]; + const Vertex &t2 = object.vertices[separate_t ? face.t.y() : face.v.y()]; + const Vertex &t3 = object.vertices[separate_t ? face.t.z() : face.v.z()]; + + bool separate_n(face.which & Face::OBJ_FACE_N); + + const Vertex &n1 = object.vertices[separate_n ? face.n.x() : face.v.x()]; + const Vertex &n2 = object.vertices[separate_n ? face.n.y() : face.v.y()]; + const Vertex &n3 = object.vertices[separate_n ? face.n.z() : face.v.z()]; + + mesh.next_vertex(); + if (p_pos >= 0) + mesh.set_attrib(p_pos, v1.v); + if (n_pos >= 0) + mesh.set_attrib(n_pos, n1.n); + if (t_pos >= 0) + mesh.set_attrib(t_pos, t1.t); + if (nt_pos >= 0) + mesh.set_attrib(nt_pos, v1.nt); + if (nb_pos >= 0) + mesh.set_attrib(nb_pos, v1.nb); + + mesh.next_vertex(); + if (p_pos >= 0) + mesh.set_attrib(p_pos, v2.v); + if (n_pos >= 0) + mesh.set_attrib(n_pos, n2.n); + if (t_pos >= 0) + mesh.set_attrib(t_pos, t2.t); + if (nt_pos >= 0) + mesh.set_attrib(nt_pos, v2.nt); + if (nb_pos >= 0) + mesh.set_attrib(nb_pos, v2.nb); + + mesh.next_vertex(); + if (p_pos >= 0) + mesh.set_attrib(p_pos, v3.v); + if (n_pos >= 0) + mesh.set_attrib(n_pos, n3.n); + if (t_pos >= 0) + mesh.set_attrib(t_pos, t3.t); + if (nt_pos >= 0) + mesh.set_attrib(nt_pos, v3.nt); + if (nb_pos >= 0) + mesh.set_attrib(nb_pos, v3.nb); } } @@ -225,6 +241,9 @@ void Model::calculate_texcoords() { + if (gotTexcoords_) + return; + // Since the model didn't come with texcoords, and we don't actually know // if it came with normals, either, we'll use positional spherical mapping // to generate texcoords for the model. See: @@ -248,6 +267,8 @@ curVertex.t.y(asinf(vnorm.y()) / M_PI + 0.5); } } + + gotTexcoords_ = true; } /** @@ -256,6 +277,9 @@ void Model::calculate_normals() { + if (gotNormals_) + return; + LibMatrix::vec3 n; for (std::vector::iterator iter = objects_.begin(); @@ -269,9 +293,9 @@ f_iter++) { const Face &face = *f_iter; - Vertex &a = object.vertices[face.a]; - Vertex &b = object.vertices[face.b]; - Vertex &c = object.vertices[face.c]; + Vertex &a = object.vertices[face.v.x()]; + Vertex &b = object.vertices[face.v.y()]; + Vertex &c = object.vertices[face.v.z()]; /* Calculate normal */ n = LibMatrix::vec3::cross(b.v - a.v, c.v - a.v); @@ -318,8 +342,9 @@ v.nt.normalize(); v.nb.normalize(); } - } + + gotNormals_ = true; } /** @@ -425,9 +450,10 @@ for (uint16_t i = 0; i < qty; i++) { float f[3]; read_or_fail(input_file, f, sizeof(float) * 3); - object->vertices[i].v.x(f[0]); - object->vertices[i].v.y(f[1]); - object->vertices[i].v.z(f[2]); + vec3& vertex = object->vertices[i].v; + vertex.x(f[0]); + vertex.y(f[1]); + vertex.z(f[2]); } } break; @@ -445,10 +471,12 @@ read_or_fail(input_file, &qty, sizeof(uint16_t)); object->faces.resize(qty); for (uint16_t i = 0; i < qty; i++) { - read_or_fail(input_file, &object->faces[i].a, sizeof(uint16_t)); - read_or_fail(input_file, &object->faces[i].b, sizeof(uint16_t)); - read_or_fail(input_file, &object->faces[i].c, sizeof(uint16_t)); - read_or_fail(input_file, &object->faces[i].face_flags, sizeof(uint16_t)); + uint16_t f[4]; + read_or_fail(input_file, f, sizeof(uint16_t) * 4); + uvec3& face = object->faces[i].v; + face.x(f[0]); + face.y(f[1]); + face.z(f[2]); } } break; @@ -467,8 +495,9 @@ for (uint16_t i = 0; i < qty; i++) { float f[2]; read_or_fail(input_file, f, sizeof(float) * 2); - object->vertices[i].t.x(f[0]); - object->vertices[i].t.y(f[1]); + vec2& texcoord = object->vertices[i].t; + texcoord.x(f[0]); + texcoord.y(f[1]); } } gotTexcoords_ = true; @@ -500,118 +529,144 @@ return true; } -/** - * Parse vec3 values from an OBJ file. + +const unsigned int Model::Face::OBJ_FACE_V = 0x1; +const unsigned int Model::Face::OBJ_FACE_T = 0x2; +const unsigned int Model::Face::OBJ_FACE_N = 0x4; + +/** + * Parse 2-element vertex attribute from an OBJ file. + * + * @param source the source line to parse + * @param v the vec2 to populate + */ +void +Model::obj_get_attrib(const string& source, vec2& v) +{ + // Our attribs are whitespace separated, so use a fuzzy split. + vector elements; + Util::split(source, ' ', elements, Util::SplitModeFuzzy); + + // Find the first value... + float x = Util::fromString(elements[0]); + // And the second value (there might be a third, but we don't care)... + float y = Util::fromString(elements[1]); + v.x(x); + v.y(y); +} + +/** + * Parse 3-element vertex attribute from an OBJ file. * * @param source the source line to parse * @param v the vec3 to populate */ -static void -obj_get_values(const string& source, vec3& v) +void +Model::obj_get_attrib(const string& source, vec3& v) { - // Skip the definition type... - string::size_type endPos = source.find(" "); - string::size_type startPos(0); - if (endPos == string::npos) - { - Log::error("Bad element '%s'\n", source.c_str()); - return; - } + // Our attribs are whitespace separated, so use a fuzzy split. + vector elements; + Util::split(source, ' ', elements, Util::SplitModeFuzzy); + // Find the first value... - startPos = endPos + 1; - endPos = source.find(" ", startPos); - if (endPos == string::npos) - { - Log::error("Bad element '%s'\n", source.c_str()); - return; - } - string::size_type numChars(endPos - startPos); - string xs(source, startPos, numChars); - float x = Util::fromString(xs); + float x = Util::fromString(elements[0]); // Then the second value... - startPos = endPos + 1; - endPos = source.find(" ", startPos); - if (endPos == string::npos) - { - Log::error("Bad element '%s'\n", source.c_str()); - return; - } - numChars = endPos - startPos; - string ys(source, startPos, numChars); - float y = Util::fromString(ys); + float y = Util::fromString(elements[1]); // And the third value (there might be a fourth, but we don't care)... - startPos = endPos + 1; - endPos = source.find(" ", startPos); - if (endPos == string::npos) - { - numChars = endPos; - } - else - { - numChars = endPos - startPos; - } - string zs(source, startPos, endPos - startPos); - float z = Util::fromString(zs); + float z = Util::fromString(elements[2]); v.x(x); v.y(y); v.z(z); } + +void +Model::obj_face_get_index(const string& tuple, unsigned int& which, + unsigned int& v, unsigned int& t, unsigned int& n) +{ + // We can use a normal split here as syntax requires no spaces around + // the '/' delimiter for a face description. + vector elements; + Util::split(tuple, '/', elements, Util::SplitModeNormal); + + if (elements.empty()) + { + which = 0; + return; + } + + which = Face::OBJ_FACE_V; + v = Util::fromString(elements[0]); + + unsigned int num_elements = elements.size(); + + if (num_elements > 1 && !elements[1].empty()) + { + which |= Face::OBJ_FACE_T; + t = Util::fromString(elements[1]); + } + + if (num_elements > 2 && !elements[2].empty()) + { + which |= Face::OBJ_FACE_N; + n = Util::fromString(elements[2]); + } + + return; +} + /** - * Parse uvec3 values from an OBJ file. + * Parse a face description from an OBJ file. + * Faces always specify position, but optionally can also contain separate + * indices for texcoords and normals. * * @param source the source line to parse * @param v the uvec3 to populate */ -static void -obj_get_values(const string& source, uvec3& v) +void +Model::obj_get_face(const string& source, Face& f) { - // Skip the definition type... - string::size_type endPos = source.find(" "); - string::size_type startPos(0); - if (endPos == string::npos) - { - Log::error("Bad element '%s'\n", source.c_str()); - return; - } + // Our indices are whitespace separated, so use a fuzzy split. + vector elements; + Util::split(source, ' ', elements, Util::SplitModeFuzzy); + // Find the first value... - startPos = endPos + 1; - endPos = source.find(" ", startPos); - if (endPos == string::npos) - { - Log::error("Bad element '%s'\n", source.c_str()); - return; - } - string::size_type numChars(endPos - startPos); - string xs(source, startPos, numChars); - unsigned int x = Util::fromString(xs); + unsigned int which(0); + unsigned int vx(0); + unsigned int tx(0); + unsigned int nx(0); + obj_face_get_index(elements[0], which, vx, tx, nx); + // Then the second value... - startPos = endPos+1; - endPos = source.find(" ", startPos); - if (endPos == string::npos) - { - Log::error("Bad element '%s'\n", source.c_str()); - return; - } - numChars = endPos - startPos; - string ys(source, startPos, numChars); - unsigned int y = Util::fromString(ys); + unsigned int vy(0); + unsigned int ty(0); + unsigned int ny(0); + obj_face_get_index(elements[1], which, vy, ty, ny); + // And the third value (there might be a fourth, but we don't care)... - startPos = endPos + 1; - endPos = source.find(" ", startPos); - if (endPos == string::npos) - { - numChars = endPos; - } - else - { - numChars = endPos - startPos; - } - string zs(source, startPos, numChars); - unsigned int z = Util::fromString(zs); - v.x(x); - v.y(y); - v.z(z); + unsigned int vz(0); + unsigned int tz(0); + unsigned int nz(0); + obj_face_get_index(elements[2], which, vz, tz, nz); + + // OBJ models start absoluted indices at '1', so subtract to re-base to + // '0'. We do not handle relative indexing (negative indices). + f.which = which; + f.v.x(vx - 1); + f.v.y(vy - 1); + f.v.z(vz - 1); + if (which & Face::OBJ_FACE_T) + { + f.t.x(tx - 1); + f.t.y(ty - 1); + f.t.z(tz - 1); + } + if (which & Face::OBJ_FACE_N) + { + f.n.x(nx - 1); + f.n.y(ny - 1); + f.n.z(nz - 1); + } } /** @@ -642,59 +697,98 @@ } // Give ourselves an object to populate. - objects_.push_back(Object(filename)); + objects_.push_back(Object(string())); Object& object(objects_.back()); + static const string object_definition("o"); static const string vertex_definition("v"); static const string normal_definition("vn"); static const string texcoord_definition("vt"); static const string face_definition("f"); + vector positions; + vector normals; + vector texcoords; for (vector::const_iterator lineIt = sourceVec.begin(); lineIt != sourceVec.end(); lineIt++) { const string& curSrc = *lineIt; // Is it a vertex attribute, a face description, comment or other? - // We only care about the first two, we ignore comments, object names, - // group names, smoothing groups, etc. + // We only care about the first two, we ignore comments, group names, + // smoothing groups, etc. string::size_type startPos(0); string::size_type spacePos = curSrc.find(" ", startPos); - string definitionType(curSrc, startPos, spacePos - startPos); + string::size_type num_chars(string::npos); + string definition; + if (spacePos != string::npos) + { + // Could be arbitrary whitespace between description type and + // the data + string::size_type defPos = curSrc.find_first_not_of(' ', spacePos); + definition = string(curSrc, defPos); + num_chars = spacePos - startPos; + } + string definitionType(curSrc, startPos, num_chars); + if (definitionType == vertex_definition) { - Vertex v; - obj_get_values(curSrc, v.v); - object.vertices.push_back(v); + vec3 p; + obj_get_attrib(definition, p); + positions.push_back(p); } else if (definitionType == normal_definition) { - // If we encounter an OBJ model with normals, we can update this - // to update object.vertices.n directly - Log::debug("We got a normal...\n"); + vec3 n; + obj_get_attrib(definition, n); + normals.push_back(n); } else if (definitionType == texcoord_definition) { - // If we encounter an OBJ model with normals, we can update this - // to update object.vertices.t directly - Log::debug("We got a texcoord...\n"); + vec2 t; + obj_get_attrib(definition, t); + texcoords.push_back(t); } else if (definitionType == face_definition) { - uvec3 v; - obj_get_values(curSrc, v); Face f; - // OBJ models index from '1'. - f.a = v.x() - 1; - f.b = v.y() - 1; - f.c = v.z() - 1; + obj_get_face(definition, f); object.faces.push_back(f); } - } + else if (definitionType == object_definition) + { + object.name = definition; + } + } + + if (!texcoords.empty()) + { + gotTexcoords_ = true; + } + if (!normals.empty()) + { + gotNormals_ = true; + } + unsigned int numVertices = positions.size(); + object.vertices.resize(numVertices); + for (unsigned int i = 0; i < numVertices; i++) + { + Vertex& curVertex = object.vertices[i]; + curVertex.v = positions[i]; + if (gotTexcoords_) + { + curVertex.t = texcoords[i]; + } + if (gotNormals_) + { + curVertex.n = normals[i]; + } + } + // Compute bounding box for perspective projection compute_bounding_box(object); - Log::debug("Object populated with %u vertices and %u faces.\n", - object.vertices.size(), object.faces.size()); + Log::debug("Object name: %s Vertex count: %u Face count: %u\n", + object.name.empty() ? "(none)" : object.name.c_str(), object.vertices.size(), object.faces.size()); return true; } === modified file 'src/model.h' --- src/model.h 2012-07-27 09:11:50 +0000 +++ src/model.h 2013-01-29 16:55:36 +0000 @@ -78,12 +78,13 @@ AttribTypeCustom } AttribType; - Model() : gotTexcoords_(false) {} + Model() : gotTexcoords_(false), gotNormals_(false) {} ~Model() {} bool load(const std::string& name); bool needTexcoords() const { return !gotTexcoords_; } + bool needNormals() const { return !gotNormals_; } void calculate_texcoords(); void calculate_normals(); void convert_to_mesh(Mesh &mesh); @@ -93,11 +94,18 @@ const LibMatrix::vec3& maxVec() const { return maxVec_; } static const ModelMap& find_models(); private: - // If the model we loaded contained texcoord data... + // If the model we loaded contained texcoord or normal data... bool gotTexcoords_; + bool gotNormals_; + struct Face { - uint32_t a, b, c; - uint16_t face_flags; + LibMatrix::uvec3 v; + LibMatrix::uvec3 n; + LibMatrix::uvec3 t; + unsigned int which; // mask of which values are relevant for this face. + static const unsigned int OBJ_FACE_V; + static const unsigned int OBJ_FACE_T; + static const unsigned int OBJ_FACE_N; }; struct Vertex { @@ -120,6 +128,11 @@ int nt_pos, int nb_pos); bool load_3ds(const std::string &filename); bool load_obj(const std::string &filename); + void obj_get_attrib(const std::string& description, LibMatrix::vec2& v); + void obj_get_attrib(const std::string& description, LibMatrix::vec3& v); + void obj_face_get_index(const std::string& tuple, unsigned int& which, + unsigned int& v, unsigned int& t, unsigned int& n); + void obj_get_face(const std::string& description, Face& face); // For vertices of the bounding box for this model. void compute_bounding_box(const Object& object); === modified file 'src/scene-build.cpp' --- src/scene-build.cpp 2012-08-15 09:45:06 +0000 +++ src/scene-build.cpp 2013-01-29 17:17:28 +0000 @@ -141,7 +141,8 @@ orientationVec_ = vec3(0.0, 1.0, 0.0); } - model.calculate_normals(); + if (model.needNormals()) + model.calculate_normals(); /* Tell the converter that we only care about position and normal attributes */ std::vector > attribs; === modified file 'src/scene-bump.cpp' --- src/scene-bump.cpp 2012-08-15 09:45:06 +0000 +++ src/scene-bump.cpp 2013-01-29 17:17:28 +0000 @@ -80,7 +80,8 @@ if(!model.load(poly_filename)) return false; - model.calculate_normals(); + if (model.needNormals()) + model.calculate_normals(); /* Tell the converter that we only care about position and normal attributes */ std::vector > attribs; @@ -177,7 +178,8 @@ if(!model.load("asteroid-low")) return false; - model.calculate_normals(); + if (model.needNormals()) + model.calculate_normals(); /* Calculate the half vector */ LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z()); @@ -234,7 +236,8 @@ if(!model.load("asteroid-low")) return false; - model.calculate_normals(); + if (model.needNormals()) + model.calculate_normals(); /* Calculate the half vector */ LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z()); === modified file 'src/scene-refract.cpp' --- src/scene-refract.cpp 2012-12-15 09:46:12 +0000 +++ src/scene-refract.cpp 2013-01-29 17:17:28 +0000 @@ -322,7 +322,8 @@ orientationVec_ = vec3(0.0, 1.0, 0.0); } - model.calculate_normals(); + if (model.needNormals()) + model.calculate_normals(); // Mesh setup vector > attribs; === modified file 'src/scene-shading.cpp' --- src/scene-shading.cpp 2012-08-15 09:45:06 +0000 +++ src/scene-shading.cpp 2013-01-29 17:17:28 +0000 @@ -220,7 +220,8 @@ orientationVec_ = vec3(0.0, 1.0, 0.0); } - model.calculate_normals(); + if (model.needNormals()) + model.calculate_normals(); /* Tell the converter that we only care about position and normal attributes */ std::vector > attribs; === modified file 'src/scene-shadow.cpp' --- src/scene-shadow.cpp 2012-12-17 16:08:05 +0000 +++ src/scene-shadow.cpp 2013-01-29 17:17:28 +0000 @@ -324,7 +324,8 @@ return false; } - model.calculate_normals(); + if (model.needNormals()) + model.calculate_normals(); // Mesh setup vector > attribs; === modified file 'src/scene-texture.cpp' --- src/scene-texture.cpp 2012-08-15 09:45:06 +0000 +++ src/scene-texture.cpp 2013-01-29 17:17:28 +0000 @@ -208,7 +208,9 @@ if (model.needTexcoords()) model.calculate_texcoords(); - model.calculate_normals(); + if (model.needNormals()) + model.calculate_normals(); + // Tell the converter which attributes we care about std::vector > attribs; attribs.push_back(std::pair(Model::AttribTypePosition, 3));