#ifdef __APPLE__ #define glGenVertexArrays glGenVertexArraysAPPLE #define glBindVertexArray glBindVertexArrayAPPLE #endif #define GL_GLEXT_PROTOTYPES // for Linux #include #include #include #include #include #include #include #include #include #include #define glError() { \ GLenum err = glGetError(); \ while (err != GL_NO_ERROR) { \ std::cerr << "glError: " << (char *)gluErrorString(err) << " at " << __LINE__; \ err = glGetError(); \ } \ } // picopng int decodePNG(std::vector& out_image, unsigned long& image_width, unsigned long& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32 = true); //// typedef struct { float pos[3]; } Vertex; typedef struct { uint32_t vbo_id; uint32_t ibo_id; uint32_t vao_id; uint32_t num_poly; } Mesh; typedef struct { uint32_t prog_id; uint32_t vert_id; uint32_t frag_id; } Shader; Shader shaders[1]; Mesh popcan; uint32_t popcan_tex_id; char basic_vert_shader[] = "varying vec4 color;" "varying vec2 texcoord;" "void main () {" " color = vec4(clamp(gl_Vertex.y,0.0,0.7),0.3,0.3,1.0);" " float theta = atan(gl_Vertex.z, gl_Vertex.x+0.1) + 3.142;" " texcoord = vec2(theta / 6.28, gl_Vertex.y / 7.0 + 0.5);" " gl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;" "}"; char basic_frag_shader[] = "uniform sampler2D texture;" "varying vec4 color;" "varying vec2 texcoord;" "void main () {" " gl_FragColor = mix(color, texture2D(texture, texcoord), 0.7);" "}"; void render () { glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity (); glUseProgram (shaders[0].prog_id ); static float x = 0; glTranslatef (-8,0,-20); glRotatef (x+=0.01, 0,1,0); glRotatef (30.0, 1.0,0,0); GLint shader_tex_loc = glGetUniformLocation(shaders[0].prog_id, "texture"); glUniform1i(shader_tex_loc, 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, popcan_tex_id); glError(); glBindVertexArray (popcan.vao_id); glDrawElements(GL_TRIANGLES, popcan.num_poly, GL_UNSIGNED_INT, 0); } void print_shader_error (uint32_t id, uint32_t type) { char *log; GLint length=0, result; if (type == GL_COMPILE_STATUS) glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length); else glGetProgramiv (id, GL_INFO_LOG_LENGTH, &length); if (length == 0) { std::cerr << "Unknown error.\n"; return; } log = new char[length]; if (type == GL_COMPILE_STATUS) glGetShaderInfoLog(id, length, &result, log); else glGetProgramInfoLog(id, length, &result, log); std::cerr << log << "\n"; delete[] log; } Mesh load_mesh (std::string filename) { Mesh m; uint32_t object_count = 0; std::ifstream fin (filename.c_str ()); if (!fin.good ()) { std::cerr << "Couldn't open " << filename << "\n"; exit(1); } std::vector vert_data; std::vector index_data; while (!fin.eof ()) { char line[128]; fin.getline(line, 128); if (line[0] == 'o') { object_count++; if (object_count != 1) { std::cerr << "More than one object in mesh.\n"; exit(1); } } else if (line[0] == 'v') { Vertex v; char *fstr = strtok(&(line[1]), " "); v.pos[0] = atof(fstr); fstr = strtok(NULL, " "); v.pos[1] = atof(fstr); fstr = strtok(NULL, " "); v.pos[2] = atof(fstr); assert (strtok (NULL, " ") == NULL); vert_data.push_back(v); } else if (line[0] == 'f') { char *intstr = strtok(&(line[1]), " "); index_data.push_back(atoi (intstr) - 1); intstr = strtok(NULL, " "); index_data.push_back(atoi (intstr) - 1); intstr = strtok(NULL, " "); index_data.push_back(atoi (intstr) - 1); if (strtok(NULL, " ") != NULL) { std::cerr << "You got some quads bro.\n"; exit (1); } } } glGenVertexArrays (1, &(m.vao_id)); glBindVertexArray (m.vao_id); glGenBuffers(2, &m.vbo_id); glBindBuffer(GL_ARRAY_BUFFER, m.vbo_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m.ibo_id); glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*vert_data.size (), &(vert_data[0]), GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t)*index_data.size (), &(index_data[0]), GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(0); glBindVertexArray (0); m.num_poly = (uint32_t)index_data.size (); std::cerr << "Loaded " << filename << "\n"; return m; } uint32_t load_texture (std::string filename) { std::vector image_data; long unsigned int image_w, image_h; size_t png_size; std::ifstream fin(filename.c_str()); if (!fin.good ()) { std::cerr << "Couldn't open " << filename << "\n"; exit(1); } fin.seekg(0, std::ios::end); png_size = fin.tellg(); fin.seekg(0, std::ios::beg); unsigned char png_data[png_size]; fin.read((char*)png_data, png_size); { int r = decodePNG(image_data, image_w, image_h, png_data, png_size); if(r) { std::cerr << "Couldn't read PNG data from " << filename << "\n"; exit(1); }} uint32_t tex_id; glGenTextures(1, &tex_id); glBindTexture(GL_TEXTURE_2D, tex_id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLint)image_w, (GLint)image_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)&(image_data.at(0))); glGenerateMipmap(GL_TEXTURE_2D); glError(); std::cerr << "Loaded " << filename << " (" << image_w << "x" << image_h << ")\n"; return tex_id; } uint32_t compile_individual_shader (const char *src, uint32_t len, uint32_t type) { uint32_t id = glCreateShader (type); glShaderSource (id, 1, &src, NULL); glCompileShader (id); GLint compiled; glGetShaderiv (id, GL_COMPILE_STATUS, &compiled); if (compiled == GL_FALSE) { std::cerr << "Error compiling shader: "; print_shader_error(id, GL_COMPILE_STATUS); exit (1); } return id; } Shader make_shader (char* vert_src, uint32_t vert_len, char* frag_src, uint32_t frag_len) { Shader s; s.vert_id = compile_individual_shader (vert_src, vert_len, GL_VERTEX_SHADER); s.frag_id = compile_individual_shader (frag_src, frag_len, GL_FRAGMENT_SHADER); s.prog_id = glCreateProgram (); glAttachShader (s.prog_id, s.frag_id); glAttachShader (s.prog_id, s.vert_id); glLinkProgram (s.prog_id); GLint linked; glGetProgramiv(s.prog_id, GL_LINK_STATUS, &linked); if (linked == GL_FALSE) { std::cerr << "Error linking shader: "; print_shader_error (s.prog_id, GL_COMPILE_STATUS); exit (1); } return s; } void GL_init (float w, float h) { glShadeModel (GL_SMOOTH); glEnable (GL_DEPTH_TEST); glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glFrontFace (GL_CCW); glCullFace (GL_BACK); glEnable (GL_CULL_FACE); glClearColor (0,0,0,0); glEnable (GL_TEXTURE_2D); glViewport (0, 0, w, h); // TODO: This code is old and bad, needs to go glMatrixMode (GL_PROJECTION); glLoadIdentity(); float near = 0.001; float far = 1000.0; float fH = tan(45.0/360.0*M_PI)*near; float fW = fH*w/h; glFrustum(-fW, fW, -fH, fH, near, far); glMatrixMode (GL_MODELVIEW); // TODO: replace above popcan = load_mesh ("can.obj"); shaders[0] = make_shader (basic_vert_shader, sizeof (basic_vert_shader), basic_frag_shader, sizeof (basic_frag_shader)); popcan_tex_id = load_texture("can.png"); } int main () { float scale = 1; float w = 480*scale; float h = 272*scale; if (!glfwInit () || !glfwOpenWindow (w, h, 8,8,8,0,32,0,GLFW_WINDOW)) { std::cerr << "Something GLFW failed.\n"; return 1; } GL_init (w, h); int running = 1; float t0 = glfwGetTime (); uint32_t frames = 0; //glfwSwapInterval (0); // Uncomment this to test FPS without vsync while (running) { float t = glfwGetTime (); if (t-t0 >= 5.0) { std::cerr << (float)frames/5.0 << " FPS\n"; t0 = t; frames = 0; } render (); frames++; glfwSwapBuffers (); running = glfwGetWindowParam (GLFW_OPENED); } glfwTerminate (); }