Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision 5192672b1c0c73942402fdf976ffca72ebdbf08d)
+++ vulkan-game.cpp	(revision 20e4c2b3628aa61e3a41469fcb866c3100801fe7)
@@ -12,7 +12,4 @@
 
 #include "utils.hpp"
-
-#include "gui/main-screen.hpp"
-#include "gui/game-screen.hpp"
 
 using namespace std;
@@ -71,4 +68,7 @@
    laser_VP_mats = {};
    explosion_UBO = {};
+
+   score = 0;
+   fps = 0.0f;
 }
 
@@ -77,4 +77,6 @@
 
 void VulkanGame::run(int width, int height, unsigned char guiFlags) {
+   // TODO: Maybe call the init code in the constructor instead of in run()
+   // Research this
    seedRandomNums();
 
@@ -83,35 +85,25 @@
    cout << "Vulkan Game" << endl;
 
-   this->score = 0;
-
+   // TODO: Move IMGUI initialization in here
    if (initUI(width, height, guiFlags) == RTWO_ERROR) {
       return;
    }
 
-   // TODO: Maybe make a struct of properties to share with each screen instead of passing
-   // in all of VulkanGame
-   screens[SCREEN_MAIN] = new MainScreen(*renderer, *this);
-   screens[SCREEN_GAME] = new GameScreen(*renderer, *this);
-
-   currentScreen = screens[SCREEN_MAIN];
-
    initVulkan();
-   mainLoop();
+
+   ImGuiIO& io = ImGui::GetIO();
+
+   currentRenderScreenFn = &VulkanGame::renderMainScreen;
+
+   initGuiValueLists(valueLists);
+
+   valueLists["stats value list"].push_back(UIValue(UIVALUE_INT, "Score", &score));
+   valueLists["stats value list"].push_back(UIValue(UIVALUE_DOUBLE, "FPS", &fps));
+   valueLists["stats value list"].push_back(UIValue(UIVALUE_DOUBLE, "IMGUI FPS", &io.Framerate));
+
+   renderLoop();
    cleanup();
 
    close_log();
-}
-
-void VulkanGame::goToScreen(Screen* screen) {
-   currentScreen = screen;
-   currentScreen->init();
-
-   // TODO: Maybe just set shouldRecreateSwapChain to true instead. Check this render loop logic
-   // to make sure there'd be no issues
-   recreateSwapChain();
-}
-
-void VulkanGame::quitGame() {
-   done = true;
 }
 
@@ -151,4 +143,5 @@
       cout << "UI library could not be initialized!" << endl;
       cout << gui->getError() << endl;
+      // TODO: Rename RTWO_ERROR to something else
       return RTWO_ERROR;
    }
@@ -163,39 +156,4 @@
    cout << "Target window size: (" << width << ", " << height << ")" << endl;
    cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
-
-   renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
-   if (renderer == nullptr) {
-      cout << "Renderer could not be created!" << endl;
-      cout << gui->getError() << endl;
-      return RTWO_ERROR;
-   }
-
-   uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
-      gui->getWindowWidth(), gui->getWindowHeight());
-
-   if (uiOverlay == nullptr) {
-      cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << endl;
-      return RTWO_ERROR;
-   }
-   if (SDL_SetTextureBlendMode(uiOverlay, SDL_BLENDMODE_BLEND) != 0) {
-      cout << "Unable to set texture blend mode! SDL Error: " << SDL_GetError() << endl;
-      return RTWO_ERROR;
-   }
-
-   SDL_SetRenderTarget(renderer, uiOverlay);
-
-   // TODO: Print the filename of the font in the error message
-
-   lazyFont = TTF_OpenFont("assets/fonts/lazy.ttf", 28);
-   if (lazyFont == nullptr) {
-      cout << "Failed to load lazy font! SDL_ttf Error: " << TTF_GetError() << endl;
-      return RTWO_ERROR;
-   }
-
-   proggyFont = TTF_OpenFont("assets/fonts/ProggyClean.ttf", 16);
-   if (proggyFont == nullptr) {
-      cout << "Failed to load proggy font! SDL_ttf Error: " << TTF_GetError() << endl;
-      return RTWO_ERROR;
-   }
 
    return RTWO_SUCCESS;
@@ -320,25 +278,4 @@
 
    initGraphicsPipelines();
-
-   overlayPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&OverlayVertex::pos));
-   overlayPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&OverlayVertex::texCoord));
-
-   overlayPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
-      VK_SHADER_STAGE_FRAGMENT_BIT, &sdlOverlayImageDescriptor);
-
-   addObject(overlayObjects, overlayPipeline,
-      {
-         {{-1.0f,  1.0f,  0.0f}, {0.0f, 1.0f}},
-         {{ 1.0f,  1.0f,  0.0f}, {1.0f, 1.0f}},
-         {{ 1.0f, -1.0f,  0.0f}, {1.0f, 0.0f}},
-         {{-1.0f, -1.0f,  0.0f}, {0.0f, 0.0f}}
-      }, {
-         0, 1, 2, 2, 3, 0
-      }, {}, false);
-
-   overlayPipeline.createDescriptorSetLayout();
-   overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
-   overlayPipeline.createDescriptorPool(swapChainImages);
-   overlayPipeline.createDescriptorSets(swapChainImages);
 
    modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::pos));
@@ -713,8 +650,4 @@
 
 void VulkanGame::initGraphicsPipelines() {
-   overlayPipeline = GraphicsPipeline_Vulkan<OverlayVertex, void*>(
-      VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
-      { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 4, 6, 0);
-
    modelPipeline = GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject>(
       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
@@ -772,10 +705,10 @@
 }
 
-void VulkanGame::mainLoop() {
-   this->startTime = high_resolution_clock::now();
-   curTime = duration<float, seconds::period>(high_resolution_clock::now() - this->startTime).count();
-
-   this->fpsStartTime = curTime;
-   this->frameCount = 0;
+void VulkanGame::renderLoop() {
+   startTime = high_resolution_clock::now();
+   curTime = duration<float, seconds::period>(high_resolution_clock::now() - startTime).count();
+
+   fpsStartTime = curTime;
+   frameCount = 0;
 
    lastSpawn_asteroid = curTime;
@@ -921,5 +854,5 @@
          }
 
-         currentScreen->handleEvent(e);
+         // currentScreen->handleEvent(e);
       }
 
@@ -973,12 +906,4 @@
       }
 
-      currentScreen->renderUI();
-
-      // Copy the UI image to a vulkan texture
-      // TODO: I'm pretty sure this severely slows down the pipeline since this functions waits for the copy to be
-      // complete before continuing. See if I can find a more efficient method.
-      VulkanUtils::populateVulkanImageFromSDLTexture(device, physicalDevice, resourceCommandPool, uiOverlay, renderer,
-         sdlOverlayImage, graphicsQueue);
-
       updateScene();
 
@@ -987,28 +912,7 @@
       ImGui::NewFrame();
 
-      {
-         ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
-         ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
-         ImGui::Begin("WndMenubar", NULL,
-            ImGuiWindowFlags_NoTitleBar |
-            ImGuiWindowFlags_NoResize |
-            ImGuiWindowFlags_NoMove);
-         ImGui::InvisibleButton("", ImVec2(155, 18));
-         ImGui::SameLine();
-         if (ImGui::Button("Main Menu")) {
-            cout << "Clicked on the main button" << endl;
-            //events.push(Event::GO_TO_MAIN_MENU);
-         }
-         ImGui::End();
-      }
+      (this->*currentRenderScreenFn)();
 
       ImGui::Render();
-
-      // There is already code in renderFrame to render screen-specific things
-      // The imgui code above should be moved to the renderUI function of the particular screen it's needed in
-      // and currentScreen->renderUI should be called in renderFrame.
-      // Since I am no longer drawing the UI to an sdl texture and then rendering that, I don't have to worry
-      // about calling populateVulkanImageFromSDLTexture at all.
-      // If I do ever want to do that again, I can still actually do it inside renderFrame
 
       gui->refreshWindowSize();
@@ -1227,5 +1131,4 @@
    cleanupSwapChain();
 
-   VulkanUtils::destroyVulkanImage(device, sdlOverlayImage);
    VulkanUtils::destroyVulkanImage(device, floorTextureImage);
    VulkanUtils::destroyVulkanImage(device, laserTextureImage);
@@ -1234,5 +1137,4 @@
 
    modelPipeline.cleanupBuffers();
-   overlayPipeline.cleanupBuffers();
    shipPipeline.cleanupBuffers();
    asteroidPipeline.cleanupBuffers();
@@ -1250,25 +1152,4 @@
 
    vkDestroyInstance(instance, nullptr);
-
-   delete screens[SCREEN_MAIN];
-   delete screens[SCREEN_GAME];
-
-   if (lazyFont != nullptr) {
-      TTF_CloseFont(lazyFont);
-      lazyFont = nullptr;
-   }
-
-   if (proggyFont != nullptr) {
-      TTF_CloseFont(proggyFont);
-      proggyFont = nullptr;
-   }
-
-   if (uiOverlay != nullptr) {
-      SDL_DestroyTexture(uiOverlay);
-      uiOverlay = nullptr;
-   }
-
-   SDL_DestroyRenderer(renderer);
-   renderer = nullptr;
 
    gui->destroyWindow();
@@ -1668,11 +1549,4 @@
    // TODO: Move all images/textures somewhere into the assets folder
 
-   VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
-
-   sdlOverlayImageDescriptor = {};
-   sdlOverlayImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
-   sdlOverlayImageDescriptor.imageView = sdlOverlayImage.imageView;
-   sdlOverlayImageDescriptor.sampler = textureSampler;
-
    VulkanUtils::createVulkanImageFromFile(device, physicalDevice, resourceCommandPool, "textures/texture.jpg",
       floorTextureImage, graphicsQueue);
@@ -1805,5 +1679,12 @@
    vkCmdBeginRenderPass(commandBuffers[imageIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
 
-   currentScreen->createRenderCommands(commandBuffers[imageIndex], imageIndex);
+   // TODO: Find a more elegant, per-screen solution for this
+   if (currentRenderScreenFn == &VulkanGame::renderGameScreen) {
+      modelPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
+      shipPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
+      asteroidPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
+      laserPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
+      explosionPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
+   }
 
    ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffers[imageIndex]);
@@ -2202,9 +2083,4 @@
    modelPipeline.createDescriptorSets(swapChainImages);
 
-   overlayPipeline.updateRenderPass(renderPass);
-   overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
-   overlayPipeline.createDescriptorPool(swapChainImages);
-   overlayPipeline.createDescriptorSets(swapChainImages);
-
    createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
       uniformBuffers_shipPipeline, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
@@ -2258,5 +2134,4 @@
    }
 
-   overlayPipeline.cleanup();
    modelPipeline.cleanup();
    shipPipeline.cleanup();
@@ -2304,2 +2179,131 @@
    vkDestroySwapchainKHR(device, swapChain, nullptr);
 }
+
+void VulkanGame::renderMainScreen() {
+   unsigned int windowWidth = 640;
+   unsigned int windowHeight = 480;
+
+   {
+      int padding = 4;
+      ImGui::SetNextWindowPos(ImVec2(-padding, -padding), ImGuiCond_Once);
+      ImGui::SetNextWindowSize(ImVec2(windowWidth + 2 * padding, windowHeight + 2 * padding), ImGuiCond_Always);
+      ImGui::Begin("WndMain", nullptr,
+         ImGuiWindowFlags_NoTitleBar |
+         ImGuiWindowFlags_NoResize |
+         ImGuiWindowFlags_NoMove);
+
+      ImGui::InvisibleButton("", ImVec2(10, 80));
+      ImGui::InvisibleButton("", ImVec2(285, 18));
+      ImGui::SameLine();
+      if (ImGui::Button("New Game")) {
+         goToScreen(&VulkanGame::renderGameScreen);
+      }
+
+      ImGui::InvisibleButton("", ImVec2(10, 15));
+      ImGui::InvisibleButton("", ImVec2(300, 18));
+      ImGui::SameLine();
+      if (ImGui::Button("Quit")) {
+         quitGame();
+      }
+
+      ImGui::End();
+   }
+}
+
+void VulkanGame::renderGameScreen() {
+   {
+      ImGui::SetNextWindowSize(ImVec2(130, 65), ImGuiCond_Once);
+      ImGui::SetNextWindowPos(ImVec2(10, 50), ImGuiCond_Once);
+      ImGui::Begin("WndStats", nullptr,
+         ImGuiWindowFlags_NoTitleBar |
+         ImGuiWindowFlags_NoResize |
+         ImGuiWindowFlags_NoMove);
+
+      //ImGui::Text(ImGui::GetIO().Framerate);
+      renderGuiValueList(valueLists["stats value list"]);
+
+      ImGui::End();
+   }
+
+   {
+      ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
+      ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
+      ImGui::Begin("WndMenubar", nullptr,
+         ImGuiWindowFlags_NoTitleBar |
+         ImGuiWindowFlags_NoResize |
+         ImGuiWindowFlags_NoMove);
+      ImGui::InvisibleButton("", ImVec2(155, 18));
+      ImGui::SameLine();
+      if (ImGui::Button("Main Menu")) {
+         goToScreen(&VulkanGame::renderMainScreen);
+      }
+      ImGui::End();
+   }
+
+   {
+      ImGui::SetNextWindowSize(ImVec2(200, 200), ImGuiCond_Once);
+      ImGui::SetNextWindowPos(ImVec2(430, 60), ImGuiCond_Once);
+      ImGui::Begin("WndDebug", nullptr,
+         ImGuiWindowFlags_NoTitleBar |
+         ImGuiWindowFlags_NoResize |
+         ImGuiWindowFlags_NoMove);
+
+      renderGuiValueList(valueLists["debug value list"]);
+
+      ImGui::End();
+   }
+}
+
+void VulkanGame::initGuiValueLists(map<string, vector<UIValue>>& valueLists) {
+   valueLists["stats value list"] = vector<UIValue>();
+   valueLists["debug value list"] = vector<UIValue>();
+}
+
+void VulkanGame::renderGuiValueList(vector<UIValue>& values) {
+   float maxWidth = 0.0f;
+   float cursorStartPos = ImGui::GetCursorPosX();
+
+   for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
+      float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
+
+      if (maxWidth < textWidth)
+         maxWidth = textWidth;
+   }
+
+   stringstream ss;
+
+   // TODO: Possibly implement this based on gui/ui-value.hpp instead and use templates
+   // to keep track of the type. This should make it a bit easier to use and maintain
+   // Also, implement this in a way that's agnostic to the UI renderer.
+   for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
+      ss.str("");
+      ss.clear();
+
+      switch (it->type) {
+      case UIVALUE_INT:
+         ss << it->label << ": " << *(unsigned int*)it->value;
+         break;
+      case UIVALUE_DOUBLE:
+         ss << it->label << ": " << *(double*)it->value;
+         break;
+      }
+
+      float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
+
+      ImGui::SetCursorPosX(cursorStartPos + maxWidth - textWidth);
+      //ImGui::Text("%s", ss.str().c_str());
+      ImGui::Text("%s: %.1f", it->label.c_str(), *(float*)it->value);
+   }
+}
+
+void VulkanGame::goToScreen(void (VulkanGame::* renderScreenFn)()) {
+   currentRenderScreenFn = renderScreenFn;
+
+   // TODO: Maybe just set shouldRecreateSwapChain to true instead. Check this render loop logic
+   // to make sure there'd be no issues
+   //recreateSwapChain();
+}
+
+void VulkanGame::quitGame() {
+   done = true;
+}
