0

我编写了自己的代码来解析 .obj 模型文件——本质上只是 ASCII 文本。根据我的测试,该文件被正确解析并存储在类中。我可以在加载函数中很好地读回值(来自数据成员)。

当我尝试读回主渲染循环中的值时,就会出现问题。以“int v”开头的行存在访问冲突错误:

 for(int i = 0; i<data.numFaces; i++){
  for(int j = 0; j<3; j++){ //Assuming triangles for now.

   int v = data.faceList[i].vertex[j]; // Access violation here.
   double vX = data.vertexList[v].x;
   double vY = data.vertexList[v].y;
   double vZ = data.vertexList[v].z;
   glVertex3d(vX, vY, vZ);
  }
 }

我不确定为什么会发生这种情况,并且我已经检查了我可能想到的所有内容。我在 C++ 方面不是很有经验。我的大部分编程经验是 Java、Python 和 PHP,尽管我之前用 C++ 编写过一个中型项目。

我确定问题是与内存分配或用于动态数组的指针有关的基本问题。

以下是 obj 加载类中的相关代码部分:

ObjData ObjLoader::LoadObj(std::string filename){

    //... Initalization ...

 // 1st pass: find number of elements so array sizes can be defined.
 while(!file.eof()){
  //...
 }

 //...close file...

 _data.faceList = new ObjFace[_data.numFaces];
 _data.vertexList = new ObjVert[_data.numVertices];
 _data.uvList = new ObjUV[_data.numUVcoords];
 _data.normalList = new ObjNormal[_data.numNormals];

    //TODO: Make size dynamic according to each face. Just use the first 3 points for now.
 for (int i = 0; i < _data.numFaces; i++){
  _data.faceList[i].vertex = new int[3];
  _data.faceList[i].normal = new int[3];
  _data.faceList[i].uv = new int[3];
 }

 //... file stuff ...

 // 2nd pass: read values into arrays.
 while(!file.eof()){
  //...

  if(type=="v"){
   _data.vertexList[currentVertex].x = atof(param1.c_str());
   _data.vertexList[currentVertex].y = atof(param2.c_str());
   _data.vertexList[currentVertex].z = atof(param3.c_str());
   currentVertex++;
  }else if(type=="vt"){
   _data.uvList[currentUV].u = atof(param1.c_str());
   _data.uvList[currentUV].v = atof(param2.c_str());
   currentUV++;
  }else if(type=="vn"){
   _data.normalList[currentNormal].x = atof(param1.c_str());
   _data.normalList[currentNormal].y = atof(param2.c_str());
   _data.normalList[currentNormal].z = atof(param3.c_str());
   currentNormal++;
  }else if(type=="f"){
  //...Within loop over each vertex in a single face ...

        if(endPos != string::npos){
        // Value before 1st "/" (Vertex index).
        // ...find value in string...
        _data.faceList[currentFace].vertex[i] = atoi(token.c_str()) -1; // File format begins indices from 1.

        // Value between slashes (UV index).
        // ...find value in string...
        _data.faceList[currentFace].uv[i] = atoi(token.c_str()) -1;

        // Value after 2nd "/" (Normal index).
        // ...find value in string...
        _data.faceList[currentFace].normal[i] = atoi(token.c_str()) -1;
   }
//...End of loop over every vertex in a single face...
currentFace++;
}

}

 return _data;

    }

结构 ObjFace、ObjVert、ObjUV 和 ObjNormal 定义为:

    struct ObjVert{
       float x, y, z;
    };

    struct ObjUV{
      float u, v;
    };

    struct ObjNormal{
       float x, y, z;
    };

    // Contains indexes.
       struct ObjFace{
       int* vertex;
       int* uv;
       int* normal;
    };

谢谢你的帮助。此外,将来避免这些类型的错误的任何良好资源将不胜感激。

4

2 回答 2

2

乍一看,我没有发现任何明显的错误。但是,如果如您所说,它正在爆炸,int v = data.faceList[i].vertex[j];那么问题似乎很可能是问题太大或太小i,或j两者都太大或太小。

除了熟悉调试器并消除此错误的明显方法之外,解决此类问题的最佳方法可能是完全避免它们。程序员做的某些事情比其他事情更容易出错。这个列表很长,但你至少在代码中做了两个。

1)您使用动态分配的数组 2)您使用手工制作的循环

尽量避免使用 C++ 提供的工具来做这些事情。从 #1 开始,摆脱动态分配的数组。

你有一个结构:

struct ObjFace{
  int* vertex;
  int* uv;
  int* normal;
};

...带有 3 个指向数组的指针int。而不是这样做,使用vector

struct ObjFace{
  vector<int> vertex;
  vector<int> uv;
  vector<int> normal;
};

...然后您以前必须编写的大量代码现在变得更加简单,并且更不容易出错:

// all this goes away
//_data.faceList = new ObjFace[_data.numFaces];
//_data.vertexList = new ObjVert[_data.numVertices];
//_data.uvList = new ObjUV[_data.numUVcoords];
//_data.normalList = new ObjNormal[_data.numNormals];

...和:

// now you ask the vector how many elements it really has
for(int i = 0; i<data.faceList.size(); i++){
  for(int j = 0; j<data.faceList.size(); j++){ //Ask the vector instead of assuming triangles

   int v = data.faceList[i].vertex[j]; // Access violation here.
   double vX = data.vertexList[v].x;
   double vY = data.vertexList[v].y;
   double vZ = data.vertexList[v].z;
   glVertex3d(vX, vY, vZ);
  }
 }

现在,看看那个循环。循环是一个非常常见的错误来源。最好的循环是你永远不必编写的循环。所以请改用 STL 的算法。向 ObjFace 添加一个函数以glVertex3d在其每个元素上执行:

struct ObjFace{
//... 
  void do_vertex() const 
  {
    typedef vector<int> ints;
    for( ints::iterator it = vertex.begin(); it != vertex.end(); ++it )
      glVertex3d(it->x, it->y, it->z);
  }
};

...然后返回并减少原始循环:(伪代码,实际语法更复杂)

typedef vector<ObjFace> ObjFaces;
for( ObjFaces::iterator it = data.faceList.begin(); it != data.faceList.end(); ++it )
  it->do_vertex();

...或者,再努力一点:

  for_each( data.faceList.begin(), data.faceList.end(), &ObjFace::do_vertex );
于 2010-06-15T20:55:03.953 回答
2

我打了一些愚蠢的回应,我意识到这是不对的……但我不得不继续思考它,我想出了另一个主意。

这个对象在哪里被分配?

您的代码不清楚是否是data_data一个对象,但我注意到您的方法似乎_data作为一个对象返回。我被引导相信也许您正在使用ObjData data = LoadObj("myfilename");某处的作业?

如果是这种情况,我相信您的问题可能来自您的ObjData类缺少复制构造函数或重载赋值运算符。(我不是 C++ 大师,所以我不记得到底属于哪一个。希望其他人可以确认我们是否走在正确的轨道上)。

如果您的指针在复制和分配期间没有被正确复制(从LoadObj调用复制构造函数 iirc 中返回,然后明显分配给data),那么即使您打算int在该位置已经有一个数组,您实际上可能是访问未初始化的内存,从而导致您的访问冲突。

我不是复制构造函数或重载赋值运算符的专家,但解决此问题的快速方法是返回指向 an 的指针,ObjData而不是返回对象本身。

于 2010-06-15T20:58:31.357 回答