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