#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; 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(vShader, program); if(program == 0) { if(vShader != 0) { glDeleteShader(vShader); } if(fShader != 0) { glDeleteShader(fShader); } 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) { Engine::client = &client; glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); uint64_t newTime = glfwGetTimerValue(); uint64_t oldTime = newTime; uint64_t lag = 0; while(!glfwWindowShouldClose(window)) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 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; } } Engine::client->renderTick((float) lag / NANOS_PER_TICK); glfwSwapBuffers(window); glfwPollEvents(); } glDeleteShader(vShader); glDeleteShader(fShader); glDeleteProgram(program); 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* vertex, const GLchar* fragment) { vShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vShader, 1, &vertex, nullptr); glCompileShader(vShader); if(checkShaderErrors("vertex", vShader)) { return 0; } fShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fShader, 1, &fragment, 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; } glUseProgram(program); return program; } GLuint Engine::createProgram(GLuint& vShader, GLuint& fShader) { GLchar* vertex = readFile("shader/vertex.vs"); if(vertex == nullptr) { cout << "cannot read vertex.vs" << endl; return 0; } GLchar* fragment = readFile("shader/fragment.fs"); if(fragment == nullptr) { cout << "cannot read fragment.fs" << 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; } }