Index: consts.hpp
===================================================================
--- consts.hpp	(revision 4e705d63b9aa0c0a2a6029899904ebf814b6f543)
+++ consts.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -13,3 +13,8 @@
 constexpr unsigned char GUI_FLAGS_WINDOW_FULLSCREEN { 1 << 0 };
 
+enum ScreenType {
+   SCREEN_MAIN,
+   SCREEN_GAME
+};
+
 #endif // _RTWO_CONSTS_H
Index: game-gui.hpp
===================================================================
--- game-gui.hpp	(revision 4e705d63b9aa0c0a2a6029899904ebf814b6f543)
+++ game-gui.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -10,4 +10,6 @@
 
 using namespace std;
+
+// TODO: See if it makes sense to combine this with the files in the gui folder
 
 enum EventType {
@@ -54,4 +56,6 @@
 };
 
+// TODO: Switch from union to std::variant
+
 union UIEvent {
    EventType type;
Index: gui/button.cpp
===================================================================
--- gui/button.cpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
+++ gui/button.cpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -0,0 +1,119 @@
+#include "button.hpp"
+
+#include <SDL2/SDL_ttf.h>
+#include <SDL2/SDL2_gfxPrimitives.h>
+
+#include "../vulkan-game.hpp"
+
+// TODO: Figure out a good way to return errors instead of just printing them
+// Probably throw an exception
+// Make sure to cleanup anythign that was initialized correctly before the error
+// TODO: Support better positioning options (e.g. align to left, right, top, bottom, center,
+// and offsets from those positions)
+Button::Button(string label, int x, int y, int padding, uint32_t color, uint32_t textColor,
+      VulkanGame& gameInfo, SDL_Renderer& renderer,
+      void (*onMouseClick)(VulkanGame& gameInfo),
+      void (*onMouseEnter)(UIElement& element),
+      void (*onMouseLeave)(UIElement& element)) :
+      UIElement(x, y, 0, 0, renderer, onMouseClick, onMouseEnter, onMouseLeave),
+      color(color),
+      focused(false),
+      gameInfo(gameInfo) {
+
+   SDL_Color sdlTextColor {
+      static_cast<Uint8>((textColor >> 24) & 0xFF),
+      static_cast<Uint8>((textColor >> 16) & 0xFF),
+      static_cast<Uint8>((textColor >> 8) & 0xFF),
+      static_cast<Uint8>(textColor & 0xFF)
+   };
+
+   SDL_Surface* labelSurface = TTF_RenderText_Blended(this->gameInfo.proggyFont, label.c_str(),
+      sdlTextColor);
+   if (labelSurface == nullptr) {
+      cout << "Unable to render text surface! SDL_ttf Error: " << TTF_GetError() << endl;
+   }
+
+   this->labelTexture = SDL_CreateTextureFromSurface(&this->renderer, labelSurface);
+   if (this->labelTexture == nullptr) {
+      cout << "Unable to create texture from rendered text! SDL Error: " << SDL_GetError() << endl;
+      // SDL_FreeSurface(labelSurface);
+   }
+
+   SDL_FreeSurface(labelSurface);
+
+   TTF_SizeText(this->gameInfo.proggyFont, label.c_str(), &this->labelWidth, &this->labelHeight);
+
+   this->width = this->labelWidth + padding;
+   this->height = this->labelHeight + padding;
+
+   uint32_t rgb = color & 0xFFFFFF00;
+   uint32_t a = color & 0xFF;
+
+   this->focusColor = (int)(rgb * 1.6) | a;
+}
+
+Button::~Button() {
+   if (this->labelTexture != nullptr) {
+      SDL_DestroyTexture(this->labelTexture);
+      this->labelTexture = nullptr;
+   }
+}
+
+void Button::init() {
+   this->focused = false;
+}
+
+void Button::render(int x, int y) {
+   uint32_t cur_color = this->focused ? this->focusColor : this->color;
+
+   uint8_t colorR = (cur_color >> 24) & 0xFF;
+   uint8_t colorG = (cur_color >> 16) & 0xFF;
+   uint8_t colorB = (cur_color >> 8) & 0xFF;
+   uint8_t colorA = cur_color & 0xFF;
+
+   boxRGBA(&this->renderer, this->x + x, this->y + y, this->x + this->width, this->y + this->height,
+      colorR, colorG, colorB, colorA);
+
+   SDL_Rect rect = {
+         this->x + (this->width - this->labelWidth) / 2 + x,
+         this->y + (this->height - this->labelHeight) / 2 + y,
+         this->labelWidth,
+         this->labelHeight
+   };
+
+   SDL_RenderCopy(&this->renderer, this->labelTexture, nullptr, &rect);
+}
+
+void Button::handleEvent(UIEvent& e) {
+   switch(e.type) {
+      case UI_EVENT_MOUSEMOTION:
+         if (this->x < e.mouse.x && e.mouse.x < this->x + this->width &&
+               this->y < e.mouse.y && e.mouse.y < this->y + this->height) {
+            if (!this->focused) {
+               this->focused = true;
+               if (this->onMouseEnter != nullptr) {
+                  this->onMouseEnter(*this);
+               }
+            }
+         } else if (this->focused) {
+            this->focused = false;
+            if (this->onMouseLeave != nullptr) {
+               this->onMouseLeave(*this);
+            }
+         }
+         break;
+      case UI_EVENT_MOUSEBUTTONDOWN:
+         break;
+      case UI_EVENT_MOUSEBUTTONUP:
+         if (this->x < e.mouse.x && e.mouse.x < this->x + this->width &&
+               this->y < e.mouse.y && e.mouse.y < this->y + this->height) {
+            if (this->onMouseClick != nullptr) {
+               this->onMouseClick(this->gameInfo);
+            }
+         }
+         break;
+      default:
+         //cout << "Unhandled UI event: " << e.type << endl;
+         break;
+   }
+}
Index: gui/button.hpp
===================================================================
--- gui/button.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
+++ gui/button.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -0,0 +1,33 @@
+#ifndef _BUTTON_HPP
+#define _BUTTON_HPP
+
+#include <string>
+
+#include <SDL2/SDL.h>
+
+#include "../game-gui.hpp"
+
+#include "ui-element.hpp"
+
+class Button : public UIElement {
+public:
+   Button(string label, int x, int y, int padding, uint32_t color, uint32_t textColor,
+      VulkanGame& gameInfo, SDL_Renderer& renderer,
+      void (*onMouseClick)(VulkanGame& gameInfo),
+      void (*onMouseEnter)(UIElement& element),
+      void (*onMouseLeave)(UIElement& element));
+   ~Button() override;
+
+   void init() override;
+   void render(int x, int y) override;
+   void handleEvent(UIEvent& e) override;
+
+private:
+   int labelWidth, labelHeight;
+   uint32_t color, focusColor;
+   bool focused;
+   SDL_Texture* labelTexture = nullptr;
+   VulkanGame& gameInfo;
+};
+
+#endif // _BUTTON_HPP
Index: gui/main-screen.cpp
===================================================================
--- gui/main-screen.cpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
+++ gui/main-screen.cpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -0,0 +1,33 @@
+#include "main-screen.hpp"
+
+#include "../vulkan-game.hpp"
+
+#include "button.hpp"
+
+MainScreen::MainScreen(SDL_Renderer& renderer, VulkanGame& gameInfo) :
+      Screen(renderer, gameInfo) {
+   addUIElement(new Button("New Game", 368, 131, 9, 0x222299FF, 0xFFFFFFFF, this->gameInfo,
+      this->renderer, newGame_onMouseClick, nullptr, nullptr));
+   addUIElement(new Button("Quit", 382, 186, 9, 0x222299FF, 0xFFFFFFFF, this->gameInfo,
+      this->renderer, quit_onMouseClick, nullptr, nullptr));
+}
+
+MainScreen::~MainScreen() {
+}
+
+void MainScreen::createRenderCommands(VkCommandBuffer& commandBuffer, uint32_t currentImage) {
+   // Always render this pipeline last
+   gameInfo.overlayPipeline.createRenderCommands(commandBuffer, currentImage);
+}
+
+void MainScreen::handleEvent(UIEvent& e) {
+   Screen::handleEvent(e);
+}
+
+void newGame_onMouseClick(VulkanGame& gameInfo) {
+   gameInfo.goToScreen(gameInfo.screens[SCREEN_GAME]);
+}
+
+void quit_onMouseClick(VulkanGame& gameInfo) {
+   gameInfo.quitGame();
+}
Index: gui/main-screen.hpp
===================================================================
--- gui/main-screen.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
+++ gui/main-screen.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -0,0 +1,21 @@
+#ifndef _MAIN_SCREEN_H
+#define _MAIN_SCREEN_H
+
+#include <map>
+
+#include "screen.hpp"
+
+class MainScreen : public Screen {
+   public:
+      MainScreen(SDL_Renderer& renderer, VulkanGame& gameInfo);
+      ~MainScreen() override;
+
+      void createRenderCommands(VkCommandBuffer& commandBuffer, uint32_t currentImage) override;
+
+      void handleEvent(UIEvent& e) override;
+};
+
+void newGame_onMouseClick(VulkanGame& gameInfo);
+void quit_onMouseClick(VulkanGame& gameInfo);
+
+#endif // _MAIN_SSCREEN_H
Index: gui/screen.cpp
===================================================================
--- gui/screen.cpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
+++ gui/screen.cpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -0,0 +1,39 @@
+#include "screen.hpp"
+
+#include "../vulkan-game.hpp"
+
+Screen::Screen(SDL_Renderer& renderer, VulkanGame& gameInfo) :
+      renderer(renderer),
+      gameInfo(gameInfo) {
+}
+
+Screen::~Screen() {
+   for (UIElement*& uiElement : this->uiElements) {
+      delete uiElement;
+   }
+}
+
+void Screen::init() {
+   for (UIElement*& uiElement : this->uiElements) {
+      uiElement->init();
+   }
+}
+
+void Screen::renderUI() {
+   SDL_SetRenderDrawColor(&this->renderer, 0x00, 0x00, 0x00, 0x00);
+   SDL_RenderClear(&this->renderer);
+
+   for (UIElement*& uiElement : this->uiElements) {
+      uiElement->render(0, 0);
+   }
+}
+
+void Screen::handleEvent(UIEvent& e) {
+   for (UIElement*& uiElement : this->uiElements) {
+      uiElement->handleEvent(e);
+   }
+}
+
+void Screen::addUIElement(UIElement* element) {
+   this->uiElements.push_back(element);
+}
Index: gui/screen.hpp
===================================================================
--- gui/screen.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
+++ gui/screen.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -0,0 +1,51 @@
+#ifndef _SCREEN_H
+#define _SCREEN_H
+
+#include <vector>
+
+#include <vulkan/vulkan.h>
+
+#include <SDL2/SDL.h>
+
+#include "../consts.hpp"
+//#include "../game-gui.hpp"
+
+#include "ui-element.hpp"
+
+using namespace std;
+
+class VulkanGame;
+
+template<class Type>
+struct ValueReference {
+   
+};
+
+// TODO: Add a function to create an SDL_Color from a uint32_t
+
+// TODO: Maybe make this a subclass of UIElement
+class Screen {
+public:
+   Screen(SDL_Renderer& renderer, VulkanGame& gameInfo);
+   virtual ~Screen();
+
+   virtual void createRenderCommands(VkCommandBuffer& commandBuffer, uint32_t currentImage) = 0;
+   virtual void init();
+   
+   virtual void renderUI();
+   virtual void handleEvent(UIEvent& e);
+   void addUIElement(UIElement* element);
+
+protected:
+   SDL_Renderer& renderer;
+   VulkanGame& gameInfo;
+
+private:
+   vector<UIElement*> uiElements;
+};
+
+// TODO: Maybe move these somewhere else
+void button_onMouseEnter(UIElement& element);
+void button_onMouseLeave(UIElement& element);
+
+#endif // _SCREEN_H
Index: gui/ui-element.cpp
===================================================================
--- gui/ui-element.cpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
+++ gui/ui-element.cpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -0,0 +1,24 @@
+#include "ui-element.hpp"
+
+UIElement::UIElement(int x, int y, int width, int height, SDL_Renderer& renderer,
+      void (*onMouseClick)(VulkanGame& gameInfo),
+      void (*onMouseEnter)(UIElement& element),
+      void (*onMouseLeave)(UIElement& element)) :
+      x(x),
+      y(y),
+      width(width),
+      height(height),
+      renderer(renderer),
+      onMouseClick(onMouseClick),
+      onMouseEnter(onMouseEnter),
+      onMouseLeave(onMouseLeave) {
+}
+
+UIElement::~UIElement() {
+}
+
+void UIElement::init() {
+}
+
+void UIElement::handleEvent(UIEvent& e) {
+}
Index: gui/ui-element.hpp
===================================================================
--- gui/ui-element.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
+++ gui/ui-element.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -0,0 +1,31 @@
+#ifndef _UI_ELEMENT_HPP
+#define _UI_ELEMENT_HPP
+
+#include <SDL2/SDL.h>
+
+#include "../game-gui.hpp"
+
+class VulkanGame;
+
+class UIElement {
+public:
+   UIElement(int x, int y, int width, int height, SDL_Renderer& renderer,
+      void (*onMouseClick)(VulkanGame& gameInfo),
+      void (*onMouseEnter)(UIElement& element),
+      void (*onMouseLeave)(UIElement& element));
+   virtual ~UIElement();
+
+   virtual void init();
+   virtual void render(int x, int y) = 0;
+   virtual void handleEvent(UIEvent& e);
+
+protected:
+   int x, y;
+   int width, height;
+   SDL_Renderer& renderer;
+   void (*onMouseClick)(VulkanGame& gameInfo);
+   void (*onMouseEnter)(UIElement& element);
+   void (*onMouseLeave)(UIElement& element);
+};
+
+#endif // _UI_ELEMENT_HPP
Index: makefile
===================================================================
--- makefile	(revision 4e705d63b9aa0c0a2a6029899904ebf814b6f543)
+++ makefile	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -45,5 +45,5 @@
 endif
 
-LIBS = `pkg-config --static --libs sdl2 sdl2_image sdl2_ttf`
+LIBS = `pkg-config --static --libs sdl2 sdl2_image sdl2_ttf sdl2_gfx`
 ifeq ($(OS),Darwin)
 	LIBS := $(VULKAN_SDK_PATH)/lib/libvulkan.dylib $(LIBS)
@@ -51,5 +51,5 @@
 ifeq ($(OS),Linux)
 	LIBS = `pkg-config --static --libs sdl2`
-	LIBS := -lvulkan $(LIBS) -lSDL2_image -lSDL2_ttf # TODO: figure out how to statically link these, ideally using pkg-config
+	LIBS := -lvulkan $(LIBS) -lSDL2_image -lSDL2_ttf -lSDL2_gfx # TODO: figure out how to statically link these, ideally using pkg-config
 endif
 
@@ -59,6 +59,9 @@
 	$(CC) $(CXX_FLAGS) -o $@ $^ $(LIB_FLAGS) -DGAMEGUI_INCLUDE_VULKAN
 
-SRC_FILES = main-vulkan.cpp vulkan-game.cpp crash-logger.cpp logger.cpp vulkan-utils.cpp utils.cpp game-gui-sdl.cpp
-HEADER_FILES = vulkan-game.hpp crash-logger.hpp logger.hpp vulkan-utils.hpp utils.hpp game-gui-sdl.hpp game-gui.hpp graphics-pipeline_vulkan.hpp
+GUI_SRC_FILES = gui/screen.cpp gui/main-screen.cpp gui/ui-element.cpp gui/button.cpp
+GUI_HEADER_FILES = gui/screen.hpp gui/main-screen.hpp gui/ui-element.hpp gui/button.hpp
+
+SRC_FILES = main-vulkan.cpp vulkan-game.cpp crash-logger.cpp logger.cpp vulkan-utils.cpp utils.cpp game-gui-sdl.cpp $(GUI_SRC_FILES)
+HEADER_FILES = vulkan-game.hpp crash-logger.hpp logger.hpp vulkan-utils.hpp utils.hpp game-gui-sdl.hpp game-gui.hpp graphics-pipeline_vulkan.hpp $(GUI_HEADER_FILES)
 
 vulkangame: $(SRC_FILES) $(HEADER_FILES)
Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision 4e705d63b9aa0c0a2a6029899904ebf814b6f543)
+++ vulkan-game.cpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -46,4 +46,8 @@
    }
 
+   screens[SCREEN_MAIN] = new MainScreen(*renderer, *this);
+
+   currentScreen = screens[SCREEN_MAIN];
+
    initVulkan();
    mainLoop();
@@ -51,4 +55,15 @@
 
    close_log();
+}
+
+void VulkanGame::goToScreen(Screen* screen) {
+   currentScreen = screen;
+   currentScreen->init();
+
+   recreateSwapChain();
+}
+
+void VulkanGame::quitGame() {
+   this->quit = true;
 }
 
@@ -121,4 +136,6 @@
    SDL_SetRenderTarget(renderer, uiOverlay);
 
+   // TODO: Print the filename of the font in the error message
+
    font = TTF_OpenFont("assets/fonts/lazy.ttf", 28);
    if (font == nullptr) {
@@ -157,4 +174,10 @@
 
    SDL_FreeSurface(imageSDLSurface);
+
+   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;
@@ -640,5 +663,5 @@
 void VulkanGame::mainLoop() {
    UIEvent e;
-   bool quit = false;
+   this->quit = false;
 
    this->startTime = high_resolution_clock::now();
@@ -647,5 +670,5 @@
    lastSpawn_asteroid = curTime;
 
-   while (!quit) {
+   while (!this->quit) {
 
       this->prevTime = curTime;
@@ -659,5 +682,5 @@
             case UI_EVENT_QUIT:
                cout << "Quit event detected" << endl;
-               quit = true;
+               this->quit = true;
                break;
             case UI_EVENT_WINDOW:
@@ -675,5 +698,5 @@
 
                if (e.key.keycode == SDL_SCANCODE_ESCAPE) {
-                  quit = true;
+                  this->quit = true;
                } else if (e.key.keycode == SDL_SCANCODE_SPACE) {
                   cout << "Adding a plane" << endl;
@@ -753,4 +776,6 @@
                cout << "Unhandled UI event: " << e.type << endl;
          }
+
+         currentScreen->handleEvent(e);
       }
 
@@ -787,5 +812,11 @@
       }
 
-      renderUI();
+      // renderUI();
+      currentScreen->renderUI();
+
+      // Copy the UI image to a vulkan texture
+      VulkanUtils::populateVulkanImageFromSDLTexture(device, physicalDevice, commandPool, uiOverlay, renderer,
+         sdlOverlayImage, graphicsQueue);
+
       renderScene();
    }
@@ -1010,4 +1041,5 @@
 }
 
+// TODO: Maybe move all/most of this to the base Screen class
 void VulkanGame::renderScene() {
    vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, numeric_limits<uint64_t>::max());
@@ -1111,4 +1143,6 @@
    vkDestroyInstance(instance, nullptr);
 
+   delete screens[SCREEN_MAIN];
+
    // TODO: Check if any of these functions accept null parameters
    // If they do, I don't need to check for that
@@ -1126,4 +1160,9 @@
    TTF_CloseFont(font);
    font = nullptr;
+
+   if (proggyFont != nullptr) {
+      TTF_CloseFont(proggyFont);
+      proggyFont = nullptr;
+   }
 
    if (uiOverlay != nullptr) {
@@ -1582,4 +1621,5 @@
       vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
 
+      /*
       modelPipeline.createRenderCommands(commandBuffers[i], i);
       shipPipeline.createRenderCommands(commandBuffers[i], i);
@@ -1590,4 +1630,7 @@
       // Always render this pipeline last
       overlayPipeline.createRenderCommands(commandBuffers[i], i);
+      */
+
+      currentScreen->createRenderCommands(commandBuffers[i], i);
 
       vkCmdEndRenderPass(commandBuffers[i]);
Index: vulkan-game.hpp
===================================================================
--- vulkan-game.hpp	(revision 4e705d63b9aa0c0a2a6029899904ebf814b6f543)
+++ vulkan-game.hpp	(revision e1f88a9873d22257993a07f9c790d1739e9961db)
@@ -3,4 +3,5 @@
 
 #include <chrono>
+#include <map>
 
 #define GLM_FORCE_RADIANS
@@ -16,8 +17,9 @@
 #include "game-gui-sdl.hpp"
 
+#include "gui/screen.hpp"
+#include "gui/main-screen.hpp"
+
 using namespace glm;
 using namespace std::chrono;
-
-// TODO: Switch from union to std::variant
 
 #ifdef NDEBUG
@@ -195,4 +197,12 @@
       void run(int width, int height, unsigned char guiFlags);
 
+      void goToScreen(Screen* screen);
+      void quitGame();
+
+      map<ScreenType, Screen*> screens;
+      Screen* currentScreen;
+
+      TTF_Font* proggyFont;
+
       GraphicsPipeline_Vulkan<OverlayVertex, void*> overlayPipeline;
 
@@ -219,4 +229,6 @@
       const int EXPLOSION_PARTICLE_COUNT = 300;
       const vec3 LASER_COLOR = vec3(0.2f, 1.0f, 0.2f);
+
+      bool quit;
 
       vec3 cam_pos;
