2013-04-30 4 views
2

Я написал загрузчик файлов C++ Obj, который я не могу нормально работать. Проблема заключается в том, что при анализе простого OBJ файла как следующее:Понимание нормальных индексов с Wavefront Obj

# Blender v2.62 (sub 0) OBJ File: '' 
# www.blender.org 
mtllib cube.mtl 
o Cube 
v 1.000000 -1.000000 -1.000000 
v 1.000000 -1.000000 1.000000 
v -1.000000 -1.000000 1.000000 
v -1.000000 -1.000000 -1.000000 
v 1.000000 1.000000 -0.999999 
v 0.999999 1.000000 1.000001 
v -1.000000 1.000000 1.000000 
v -1.000000 1.000000 -1.000000 
vn 0.000000 0.000000 -1.000000 
vn -1.000000 -0.000000 -0.000000 
vn -0.000000 -0.000000 1.000000 
vn 1.000000 -0.000000 0.000000 
vn 1.000000 0.000000 0.000001 
vn -0.000000 1.000000 0.000000 
vn 0.000000 -1.000000 0.000000 
usemtl Material 
s off 
f 5//1 1//1 4//1 
f 5//1 4//1 8//1 
f 3//2 7//2 8//2 
f 3//2 8//2 4//2 
f 2//3 6//3 3//3 
f 6//3 7//3 3//3 
f 1//4 5//4 2//4 
f 5//5 6//5 2//5 
f 5//6 8//6 6//6 
f 8//6 7//6 6//6 
f 1//7 2//7 3//7 
f 1//7 3//7 4//7 

я не могу понять, как правильно передать нормали к OpenGL. Я всегда получаю результаты, как это: enter image description here

ObjLoader.h

#include <Eigen/Core> 

class ObjLoader 
{ 
public: 
    ObjLoader(); 
    bool load(const std::string &filename); 

    void draw(); 
private: 
    bool loadFace(const std::string &line,int lineNumber); 


    std::vector< Eigen::Vector3d> verticesCoord,verticesNormals; 
    std::vector< Eigen::Vector2d> textureCoords; 
    std::vector<GLuint> vertexIndices,normalIndices,textureIndices; 
    Eigen::Vector3d calculateNormal(const Eigen::Vector3d &coord1, const Eigen::Vector3d &coord2, const Eigen::Vector3d &coord3); 
    std::string mtlFile; 
    unsigned int nVerticesPerFace; 

}; 

ObjLoader.cpp

#include <iostream> 
#include <fstream> 
#include <string> 
#include <sstream> 
#include <Eigen/Core> 
#include "ObjLoader.h" 

using namespace std; 
using namespace Eigen; 

ObjLoader::ObjLoader() 
{ 

} 

bool ObjLoader::load(const string &filename) 
{ 
    ifstream is(filename.c_str()); 
    if (is.is_open()) 
    { 
    cerr << "File " + filename + " loaded successfully" << endl; 
    } 

    std::vector<Vector3d> temporaryNormals; // a vector to contain the normals as they are read from the obj 

    string line; 
    unsigned int lineNumber=0; 
    while (getline(is,line)) 
    { 
    lineNumber++; 
    if (line.empty() || line.at(0)=='#') 
     continue; 
    if (line.substr(0,6)=="mtllib") 
    { 
     this->mtlFile = line.substr(7,line.size()-1); 
     cerr << "MTLIB support file= " << mtlFile << endl; 
    } 
    stringstream stream(line); 
    char identifier ; 
    stream >> std::skipws >> identifier; 

    char specifier; 
    stream >> specifier; 
    if (specifier != 't' && specifier != 'n' && specifier!='p') 
    { 
     stream.seekg(0); 
     specifier=0; 
    } 

    switch (identifier) 
    { 
    case 'v': //is a vertex specification 
    { 
     switch (specifier) // if there is a space then is a simple vertex coordinates 
     { 
     case 0: 
     { 
      char tmp; stream >> tmp; 
      Eigen::Vector3d vertex(0.0,0.0,0.0); 
      stream >> vertex[0] >> vertex[1] >> vertex[2]; 
      this->verticesCoord.push_back(vertex); 
     } 
      break; 
     case 't': 
     { 
      Eigen::Vector2d textures(0,0); 
      stream >> textures[0] >> textures[1]; 
      this->textureCoords.push_back(textures); 
     } 
      break; 
     case 'n': 
     { 
      Eigen::Vector3d vertexNormal(0,0,0); 
      stream >> vertexNormal[0] >> vertexNormal[1] >> vertexNormal[2]; 
      temporaryNormals.push_back(vertexNormal); 
     } 
      break; 
     } 
    } 
     break; 
    case 'f': // is a face specification 
    { 
     this->loadFace(line,lineNumber); 
    } 
     break; 
    } 
    } 

    // Rearrange the normals 
    verticesNormals.resize(temporaryNormals.size(),Vector3d(1,1,1)); 
    for(unsigned int i=0;i<vertexIndices.size();i++) 
    { 
    GLuint nI = normalIndices.at(i); 
    GLuint vI = vertexIndices.at(i); 
    if(nI!=vI) 
    { 
     this->verticesNormals.at(vI) = temporaryNormals.at(nI); 
     std::cerr<< "Normal index doesn't match vertex index: " << vertexIndices[i] << " " << normalIndices[i] << std::endl; 
    } 
    else 
    { 
     this->verticesNormals.at(vI) = temporaryNormals.at(vI); 
    } 

    cerr << "Vertices=" << this->verticesCoord.size() << endl; 
    cerr << "Normals=" << this->verticesNormals.size() << endl; 
    cerr << "Textures=" << this->textureCoords.size() << endl; 
    cerr << "NVertices per face= " << this->nVerticesPerFace << endl; 
    cerr << "Faces= " << this->vertexIndices.size()/nVerticesPerFace << endl; 

    return 0; 
    } 
} 

bool BothAreSpaces(char lhs, char rhs) 
{ 
    return (lhs == rhs) && (lhs == ' '); 
} 

bool ObjLoader::loadFace(const string &_line, int lineNumber) 
{ 
    std::string line = _line; 
    std::string::iterator new_end = std::unique(line.begin(), line.end(), BothAreSpaces); 
    line.erase(new_end, line.end()); 

    stringstream stream(line),countVerticesStream(line); 
    string val; 
    stream >> val; 
    if (val!="f") 
    { 
    string lineString= static_cast<ostringstream*>(&(ostringstream() << lineNumber))->str(); 
    throw std::logic_error("Error loading face at line " + lineString); 
    } 

    // Count the number of vertices per face by counting the/
    int nVertices = 0; 
    while (countVerticesStream.good()) 
    { 
    if (countVerticesStream.get()==' ' && countVerticesStream.good()) 
     nVertices++; 
    } 

    if (nVerticesPerFace !=0 && nVerticesPerFace != nVertices) 
    { 
    string lineString= static_cast<ostringstream*>(&(ostringstream() << lineNumber))->str(); 
    throw std::logic_error("Can't support non uniform faces definitions. You must use the same number of vertices for every faces. Check line "+lineString); 
    } 
    this->nVerticesPerFace = nVertices; 

    GLuint indexPosition = 0, indexTexture = 0, indexNormal = 0; 

    // Compute the normal 
    Vector3d faceVertices[nVerticesPerFace]; 
    for (unsigned int iFace = 0; iFace < nVerticesPerFace; iFace++) 
    { 
    stream >> indexPosition; 
    faceVertices[iFace] = verticesCoord.at(indexPosition-1); 
    if('/' == stream.peek()) 
    { 
     stream.ignore(); 
     if('/' != stream.peek()) 
     { 
      stream >> indexTexture; 
     } 
     if('/' == stream.peek()) 
     { 
      stream.ignore(); 
      // Optional vertex normal 
      stream >> indexNormal; 
     } 
    } 
    this->vertexIndices.push_back(indexPosition-1); // that's because Obj format starts counting from 1 
    this->textureIndices.push_back(indexTexture-1); // that's because Obj format starts counting from 1 
    this->normalIndices.push_back(indexNormal-1); // that's because Obj format starts counting from 1 

    } 
} 

void ObjLoader::draw() 
{ 
    double *pVerticesCoords = &this->verticesCoord.at(0)[0]; 
    glEnableClientState(GL_VERTEX_ARRAY); 
    glVertexPointer(3,GL_DOUBLE, 0,pVerticesCoords); 
    glDrawArrays(GL_POINTS, 0, this->verticesCoord.size()); 
    glDisableClientState(GL_VERTEX_ARRAY); 

    GLint coordsPerVertex=3; 
    GLint stride=0; // Our coords are tightly packed into their arrays so we set this to 0 
    //double *pVerticesCoords = &this->verticesCoord.at(0)[0]; 
    double *pNormalCoords = &this->verticesNormals.at(0)[0]; 

    glEnableClientState(GL_NORMAL_ARRAY); 
    glEnableClientState(GL_VERTEX_ARRAY); 

    glNormalPointer(GL_DOUBLE, 0, pNormalCoords); // Normal pointer to normal array 
    glVertexPointer(coordsPerVertex,GL_DOUBLE, stride,pVerticesCoords); 

    switch (nVerticesPerFace) 
    { 
    case 3: 
    glDrawElements(GL_TRIANGLES, vertexIndices.size(), GL_UNSIGNED_INT, this->vertexIndices.data()); 
    break; 
    case 4: 
    glDrawElements(GL_QUADS, vertexIndices.size(), GL_UNSIGNED_INT, this->vertexIndices.data()); 
    break; 
    default: 
    glDrawElements(GL_POLYGON, vertexIndices.size(), GL_UNSIGNED_INT, this->vertexIndices.data()); 
    } 
    glDisableClientState(GL_VERTEX_ARRAY); 
    glDisableClientState(GL_NORMAL_ARRAY); 

} 

Как следует реорганизовать нормалей таким образом, что они отражают тот же порядок вершины?

+0

Представляет ли ваша проблема действительно много кода? На первый взгляд, я думаю, что нет. Не могли бы вы просто сузить код, который вы даете, чтобы мы могли (намного) лучше понять вашу проблему, и поэтому мы можем дать гораздо лучший ответ. –

+0

Я предоставил этот код для компилятивного примера для вас. Проблема (я думаю) заключается в загрузке файла и нажатии нормалей в нормалях 'std :: vector " в том же порядке, в котором они встречаются. Я хотел бы предоставить нормали и вершины с одинаковыми индексами, но не знаю, как это сделать. – linello

ответ

4

Ваша проблема в структуре данных. По крайней мере, при загрузке OBJ вам нужно загрузить ваше лицо во что-то вроде:

struct Vertex 
{ 
    unsigned int vertex; 
    unsigned int normal; 
    unsigned int texturecoord; 
}; 

struct Face 
{ 
    // not dynamic, but you get the idea. 
    Vertex vertexes[N]; 
}; 

Тогда, если вы хотите массив нормалей (и, вероятно, texturecoords), что соответствует вершинам, вам необходимо создать новый массив, Матчи. Формат OBJ оптимизирован для хранения, а не для рендеринга.

Дополнительным преимуществом этого двухэтапного процесса является то, что вы можете удалить ограничение на гомогенные грани, разбив каждый не треугольник на треугольник.

+0

Спасибо, но как же тогда использовать структуру данных 'vertexes' для рисования координат, нормалей и текстур вместе? Я имею в виду, разве индексы не испортились? – linello

+0

Вы вычисляете новый набор массивов, соответствующих структуре VBO. Шаг загрузки должен быть полностью отделен от этапа рендеринга. Вероятно, вы захотите загрузить в будущем другие форматы, с другими причудами, не заставляйте рендеринг зависеть от кода загрузки. – rioki

-1

Я знаю, что это старый вопрос, но раньше у меня была такая же проблема, когда я строил парсер Obj. В моем коде были две проблемы, которые привели к тому, что освещение было очень похоже на то, что вы показали: 1- Первое, когда я неправильно использовал номер индекса (1,2,3, ..., n) I получен из файла Obj, чтобы ссылаться на нормальное значение в моем массиве. 2- Другая проблема заключалась в том, что вместо значений vn принимались значения vt.

И это, как вы передаете нормальные значения OpenGL при использовании glBegin/glEnd:

glNormal3f(....); 
glVertex3f(....); 

Вы установили нормальное значение перед нанесением каждой вершины. Всякий раз, когда вы устанавливаете нормальный, все последующие вершины будут затронуты.

+0

Непосредственный режим? Убирайся отсюда – RecursiveExceptionException

Смежные вопросы