diff options
author | rtk0c <[email protected]> | 2022-04-25 20:22:07 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2022-04-25 20:22:07 -0700 |
commit | 855da86feae1a5cc14dc2d486ccf115f484dbc2e (patch) | |
tree | 8284c6a6bdfb1a919eb1a22f466f4180a329c7f3 /source/main.cpp | |
parent | d78a55de5003dbb040f1d1c369409e63a2c806d8 (diff) |
Changeset: 16 Initial work on rendering sprites to screen
Diffstat (limited to 'source/main.cpp')
-rw-r--r-- | source/main.cpp | 304 |
1 files changed, 250 insertions, 54 deletions
diff --git a/source/main.cpp b/source/main.cpp index b166754..99a11c0 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,9 +1,10 @@ #include "App.hpp" #include "AppConfig.hpp" -#include "EditorNotification.hpp" #include "Ires.hpp" -#include "Mesh.hpp" +#include "Material.hpp" +#include "Shader.hpp" +#include "VertexIndex.hpp" #define GLFW_INCLUDE_NONE #include <GLFW/glfw3.h> @@ -13,47 +14,86 @@ #include <backends/imgui_impl_opengl3.h> #include <glad/glad.h> #include <imgui.h> +#include <imgui_internal.h> #include <cstdlib> #include <cxxopts.hpp> #include <filesystem> #include <string> namespace fs = std::filesystem; +using namespace std::literals; -static void GlfwErrorCallback(int error, const char* description) { - fprintf(stderr, "Glfw Error %d: %s\n", error, description); +struct GlfwUserData { + App* app = nullptr; +}; + +void GlfwErrorCallback(int error, const char* description) { + fprintf(stderr, "[GLFW] Error %d: %s\n", error, description); +} + +void GlfwFramebufferResizeCallback(GLFWwindow* window, int width, int height) { + AppConfig::mainWidnowWidth = width; + AppConfig::mainWindowHeight = height; + AppConfig::mainWindowAspectRatio = (float)width / height; } -static void GlfwMouseCallback(GLFWwindow* window, int button, int action, int mods) { +void GlfwMouseCallback(GLFWwindow* window, int button, int action, int mods) { if (ImGui::GetIO().WantCaptureMouse) { return; } - App* app = static_cast<App*>(glfwGetWindowUserPointer(window)); + auto userData = static_cast<GlfwUserData*>(glfwGetWindowUserPointer(window)); + auto app = userData->app; app->HandleMouse(button, action); } -static void GlfwMouseMotionCallback(GLFWwindow* window, double xOff, double yOff) { +void GlfwMouseMotionCallback(GLFWwindow* window, double xOff, double yOff) { if (ImGui::GetIO().WantCaptureMouse) { return; } - App* app = static_cast<App*>(glfwGetWindowUserPointer(window)); + auto userData = static_cast<GlfwUserData*>(glfwGetWindowUserPointer(window)); + auto app = userData->app; app->HandleMouseMotion(xOff, yOff); } -static void GlfwKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { +void GlfwKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (ImGui::GetIO().WantCaptureKeyboard) { return; } GLFWkeyboard* keyboard = glfwGetLastActiveKeyboard(); if (keyboard) { - App* app = static_cast<App*>(glfwGetWindowUserPointer(window)); + auto userData = static_cast<GlfwUserData*>(glfwGetWindowUserPointer(window)); + auto app = userData->app; app->HandleKey(keyboard, key, action); } } +// https://stackoverflow.com/questions/54499256/how-to-find-the-saved-games-folder-programmatically-in-c-c +#if defined(_WIN32) +# if defined(__MINGW32__) +# include <ShlObj.h> +# else +# include <ShlObj_core.h> +# endif +# include <objbase.h> +# pragma comment(lib, "shell32.lib") +# pragma comment(lib, "ole32.lib") +#elif defined(__linux__) +fs::path GetEnvVar(const char* name, const char* backup) { + if (const char* path = std::getenv(name)) { + fs::path dataDir(path); + fs::create_directories(dataDir); + return dataDir; + } else { + fs::path dataDir(backup); + fs::create_directories(dataDir); + return dataDir; + } +} +#endif + int main(int argc, char* argv[]) { using namespace Tags; @@ -72,7 +112,7 @@ int main(int argc, char* argv[]) { auto args = options.parse(argc, argv); bool imguiUseOpenGL3; - { + if (args.count(kImGuiBackend) > 0) { auto imguiBackend = args[kImGuiBackend].as<std::string>(); if (imguiBackend == "opengl2") { imguiUseOpenGL3 = false; @@ -80,11 +120,13 @@ int main(int argc, char* argv[]) { imguiUseOpenGL3 = true; } else { // TODO support more backends? - imguiUseOpenGL3 = false; + imguiUseOpenGL3 = true; } + } else { + imguiUseOpenGL3 = true; } - { + if (args.count(kGameAssetDir) > 0) { auto assetDir = args[kGameAssetDir].as<std::string>(); fs::path assetDirPath(assetDir); @@ -95,6 +137,31 @@ int main(int argc, char* argv[]) { AppConfig::assetDir = std::move(assetDir); AppConfig::assetDirPath = std::move(assetDirPath); + } else { +#if defined(_WIN32) + fs::path dataDir; + + PWSTR path = nullptr; + HRESULT hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, nullptr, &path); + if (SUCCEEDED(hr)) { + dataDir = fs::path(path) / AppConfig::kAppName; + CoTaskMemFree(path); + + fs::create_directories(dataDir); + } else { + std::string msg; + msg += "Failed to find/create the default user data directory at %APPDATA%. Error code: "; + msg += hr; + throw std::runtime_error(msg); + } +#elif defined(__APPLE__) + // MacOS programming guide recommends apps to hardcode the path - user customization of "where data are stored" is done in Finder + auto dataDir = fs::path("~/Library/Application Support/") / AppConfig::kAppName; + fs::create_directories(dataDir); +#elif defined(__linux__) + auto dataDir = GetEnvVar("XDG_DATA_HOME", "~/.local/share") / AppConfig::kAppName; + fs::create_directories(dataDir); +#endif } if (args.count(kGameDataDir) > 0) { @@ -118,13 +185,7 @@ int main(int argc, char* argv[]) { glfwSetErrorCallback(&GlfwErrorCallback); // Decide GL+GLSL versions -#if defined(IMGUI_IMPL_OPENGL_ES2) - // GL ES 2.0 + GLSL 100 - const char* imguiGlslVersion = "#version 100"; - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); -#elif defined(__APPLE__) +#if defined(__APPLE__) // GL 3.2 + GLSL 150 const char* imguiGlslVersion = "#version 150"; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); @@ -132,29 +193,37 @@ int main(int argc, char* argv[]) { glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac #else - // GL 3.0 + GLSL 130 + // GL 3.3 + GLSL 130 const char* imguiGlslVersion = "#version 130"; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); #endif - App app; + GlfwUserData glfwUserData; GLFWwindow* window = glfwCreateWindow(1280, 720, "ImGui Command Palette Example", nullptr, nullptr); if (window == nullptr) { return -2; } - glfwSetWindowUserPointer(window, &app); + glfwSetWindowUserPointer(window, &glfwUserData); // Window callbacks are retained by ImGui GLFW backend + glfwSetFramebufferSizeCallback(window, &GlfwFramebufferResizeCallback); glfwSetKeyCallback(window, &GlfwKeyCallback); glfwSetMouseButtonCallback(window, &GlfwMouseCallback); glfwSetCursorPosCallback(window, &GlfwMouseMotionCallback); + { + int width, height; + glfwGetFramebufferSize(window, &width, &height); + GlfwFramebufferResizeCallback(window, width, height); + } + glfwMakeContextCurrent(window); glfwSwapInterval(1); + // TODO setup opengl debug context if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { return -3; } @@ -169,6 +238,9 @@ int main(int argc, char* argv[]) { ImGui_ImplOpenGL2_Init(); } + auto& io = ImGui::GetIO(); + auto& ctx = *ImGui::GetCurrentContext(); + IresManager::instance = new IresManager(); IresManager::instance->DiscoverFilesDesignatedLocation(); @@ -206,45 +278,169 @@ int main(int argc, char* argv[]) { .semantic = VES_Color1, }); - app.Init(); - while (!glfwWindowShouldClose(window)) { - glfwPollEvents(); + // Matches gVformatStandardPacked + gDefaultShader.Attach(new Shader()); + gDefaultShader->InitFromSources(Shader::ShaderSources{ + .vertex = R"""( +#version 330 core +layout(location = 0) in vec3 pos; +layout(location = 1) in vec2 texcoord; +layout(location = 2) in vec4 color; +out vec4 v2fColor; +uniform mat4 transformation; +void main() { + gl_Position = transformation * vec4(pos, 1.0); + v2fColor = color; +} +)"""sv, + .fragment = R"""( +#version 330 core +in vec4 v2fColor; +out vec4 fragColor; +void main() { + fragColor = v2fColor; +} +)"""sv, + }); + { // in vec3 pos; + ShaderMathVariable var; + var.scalarType = GL_FLOAT; + var.width = 1; + var.height = 3; + var.arrayLength = 1; + var.semantic = VES_Position; + var.location = 0; + gDefaultShader->GetInfo().inputs.push_back(std::move(var)); + gDefaultShader->GetInfo().things.try_emplace( + "pos"s, + ShaderThingId{ + .kind = ShaderThingId::KD_Input, + .index = (int)gDefaultShader->GetInfo().inputs.size() - 1, + }); + } + { // in vec2 texcoord; + ShaderMathVariable var; + var.scalarType = GL_FLOAT; + var.width = 1; + var.height = 2; + var.arrayLength = 1; + var.semantic = VES_TexCoords1; + var.location = 1; + gDefaultShader->GetInfo().inputs.push_back(std::move(var)); + gDefaultShader->GetInfo().things.try_emplace( + "texcoord"s, + ShaderThingId{ + .kind = ShaderThingId::KD_Input, + .index = (int)gDefaultShader->GetInfo().inputs.size() - 1, + }); + } + { // in vec4 color; + ShaderMathVariable var; + var.scalarType = GL_FLOAT; + var.width = 1; + var.height = 4; + var.arrayLength = 1; + var.semantic = VES_Color1; + var.location = 2; + gDefaultShader->GetInfo().inputs.push_back(std::move(var)); + gDefaultShader->GetInfo().things.try_emplace( + "color"s, + ShaderThingId{ + .kind = ShaderThingId::KD_Input, + .index = (int)gDefaultShader->GetInfo().inputs.size() - 1, + }); + } + { // out vec4 fragColor; + ShaderMathVariable var; + var.scalarType = GL_FLOAT; + var.width = 1; + var.height = 4; + var.arrayLength = 1; + gDefaultShader->GetInfo().outputs.push_back(std::move(var)); + gDefaultShader->GetInfo().things.try_emplace( + "fragColor"s, + ShaderThingId{ + .kind = ShaderThingId::KD_Output, + .index = (int)gDefaultShader->GetInfo().outputs.size() - 1, + }); + } + // NOTE: autofill uniforms not recorded here - int fbWidth, fbHeight; - glfwGetFramebufferSize(window, &fbWidth, &fbHeight); - glViewport(0, 0, fbWidth, fbHeight); - auto clearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - glClearColor(clearColor.x * clearColor.w, clearColor.y * clearColor.w, clearColor.z * clearColor.w, clearColor.w); - glClear(GL_COLOR_BUFFER_BIT); + gDefaultMaterial.Attach(new Material()); + gDefaultMaterial->SetShader(gDefaultShader.Get()); - { // Regular draw - app.Update(); - app.Draw(); - } + { // Main loop + App app; + glfwUserData.app = &app; + + // 60 updates per second + constexpr double kMsPerUpdate = 1000.0 / 60; + constexpr double kSecondsPerUpdate = kMsPerUpdate / 1000; + double prevTime = glfwGetTime(); + double accumulatedTime = 0.0; + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + + double currTime = glfwGetTime(); + double deltaTime = prevTime - currTime; - { // ImGui stuff - if (imguiUseOpenGL3) { - ImGui_ImplOpenGL3_NewFrame(); - } else { - ImGui_ImplOpenGL2_NewFrame(); + // In seconds + accumulatedTime += currTime - prevTime; + + // Update + // Play "catch up" to ensure a deterministic number of Update()'s per second + while (accumulatedTime >= kSecondsPerUpdate) { + double beg = glfwGetTime(); + { + app.Update(); + } + double end = glfwGetTime(); + + // Update is taking longer than it should be, start skipping updates + auto diff = end - beg; + if (diff >= kSecondsPerUpdate) { + auto skippedUpdates = (int)(accumulatedTime / kSecondsPerUpdate); + accumulatedTime = 0.0; + fprintf(stderr, "Elapsed time %f, skipped %d updates.", diff, skippedUpdates); + } else { + accumulatedTime -= kSecondsPerUpdate; + } } - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - app.Show(); - ImGui::ShowNotifications(); + int fbWidth = AppConfig::mainWidnowWidth; + int fbHeight = AppConfig::mainWindowHeight; + glfwGetFramebufferSize(window, &fbWidth, &fbHeight); + glViewport(0, 0, fbWidth, fbHeight); + auto clearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + glClearColor(clearColor.x * clearColor.w, clearColor.y * clearColor.w, clearColor.z * clearColor.w, clearColor.w); + glClear(GL_COLOR_BUFFER_BIT); - ImGui::Render(); - if (imguiUseOpenGL3) { - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - } else { - ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); + { // Regular draw + app.Draw(currTime, deltaTime); } - } - glfwSwapBuffers(window); + { // ImGui draw + if (imguiUseOpenGL3) { + ImGui_ImplOpenGL3_NewFrame(); + } else { + ImGui_ImplOpenGL2_NewFrame(); + } + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + app.Show(); + + ImGui::Render(); + if (imguiUseOpenGL3) { + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + } else { + ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); + } + } + + glfwSwapBuffers(window); + } } - app.Shutdown(); if (imguiUseOpenGL3) { ImGui_ImplOpenGL3_Shutdown(); |