#include "engine.h" #include #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION #include #include #include "camera.h" #include "constants.h" #include "defaults.h" #include "errordialog.h" #include "gameobject.h" #include "objparser.h" #include "physics.h" #include "resource.h" #include "scene.h" #include "ui.h" #include "uielements.h" #include "internal.h" #include "internal/input.h" #include "internal/ui.h" kek::KekData kek::kekData; namespace kek::Engine { static TextElement *fpsText; static void framebufferSizeCallback(GLFWwindow *window, int w, int h) { glViewport(0, 0, w, h); kekData.screenWidth = w; kekData.screenHeight = h; } static void glDebugOutput(GLenum source, GLenum type, unsigned int id, GLenum severity, GLsizei length, const char *message, const void *userParam) { // if(id == 0x20071) return; if(severity == GL_DEBUG_SEVERITY_NOTIFICATION) return; std::cout << "OpenGL Debug (" << id << "): " << message << std::endl; } int init() { // Init GLFW if(glfwInit() != GL_TRUE) { const char *errorMsg; int code = glfwGetError(&errorMsg); if(code != GLFW_NO_ERROR) { ErrorDialog::showError("Failed to initialize GLFW: " + std::string(errorMsg) + std::string(" (") + std::to_string(code) + std::string(")")); } else { ErrorDialog::showError("Failed to initialize GLFW: Unknown error"); } return KEK_ERROR; } std::cout << "Initialized GLFW" << std::endl; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_SAMPLES, 4); glfwWindowHint(GLFW_MAXIMIZED, GL_TRUE); glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); kekData.screenWidth = 800.0f; kekData.screenHeight = 600.0f; kekData.window = glfwCreateWindow(kekData.screenWidth, kekData.screenHeight, "KekEngine", nullptr, nullptr); if(!kekData.window) { const char *errorMsg; int code = glfwGetError(&errorMsg); if(code != GLFW_NO_ERROR) { ErrorDialog::showError("Failed to create window: " + std::string(errorMsg) + " (" + std::to_string(code) + ")"); } else { ErrorDialog::showError("Failed to create window: Unknown error"); } glfwTerminate(); return KEK_ERROR; } glfwMakeContextCurrent(kekData.window); std::cout << "Initialized window" << std::endl; // Init GLEW glewExperimental = GL_TRUE; if(glewInit() != GLEW_OK) { ErrorDialog::showError("Failed to initialize GLEW"); glfwTerminate(); return KEK_ERROR; } std::cout << "Initialized GLEW" << std::endl; std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl; int flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags); if(flags & GL_CONTEXT_FLAG_DEBUG_BIT) { glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback(glDebugOutput, nullptr); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); } glViewport(0, 0, kekData.screenWidth, kekData.screenHeight); glfwSetFramebufferSizeCallback(kekData.window, framebufferSizeCallback); glEnable(GL_DEPTH_TEST); glEnable(GL_MULTISAMPLE); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glClearColor(0.1f, 0.3f, 0.1f, 0.0f); // glfwSwapInterval(0); stbi_set_flip_vertically_on_load(true); if(Resource::init() != KEK_SUCCESS) { glfwTerminate(); return KEK_ERROR; } FT_Error error = FT_Init_FreeType(&kekData.freetype); if(error) { ErrorDialog::showError("Failed to initialize FreeType: " + std::string(FT_Error_String(error))); glfwTerminate(); return KEK_ERROR; } UI::init(); Input::init(); Physics::init(); Defaults::init(); MemoryBuffer *buf = Resource::loadResource("image/icon.png"); if(buf) { int width, height, nrChannels; unsigned char *data = stbi_load_from_memory((stbi_uc *) buf->buffer, buf->length, &width, &height, &nrChannels, 0); GLFWimage image; image.width = width; image.height = height; image.pixels = data; glfwSetWindowIcon(kekData.window, 1, &image); delete buf; } kekData.activeCamera = new Camera(); kekData.shader = new Shader("shader/mesh/vertex.glsl", "shader/mesh/fragment.glsl"); kekData.shader->initLighting(); glfwSetCursorPosCallback(kekData.window, Input::onCursorPosCallback); Input::setCursorMode(GLFWCursorMode::CAPTURE); glfwSetKeyCallback(kekData.window, Input::onKeyCallback); glfwSetCharCallback(kekData.window, Input::onKeyCharCallback); glfwSetMouseButtonCallback(kekData.window, Input::onMouseButtonCallback); fpsText = new TextElement(uiPx(0), uiPx(0)); UI::addElement(fpsText); return KEK_SUCCESS; } int start() { int prevTime = 0; while(!glfwWindowShouldClose(kekData.window)) { auto start = std::chrono::high_resolution_clock::now(); // Clear the screen glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, kekData.screenWidth, kekData.screenHeight); Physics::step(kekData.lastFrameTime); for(auto cb : kekData.input->periodicCallbacks) { cb.second(kekData.window); } if(kekData.activeScene) { kekData.shader->use(); glm::mat4 view = kekData.activeCamera->transformationMatrix(); glm::mat4 projection; projection = glm::perspective(glm::radians(90.0f), kekData.screenWidth / (float) kekData.screenHeight, KEK_CAMERA_NEAR, KEK_CAMERA_FAR); glm::vec3 position = kekData.activeCamera->getPosition(); glUniformMatrix4fv(glGetUniformLocation(kekData.shader->id, "view"), 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(glGetUniformLocation(kekData.shader->id, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); glUniform3fv(glGetUniformLocation(kekData.shader->id, "cameraPos"), 1, glm::value_ptr(position)); if(kekData.shader->lighting) { LightList *lights = kekData.activeScene->lights; std::vector shaderLights; // TODO: Maybe don't compute on every frame for(PointLight *light : lights->point) { if(glm::length2(glm::abs(light->getPosition() - kekData.activeCamera->getPosition())) < KEK_LIGHT_MAX_DISTANCE_SQUARED) { shaderLights.push_back(light); } } for(DirectionalLight *light : lights->directional) { shaderLights.push_back(light); } for(SpotLight *light : lights->spot) { if(glm::length2(glm::abs(light->getPosition() - kekData.activeCamera->getPosition())) < KEK_LIGHT_MAX_DISTANCE_SQUARED) { shaderLights.push_back(light); } } unsigned int i = 0; int numPointLights = 0, numDirectionalLights = 0, numSpotLights = 0; for(Light *light : shaderLights) { ShaderLight shLight; memcpy(shLight.color, glm::value_ptr(light->color), sizeof(shLight.color)); switch(light->getType()) { case LightType::POINT: { PointLight *l = (PointLight *) light; memcpy(&shLight.position, glm::value_ptr(l->getPosition()), sizeof(shLight.position)); memcpy(&shLight.attenuation, glm::value_ptr(glm::vec3(l->constant, l->linear, l->quadratic)), sizeof(shLight.attenuation)); numPointLights++; break; } case LightType::DIRECTIONAL: { DirectionalLight *l = (DirectionalLight *) light; memcpy(&shLight.direction, glm::value_ptr(l->direction), sizeof(shLight.direction)); numDirectionalLights++; break; } case LightType::SPOT: { SpotLight *l = (SpotLight *) light; memcpy(&shLight.position, glm::value_ptr(l->getPosition()), sizeof(shLight.position)); memcpy(&shLight.direction, glm::value_ptr(l->direction), sizeof(shLight.direction)); memcpy(&shLight.attenuation, glm::value_ptr(glm::vec3(l->constant, l->linear, l->quadratic)), sizeof(shLight.attenuation)); memcpy(&shLight.cutoff, glm::value_ptr(glm::vec2(glm::cos(l->innerCutoff), glm::cos(l->outerCutoff))), sizeof(shLight.cutoff)); numSpotLights++; break; } } kekData.shader->lighting->buffer[i] = shLight; i++; } glNamedBufferData(kekData.shader->lighting->ubo, kekData.shader->lighting->blockSize, &kekData.shader->lighting->buffer, GL_DYNAMIC_DRAW); glBindBufferBase(GL_UNIFORM_BUFFER, KEK_UNIFORM_LIGHTS_BINDING, kekData.shader->lighting->ubo); glUniform1i(glGetUniformLocation(kekData.shader->id, "numPointLights"), numPointLights); glUniform1i(glGetUniformLocation(kekData.shader->id, "numDirectionalLights"), numDirectionalLights); glUniform1i(glGetUniformLocation(kekData.shader->id, "numSpotLights"), numSpotLights); } kekData.activeScene->draw(kekData.shader); } glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glm::mat4 uiProjection = glm::ortho(0.0f, (float) kekData.screenWidth, (float) kekData.screenHeight, 0.0f); int time = (int) (glfwGetTime() * 10); if(time != prevTime) fpsText->setText("FPS: " + std::to_string((int) floor(1.0f / kekData.lastFrameTime)) + " (" + std::to_string(kekData.lastFrameTime * 1000) + " ms)"); prevTime = time; for(UIElement *uiEl : kekData.ui->elements) { UIPoint pos = uiEl->getScreenPosition(); uiEl->drawAll(pos, uiProjection); } glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); // Swap buffers and poll window events glfwSwapBuffers(kekData.window); glfwPollEvents(); auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration secsTaken = end - start; kekData.lastFrameTime = secsTaken.count(); } return KEK_SUCCESS; } void exit() { Defaults::destroy(); Physics::destroy(); Input::destroy(); UI::destroy(); Resource::destroy(); FT_Done_FreeType(kekData.freetype); } void setActiveScene(Scene *scene) { kekData.activeScene = scene; } Scene *getActiveScene() { return kekData.activeScene; } Player *getPlayer() { return kekData.player; } Camera *getActiveCamera() { return kekData.activeCamera; } }