Changeset 40eb092 in opengl-game


Ignore:
Timestamp:
Mar 9, 2021, 2:47:00 AM (3 years ago)
Author:
Dmitry Portnoy <dportnoy@…>
Branches:
feature/imgui-sdl
Children:
20e4c2b
Parents:
429ac01
Message:

In SDLGame, implement the game UI using ImGui

Files:
2 edited

Legend:

Unmodified
Added
Removed
  • sdl-game.cpp

    r429ac01 r40eb092  
    44#include <iostream>
    55#include <set>
    6 #include <stdexcept>
    7 
    8 #include <SDL2/SDL_vulkan.h>
    96
    107#include "IMGUI/imgui_impl_sdl.h"
     
    5451
    5552   shouldRecreateSwapChain = false;
     53
     54   score = 0;
     55   fps = 0.0f;
    5656}
    5757
     
    6060
    6161void VulkanGame::run(int width, int height, unsigned char guiFlags) {
     62   // TODO: Maybe call the init code in the constructor instead of in run()
     63   // Research this
    6264   cout << "DEBUGGING IS " << (ENABLE_VALIDATION_LAYERS ? "ON" : "OFF") << endl;
    6365
    6466   cout << "Vulkan Game" << endl;
    6567
     68   // TODO: Move IMGUI initialization in here
    6669   if (initUI(width, height, guiFlags) == RTWO_ERROR) {
    6770      return;
     
    152155   }
    153156
    154    done = false;
    155    while (!done) {
    156 
    157       gui->processEvents();
    158 
    159       UIEvent uiEvent;
    160       while (gui->pollEvent(&uiEvent)) {
    161          GameEvent& e = uiEvent.event;
    162          SDL_Event sdlEvent = uiEvent.rawEvent.sdl;
    163 
    164          ImGui_ImplSDL2_ProcessEvent(&sdlEvent);
    165          if (io.WantCaptureMouse &&
    166                (e.type == UI_EVENT_MOUSEBUTTONDOWN || e.type == UI_EVENT_MOUSEBUTTONUP || e.type == UI_EVENT_UNKNOWN)) {
    167             if (sdlEvent.type == SDL_MOUSEWHEEL || sdlEvent.type == SDL_MOUSEBUTTONDOWN || sdlEvent.type == SDL_MOUSEBUTTONUP) {
    168                continue;
    169             }
    170          }
    171          if (io.WantCaptureKeyboard &&
    172                (e.type == UI_EVENT_KEYDOWN || e.type == UI_EVENT_KEYUP)) {
    173             if (sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP) {
    174                continue;
    175             }
    176          }
    177          if (io.WantTextInput) {
    178             // show onscreen keyboard if on mobile
    179          }
    180 
    181          if (e.type == UI_EVENT_QUIT) {
    182             done = true;
    183          }
    184       }
    185 
    186       if (shouldRecreateSwapChain) {
    187          gui->refreshWindowSize();
    188          const bool isMinimized = gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0;
    189 
    190          if (!isMinimized) {
    191             // TODO: This should be used if the min image count changes, presumably because a new surface was created
    192             // with a different image count or something like that. Maybe I want to add code to query for a new min image count
    193             // during swapchain recreation to take advantage of this
    194             ImGui_ImplVulkan_SetMinImageCount(swapChainMinImageCount);
    195 
    196             recreateSwapChain();
    197 
    198             imageIndex = 0;
    199             shouldRecreateSwapChain = false;
    200          }
    201       }
    202 
    203       // Start the Dear ImGui frame
    204       ImGui_ImplVulkan_NewFrame();
    205       ImGui_ImplSDL2_NewFrame(window);
    206       ImGui::NewFrame();
    207 
    208       // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
    209       {
    210          static int counter = 0;
    211 
    212          ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.
    213 
    214          ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
    215 
    216          if (ImGui::Button("Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated)
    217             counter++;
    218          ImGui::SameLine();
    219          ImGui::Text("counter = %d", counter);
    220 
    221          ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
    222          ImGui::End();
    223       }
    224 
    225       ImGui::Render();
    226 
    227       gui->refreshWindowSize();
    228       const bool isMinimized = gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0;
    229 
    230       if (!isMinimized) {
    231          renderFrame(ImGui::GetDrawData());
    232          presentFrame();
    233       }
    234    }
    235 
     157   currentRenderScreenFn = &VulkanGame::renderMainScreen;
     158
     159   initGuiValueLists(valueLists);
     160
     161   valueLists["stats value list"].push_back(UIValue(UIVALUE_INT, "Score", &score));
     162   valueLists["stats value list"].push_back(UIValue(UIVALUE_DOUBLE, "FPS", &fps));
     163   valueLists["stats value list"].push_back(UIValue(UIVALUE_DOUBLE, "IMGUI FPS", &io.Framerate));
     164
     165   renderLoop();
    236166   cleanup();
    237167
     
    321251
    322252   createSyncObjects();
     253}
     254
     255void VulkanGame::renderLoop() {
     256   startTime = high_resolution_clock::now();
     257   curTime = duration<float, seconds::period>(high_resolution_clock::now() - startTime).count();
     258
     259   fpsStartTime = curTime;
     260   frameCount = 0;
     261
     262   ImGuiIO& io = ImGui::GetIO();
     263
     264   done = false;
     265   while (!done) {
     266
     267      curTime = duration<float, seconds::period>(high_resolution_clock::now() - startTime).count();
     268
     269      if (curTime - fpsStartTime >= 1.0f) {
     270         fps = (float)frameCount / (curTime - fpsStartTime);
     271
     272         frameCount = 0;
     273         fpsStartTime = curTime;
     274      }
     275
     276      frameCount++;
     277
     278      gui->processEvents();
     279
     280      UIEvent uiEvent;
     281      while (gui->pollEvent(&uiEvent)) {
     282         GameEvent& e = uiEvent.event;
     283         SDL_Event sdlEvent = uiEvent.rawEvent.sdl;
     284
     285         ImGui_ImplSDL2_ProcessEvent(&sdlEvent);
     286         if (io.WantCaptureMouse &&
     287            (e.type == UI_EVENT_MOUSEBUTTONDOWN || e.type == UI_EVENT_MOUSEBUTTONUP || e.type == UI_EVENT_UNKNOWN)) {
     288            if (sdlEvent.type == SDL_MOUSEWHEEL || sdlEvent.type == SDL_MOUSEBUTTONDOWN || sdlEvent.type == SDL_MOUSEBUTTONUP) {
     289               continue;
     290            }
     291         }
     292         if (io.WantCaptureKeyboard &&
     293            (e.type == UI_EVENT_KEYDOWN || e.type == UI_EVENT_KEYUP)) {
     294            if (sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP) {
     295               continue;
     296            }
     297         }
     298         if (io.WantTextInput) {
     299            // show onscreen keyboard if on mobile
     300         }
     301
     302         switch (e.type) {
     303         case UI_EVENT_MOUSEMOTION:
     304            // Currently unused
     305            break;
     306         case UI_EVENT_WINDOW:
     307            // Currently unused
     308            break;
     309         case UI_EVENT_QUIT:
     310            cout << "Quit event detected" << endl;
     311            done = true;
     312            break;
     313         case UI_EVENT_UNHANDLED:
     314            cout << "Unhandled event type: 0x" << hex << sdlEvent.type << dec << endl;
     315            break;
     316         case UI_EVENT_UNKNOWN:
     317         default:
     318            cout << "Unknown event type: 0x" << hex << sdlEvent.type << dec << endl;
     319            break;
     320         }
     321      }
     322
     323      if (shouldRecreateSwapChain) {
     324         gui->refreshWindowSize();
     325         const bool isMinimized = gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0;
     326
     327         if (!isMinimized) {
     328            // TODO: This should be used if the min image count changes, presumably because a new surface was created
     329            // with a different image count or something like that. Maybe I want to add code to query for a new min image count
     330            // during swapchain recreation to take advantage of this
     331            ImGui_ImplVulkan_SetMinImageCount(swapChainMinImageCount);
     332
     333            recreateSwapChain();
     334
     335            imageIndex = 0;
     336            shouldRecreateSwapChain = false;
     337         }
     338      }
     339
     340      // Start the Dear ImGui frame
     341      ImGui_ImplVulkan_NewFrame();
     342      ImGui_ImplSDL2_NewFrame(window);
     343      ImGui::NewFrame();
     344
     345      (this->*currentRenderScreenFn)();
     346
     347      ImGui::Render();
     348
     349      gui->refreshWindowSize();
     350      const bool isMinimized = gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0;
     351
     352      if (!isMinimized) {
     353         renderFrame(ImGui::GetDrawData());
     354         presentFrame();
     355      }
     356   }
    323357}
    324358
     
    964998   vkDestroySwapchainKHR(device, swapChain, nullptr);
    965999}
     1000
     1001void VulkanGame::renderMainScreen() {
     1002   unsigned int windowWidth = 640;
     1003   unsigned int windowHeight = 480;
     1004
     1005   {
     1006      int padding = 4;
     1007      ImGui::SetNextWindowPos(ImVec2(-padding, -padding), ImGuiCond_Once);
     1008      ImGui::SetNextWindowSize(ImVec2(windowWidth + 2 * padding, windowHeight + 2 * padding), ImGuiCond_Always);
     1009      ImGui::Begin("WndMain", nullptr,
     1010         ImGuiWindowFlags_NoTitleBar |
     1011         ImGuiWindowFlags_NoResize |
     1012         ImGuiWindowFlags_NoMove);
     1013
     1014      ImGui::InvisibleButton("", ImVec2(10, 80));
     1015      ImGui::InvisibleButton("", ImVec2(285, 18));
     1016      ImGui::SameLine();
     1017      if (ImGui::Button("New Game")) {
     1018         goToScreen(&VulkanGame::renderGameScreen);
     1019      }
     1020
     1021      ImGui::InvisibleButton("", ImVec2(10, 15));
     1022      ImGui::InvisibleButton("", ImVec2(300, 18));
     1023      ImGui::SameLine();
     1024      if (ImGui::Button("Quit")) {
     1025         quitGame();
     1026      }
     1027
     1028      ImGui::End();
     1029   }
     1030}
     1031
     1032void VulkanGame::renderGameScreen() {
     1033   {
     1034      ImGui::SetNextWindowSize(ImVec2(130, 65), ImGuiCond_Once);
     1035      ImGui::SetNextWindowPos(ImVec2(10, 50), ImGuiCond_Once);
     1036      ImGui::Begin("WndStats", nullptr,
     1037         ImGuiWindowFlags_NoTitleBar |
     1038         ImGuiWindowFlags_NoResize |
     1039         ImGuiWindowFlags_NoMove);
     1040
     1041      //ImGui::Text(ImGui::GetIO().Framerate);
     1042      renderGuiValueList(valueLists["stats value list"]);
     1043
     1044      ImGui::End();
     1045   }
     1046
     1047   {
     1048      ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
     1049      ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
     1050      ImGui::Begin("WndMenubar", nullptr,
     1051         ImGuiWindowFlags_NoTitleBar |
     1052         ImGuiWindowFlags_NoResize |
     1053         ImGuiWindowFlags_NoMove);
     1054      ImGui::InvisibleButton("", ImVec2(155, 18));
     1055      ImGui::SameLine();
     1056      if (ImGui::Button("Main Menu")) {
     1057         goToScreen(&VulkanGame::renderMainScreen);
     1058      }
     1059      ImGui::End();
     1060   }
     1061
     1062   {
     1063      ImGui::SetNextWindowSize(ImVec2(200, 200), ImGuiCond_Once);
     1064      ImGui::SetNextWindowPos(ImVec2(430, 60), ImGuiCond_Once);
     1065      ImGui::Begin("WndDebug", nullptr,
     1066         ImGuiWindowFlags_NoTitleBar |
     1067         ImGuiWindowFlags_NoResize |
     1068         ImGuiWindowFlags_NoMove);
     1069
     1070      renderGuiValueList(valueLists["debug value list"]);
     1071
     1072      ImGui::End();
     1073   }
     1074}
     1075
     1076void VulkanGame::initGuiValueLists(map<string, vector<UIValue>>& valueLists) {
     1077   valueLists["stats value list"] = vector<UIValue>();
     1078   valueLists["debug value list"] = vector<UIValue>();
     1079}
     1080
     1081void VulkanGame::renderGuiValueList(vector<UIValue>& values) {
     1082   float maxWidth = 0.0f;
     1083   float cursorStartPos = ImGui::GetCursorPosX();
     1084
     1085   for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
     1086      float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
     1087
     1088      if (maxWidth < textWidth)
     1089         maxWidth = textWidth;
     1090   }
     1091
     1092   stringstream ss;
     1093
     1094   // TODO: Possibly implement this based on gui/ui-value.hpp instead and use templates
     1095   // to keep track of the type. This should make it a bit easier to use and maintain
     1096   // Also, implement this in a way that's agnostic to the UI renderer.
     1097   for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
     1098      ss.str("");
     1099      ss.clear();
     1100
     1101      switch (it->type) {
     1102      case UIVALUE_INT:
     1103         ss << it->label << ": " << *(unsigned int*)it->value;
     1104         break;
     1105      case UIVALUE_DOUBLE:
     1106         ss << it->label << ": " << *(double*)it->value;
     1107         break;
     1108      }
     1109
     1110      float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
     1111
     1112      ImGui::SetCursorPosX(cursorStartPos + maxWidth - textWidth);
     1113      //ImGui::Text("%s", ss.str().c_str());
     1114      ImGui::Text("%s: %.1f", it->label.c_str(), *(float*)it->value);
     1115   }
     1116}
     1117
     1118void VulkanGame::goToScreen(void (VulkanGame::* renderScreenFn)()) {
     1119   currentRenderScreenFn = renderScreenFn;
     1120}
     1121
     1122void VulkanGame::quitGame() {
     1123   done = true;
     1124}
  • sdl-game.hpp

    r429ac01 r40eb092  
    22#define _SDL_GAME_H
    33
     4#include <chrono>
     5#include <map>
    46#include <vector>
     7
    58#include <vulkan/vulkan.h>
    69
     
    1518
    1619using namespace std;
     20using namespace std::chrono;
    1721
    1822#define VulkanGame NewVulkanGame
     
    2327   const bool ENABLE_VALIDATION_LAYERS = true;
    2428#endif
     29
     30// TODO: Maybe move this to a different header
     31
     32enum UIValueType {
     33   UIVALUE_INT,
     34   UIVALUE_DOUBLE,
     35};
     36
     37struct UIValue {
     38   UIValueType type;
     39   string label;
     40   void* value;
     41
     42   UIValue(UIValueType _type, string _label, void* _value) : type(_type), label(_label), value(_value) {}
     43};
    2544
    2645class VulkanGame {
     
    88107      bool shouldRecreateSwapChain;
    89108
    90       // My code, but not complete. Skips creating the SDL renderer, probably because it doesn't use hardware acceleration.
    91       // I should try to get uncapped framerate and compare performance w/ and w/out an SDL renderer
     109      /*** High-level vars ***/
     110
     111      void (VulkanGame::* currentRenderScreenFn)();
     112
     113      map<string, vector<UIValue>> valueLists;
     114
     115      int score;
     116      float fps;
     117
     118      // TODO: Make a separate singleton Timer class
     119      // It could also deal with the steady_clock vs high_resolution_clock issue
     120      time_point<steady_clock> startTime;
     121      float fpsStartTime, curTime;
     122
     123      int frameCount;
     124
     125      /*** Functions ***/
     126
    92127      bool initUI(int width, int height, unsigned char guiFlags);
    93       void initVulkan(); // Mostly example code
    94       void cleanup(); // Mostly example
     128      void initVulkan();
     129      void renderLoop();
     130      void cleanup();
    95131
    96132      void createVulkanInstance(const vector<const char*>& validationLayers);
     
    120156      void cleanupSwapChain();
    121157
     158      /*** High-level functions ***/
     159
     160      void renderMainScreen();
     161      void renderGameScreen();
     162
     163      void initGuiValueLists(map<string, vector<UIValue>>& valueLists);
     164      void renderGuiValueList(vector<UIValue>& values);
     165
     166      void goToScreen(void (VulkanGame::* renderScreenFn)());
     167      void quitGame();
     168
    122169      // Pipeline variables. Hopefully, I can eventually use the GraphicsPipeline_Vulkan class for the imgui pipeline
    123170      VkDescriptorPool descriptorPool;
    124 
    125171};
    126172
Note: See TracChangeset for help on using the changeset viewer.