/* Libraries we use */ #define GL_GLEXT_PROTOTYPES #include #include #include #include #include #include #include #include "wfobj/WFObj.h" #include "loadTexture/loadTexture.h" using namespace std; /* Some definitions */ #define WIDTH 800 #define HEIGHT 800 #define TITLE "Josh's Wavefront Object Viewer" enum Locations { LOC_POSITION, LOC_NORMAL }; class Viewer { public: Viewer(const char * filename); void run(); private: void initgl(); void display(); void setProjection(); WFObj m_obj; GLuint m_list; float m_rotationMatrix[16]; int m_startx, m_starty; bool m_dragging; float m_dist; GLuint m_program, m_vs, m_fs; GLint m_ambient_loc, m_diffuse_loc, m_specular_loc, m_shininess_loc; }; static GLuint load_texture(const char *fname) { GLuint id = loadTexture(fname); if (id != 0) { glBindTexture(GL_TEXTURE_2D, id); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } return id; } char * loadFile(const char *fname) { struct stat st; if (stat(fname, &st) != 0) return NULL; if (st.st_size <= 0) return NULL; char * buff = new char[st.st_size + 1]; int fd = open(fname, O_RDONLY); read(fd, buff, st.st_size); close(fd); buff[st.st_size] = '\0'; return buff; } char *getShaderLog(GLuint id) { GLint log_length; glGetShaderiv(id, GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { char *log = new char[log_length]; glGetShaderInfoLog(id, log_length, &log_length, log); return log; } return NULL; } char *getProgramLog(GLuint id) { GLint log_length; glGetProgramiv(id, GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { char *log = new char[log_length]; glGetProgramInfoLog(id, log_length, &log_length, log); return log; } return NULL; } GLuint makeShader(GLenum shaderType, const char *fname) { GLuint id; const char *source; GLint status; id = glCreateShader(shaderType); if (id <= 0) { cerr << "Error creating shader object" << endl; goto out; } source = loadFile(fname); if (source == NULL) { fprintf(stderr, "Error reading file '%s'\n", fname); goto cleanup_shader; } glShaderSource(id, 1, &source, NULL); delete[] source; glCompileShader(id); glGetShaderiv(id, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { cerr << "Error compiling shader" << endl; char *log = getShaderLog(id); cerr << "Shader Log:" << endl << log << endl; delete[] log; goto cleanup_shader; } return id; cleanup_shader: glDeleteShader(id); out: return 0; } /* The program's main entry point */ int main(int argc, char * argv[]) { if (argc < 2) { cerr << "Usage: " << argv[0] << " " << endl; return -1; } if (SDL_Init(SDL_INIT_VIDEO)) { printf("Failed to initialize SDL!\n"); return -2; } atexit(SDL_Quit); SDL_Surface * screen; SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); if (!(screen = SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_OPENGL))) { printf("Failed to set video mode!\n"); SDL_Quit(); return -3; } SDL_WM_SetCaption(TITLE, TITLE); Viewer v(argv[1]); v.run(); return 0; } Viewer::Viewer(const char * filename) { m_dist = 5.0; m_dragging = false; if (!m_obj.load(filename, NULL, load_texture)) { cerr << "Error loading " << filename << endl; exit(1); } m_vs = makeShader(GL_VERTEX_SHADER, "v_shader.glsl"); m_fs = makeShader(GL_FRAGMENT_SHADER, "f_shader.glsl"); m_program = glCreateProgram(); glAttachShader(m_program, m_vs); glAttachShader(m_program, m_fs); glBindAttribLocation(m_program, LOC_POSITION, "pos"); glBindAttribLocation(m_program, LOC_NORMAL, "normal"); glLinkProgram(m_program); GLint link_status; glGetProgramiv(m_program, GL_LINK_STATUS, &link_status); if (link_status != GL_TRUE) { char *log = getProgramLog(m_program); cerr << "Program log:" << endl << log << endl; delete[] log; glDeleteShader(m_vs); glDeleteShader(m_fs); exit(1); } m_ambient_loc = glGetUniformLocation(m_program, "ambient"); m_diffuse_loc = glGetUniformLocation(m_program, "diffuse"); m_specular_loc = glGetUniformLocation(m_program, "specular"); m_shininess_loc = glGetUniformLocation(m_program, "shininess"); if (m_ambient_loc < 0 || m_diffuse_loc < 0 || m_specular_loc < 0 || m_shininess_loc < 0) { cerr << "glGetUniformLocation() returned < 0" << endl; exit(1); } glUseProgram(m_program); glUniform4f(m_ambient_loc, 0.2, 0.2, 0.2, 1.0); glUniform4f(m_diffuse_loc, 1.0, 1.0, 1.0, 1.0); glUniform4f(m_specular_loc, 1.0, 1.0, 1.0, 1.0); glUniform1f(m_shininess_loc, 85.0); /* Print out the object's size */ const float * aabb = m_obj.getAABB(); cout << "Object width: " << (aabb[3]-aabb[0]) << endl; cout << "Object depth: " << (aabb[4]-aabb[1]) << endl; cout << "Object height: " << (aabb[5]-aabb[2]) << endl; } void Viewer::initgl() { glClearColor(0.0, 0.0, 0.0, 0.0); glEnable(GL_DEPTH_TEST); glViewport(0, 0, WIDTH, HEIGHT); setProjection(); glLoadIdentity(); glGetFloatv(GL_MODELVIEW_MATRIX, m_rotationMatrix); } void Viewer::setProjection() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)WIDTH/(GLfloat)WIDTH, 0.01, 10000.0); // gluLookAt(0, m_dist, 0, 0, 0, 0, 0, 0, 1); glMatrixMode(GL_MODELVIEW); } void Viewer::display() { glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluLookAt(0, -m_dist, 0, 0, 0, 0, 0, 0, 1); glMultMatrixf(m_rotationMatrix); m_obj.bindBuffers(); glUseProgram(m_program); glEnableVertexAttribArray(LOC_POSITION); glEnableVertexAttribArray(LOC_NORMAL); int stride = m_obj.getStride(); glVertexAttribPointer(LOC_POSITION, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid *) m_obj.getVertexOffset()); glVertexAttribPointer(LOC_NORMAL, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid *) m_obj.getNormalOffset()); if (m_obj.doTextures()) { /* TODO */ } for (map::iterator it = m_obj.getMaterials().begin(); it != m_obj.getMaterials().end(); it++) { WFObj::Material & m = it->second; if (m.flags & WFObj::Material::SHININESS_BIT) glUniform1f(m_shininess_loc, m.shininess); if (m.flags & WFObj::Material::AMBIENT_BIT) glUniform4fv(m_ambient_loc, 1, &m.ambient[0]); if (m.flags & WFObj::Material::DIFFUSE_BIT) glUniform4fv(m_diffuse_loc, 1, &m.diffuse[0]); if (m.flags & WFObj::Material::SPECULAR_BIT) glUniform4fv(m_specular_loc, 1, &m.specular[0]); if (m.flags & WFObj::Material::TEXTURE_BIT) { cerr << "error: textured materials not implemented yet" << endl; } glDrawElements(GL_TRIANGLES, it->second.num_vertices, GL_UNSIGNED_SHORT, (GLvoid *) (sizeof(GLushort) * it->second.first_vertex)); } SDL_GL_SwapBuffers(); } void Viewer::run() { initgl(); display(); SDL_Event event; while (SDL_WaitEvent(&event)) { if (event.type == SDL_QUIT) break; else if (event.type == SDL_KEYDOWN) { if (event.key.keysym.sym == SDLK_ESCAPE) break; if (event.key.keysym.sym == SDLK_q) break; } else if (event.type == SDL_MOUSEBUTTONDOWN) { if (event.button.button == SDL_BUTTON_LEFT) { if (event.button.state == SDL_PRESSED) { m_dragging = true; m_startx = event.button.x; m_starty = event.button.y; } } else if (event.button.button == 4) { m_dist *= 0.85; if (m_dist < 1.0f) m_dist = 1.0f; setProjection(); display(); } else if (event.button.button == 5) { m_dist *= 1.2; setProjection(); display(); } } else if (event.type == SDL_MOUSEBUTTONUP) { if (event.button.button == SDL_BUTTON_LEFT) { m_dragging = false; } } else if (event.type == SDL_MOUSEMOTION) { if (m_dragging) { glLoadIdentity(); glRotatef(event.motion.y - m_starty, 1, 0, 0); glRotatef(event.motion.x - m_startx, 0, 0, 1); glMultMatrixf(m_rotationMatrix); glGetFloatv(GL_MODELVIEW_MATRIX, m_rotationMatrix); m_startx = event.motion.x; m_starty = event.motion.y; display(); } } } }