| /* |
| * Copyright © 2008 Ben Smith |
| * Copyright © 2010-2011 Linaro Limited |
| * |
| * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark. |
| * |
| * glmark2 is free software: you can redistribute it and/or modify it under the |
| * terms of the GNU General Public License as published by the Free Software |
| * Foundation, either version 3 of the License, or (at your option) any later |
| * version. |
| * |
| * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * glmark2. If not, see <http://www.gnu.org/licenses/>. |
| * |
| * Authors: |
| * Ben Smith (original glmark benchmark) |
| * Alexandros Frantzis (glmark2) |
| */ |
| #include "mesh.h" |
| #include "model.h" |
| #include "vec.h" |
| #include "log.h" |
| #include "options.h" |
| #include "util.h" |
| #include "float.h" |
| #include "math.h" |
| #include <fstream> |
| #include <sstream> |
| #include <memory> |
| |
| using std::string; |
| using std::vector; |
| using LibMatrix::vec3; |
| using LibMatrix::uvec3; |
| |
| #define read_or_fail(file, dst, size) do { \ |
| file.read(reinterpret_cast<char *>((dst)), (size)); \ |
| if (file.gcount() < (std::streamsize)(size)) { \ |
| Log::error("%s: %d: Failed to read %zd bytes from 3ds file (read %zd)\n", \ |
| __FUNCTION__, __LINE__, \ |
| (size_t)(size), file.gcount()); \ |
| return false; \ |
| } \ |
| } while(0); |
| |
| /** |
| * Computes the bounding box for a Model::Object. |
| * |
| * @param object the Model object |
| */ |
| void |
| Model::compute_bounding_box(const Object& object) |
| { |
| float minX(FLT_MAX); |
| float maxX(FLT_MIN); |
| float minY(FLT_MAX); |
| float maxY(FLT_MIN); |
| float minZ(FLT_MAX); |
| float maxZ(FLT_MIN); |
| for (vector<Vertex>::const_iterator vIt = object.vertices.begin(); vIt != object.vertices.end(); vIt++) |
| { |
| const vec3& curVtx = vIt->v; |
| if (curVtx.x() < minX) |
| { |
| minX = curVtx.x(); |
| } |
| if (curVtx.x() > maxX) |
| { |
| maxX = curVtx.x(); |
| } |
| if (curVtx.y() < minY) |
| { |
| minY = curVtx.y(); |
| } |
| if (curVtx.y() > maxY) |
| { |
| maxY = curVtx.y(); |
| } |
| if (curVtx.z() < minZ) |
| { |
| minZ = curVtx.z(); |
| } |
| if (curVtx.z() > maxZ) |
| { |
| maxZ = curVtx.z(); |
| } |
| } |
| maxVec_ = vec3(maxX, maxY, maxZ); |
| minVec_ = vec3(minX, minY, minZ); |
| } |
| |
| /** |
| * Appends the vertices of a Model::Object to a Mesh. |
| * |
| * @param object the object to append |
| * @param mesh the mesh to append to |
| * @param p_pos the attribute position to use for the 'position' attribute |
| * @param n_pos the attribute position to use for the 'normal' attribute |
| * @param t_pos the attribute position to use for the 'texcoord' attribute |
| */ |
| void |
| Model::append_object_to_mesh(const Object &object, Mesh &mesh, |
| 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) |
| { |
| 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); |
| } |
| } |
| |
| /** |
| * Converts a model to a mesh using the default attributes bindings. |
| * |
| * The default attributes and their order is: Position, Normal, Texcoord |
| * |
| * @param mesh the mesh to populate |
| */ |
| void |
| Model::convert_to_mesh(Mesh &mesh) |
| { |
| std::vector<std::pair<AttribType, int> > attribs; |
| |
| attribs.push_back(std::pair<AttribType, int>(AttribTypePosition, 3)); |
| attribs.push_back(std::pair<AttribType, int>(AttribTypeNormal, 3)); |
| attribs.push_back(std::pair<AttribType, int>(AttribTypeTexcoord, 2)); |
| |
| convert_to_mesh(mesh, attribs); |
| } |
| |
| /** |
| * Converts a model to a mesh using custom attribute bindings. |
| * |
| * The attribute bindings are pairs of <AttribType, dimensionality>. |
| * |
| * @param mesh the mesh to populate |
| * @param attribs the attribute bindings to use |
| */ |
| void |
| Model::convert_to_mesh(Mesh &mesh, |
| const std::vector<std::pair<AttribType, int> > &attribs) |
| { |
| std::vector<int> format; |
| int p_pos = -1; |
| int n_pos = -1; |
| int t_pos = -1; |
| int nt_pos = -1; |
| int nb_pos = -1; |
| |
| mesh.reset(); |
| |
| for (std::vector<std::pair<AttribType, int> >::const_iterator ai = attribs.begin(); |
| ai != attribs.end(); |
| ai++) |
| { |
| format.push_back(ai->second); |
| if (ai->first == AttribTypePosition) |
| p_pos = ai - attribs.begin(); |
| else if (ai->first == AttribTypeNormal) |
| n_pos = ai - attribs.begin(); |
| else if (ai->first == AttribTypeTexcoord) |
| t_pos = ai - attribs.begin(); |
| else if (ai->first == AttribTypeTangent) |
| nt_pos = ai - attribs.begin(); |
| else if (ai->first == AttribTypeBitangent) |
| nb_pos = ai - attribs.begin(); |
| } |
| |
| mesh.set_vertex_format(format); |
| |
| for (std::vector<Object>::const_iterator iter = objects_.begin(); |
| iter != objects_.end(); |
| iter++) |
| { |
| append_object_to_mesh(*iter, mesh, p_pos, n_pos, t_pos, nt_pos, nb_pos); |
| } |
| } |
| |
| void |
| Model::calculate_texcoords() |
| { |
| // 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: |
| // http://www.mvps.org/directx/articles/spheremap.htm for more details. |
| vec3 centerVec = maxVec_ + minVec_; |
| centerVec *= 0.5; |
| |
| for (std::vector<Object>::iterator iter = objects_.begin(); |
| iter != objects_.end(); |
| iter++) |
| { |
| Object &object = *iter; |
| for (vector<Vertex>::iterator vertexIt = object.vertices.begin(); |
| vertexIt != object.vertices.end(); |
| vertexIt++) |
| { |
| Vertex& curVertex = *vertexIt; |
| vec3 vnorm(curVertex.v - centerVec); |
| vnorm.normalize(); |
| curVertex.t.x(asinf(vnorm.x()) / M_PI + 0.5); |
| curVertex.t.y(asinf(vnorm.y()) / M_PI + 0.5); |
| } |
| } |
| } |
| |
| /** |
| * Calculates the normal vectors of the model vertices. |
| */ |
| void |
| Model::calculate_normals() |
| { |
| LibMatrix::vec3 n; |
| |
| for (std::vector<Object>::iterator iter = objects_.begin(); |
| iter != objects_.end(); |
| iter++) |
| { |
| Object &object = *iter; |
| |
| for (vector<Face>::const_iterator f_iter = object.faces.begin(); |
| f_iter != object.faces.end(); |
| 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]; |
| |
| /* Calculate normal */ |
| n = LibMatrix::vec3::cross(b.v - a.v, c.v - a.v); |
| n.normalize(); |
| a.n += n; |
| b.n += n; |
| c.n += n; |
| |
| LibMatrix::vec3 q1(b.v - a.v); |
| LibMatrix::vec3 q2(c.v - a.v); |
| LibMatrix::vec2 u1(b.t - a.t); |
| LibMatrix::vec2 u2(c.t - a.t); |
| float det = (u1.x() * u2.y() - u2.x() * u1.y()); |
| |
| /* Calculate tangent */ |
| LibMatrix::vec3 nt; |
| nt.x(det * (u2.y() * q1.x() - u1.y() * q2.x())); |
| nt.y(det * (u2.y() * q1.y() - u1.y() * q2.y())); |
| nt.z(det * (u2.y() * q1.z() - u1.y() * q2.z())); |
| nt.normalize(); |
| a.nt += nt; |
| b.nt += nt; |
| c.nt += nt; |
| |
| /* Calculate bitangent */ |
| LibMatrix::vec3 nb; |
| nb.x(det * (u1.x() * q2.x() - u2.x() * q1.x())); |
| nb.y(det * (u1.x() * q2.y() - u2.x() * q1.y())); |
| nb.z(det * (u1.x() * q2.z() - u2.x() * q1.z())); |
| nb.normalize(); |
| a.nb += nb; |
| b.nb += nb; |
| c.nb += nb; |
| } |
| |
| for (vector<Vertex>::iterator v_iter = object.vertices.begin(); |
| v_iter != object.vertices.end(); |
| v_iter++) |
| { |
| Vertex &v = *v_iter; |
| /* Orthogonalize */ |
| v.nt = (v.nt - v.n * LibMatrix::vec3::dot(v.nt, v.n)); |
| v.n.normalize(); |
| v.nt.normalize(); |
| v.nb.normalize(); |
| } |
| |
| } |
| } |
| |
| /** |
| * Load a model from a 3DS file. |
| * |
| * @param filename the name of the file |
| * |
| * @return whether loading succeeded |
| */ |
| bool |
| Model::load_3ds(const std::string &filename) |
| { |
| Object *object(0); |
| |
| Log::debug("Loading model from 3ds file '%s'\n", filename.c_str()); |
| |
| const std::auto_ptr<std::istream> input_file_ptr(Util::get_resource(filename)); |
| std::istream& input_file(*input_file_ptr); |
| |
| if (!input_file) { |
| Log::error("Could not open 3ds file '%s'\n", filename.c_str()); |
| return false; |
| } |
| |
| // Loop to scan the whole file |
| while (!input_file.eof()) { |
| uint16_t chunk_id; |
| uint32_t chunk_length; |
| |
| // Read the chunk header |
| input_file.read(reinterpret_cast<char *>(&chunk_id), 2); |
| if (input_file.gcount() == 0) { |
| continue; |
| } |
| else if (input_file.gcount() < 2) { |
| Log::error("%s: %d: Failed to read %zd bytes from 3ds file (read %zd)\n", |
| __FUNCTION__, __LINE__, 2, input_file.gcount()); |
| return false; |
| } |
| |
| //Read the lenght of the chunk |
| read_or_fail(input_file, &chunk_length, 4); |
| |
| switch (chunk_id) |
| { |
| //----------------- MAIN3DS ----------------- |
| // Description: Main chunk, contains all the other chunks |
| // Chunk ID: 4d4d |
| // Chunk Lenght: 0 + sub chunks |
| //------------------------------------------- |
| case 0x4d4d: |
| break; |
| |
| //----------------- EDIT3DS ----------------- |
| // Description: 3D Editor chunk, objects layout info |
| // Chunk ID: 3d3d (hex) |
| // Chunk Lenght: 0 + sub chunks |
| //------------------------------------------- |
| case 0x3d3d: |
| break; |
| |
| //--------------- EDIT_OBJECT --------------- |
| // Description: Object block, info for each object |
| // Chunk ID: 4000 (hex) |
| // Chunk Lenght: len(object name) + sub chunks |
| //------------------------------------------- |
| case 0x4000: |
| { |
| std::stringstream ss; |
| unsigned char c = 1; |
| |
| for (int i = 0; i < 20 && c != '\0'; i++) { |
| read_or_fail(input_file, &c, 1); |
| ss << c; |
| } |
| |
| objects_.push_back(Object(ss.str())); |
| object = &objects_.back(); |
| } |
| break; |
| |
| //--------------- OBJ_TRIMESH --------------- |
| // Description: Triangular mesh, contains chunks for 3d mesh info |
| // Chunk ID: 4100 (hex) |
| // Chunk Lenght: 0 + sub chunks |
| //------------------------------------------- |
| case 0x4100: |
| break; |
| |
| //--------------- TRI_VERTEXL --------------- |
| // Description: Vertices list |
| // Chunk ID: 4110 (hex) |
| // Chunk Lenght: 1 x unsigned short (number of vertices) |
| // + 3 x float (vertex coordinates) x (number of vertices) |
| // + sub chunks |
| //------------------------------------------- |
| case 0x4110: |
| { |
| uint16_t qty; |
| read_or_fail(input_file, &qty, sizeof(uint16_t)); |
| object->vertices.resize(qty); |
| |
| 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]); |
| } |
| } |
| break; |
| |
| //--------------- TRI_FACEL1 ---------------- |
| // Description: Polygons (faces) list |
| // Chunk ID: 4120 (hex) |
| // Chunk Lenght: 1 x unsigned short (number of polygons) |
| // + 3 x unsigned short (polygon points) x (number of polygons) |
| // + sub chunks |
| //------------------------------------------- |
| case 0x4120: |
| { |
| uint16_t qty; |
| 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)); |
| } |
| } |
| break; |
| |
| //------------- TRI_MAPPINGCOORS ------------ |
| // Description: Vertices list |
| // Chunk ID: 4140 (hex) |
| // Chunk Lenght: 1 x unsigned short (number of mapping points) |
| // + 2 x float (mapping coordinates) x (number of mapping points) |
| // + sub chunks |
| //------------------------------------------- |
| case 0x4140: |
| { |
| uint16_t qty; |
| read_or_fail(input_file, &qty, sizeof(uint16_t)); |
| 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]); |
| } |
| } |
| gotTexcoords_ = true; |
| break; |
| |
| //----------- Skip unknow chunks ------------ |
| //We need to skip all the chunks that currently we don't use |
| //We use the chunk lenght information to set the file pointer |
| //to the same level next chunk |
| //------------------------------------------- |
| default: |
| input_file.seekg(chunk_length - 6, std::ios::cur); |
| } |
| } |
| |
| // Compute bounding box for perspective projection |
| compute_bounding_box(*object); |
| |
| if (Options::show_debug) { |
| for (std::vector<Object>::const_iterator iter = objects_.begin(); |
| iter != objects_.end(); |
| iter++) |
| { |
| Log::debug(" Object name: %s Vertex count: %d Face count: %d\n", |
| iter->name.c_str(), iter->vertices.size(), iter->faces.size()); |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Parse vec3 values 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) |
| { |
| // 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; |
| } |
| // 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); |
| // 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); |
| // 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); |
| v.x(x); |
| v.y(y); |
| v.z(z); |
| } |
| |
| /** |
| * Parse uvec3 values from an OBJ file. |
| * |
| * @param source the source line to parse |
| * @param v the uvec3 to populate |
| */ |
| static void |
| obj_get_values(const string& source, uvec3& 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; |
| } |
| // 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); |
| // 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); |
| // 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); |
| } |
| |
| /** |
| * Load a model from an OBJ file. |
| * |
| * @param filename the name of the file |
| * |
| * @return whether loading succeeded |
| */ |
| bool |
| Model::load_obj(const std::string &filename) |
| { |
| Log::debug("Loading model from obj file '%s'\n", filename.c_str()); |
| |
| const std::auto_ptr<std::istream> input_file_ptr(Util::get_resource(filename)); |
| std::istream& inputFile(*input_file_ptr); |
| if (!inputFile) |
| { |
| Log::error("Failed to open '%s'\n", filename.c_str()); |
| return false; |
| } |
| |
| vector<string> sourceVec; |
| string curLine; |
| while (getline(inputFile, curLine)) |
| { |
| sourceVec.push_back(curLine); |
| } |
| |
| // Give ourselves an object to populate. |
| objects_.push_back(Object(filename)); |
| Object& object(objects_.back()); |
| |
| static const string vertex_definition("v"); |
| static const string normal_definition("vn"); |
| static const string texcoord_definition("vt"); |
| static const string face_definition("f"); |
| 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. |
| string::size_type startPos(0); |
| string::size_type spacePos = curSrc.find(" ", startPos); |
| string definitionType(curSrc, startPos, spacePos - startPos); |
| if (definitionType == vertex_definition) |
| { |
| Vertex v; |
| obj_get_values(curSrc, v.v); |
| object.vertices.push_back(v); |
| } |
| 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"); |
| } |
| 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"); |
| } |
| 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; |
| object.faces.push_back(f); |
| } |
| } |
| // 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()); |
| return true; |
| } |
| |
| namespace ModelPrivate |
| { |
| ModelMap modelMap; |
| } |
| |
| /** |
| * Locate all available models. |
| * |
| * This method scans the built-in data paths and build a database of usable |
| * models available to scenes. Map is available on a read-only basis to scenes |
| * that might find it useful for listing models, etc. |
| * |
| * @return a map containing information about the located models |
| */ |
| const ModelMap& |
| Model::find_models() |
| { |
| if (!ModelPrivate::modelMap.empty()) |
| { |
| return ModelPrivate::modelMap; |
| } |
| vector<string> pathVec; |
| string dataDir(GLMARK_DATA_PATH"/models"); |
| Util::list_files(dataDir, pathVec); |
| #ifdef GLMARK_EXTRAS_PATH |
| string extrasDir(GLMARK_EXTRAS_PATH"/models"); |
| Util::list_files(extrasDir, pathVec); |
| #endif |
| |
| // Now that we have a list of all of the model files available to us, |
| // let's go through and pull out the names and what format they're in |
| // so the scene can decide which ones to use. |
| for(vector<string>::const_iterator pathIt = pathVec.begin(); |
| pathIt != pathVec.end(); |
| pathIt++) |
| { |
| const string& curPath = *pathIt; |
| string::size_type namePos(0); |
| string::size_type slashPos = curPath.rfind("/"); |
| if (slashPos != string::npos) |
| { |
| // Advance to the first character after the last slash |
| namePos = slashPos + 1; |
| } |
| |
| ModelFormat format(MODEL_INVALID); |
| string::size_type extPos = curPath.rfind(".3ds"); |
| if (extPos == string::npos) |
| { |
| // It's not a 3ds model |
| extPos = curPath.rfind(".obj"); |
| if (extPos == string::npos) |
| { |
| // It's not an obj model either, so skip it. |
| continue; |
| } |
| format = MODEL_OBJ; |
| } |
| else |
| { |
| // It's a 3ds model |
| format = MODEL_3DS; |
| } |
| |
| string name(curPath, namePos, extPos - namePos); |
| ModelDescriptor* desc = new ModelDescriptor(name, format, curPath); |
| ModelPrivate::modelMap.insert(std::make_pair(name, desc)); |
| } |
| |
| return ModelPrivate::modelMap; |
| } |
| |
| /** |
| * Load a model by name. |
| * |
| * You must initialize the available model collection using |
| * Model::find_models() before using this method. |
| * |
| * @param modelName the model name |
| * |
| * @return whether the operation succeeded |
| */ |
| bool |
| Model::load(const string& modelName) |
| { |
| bool retVal(false); |
| ModelMap::const_iterator modelIt = ModelPrivate::modelMap.find(modelName); |
| if (modelIt == ModelPrivate::modelMap.end()) |
| { |
| return retVal; |
| } |
| |
| ModelDescriptor* desc = modelIt->second; |
| switch (desc->format()) |
| { |
| case MODEL_INVALID: |
| break; |
| case MODEL_3DS: |
| retVal = load_3ds(desc->pathname()); |
| break; |
| case MODEL_OBJ: |
| retVal = load_obj(desc->pathname()); |
| break; |
| } |
| |
| return retVal; |
| } |