object loader in opengl
I have create a program in C++ that contains also opengl and I want to create also an obj loader in order to load an obj file I have ! I have already created two functions which are :
void ReadFile(model *md)
{
// Open the file for reading OBJINFO.TXT
ifstream obj_file("tree.obj");
if (obj_file.fail())
exit(1);
// Get the number of vertices
obj_file >> md->vertices;
// Get the number of faces
obj_file >> md->faces;
// Get the vertex coordinates
for (int i = 0; i < md->vertices; i++)
{
obj_file >> md->obj_points[i].x;
obj_file >> md->obj_points[i].y;
obj_file >> md->obj_points[i].z;
}
// Get the face structure
for (int i = 0; i < md->faces; i++)
{
obj_file >> md->obj_faces[i].vn[0];
obj_file >> md->obj_faces[i].vn[1];
obj_file >> md->obj_faces[i].vn[2];
obj_file >> md->obj_faces[i].vn[3];
}
obj_file.close();
}
void DisplayModel(model md)
{
glPushMatrix();
glBegin(GL_TRIANGLES);
for (int i = 0; i < md.faces; i++)
{
glVertex3f(md.obj_points[md.obj_faces[i].vn[0]-1].x, md.obj_points[md.obj_faces[i].vn[0]-1].y, md.obj_points[md.obj_faces[i].vn[0]-1].z);
glVertex3f(md.obj_points[md.obj_faces[i].vn[1]-1].x, md.obj_points[md.obj_faces[i].vn[1]-1].y, md.obj_points[md.obj_faces[i].vn[1]-1].z);
glVertex3f(md.obj_points[md.obj_faces[i].vn[2]-1].x, md.obj_points[md.obj_faces[i].vn[2]-1].y, md.obj_points[md.obj_faces[i].vn[2]-1].z);
glVertex3f(md.obj_points[md.obj_faces[i].vn[2]-1].x, md.obj_points[md.obj_faces[i].vn[2]-1].y, md.obj_points[md.obj_faces[i].vn[3]-1].z);
}
glEnd();
glPopMatrix();
}
The main problem is that when I compile the project and run it , nothing appears in viewport . I increased also the dimensions of viewport in case it is small in order to appear the object , but the situation remains the same ! So I reach the result that I have done something wrong in these functions ! Could anyone help me ?!
Also I give some values of my obj file :
v 0.158000 0.975000 0.151491 1.000000
v 0.188743 0.025000 0.173826 1.000000
v 0.196000 0.025000 0.151491 1.000000
v 0.158000 0.025000 0.151491 1.000000
v 0.169743 0.025000 0.187631 1.000000
v 0.146257 0.025000 0.187631 1.000000
v 0.127257 0.025000 0.173826 1.000000
vn 0.950370 0.038015 0.308791
vn 0.950370 0.038015 0.308791
vn 0.950370 0.038015 0.308791
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 -0.000000
vn 0.587380 0.038015 0.808418
f 1//1 2//2 3//3
f 4//4 3//5 2//6
f 1//7 5//8 2//9
f 4//10 2//11 5//12
f 1//13 6//14 5//15
f 4//16 5//17 6//18
f 1//19 7//20 6//21
Solution 1:
// Get the number of vertices
obj_file >> md->vertices;
// Get the number of faces
obj_file >> md->faces;
Read the spec again. That's not how OBJs work. You have to parse out the vertexes/texture coordinates/normals/faces as you go along.
Use something like this:
#include <GL/glut.h>
#include <glm/glm.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/component_wise.hpp>
#include <vector>
#include <fstream>
#include <sstream>
struct Vertex
{
glm::vec3 position;
glm::vec2 texcoord;
glm::vec3 normal;
};
struct VertRef
{
VertRef( int v, int vt, int vn ) : v(v), vt(vt), vn(vn) { }
int v, vt, vn;
};
std::vector< Vertex > LoadOBJ( std::istream& in )
{
std::vector< Vertex > verts;
std::vector< glm::vec4 > positions( 1, glm::vec4( 0, 0, 0, 0 ) );
std::vector< glm::vec3 > texcoords( 1, glm::vec3( 0, 0, 0 ) );
std::vector< glm::vec3 > normals( 1, glm::vec3( 0, 0, 0 ) );
std::string lineStr;
while( std::getline( in, lineStr ) )
{
std::istringstream lineSS( lineStr );
std::string lineType;
lineSS >> lineType;
// vertex
if( lineType == "v" )
{
float x = 0, y = 0, z = 0, w = 1;
lineSS >> x >> y >> z >> w;
positions.push_back( glm::vec4( x, y, z, w ) );
}
// texture
if( lineType == "vt" )
{
float u = 0, v = 0, w = 0;
lineSS >> u >> v >> w;
texcoords.push_back( glm::vec3( u, v, w ) );
}
// normal
if( lineType == "vn" )
{
float i = 0, j = 0, k = 0;
lineSS >> i >> j >> k;
normals.push_back( glm::normalize( glm::vec3( i, j, k ) ) );
}
// polygon
if( lineType == "f" )
{
std::vector< VertRef > refs;
std::string refStr;
while( lineSS >> refStr )
{
std::istringstream ref( refStr );
std::string vStr, vtStr, vnStr;
std::getline( ref, vStr, '/' );
std::getline( ref, vtStr, '/' );
std::getline( ref, vnStr, '/' );
int v = atoi( vStr.c_str() );
int vt = atoi( vtStr.c_str() );
int vn = atoi( vnStr.c_str() );
v = ( v >= 0 ? v : positions.size() + v );
vt = ( vt >= 0 ? vt : texcoords.size() + vt );
vn = ( vn >= 0 ? vn : normals.size() + vn );
refs.push_back( VertRef( v, vt, vn ) );
}
// triangulate, assuming n>3-gons are convex and coplanar
for( size_t i = 1; i+1 < refs.size(); ++i )
{
const VertRef* p[3] = { &refs[0], &refs[i], &refs[i+1] };
// http://www.opengl.org/wiki/Calculating_a_Surface_Normal
glm::vec3 U( positions[ p[1]->v ] - positions[ p[0]->v ] );
glm::vec3 V( positions[ p[2]->v ] - positions[ p[0]->v ] );
glm::vec3 faceNormal = glm::normalize( glm::cross( U, V ) );
for( size_t j = 0; j < 3; ++j )
{
Vertex vert;
vert.position = glm::vec3( positions[ p[j]->v ] );
vert.texcoord = glm::vec2( texcoords[ p[j]->vt ] );
vert.normal = ( p[j]->vn != 0 ? normals[ p[j]->vn ] : faceNormal );
verts.push_back( vert );
}
}
}
}
return verts;
}
int btn;
glm::ivec2 startMouse;
glm::ivec2 startRot, curRot;
glm::ivec2 startTrans, curTrans;
void mouse(int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN )
{
btn = button;
startMouse = glm::ivec2( x, glutGet( GLUT_WINDOW_HEIGHT ) - y );
startRot = curRot;
}
if( button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN )
{
btn = button;
startMouse = glm::ivec2( x, glutGet( GLUT_WINDOW_HEIGHT ) - y );
startTrans = curTrans;
}
}
void motion( int x, int y )
{
glm::ivec2 curMouse( x, glutGet( GLUT_WINDOW_HEIGHT ) - y );
if( btn == GLUT_LEFT_BUTTON )
{
curRot = startRot + ( curMouse - startMouse );
}
else if( btn == GLUT_RIGHT_BUTTON )
{
curTrans = startTrans + ( curMouse - startMouse );
}
glutPostRedisplay();
}
std::vector< Vertex > model;
void display()
{
glClearColor( 0.2f, 0.2f, 0.2f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
double ar = w / h;
glTranslatef( curTrans.x / w * 2, curTrans.y / h * 2, 0 );
gluPerspective( 60, ar, 0.1, 100 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -10 );
glPushMatrix();
{
glRotatef( curRot.x % 360, 0, 1, 0 );
glRotatef( -curRot.y % 360, 1, 0, 0 );
// object
glColor3ub( 255, 0, 0 );
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glVertexPointer( 3, GL_FLOAT, sizeof(Vertex), &model[0].position );
glTexCoordPointer( 2, GL_FLOAT, sizeof(Vertex), &model[0].texcoord );
glNormalPointer( GL_FLOAT, sizeof(Vertex), &model[0].normal );
glDrawArrays( GL_TRIANGLES, 0, model.size() );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
// bounding cube
glDisable( GL_LIGHTING );
glColor3ub( 255, 255, 255 );
glutWireCube( 7 );
glEnable( GL_LIGHTING );
}
glPopMatrix();
glutSwapBuffers();
}
// return the min/max points of pts
template< typename Vec >
std::pair< Vec, Vec > GetExtents( const Vec* pts, size_t stride, size_t count )
{
unsigned char* base = (unsigned char*)pts;
Vec pmin( *(Vec*)base );
Vec pmax( *(Vec*)base );
for( size_t i = 0; i < count; ++i, base += stride )
{
const Vec& pt = *(Vec*)base;
pmin = glm::min( pmin, pt );
pmax = glm::max( pmax, pt );
}
return std::make_pair( pmin, pmax );
}
// centers geometry around the origin
// and scales it to fit in a size^3 box
template< typename Vec >
void CenterAndScale( Vec* pts, size_t stride, size_t count, const typename Vec::value_type& size )
{
typedef typename Vec::value_type Scalar;
// get min/max extents
std::pair< Vec, Vec > exts = GetExtents( pts, stride, count );
// center and scale
const Vec center = ( exts.first * Scalar( 0.5 ) ) + ( exts.second * Scalar( 0.5f ) );
const Scalar factor = size / glm::compMax( exts.second - exts.first );
unsigned char* base = (unsigned char*)pts;
for( size_t i = 0; i < count; ++i, base += stride )
{
Vec& pt = *(Vec*)base;
pt = ( ( pt - center ) * factor );
}
}
int main( int argc, char **argv )
{
// https://en.wikipedia.org/wiki/Stanford_bunny
std::ifstream ifile( "bunny.obj" );
model = LoadOBJ( ifile );
CenterAndScale( &model[0].position, sizeof( Vertex ), model.size(), 7 );
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "OBJ" );
glutDisplayFunc( display );
glutMouseFunc( mouse );
glutMotionFunc( motion );
glEnable( GL_DEPTH_TEST );
// set up "headlamp"-like light
glShadeModel( GL_SMOOTH );
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
GLfloat position[] = { 0, 0, 1, 0 };
glLightfv( GL_LIGHT0, GL_POSITION, position );
glPolygonMode( GL_FRONT, GL_FILL );
glPolygonMode( GL_BACK, GL_LINE );
glutMainLoop();
return 0;
}
LMB-drag rotates, RMB-drag "pans".
And use this:
v 0.000000 2.000000 2.000000
v 0.000000 0.000000 2.000000
v 2.000000 0.000000 2.000000
v 2.000000 2.000000 2.000000
f -4 -3 -2 -1
v 2.000000 2.000000 0.000000
v 2.000000 0.000000 0.000000
v 0.000000 0.000000 0.000000
v 0.000000 2.000000 0.000000
f -4 -3 -2 -1
v 2.000000 2.000000 2.000000
v 2.000000 0.000000 2.000000
v 2.000000 0.000000 0.000000
v 2.000000 2.000000 0.000000
f -4 -3 -2 -1
v 0.000000 2.000000 0.000000
v 0.000000 2.000000 2.000000
v 2.000000 2.000000 2.000000
v 2.000000 2.000000 0.000000
f -4 -3 -2 -1
v 0.000000 2.000000 0.000000
v 0.000000 0.000000 0.000000
v 0.000000 0.000000 2.000000
v 0.000000 2.000000 2.000000
f -4 -3 -2 -1
v 0.000000 0.000000 2.000000
v 0.000000 0.000000 0.000000
v 2.000000 0.000000 0.000000
v 2.000000 0.000000 2.000000
f -4 -3 -2 -1
Or this:
v 0.000000 2.000000 0.000000
v 0.000000 0.000000 0.000000
v 2.000000 0.000000 0.000000
v 2.000000 2.000000 0.000000
v 4.000000 0.000000 -1.255298
v 4.000000 2.000000 -1.255298
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.276597 0.000000 0.960986
vn 0.276597 0.000000 0.960986
vn 0.531611 0.000000 0.846988
vn 0.531611 0.000000 0.846988
# 6 vertices
# 6 normals
g all
s 1
f 1//1 2//2 3//3 4//4
f 4//4 3//3 5//5 6//6
# 2 elements
for test.obj
.
glm.hpp
is from the GLM library.