#include "Wrapper.h" DummyClient DummyClient::dummy; IClient* Engine::client = &DummyClient::dummy; GLFWwindow* Engine::window = nullptr; // active program GLuint Engine::activeProgram = 0; // world data ShaderProgram Engine::worldShader; GLuint Engine::worldFrameBuffer = 0; GLuint Engine::worldPositionTexture = 0; GLuint Engine::worldNormalTexture = 0; GLuint Engine::worldColorTexture = 0; GLuint Engine::worldDepthRenderBuffer = 0; // post shader GLuint Engine::postVba = 0; GLuint Engine::postVbo = 0; ShaderProgram Engine::postShader; int Engine::scale = 1; int Engine::width = 0; int Engine::height = 0; bool Engine::lineMode = false; float Engine::testX = 0; float Engine::testY = 0; float Engine::testZ = 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; worldShader.compile("shader/worldVertex.vs", "shader/worldFragment.fs"); if(!worldShader.isValid()) { glfwDestroyWindow(window); glfwTerminate(); return false; } activeProgram = worldShader.getProgram(); postShader.compile("shader/postVertex.vs", "shader/postFragment.fs"); if(!postShader.isValid()) { 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; } onInit(); 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; } } onRenderTick((float) lag / NANOS_PER_TICK); glfwSwapBuffers(window); glfwPollEvents(); } onTerm(); glfwDestroyWindow(window); glfwTerminate(); } void Engine::stop() { glfwSetWindowShouldClose(window, 1); } 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(); /*glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); glBindTexture(GL_TEXTURE_2D, frameTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glBindTexture(GL_TEXTURE_2D, 0); glBindRenderbuffer(GL_RENDERBUFFER, depthTexture); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);*/ } 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(activeProgram, 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::onInit() { // generate framebuffer and textures for world buffer glGenFramebuffers(1, &worldFrameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, worldFrameBuffer); // world position texture glGenTextures(1, &worldPositionTexture); glBindTexture(GL_TEXTURE_2D, worldPositionTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, worldPositionTexture, 0); // world normal texture glGenTextures(1, &worldNormalTexture); glBindTexture(GL_TEXTURE_2D, worldNormalTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, worldNormalTexture, 0); // world color texture glGenTextures(1, &worldColorTexture); glBindTexture(GL_TEXTURE_2D, worldColorTexture); 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_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, worldColorTexture, 0); // set color attachements for the worldFrameBuffer GLuint attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; glDrawBuffers(3, attachments); // generate depth render buffer glGenRenderbuffers(1, &worldDepthRenderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, worldDepthRenderBuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, worldDepthRenderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, 0); // check if world framebuffer is okay if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { cout << "world frame buffer is not complete!" << endl; } glBindFramebuffer(GL_FRAMEBUFFER, 0); // generate data for drawing previously generated framebuffer as rectangle 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::onRenderTick(float lag) { //-------------------------------------------------------------------------- // Stage 1: draw scene in world framebuffer //-------------------------------------------------------------------------- glActiveTexture(GL_TEXTURE0); activeProgram = worldShader.getProgram(); glUseProgram(activeProgram); glBindFramebuffer(GL_FRAMEBUFFER, worldFrameBuffer); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glEnable(GL_DEPTH_TEST); if(lineMode) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); Engine::client->renderTick(lag); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } else { Engine::client->renderTick(lag); } //-------------------------------------------------------------------------- // Stage 2: draw textured framebuffer //-------------------------------------------------------------------------- glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, worldPositionTexture); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, worldNormalTexture); glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, worldColorTexture); activeProgram = postShader.getProgram(); glUseProgram(activeProgram); glUniform3f(glGetUniformLocation(activeProgram, "viewPos"), testX, testY, testZ); glBindFramebuffer(GL_FRAMEBUFFER, 0); glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_DEPTH_TEST); glBindVertexArray(postVba); glBindBuffer(GL_ARRAY_BUFFER, postVbo); glDrawArrays(GL_TRIANGLES, 0, 6); } void Engine::onTerm() { glDeleteVertexArrays(1, &postVba); glDeleteBuffers(1, &postVbo); glDeleteFramebuffers(1, &worldFrameBuffer); glDeleteTextures(1, &worldPositionTexture); glDeleteTextures(1, &worldNormalTexture); glDeleteTextures(1, &worldColorTexture); glDeleteRenderbuffers(1, &worldDepthRenderBuffer); } void Engine::setLineMode(bool mode) { lineMode = mode; }