2010-06-15 73 views
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; 
    }; 

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

+0

请问您可以添加更多的相关代码吗?例如,什么类型是'_data'?另外,在最上面的代码中,在后面的代码中'data'与'_data'是一样的,还是在某处我们没有看到一个赋值?在我看来,我们被要求对你的代码做太多的假设。 – KevenK 2010-06-15 20:33:46

+0

哦,对,我试图删除尽可能多的无关代码。 顶部段是在我的主渲染循环,和之前只是,有到LoadObj的呼叫(这是低2个片段),其中所返回的值,如下所示被分配给数据: \t数据= LoaderClass.LoadObj( “cube.obj”); _data和data都是ObjData类型,它存储加载的.obj。 – usm 2010-06-15 20:44:26

+0

虽然问题是(有点)不同,但您可能需要查看以前发布的关于读取obj文件的答案中的代码:http://stackoverflow.com/questions/2908854/opengl-problem-with-texture-in -mobi-obj/2909187#2909187 – 2010-06-15 20:44:31

回答

2

我已经打完了,我认识是不正确的一些愚蠢的反应......但我必须继续思考它,I C记住另一个想法。

该对象在哪里分配?

如果data_data是同一个对象,但您注意到您的方法似乎将_data作为对象返回,您的代码不清楚。我被引导认为,也许你在某处使用像ObjData data = LoadObj("myfilename");这样的任务?

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

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

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

+0

我认为你是对的。我没有ObjData类的复制构造函数或赋值运算符。做一点阅读告诉我它们是具有指针属性的对象所必需的。 这也解释了为什么一切都在LoadObj函数中工作,但不在其外部。在LoadObj之外,原始数据的析构函数被调用,并且副本仍然指向内存的相同部分,现在它是无效的。我将在明天早上写一份拷贝构造函数和赋值操作符并报告。谢谢! – usm 2010-06-15 22:12:55

+0

我写了一个拷贝构造函数并重载了赋值操作符,问题就没有了。非常感谢! – usm 2010-06-16 11:48:21

2

乍一看,我没有看到任何明显的错误。但如果你说它在int v = data.faceList[i].vertex[j];处爆炸,那么它很可能是ij或者两者都太大或太小。

除了让你的调试器变得更加舒适,并且把这个bug弄平的最明显的方法之外,解决这些问题的最好方法可能是完全避免它们。程序员做的某些事情比其他人更容易出错。这个列表很长,但是你至少在你的代码中做了两个,在黑桃中。

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

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

你有一个结构:

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

...与3个指针到数组-OF- 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); 
    } 
}; 

...然后回去削减该原始循环: (psudocode,实际的语法比较复杂)

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

谢谢,我想现在我将使用矢量来处理大多数事情。我最初回避他们,因为我认为他们是基于链表(我确定他们在Java中),但似乎并非如此。 我也喜欢ObjFace中循环的结构。关于如何良好/正确地构造代码有没有什么好的资源? 当然,在我写完复制构造函数和赋值操作符之后,我将会看到我现在的工作是否如KevenK所建议的那样。否则,我想我会不停地想知道出了什么问题。 – usm 2010-06-15 22:28:41

+1

因为你说你是C++的新手,我强烈推荐阅读Scott Meyers的Effective C++书籍。他讨论了C++中的许多问题,并就如何安全,清晰和高效地编写C++提供了许多建议。另外,他是一个有趣的作家阅读。事实上,在“Effective C++”第二版中,第11项说“声明一个具有动态分配内存的类的复制构造函数和赋值操作符”。所以你会看到关于你确切问题的提示。祝你好运! – PhysicalEd 2010-06-16 14:49:25