No meu artigo no Projeto Virtual Worlds sobre leitura de modelos no formato Wavefront (aqui) usei as funções de streaming da biblioteca padrão de C. Aqui vai uma ligeira modificação, usando a STL, de C++:
#include <stdlib.h> #include <stdio.h> #include <string> #include <sstream> #include <fstream> struct vec3 { float x, y, z; } struct indexes { int v, n; } int readObj(const char *filename) { std::ifstream f(filename); if (!f.is_open()) { fprintf(stderr, "Unable to open file.\n"); exit(EXIT_FAILURE); } vec3 v; indexes i; std::string line; std::vector<vec3> vertices; std::vector<vec3> normals; std::vector<indexes> idxs; while (!getline(f, line).eof()) { std::stringstream sstrm(line); std::string token; sstrm >> token; /* Não me preocupei com texturas aqui. */ if (token == "v") { sstrm >> v.x; sstrm >> v.y; sstrm >> v.z; vertices.push_back(v); } else if (token == "vn") { sstrm >> v.x; sstrm >> v.y; sstrm >> v.z; normals.push_back(v); } else if (token == "f") { /* NOTA: Como os índices começam com 1, no arquivo OBJ, então decremento antes de colocar na lista. */ std::string value; sstrm >> value; sscanf(value.c_str(), "%d//%d", &i.v, &i.n); i.v--; i.n--; idxs.push_back(i); sstrm >> value; sscanf(value.c_str(), "%d//%d", &i.v, &i.n); i.v--; i.n--; idxs.push_back(i); sstrm >> value; sscanf(value.c_str(), "%d//%d", &i.v, &i.n); i.v--; i.n--; idxs.push_back(i); } } f.close(); /* Usando display lists pq o teste foi feito com OpenGL 1.4 (vertex buffers existem no 1.5+). Poderíamos usar o mesmo loop abaixo para monstar um vertex buffer object. */ int displayList = glGenLists(1); if (displayList) { glNewList(displayList, GL_COMPILE); glBegin( GL_TRIANGLES ); std::vector<indexes>::iterator nIdx; for (nIdx = idxs.begin(); nIdx != idxs.end(); ++nIdx) { v = normals[nIdx->n]; glNormal3fv( (float *)&v ); v = vertices[nIdx->v]; glVertex3fv( (float *)&v ); } glEnd(); glEndList(); } return displayList; /* NOTA: Todos os vetores serão limpos aqui... */ }
A rotina é essencialmente a mesma do artigo original, só uso a classe stringstream para separar as substrings automaticamente e para realizar as conversões para float… O uso da classe ifstream também facilita a vida, mas não é tão diferente assim do que usar fopen e fclose.
Outra vantagem é que as strings são alocadas sem a interferência do programador. A STL faz isso de uma maneira performática… A liberação de recursos também é “automática” assim que os objetos saem do escopo…
A desvantagem, é claro, é que o código final fica um pouco balofo. Mas, só um pouquinho…