libs/mrgui/examples/main.cc
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "SDL.h" | ||
| 2 | #include "SDL_timer.h" | ||
| 3 | |||
| 4 | #include <cassert> | ||
| 5 | #include <optional> | ||
| 6 | #include <vector> | ||
| 7 | #include <unordered_map> | ||
| 8 | #include <string> | ||
| 9 | #include <algorithm> | ||
| 10 | #include <cstdint> | ||
| 11 | |||
| 12 | #include "dependency_glad.h" | ||
| 13 | #include "OpenSans-Regular.ttf.h" | ||
| 14 | #include "uv-texture.png.h" | ||
| 15 | #include "spritesheet.png.h" | ||
| 16 | |||
| 17 | #include "eu/log/log.h" | ||
| 18 | #include "eu/base/memorychunk.h" | ||
| 19 | |||
| 20 | #include "eu/render/canvas.h" | ||
| 21 | #include "eu/render/state.h" | ||
| 22 | #include "eu/render/font.h" | ||
| 23 | #include "eu/render/opengl_utils.h" | ||
| 24 | #include "eu/render/texture.io.h" | ||
| 25 | #include "eu/render/enable_high_performance_graphics.h" | ||
| 26 | |||
| 27 | #include "data/spritesheet.h" | ||
| 28 | |||
| 29 | #include "eu/mrgui/widgets.h" | ||
| 30 | |||
| 31 | using namespace eu::mrgui; | ||
| 32 | |||
| 33 | ENABLE_HIGH_PERFORMANCE_GRAPHICS | ||
| 34 | |||
| 35 | ✗ | eu::MemoryChunk chunk_from_embed(const embedded_binary& binary) | |
| 36 | { | ||
| 37 | ✗ | return { .bytes = reinterpret_cast<const char*>(binary.data), .size = binary.size }; | |
| 38 | } | ||
| 39 | |||
| 40 | ✗ | int main(int, char**) | |
| 41 | { | ||
| 42 | ✗ | int window_width = 1280; | |
| 43 | ✗ | int window_height = 720; | |
| 44 | |||
| 45 | ✗ | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) < 0) { | |
| 46 | ✗ | LOG_ERR("Error initializing SDL: {}", SDL_GetError()); | |
| 47 | ✗ | return -1; | |
| 48 | } | ||
| 49 | |||
| 50 | |||
| 51 | #if defined(__APPLE__) | ||
| 52 | // GL 3.2 Core + GLSL 150 | ||
| 53 | const char* glsl_version = "#version 150"; | ||
| 54 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac | ||
| 55 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | ||
| 56 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); | ||
| 57 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); | ||
| 58 | #else | ||
| 59 | // GL 3.0 + GLSL 130 | ||
| 60 | // const char* glsl_version = "#version 130"; | ||
| 61 | ✗ | SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); | |
| 62 | ✗ | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | |
| 63 | ✗ | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); | |
| 64 | ✗ | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); // was 0 in dear imgui example?? | |
| 65 | #endif | ||
| 66 | |||
| 67 | ✗ | SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); | |
| 68 | ✗ | SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); | |
| 69 | |||
| 70 | ✗ | SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | |
| 71 | ✗ | SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); | |
| 72 | |||
| 73 | ✗ | SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); | |
| 74 | ✗ | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); | |
| 75 | ✗ | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | |
| 76 | ✗ | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); | |
| 77 | |||
| 78 | ✗ | SDL_Window* window = SDL_CreateWindow("mrgui sample", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, | |
| 79 | window_width, window_height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE); | ||
| 80 | ✗ | if (!window) { | |
| 81 | ✗ | LOG_ERR("Error creating window: {}", SDL_GetError()); | |
| 82 | ✗ | return -1; | |
| 83 | } | ||
| 84 | |||
| 85 | ✗ | auto* glContext = SDL_GL_CreateContext(window); | |
| 86 | ✗ | SDL_GetWindowSize(window, &window_width, &window_height); | |
| 87 | ✗ | if (!glContext) { | |
| 88 | ✗ | LOG_ERR("Error creating gl context: {}", SDL_GetError()); | |
| 89 | ✗ | return -1; | |
| 90 | } | ||
| 91 | |||
| 92 | /* OpenGL setup */ | ||
| 93 | ✗ | const int glad_result = gladLoadGLLoader(SDL_GL_GetProcAddress); | |
| 94 | ✗ | if (glad_result == 0) | |
| 95 | { | ||
| 96 | ✗ | LOG_ERR("Failed to init glad, error: {0}", glad_result); | |
| 97 | ✗ | return -1; | |
| 98 | } | ||
| 99 | |||
| 100 | { | ||
| 101 | ✗ | const std::string gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); | |
| 102 | ✗ | const std::string gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); | |
| 103 | ✗ | const std::string gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); | |
| 104 | ✗ | const std::string gl_shading_language_version = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)); | |
| 105 | |||
| 106 | ✗ | LOG_INFO("Vendor: {0}", gl_vendor); | |
| 107 | ✗ | LOG_INFO("Renderer: {0}", gl_renderer); | |
| 108 | ✗ | LOG_INFO("Version OpenGL: {0}", gl_version); | |
| 109 | ✗ | LOG_INFO("Version GLSL: {0}", gl_shading_language_version); | |
| 110 | ✗ | } | |
| 111 | |||
| 112 | ✗ | eu::render::State states; | |
| 113 | ✗ | eu::render::Render2 render{ &states }; | |
| 114 | |||
| 115 | ✗ | UiState uistate; | |
| 116 | ✗ | IdStack idstack; | |
| 117 | ✗ | eu::render::DrawableFont font{chunk_from_embed(OPENSANS_REGULAR_TTF)}; | |
| 118 | |||
| 119 | ✗ | const auto sample_texture = eu::render::load_image_from_embedded(SEND_DEBUG_LABEL_MANY("uv-texture.png") UV_TEXTURE_PNG, | |
| 120 | ✗ | eu::render::TextureEdge::clamp, eu::render::TextureRenderStyle::linear, eu::render::Transparency::exclude, eu::render::ColorData::color_data); | |
| 121 | |||
| 122 | ✗ | const auto spritesheet = eu::render::load_image_from_embedded(SEND_DEBUG_LABEL_MANY("spritesheet.png") SPRITESHEET_PNG, | |
| 123 | ✗ | eu::render::TextureEdge::repeat, eu::render::TextureRenderStyle::linear, eu::render::Transparency::include, eu::render::ColorData::color_data); | |
| 124 | |||
| 125 | ✗ | bool running = true; | |
| 126 | ✗ | std::string editable_text = "Editable text"; | |
| 127 | |||
| 128 | ✗ | LOG_INFO("Mrgui sample started"); | |
| 129 | ✗ | while (running) | |
| 130 | { | ||
| 131 | // events | ||
| 132 | SDL_Event e; | ||
| 133 | ✗ | while (SDL_PollEvent(&e) != 0) | |
| 134 | { | ||
| 135 | ✗ | switch (e.type) | |
| 136 | { | ||
| 137 | ✗ | case SDL_MOUSEMOTION: | |
| 138 | ✗ | uistate.mouse = { static_cast<float>(e.motion.x), static_cast<float>(window_height - e.motion.y) }; | |
| 139 | ✗ | break; | |
| 140 | ✗ | case SDL_MOUSEBUTTONDOWN: | |
| 141 | ✗ | if (e.button.button == 1) | |
| 142 | { | ||
| 143 | ✗ | uistate.mousedown = true; | |
| 144 | } | ||
| 145 | ✗ | break; | |
| 146 | ✗ | case SDL_MOUSEBUTTONUP: | |
| 147 | ✗ | if (e.button.button == 1) | |
| 148 | { | ||
| 149 | ✗ | uistate.mousedown = false; | |
| 150 | } | ||
| 151 | ✗ | break; | |
| 152 | ✗ | case SDL_WINDOWEVENT: | |
| 153 | ✗ | if (e.window.event == SDL_WINDOWEVENT_RESIZED || e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) | |
| 154 | { | ||
| 155 | ✗ | LOG_INFO("Resized"); | |
| 156 | ✗ | SDL_GetWindowSize(window, &window_width, &window_height); | |
| 157 | } | ||
| 158 | ✗ | break; | |
| 159 | ✗ | case SDL_KEYDOWN: | |
| 160 | ✗ | uistate.key = KeyboardInput {.key = e.key.keysym.sym, .mod = e.key.keysym.mod }; | |
| 161 | ✗ | break; | |
| 162 | ✗ | case SDL_TEXTINPUT: | |
| 163 | { | ||
| 164 | // todo(Gustav): handle unicode | ||
| 165 | ✗ | char c = e.text.text[0]; | |
| 166 | ✗ | if ((c & 0xFF80) == 0) | |
| 167 | ✗ | uistate.keychar = static_cast<char>(c & 0x7f); | |
| 168 | } | ||
| 169 | ✗ | break; | |
| 170 | ✗ | case SDL_QUIT: running = false; break; | |
| 171 | ✗ | default: | |
| 172 | // ignore other events | ||
| 173 | ✗ | break; | |
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | // render | ||
| 178 | { | ||
| 179 | ✗ | eu::render::RenderCommand cmd {.states = &states, .render = &render, .size = {.width = window_width, .height = window_height} }; | |
| 180 | |||
| 181 | // todo(Gustav): provide a pixel layout | ||
| 182 | ✗ | const auto screen = eu::render::LayoutData{ .style = eu::render::ViewportStyle::extended, | |
| 183 | ✗ | .requested_width = static_cast<float>(window_width), .requested_height = static_cast<float>(window_height) }; | |
| 184 | |||
| 185 | ✗ | cmd.clear(eu::colors::blue_sky, screen); | |
| 186 | |||
| 187 | ✗ | auto layer = eu::render::with_layer2(cmd, screen); | |
| 188 | ✗ | eu::render::Quad{ .tint = eu::colors::green_bluish }.draw(layer.batch, layer.viewport_aabb_in_worldspace.get_bottom(50)); | |
| 189 | |||
| 190 | static eu::An rotation = eu::no_rotation; | ||
| 191 | static float slider_a = 0.5f; | ||
| 192 | static float slider_b = 0.5f; | ||
| 193 | |||
| 194 | ✗ | eu::render::Quad{ | |
| 195 | ✗ | .texture = &sample_texture, | |
| 196 | // select bottom right of the texture, assuming it is split in 4 smaller rects | ||
| 197 | ✗ | .texturecoord = eu::Rect::from_bottom_left_size({0.5f, 0}, {0.5f, 0.5f}), | |
| 198 | ✗ | .rotation = eu::render::RotateQuad{.angle = rotation, .center = {slider_a, slider_b} } | |
| 199 | ✗ | }.draw(layer.batch, eu::Rect::from_bottom_left_size({ 300, 300 }, { 300, 300 })); | |
| 200 | |||
| 201 | ✗ | const auto button_size = eu::v2{ 64, 64 }; | |
| 202 | |||
| 203 | ✗ | uistate.begin(); | |
| 204 | ✗ | if (basic_button(idstack.get("a"), eu::Rect::from_bottom_left_size({ 100, 100 }, button_size), uistate, layer.batch)) | |
| 205 | { | ||
| 206 | ✗ | rotation -= eu::An::from_degrees(15.0f); | |
| 207 | } | ||
| 208 | ✗ | if (basic_button(idstack.get("b"), eu::Rect::from_bottom_left_size({ 200, 100 }, button_size), uistate, layer.batch)) | |
| 209 | { | ||
| 210 | ✗ | rotation += eu::An::from_degrees(15.0f); | |
| 211 | } | ||
| 212 | ✗ | if (basic_button(idstack.get("c"), eu::Rect::from_bottom_left_size({ 300, 100 }, button_size), uistate, layer.batch)) | |
| 213 | { | ||
| 214 | ✗ | rotation = eu::no_rotation; | |
| 215 | } | ||
| 216 | ✗ | button_texture(idstack.get("d"), eu::Rect::from_bottom_left_size({ 400, 100 }, button_size), uistate, layer.batch, &spritesheet, | |
| 217 | ::spritesheet::button_square_flat, | ||
| 218 | ::spritesheet::button_square_depth_flat, | ||
| 219 | ::spritesheet::button_square_depth_gradient | ||
| 220 | ); | ||
| 221 | |||
| 222 | ✗ | const auto slider_size = eu::v2{ 256, 25 }; | |
| 223 | ✗ | slider(idstack.get("slider_a"), &slider_a, eu::Rect::from_bottom_left_size({ 100, 200 }, slider_size), uistate, layer.batch); | |
| 224 | ✗ | slider(idstack.get("slider_b"), &slider_b, eu::Rect::from_bottom_left_size({ 100, 250 }, slider_size), uistate, layer.batch); | |
| 225 | ✗ | textfield(idstack.get("textfield"), &editable_text, &font, 20, {100, 300}, uistate, layer.batch); | |
| 226 | ✗ | uistate.end(); | |
| 227 | |||
| 228 | ✗ | draw_text(layer.batch, &font, "Hello world", 60, {200, 400 + 200}, eu::colors::black); | |
| 229 | ✗ | } | |
| 230 | ✗ | SDL_GL_SwapWindow(window); | |
| 231 | } | ||
| 232 | |||
| 233 | ✗ | LOG_INFO("Shutting down"); | |
| 234 | ✗ | SDL_GL_DeleteContext(glContext); | |
| 235 | ✗ | SDL_DestroyWindow(window); | |
| 236 | ✗ | SDL_Quit(); | |
| 237 | |||
| 238 | ✗ | return 0; | |
| 239 | ✗ | } | |
| 240 |