使用Wavefront Obj了解法线指数 [英] Understanding normals indices with Wavefront Obj

查看:163
本文介绍了使用Wavefront Obj了解法线指数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个C ++ Obj文件加载器,我无法正常工作。问题是,在解析一个简单的obj文件像下面:

I've written a C++ Obj file loader that I can't get to work correctly. The problem is that while parsing a simple obj file like the following:

# 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的法线。我总是得到这样的结果:

i can't understand the proper way to pass the normals to OpenGL. I always get results like this:

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);

}

我应该如何重新组织法线,顶点的顺序?

How should I reorganize the normals such that they reflect the same order of the vertices?

推荐答案

您的问题是在数据结构中。至少在加载OBJ时,您需要将面部加载到以下内容中:

Your problem is in the data structure. At least while loading an OBJ you need to load your faces into something like:

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

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

然后,如果你想要一个与顶点匹配的法线创建一个匹配的新数组。 OBJ格式针对存储进行了优化,而不是渲染。

Then you if you want an array of normals (and probably texturecoords) that matches the vertices, you need to create a new array that matches. The OBJ format is optimized for storage, not rendering.

这两步过程的另一个好处是,您可以通过将每个非三角形夹成三角形来删除同质面上的限制。

The added benefit from this two step process, is that you can remove the restriction on homogene faces, by splinting each non triangle into a triangle.

这篇关于使用Wavefront Obj了解法线指数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆