OpenGL - Wavefront .obj Loader



  • Hallo,
    ich habe anhand der Youtube-Tutorials von "thecpluspplusguy" versucht einen .obj Loader zu basteln, nur leider bekomme ich beim Starten des Programms diesen Fehler:

    Debug Assertion Failed!
    Program:
    ....\MeinProgramm.exe
    File: ...\visual studiio10.0\vc\include\vector
    Line: 932

    Expression: vector subscript out of range

    Das klingt für mich alles ziemlich nach einem "Vector_Überlauf".

    Mein cube.obj:

    # cube.obj
    #
    
    v  0.0  0.0  0.0
    v  0.0  0.0  1.0
    v  0.0  1.0  0.0
    v  0.0  1.0  1.0
    v  1.0  0.0  0.0
    v  1.0  0.0  1.0
    v  1.0  1.0  0.0
    v  1.0  1.0  1.0
    
    vn  0.0  0.0  1.0
    vn  0.0  0.0 -1.0
    vn  0.0  1.0  0.0
    vn  0.0 -1.0  0.0
    vn  1.0  0.0  0.0
    vn -1.0  0.0  0.0
    
    f  1//2  7//2  5//2
    f  1//2  3//2  7//2 
    f  1//6  4//6  3//6 
    f  1//6  2//6  4//6 
    f  3//3  8//3  7//3 
    f  3//3  4//3  8//3 
    f  5//5  7//5  8//5 
    f  5//5  8//5  6//5 
    f  1//4  5//4  6//4 
    f  1//4  6//4  2//4 
    f  2//1  6//1  8//1 
    f  2//1  8//1  4//1
    

    und hier mein Objektloader:

    int objloader::loadObject(const char* dateiname){
    
    	this->filename = dateiname;
    	vector<string*> coord; //speichert ganze zeilen als string
    	vector<coordinate*> vertex; //member der struktur coordinate um die vertex zu speichern
    	vector<face*> faces;//member der struktur face um die faces zu speichern
    	vector<coordinate*> normals;
    
    	ifstream in(filename);//lädt die datei
    	if(!in.is_open() ){
    		MessageBox(NULL, "Objektfile konnte nicht geladen werden", "Error", MB_OK);
    		return -1;
    	}
    
    	char buf[256]; //buffer für die zeichen
    	while(!in.eof()){//solang nicht ende der datei...
    		in.getline(buf,256); //lädt in die variable buffer den wert der aktuellen zeile
    		coord.push_back(new string(buf)); //erstellt einen neuen member des vectors coord und setzt diesen string ans ende an (alles dynamisch durch "new")
    	}
    
    	for(int i=0;i<coord.size();i++){//geht alle zeilen durch
    		if(coord[i]->c_str()[0]=='#'){//testet ob in das erste zeichen der zeile ein # ist -> nur ein kommentar
    			continue;
    		}
    		else if(coord[i]->c_str()[0]=='v' && coord[i]->c_str()[1]==' '){ //if vector
    			float tmpx, tmpy, tmpz;
    			sscanf(coord[i]->c_str(),"v %f %f %f",&tmpx,&tmpy,&tmpz);//putts first char in tmp, first float in tmpx, second float in tmpy,....
    			vertex.push_back(new coordinate(tmpx, tmpy, tmpz));
    		}
    		else if(coord[i]->c_str()[0]=='v' && coord[i]->c_str()[1]=='n'){        //if normal vector
    			float tmpx,tmpy,tmpz;   
    			sscanf(coord[i]->c_str(),"vn %f %f %f",&tmpx,&tmpy,&tmpz);
    			normals.push_back(new coordinate(tmpx,tmpy,tmpz));
    		}
    		else if(coord[i]->c_str()[0]=='f'){     //if face
    			int a,b,c,d,e;
    			if(count(coord[i]->begin(),coord[i]->end(),' ')==3){     //if it is a triangle (it has 3 space in it)
    				 sscanf(coord[i]->c_str(),"f %d//%d %d//%d %d//%d",&a,&b,&c,&b,&d,&b);
    				 faces.push_back(new face(b,a,c,d));     //read in, and add to the end of the face list
    			}
    			else{
    				sscanf(coord[i]->c_str(),"f %d//%d %d//%d %d//%d %d//%d",&a,&b,&c,&b,&d,&b,&e,&b);
    				faces.push_back(new face(b,a,c,d,e));   //do the same, except we call another constructor, and we use different pattern
    			}
    		}
    	}
    //raw
            int num;        //the id for the list
            num=glGenLists(1);      //generate a uniqe
            glNewList(num,GL_COMPILE);      //and create it
            for(int i=0;i<faces.size();i++)
            {
                    if(faces[i]->bquad_face)      //if it's a quad draw a quad
                    {
                            glBegin(GL_QUADS);
                                    //basically all I do here, is use the facenum (so the number of the face) as an index for the normal, so the 1st normal owe to the first face
                                    //I subtract 1 because the index start from 0 in C++
                                    glNormal3f(normals[faces[i]->iface_number-1]->x,normals[faces[i]->iface_number-1]->y,normals[faces[i]->iface_number-1]->z);
                                    //draw the faces
                                    glVertex3f(vertex[faces[i]->faces[0]-1]->x,vertex[faces[i]->faces[0]-1]->y,vertex[faces[i]->faces[0]-1]->z);
                                    glVertex3f(vertex[faces[i]->faces[1]-1]->x,vertex[faces[i]->faces[1]-1]->y,vertex[faces[i]->faces[1]-1]->z);
                                    glVertex3f(vertex[faces[i]->faces[2]-1]->x,vertex[faces[i]->faces[2]-1]->y,vertex[faces[i]->faces[2]-1]->z);
                                    glVertex3f(vertex[faces[i]->faces[3]-1]->x,vertex[faces[i]->faces[3]-1]->y,vertex[faces[i]->faces[3]-1]->z);
                            glEnd();
                    }else{
                            glBegin(GL_TRIANGLES);
                                    glNormal3f(normals[faces[i]->iface_number-1]->x,normals[faces[i]->iface_number-1]->y,normals[faces[i]->iface_number-1]->z);
                                    glVertex3f(vertex[faces[i]->faces[0]-1]->x,vertex[faces[i]->faces[0]-1]->y,vertex[faces[i]->faces[0]-1]->z);
                                    glVertex3f(vertex[faces[i]->faces[1]-1]->x,vertex[faces[i]->faces[1]-1]->y,vertex[faces[i]->faces[1]-1]->z);
                                    glVertex3f(vertex[faces[i]->faces[2]-1]->x,vertex[faces[i]->faces[2]-1]->y,vertex[faces[i]->faces[2]-1]->z);
                            glEnd();
                    }
            }
            glEndList();
    
        //delete everything to avoid memory leaks
    	for(int i=0; i<coord.size();i++)
    		delete coord[i];
    	for(int i=0;i<faces.size();i++)
    		delete faces[i];
    	for(int i=0;i<normals.size();i++)
    		delete normals[i];
    	for(int i=0;i<vertex.size();i++)
    		delete vertex[i];
    
    }
    

    Würde mich sehr freuen, wenn jemand mir helfen könnte (auf Wunsch gern auch über Teamviewer), oder mir Tipps geben könnte meinen Fehler zu finden, sowie meine Code zu verbessern :).

    Gruß
    silent12



  • Die Fehlermeldung sagt ja schon, dass du auf ein Element eines vectors zuzugreifen versuchst, das nicht existiert. Der Debugger sagt dir, wo genau das passiert, einfach am Callstack raufgehen bis du in deinem Code landest...

    Abgesehen davon: Warum genau speicherst du erst alle Zeilen in einem vector? Du könntest doch einfach das Einlesen und Interpretieren der Zeilen in die selbe Schleife packen. Und wieso speicherst du Pointer in deinem vector und nicht einfach direkt die Werte? Und wenn es schon Pointer sein müssen, wäre es wohl empfehlenswert, Smartpointer zu verwenden; std::unique_ptr bietet sich an...



  • Ok habe jetzt die entscheidende Zeile:

    if(count(coord[i]->begin(),coord[i]->end(),' ')==3)

    Ich habe es jetzt einfach in:

    if(count(coord[i]->begin(),coord[i]->end(),' ')==6)

    Änderung: 6 am Ende, da bei mir immer 2 Leerzeichen statt eins zwischen den Werten für jedes face stehen.

    geändert und jetzt startet mein Programm, das Model wird aber nicht gezeichnet.

    Code der ausgeführt wird:

    Loadfunktion:

    this->filename = dateiname;
    	vector<string*> coord; //speichert ganze zeilen als string
    	vector<coordinate*> vertex; //member der struktur coordinate um die vertex zu speichern
    	vector<face*> faces;//member der struktur face um die faces zu speichern
    	vector<coordinate*> normals;
    
    	ifstream in(filename);//lädt die datei
    	if(!in.is_open() ){
    		MessageBox(NULL, "Objektfile konnte nicht geladen werden", "Error", MB_OK);
    		return -1;
    	}
    
    	char buf[256]; //buffer für die zeichen
    	while(!in.eof()){//solang nicht ende der datei...
    		in.getline(buf,256); //lädt in die variable buffer den wert der aktuellen zeile
    		coord.push_back(new string(buf)); //erstellt einen neuen member des vectors coord und setzt diesen string ans ende an (alles dynamisch durch "new")
    	}
    
    //#########################Hier ist der Inhalt der If-bedingung, die aufgerufen wird (habe ich getestet)#####################################
    sscanf(coord[i]->c_str(),"f  %d//%d  %d//%d  %d//%d",&a,&b,&c,&b,&d,&b);
    faces.push_back(new face(b,a,c,d));     //read in, and add to the end of the face list
    

    Initfunktion:

    const char* tmp_filename = get_filename();
            this->cube=loadObject(tmp_filename);    //load the test.obj file
            glEnable(GL_LIGHTING);  //we enable lighting, to make the 3D object to 3D
            glEnable(GL_LIGHT0);
            float col[]={1.0,1.0,1.0,1.0};  //light color is white
            glLightfv(GL_LIGHT0,GL_DIFFUSE,col);
    

    Displayfunktion:

    float pos[]={-1.0,1.0,-2.0,1.0};        //set the position
            glLightfv(GL_LIGHT0,GL_POSITION,pos);
            glCallList(this->cube);     //draw the 3D mesh
    

    Aufruf der Funktionen:

    objloader Myobjloader;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    //andere Objekte (mit direkten Verticesangaben werden aufgerufen)
    Myobjloader.loadObject("Data/cube.obj");
    Myobjloader.init();
    Myobjloader.display();
    

    Was sollte ich anpassen, oder hat jemand eine Idee wo der Fehler liegen könnte ?

    Gruß
    silent12



  • Stimmen die eingelesenen Koordinaten? Wird die Display List korrekt erstellt? Hast du auch eine korrekte und passende ModelView und Projection Matrix gesetzt (wenn die Kamera das Modell nicht anschaut, gibts nicht viel zu sehen)?



  • Die eingelesenen Koordinaten stimmen (zumindest die die ich gerade überprüft habe).
    Ich habe das alles in eine "Egoshooter"-Perspektive eingebaut, sodass ich in meiner Welt rumlaufen kann, aber das Modell ist nirgends zu sehen...
    Das Objekt WIRD nicht erstellt (habe gerade alles ausgeblendet, aber ich kann mich in alle Richtungen drehen und es ist nirgends zu sehen 😕

    Ach ich merke gerade, dass die initFunktion die LoadObject-Funktion aufruft. Evtl gibt es da Probleme...



  • Habe das Problem gelöst, wenn auch etwas unschön.
    Und zwar habe ich direkt in der LoadObjekt-Funktion den Befehl:

    glNewList(num,GL_COMPILE_AND_EXECUTE);
    

    statt den Befehl:

    glNewList(num,GL_COMPILE);
    

    benutzt, wodurch das Objekt direkt und nicht erst von der DisplayFunktion mit dem Befehl:

    glCallList(this->cube);
    

    gezeichnet wird 😃

    Gruß
    silent12



  • Sorry wegen den Mehrfachposts, aber es passt immer noch zum Thema.
    Und zwar klappt alles wunderbar mit einfachen Modellen wie einem Würfel, aber sobald ich etwas komplexere Objekte erstelle, wie zB der Monkey von Blender, dann läuft alles nur noch ruckelnd (Objekt dreht sich ruckelnd und ich laufe ruckelnd).
    An der Hardware kann es eig nicht liegen, da ich eine GTX580 Lightning und einen i5-2500k nutze.

    Woran kann es liegen ?

    Gruß
    silent12



  • Kann es sein, dass du nun in jedem Frame das Model neu aus der obj Datei lädst?



  • Ohman bin ich blöd 😃 Danke nochmal 🙂

    Gruß
    silent12


Log in to reply