#include "Wrapper.h" #include #include #include using namespace std; DummyClient DummyClient::dummy; IClient* Engine::client = &DummyClient::dummy; GLFWwindow* Engine::window = nullptr; GLuint Engine::vShader = 0; GLuint Engine::fShader = 0; GLuint Engine::program = 0; GLuint Engine::postVba = 0; GLuint Engine::postVbo = 0; GLuint Engine::postVShader = 0; GLuint Engine::postFShader = 0; GLuint Engine::postProgram = 0; GLuint Engine::frameBuffer = 0; GLuint Engine::frameTexture = 0; GLuint Engine::renderBuffer = 0; int Engine::scale = 1; int Engine::width = 0; int Engine::height = 0; bool Engine::init(int width, int height, const char* name) { Engine::width = width; Engine::height = height; updateScale(); if(!glfwInit()) { cout << "could not initialize GLFW" << endl; return false; } glfwDefaultWindowHints(); glfwWindowHint(GLFW_VISIBLE, 0); glfwWindowHint(GLFW_RESIZABLE, 1); window = glfwCreateWindow(width, height, name, nullptr, nullptr); if(!window) { cout << "could not create window" << endl; glfwTerminate(); return false; } glfwMakeContextCurrent(window); glfwSwapInterval(1); glfwShowWindow(window); GLenum err = glewInit(); if(GLEW_OK != err) { cout << "could not initialize GLEW: " << glewGetErrorString(err) << endl; return false; } cout << "Status: Using GLEW " << glewGetString(GLEW_VERSION) << endl; vShader = 0; fShader = 0; program = createProgram("shader/vertex.vs", "shader/fragment.fs", vShader, program); if(program == 0) { if(vShader != 0) { glDeleteShader(vShader); } if(fShader != 0) { glDeleteShader(fShader); } glfwDestroyWindow(window); glfwTerminate(); return false; } postVShader = 0; postFShader = 0; postProgram = createProgram("shader/postVertex.vs", "shader/postFragment.fs", postVShader, postFShader); if(postProgram == 0) { if(postVShader != 0) { glDeleteShader(postVShader); } if(postFShader != 0) { glDeleteShader(postFShader); } glfwDestroyWindow(window); glfwTerminate(); return false; } glfwSetKeyCallback(window, onKeyEvent); glfwSetMouseButtonCallback(window, onMouseClick); glfwSetFramebufferSizeCallback(window, onWindowResize); //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); //glfwSetCursorPosCallback(window, onMouseMove); return true; } void Engine::start(IClient* client) { if(client != nullptr) { Engine::client = client; } generateFramebuffer(); generatePostData(); glEnable(GL_CULL_FACE); glDepthFunc(GL_LEQUAL); uint64_t newTime = glfwGetTimerValue(); uint64_t oldTime = newTime; uint64_t lag = 0; while(!glfwWindowShouldClose(window)) { oldTime = newTime; newTime = glfwGetTimerValue(); lag += newTime - oldTime; int ticksPerFrame = 0; while(lag >= NANOS_PER_TICK) { lag -= NANOS_PER_TICK; Engine::client->tick(); ticksPerFrame++; if(ticksPerFrame >= MAX_TICKS_PER_FRAME) { long skip = lag / NANOS_PER_TICK; lag -= skip * NANOS_PER_TICK; if(skip > 0) { cout << "skipped " << skip << " game ticks " << lag << endl; } break; } } // draw scene in framebuffer glUseProgram(program); glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); Engine::client->renderTick((float) lag / NANOS_PER_TICK); // draw textured framebuffer glUseProgram(postProgram); glBindFramebuffer(GL_FRAMEBUFFER, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDisable(GL_DEPTH_TEST); glBindTexture(GL_TEXTURE_2D, frameTexture); glBindVertexArray(postVba); glBindBuffer(GL_ARRAY_BUFFER, postVbo); glDrawArrays(GL_TRIANGLES, 0, 6); glfwSwapBuffers(window); glfwPollEvents(); } destroyPostData(); glDeleteFramebuffers(1, &frameBuffer); glDeleteShader(vShader); glDeleteShader(fShader); glDeleteProgram(program); glDeleteShader(postVShader); glDeleteShader(postFShader); glDeleteProgram(postProgram); glfwDestroyWindow(window); glfwTerminate(); } void Engine::stop() { glfwSetWindowShouldClose(window, 1); } GLchar* Engine::readFile(const char* name) { ifstream in; in.open(name); if(!in.fail()) { int size = 128; int index = 0; GLchar* content = new GLchar[size]; while(true) { GLchar c = in.get(); if(in.eof()) { break; } if(index >= size - 1) { GLchar* newContent = new GLchar[size * 2]; memcpy(newContent, content, size); size *= 2; delete[] content; content = newContent; } content[index] = c; index++; } content[index] = '\0'; index++; in.close(); return content; } return nullptr; } bool Engine::checkShaderErrors(const char* name, GLuint shader) { bool returnValue = false; cout << "compiling " << name << " shader ..." << endl; GLenum error = glGetError(); if(error) { cout << "error: " << glGetError() << endl; returnValue = true; } else { cout << "no error occured ..." << endl; } GLint compiled[1]; glGetShaderiv(shader, GL_COMPILE_STATUS, compiled); if(compiled[0]) { cout << name << " shader successfully compiled" << endl; } else { cout << name << "compiling of " << name << " failed:" << endl; GLchar buffer[512]; GLsizei bufferSize = 512; GLsizei charsUsed = 0; glGetShaderInfoLog(shader, bufferSize, &charsUsed, buffer); // glGetProgramInfoLog should be null terminated ... buffer[bufferSize - 1] = '\0'; cout << buffer << endl; returnValue = true; } return returnValue; } GLuint Engine::compileProgram(GLuint& vShader, GLuint& fShader, const GLchar* v, const GLchar* f) { vShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vShader, 1, &v, nullptr); glCompileShader(vShader); if(checkShaderErrors("vertex", vShader)) { return 0; } fShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fShader, 1, &f, nullptr); glCompileShader(fShader); if(checkShaderErrors("fragment", fShader)) { return 0; } GLuint program = glCreateProgram(); glAttachShader(program, vShader); glAttachShader(program, fShader); glLinkProgram(program); cout << "linking shaders to program ..." << endl; GLenum error = glGetError(); if(error) { cout << "error: " << glGetError() << endl; return 0; } else { cout << "no error occured ..." << endl; } GLint compiled[1]; glGetProgramiv(program, GL_LINK_STATUS, compiled); if(compiled[0]) { cout << "shaders successfully linked" << endl; } else { cout << "linking of shaders failed:" << endl; GLchar buffer[512]; GLsizei bufferSize = 512; GLsizei charsUsed = 0; glGetProgramInfoLog(program, bufferSize, &charsUsed, buffer); // glGetProgramInfoLog should be null terminated ... buffer[bufferSize - 1] = '\0'; cout << buffer << endl; return 0; } return program; } GLuint Engine::createProgram(const char* v, const char* f, GLuint& vShader, GLuint& fShader) { GLchar* vertex = readFile(v); if(vertex == nullptr) { cout << "cannot read " << v << endl; return 0; } GLchar* fragment = readFile(f); if(fragment == nullptr) { cout << "cannot read " << f << endl; delete[] vertex; return 0; } GLuint program = compileProgram(vShader, fShader, vertex, fragment); delete[] vertex; delete[] fragment; return program; } void Engine::onKeyEvent(GLFWwindow* w, int key, int scancode, int action, int mods) { client->onKeyEvent(key, scancode, action, mods); } void Engine::onMouseClick(GLFWwindow* w, int button, int action, int mods) { client->onMouseClick(button, action, mods); } void Engine::onWindowResize(GLFWwindow* w, int width, int height) { glViewport(0, 0, width, height); Engine::width = width; Engine::height = height; updateScale(); } void Engine::updateScale() { scale = 1; while(width / (scale + 1) >= 400 && height / (scale + 1) >= 300) { scale++; } } int Engine::getScale() { return scale; } int Engine::getWidth() { return width; } int Engine::getHeight() { return height; } GLint Engine::getUniformLocation(const GLchar* name) { return glGetUniformLocation(program, name); } void Engine::setMatrix(GLint location, const GLfloat* m) { glUniformMatrix4fv(location, 1, 0, m); } void Engine::setInt(GLint location, GLint i) { glUniform1i(location, i); } void Engine::setFloat(GLint location, GLfloat f1, GLfloat f2, GLfloat f3, GLfloat f4) { glUniform4f(location, f1, f2, f3, f4); } void Engine::printError() { GLenum error = glGetError(); switch(error) { case GL_NO_ERROR: //cout << "> No error has been recorded." << endl; break; case GL_INVALID_ENUM: cout << "> An unacceptable value is specified for an enumerated argument." << endl; break; case GL_INVALID_VALUE: cout << "> A numeric argument is out of range." << endl; break; case GL_INVALID_OPERATION: cout << "> The specified operation is not allowed in the current state." << endl; break; case GL_INVALID_FRAMEBUFFER_OPERATION: cout << "> The framebuffer object is not complete." << endl; break; case GL_OUT_OF_MEMORY: cout << "> There is not enough memory left to execute the command." << endl; break; case GL_STACK_UNDERFLOW: cout << "> An attempt has been made to perform an operation that would cause an internal stack to underflow." << endl; break; case GL_STACK_OVERFLOW: cout << "> An attempt has been made to perform an operation that would cause an internal stack to overflow." << endl; break; default: cout << "> Unknown OpenGL error: " << error << endl; } } void Engine::generateFramebuffer() { glGenFramebuffers(1, &frameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); glGenTextures(1, &frameTexture); glBindTexture(GL_TEXTURE_2D, frameTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, frameTexture, 0); glGenRenderbuffers(1, &renderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); glBindRenderbuffer(GL_RENDERBUFFER, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer); if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl; } else { cout << "WORKS" << endl; } glBindFramebuffer(GL_FRAMEBUFFER, 0); } void Engine::generatePostData() { glGenVertexArrays(1, &postVba); glBindVertexArray(postVba); glGenBuffers(1, &postVbo); glBindBuffer(GL_ARRAY_BUFFER, postVbo); glVertexAttribPointer(0, 2, GL_FLOAT, 0, sizeof(float) * 4, (GLvoid*) 0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, 0, sizeof(float) * 4, (GLvoid*) (sizeof(float) * 2)); glEnableVertexAttribArray(1); float data[] = { -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f }; glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 24, data, GL_STATIC_DRAW); } void Engine::destroyPostData() { glDeleteVertexArrays(1, &postVba); glDeleteBuffers(1, &postVbo); }