=== 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<Face>::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<Object>::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<string> elements;
+    Util::split(source, ' ', elements, Util::SplitModeFuzzy);
+
+    // Find the first value...
+    float x = Util::fromString<float>(elements[0]);
+    // And the second value (there might be a third, but we don't care)...
+    float y = Util::fromString<float>(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<string> 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<float>(xs);
+    float x = Util::fromString<float>(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<float>(ys);
+    float y = Util::fromString<float>(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<float>(zs);
+    float z = Util::fromString<float>(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<string> elements;
+    Util::split(tuple, '/', elements, Util::SplitModeNormal);
+
+    if (elements.empty())
+    {
+        which = 0;
+        return;
+    }
+
+    which = Face::OBJ_FACE_V;
+    v = Util::fromString<unsigned int>(elements[0]);
+
+    unsigned int num_elements = elements.size();
+
+    if (num_elements > 1 && !elements[1].empty())
+    {
+        which |= Face::OBJ_FACE_T;
+        t = Util::fromString<unsigned int>(elements[1]);
+    }
+
+    if (num_elements > 2 && !elements[2].empty())
+    {
+        which |= Face::OBJ_FACE_N;
+        n = Util::fromString<unsigned int>(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<string> 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<unsigned int>(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<unsigned int>(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<unsigned int>(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<vec3> positions;
+    vector<vec3> normals;
+    vector<vec2> texcoords;
     for (vector<string>::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<std::pair<Model::AttribType, int> > 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<std::pair<Model::AttribType, int> > 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<std::pair<Model::AttribType, int> > 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<std::pair<Model::AttribType, int> > 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<std::pair<Model::AttribType, int> > 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<std::pair<Model::AttribType, int> > attribs;
     attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypePosition, 3));

