#include #include #include #include #include // glad must be loaded before GLFW #include "graphics.hpp" #include "main.hpp" #include using namespace std; ShaderProgram::Base ShaderProgram::baseFromName(string name) { string lname = name; boost::algorithm::to_lower(lname); // yes, cpp needs a library to do this. // luckily I'm already using boost so I // don't need to worry too much about it. // can't do a switch over strings in cpp. if (lname == "empty") { return Empty; } if (lname == "triangle") { return Triangle; } else { BOOST_LOG_TRIVIAL(fatal) << "unknown base program \"" << name << "\""; exit(EXIT_USAGE); } } /* class ShaderProgram Implementations */ string ShaderProgram::baseName() { /* BOOST_LOG_TRIVIAL(debug) << "this->base: " << this->base << " Base::Empty: " << Base::Empty << " Base::Triangle: " << Base::Triangle; */ switch (this->base) { case ShaderProgram::Base::Empty: return "Empty"; case ShaderProgram::Base::Triangle: return "Triangle"; default: // this should never occur, as the switch case should cover all variants. throw std::runtime_error("Undefined base Program"); } } /** * @param vertexSource C style string with NULL termination * @param fragSource C style string with NULL termination */ ShaderProgram::ShaderProgram(Base base, string vertexSource, string fragSource) { int success; char infoLog[512]; const char *vertexSource_c = vertexSource.c_str(); const char *fragSource_c = fragSource.c_str(); this->base = base; BOOST_LOG_TRIVIAL(info) << "With base program \"" << this->baseName() << "\""; // NOTE: char arrays and char pointers are not actually the same. // glShaderSource requires a char[] as it appears. We pass the // variable by reference to avoid duplication in memory. BOOST_LOG_TRIVIAL(trace) << "creating empty shaders"; this->vertex = glCreateShader(GL_VERTEX_SHADER); this->frag = glCreateShader(GL_FRAGMENT_SHADER); BOOST_LOG_TRIVIAL(trace) << "loading vertex shader"; glShaderSource(this->vertex, 1, &vertexSource_c, NULL); BOOST_LOG_TRIVIAL(trace) << "compiling vertex shader"; glCompileShader(this->vertex); // check if it worked glGetShaderiv(this->vertex, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(this->vertex, 512, NULL, infoLog); BOOST_LOG_TRIVIAL(fatal) << "could not compile vertex shader:\n\n" << infoLog << std::endl; exit(EXIT_SHADER); } BOOST_LOG_TRIVIAL(trace) << "loading fragments shader"; glShaderSource(this->frag, 1, &fragSource_c, NULL); BOOST_LOG_TRIVIAL(trace) << "compiling fragments shader"; glCompileShader(this->frag); // check if it worked glGetShaderiv(this->frag, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(this->frag, 512, NULL, infoLog); BOOST_LOG_TRIVIAL(fatal) << "could not compile fragments shader:\n\n" << infoLog << std::endl; exit(EXIT_SHADER); } // combine it all in a actual program this->program = glCreateProgram(); glAttachShader(this->program, this->vertex); glAttachShader(this->program, this->frag); glLinkProgram(this->program); glGetProgramiv(this->program, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(this->program, 512, NULL, infoLog); BOOST_LOG_TRIVIAL(fatal) << "could not link shader program:\n\n" << infoLog << std::endl; exit(EXIT_SHADER); } // finalizing our setup glDeleteShader(this->vertex); glDeleteShader(this->frag); } /** defines the base program behavior. * * will be called in `mainWindow`. */ int ShaderProgram::run(GLFWwindow *window) { if (this->base == Triangle) { // set up vertex data (and buffer(s)) and configure vertex attributes // ------------------------------------------------------------------ float vTriangle[] = { -0.5f, -0.5f, 0.0f, // left 0.5f, -0.5f, 0.0f, // right 0.0f, 0.5f, 0.0f // top }; unsigned int VBOTriangle, VAOTriangle; glGenVertexArrays(1, &VAOTriangle); glGenBuffers(1, &VBOTriangle); // bind the Vertex Array Object first, then bind and set vertex buffer(s), // and then configure vertex attributes(s). glBindVertexArray(VAOTriangle); glBindBuffer(GL_ARRAY_BUFFER, VBOTriangle); glBufferData(GL_ARRAY_BUFFER, sizeof(vTriangle), vTriangle, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0); glEnableVertexAttribArray(0); // note that this is allowed, the call to glVertexAttribPointer registered // VBO as the vertex attribute's bound vertex buffer object so afterwards we // can safely unbind glBindBuffer(GL_ARRAY_BUFFER, 0); // You can unbind the VAO afterwards so other VAO calls won't accidentally // modify this VAO, but this rarely happens. Modifying other VAOs requires a // call to glBindVertexArray anyways so we generally don't unbind VAOs (nor // VBOs) when it's not directly necessary. glBindVertexArray(0); // uncomment this call to draw in wireframe polygons. // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); while (!glfwWindowShouldClose(window)) { // input // ----- processInput(window); // render // ------ glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // draw our first triangle glUseProgram(this->program); glBindVertexArray(VAOTriangle); // seeing as we only have a single VAO there's no // need to bind it every time, but we'll do so to // keep things a bit more organized glDrawArrays(GL_TRIANGLES, 0, 3); // glBindVertexArray(0); // no need to unbind it every time // glfw: swap buffers and poll IO events (keys pressed/released, mouse // moved etc.) // ------------------------------------------------------------------------------- glfwSwapBuffers(window); glfwPollEvents(); } // optional: de-allocate all resources once they've outlived their purpose: // ------------------------------------------------------------------------ glDeleteVertexArrays(1, &VAOTriangle); glDeleteBuffers(1, &VBOTriangle); glDeleteProgram(this->program); // glfw: terminate, clearing all previously allocated GLFW resources. // ------------------------------------------------------------------ glfwTerminate(); return 0; } else // Empty { // use the compiled shader glUseProgram(this->program); while (!glfwWindowShouldClose(window)) { processInput(window); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glfwSwapBuffers(window); glfwPollEvents(); } return 0; } } /* functions that run out graphics stuff */ void framebuffer_size_callback(GLFWwindow *window, int width, int height) { glViewport(0, 0, width, height); } GLFWwindow *initGl() { /* graphics stuff */ glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); GLFWwindow *window = glfwCreateWindow(800, 600, "Shader Loader", NULL, NULL); if (window == NULL) { printf("Failed to create GLFW window\n"); glfwTerminate(); exit(EXIT_GL); } glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { printf("Failed to initialize GLAD\n"); exit(EXIT_GL); } glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); return window; } int mainWindow(ShaderProgram *shaderProgram, GLFWwindow *window) { int result = 0; BOOST_LOG_TRIVIAL(trace) << "Base program"; result = shaderProgram->run(window); BOOST_LOG_TRIVIAL(trace) << "Left base program"; glfwTerminate(); return result; } void processInput(GLFWwindow *window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); }