anders_tale_room_editor

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | LICENSE

main.c (15844B)


      1 #define HS_IMPL
      2 #define HS_NUKLEAR
      3 #define HS_SFD
      4 #include "hs/hs_graphics.h"
      5 
      6 #define MAX_VERTEX_BUFFER 512 * 1024
      7 #define MAX_ELEMENT_BUFFER 128 * 1024
      8 
      9 struct nk_context *ctx;
     10 struct nk_glfw glfw = {0};
     11 
     12 hs_tilemap tilemap;
     13 uint32_t selected_tile = 0;
     14 uint32_t tileset_chosen = false;
     15 hs_aroom current_room;
     16 hs_aroom undo_room;
     17 
     18 hs_key* undo_key = &(hs_key){.key = GLFW_KEY_Z};
     19 
     20 uint32_t popup_new_room_on = 0;
     21 
     22 hs_game_data gd = {
     23         .width = 960,
     24         .height = 540
     25 };
     26 
     27 sfd_Options anders_tale_room = {
     28   .title        = "Johan, you may now choose an Anders Tale Room file",
     29   .filter_name  = "Anders tale room",
     30   .filter       = "*.aroom",
     31 };
     32 
     33 uint32_t tilemap_offset_x, tilemap_offset_y, tilemap_screen_width, tilemap_screen_height;
     34 
     35 static void
     36 framebuffer_size_callback(GLFWwindow* win, const int width, const int height)
     37 {
     38         gd.width = width;
     39         tilemap_screen_width = width - 200;
     40         gd.height = height;
     41         if (tilemap.vertices) {
     42                 const float viewport_height = (float)tilemap_screen_width * ((float)tilemap.height/tilemap.width);
     43                 if (viewport_height > gd.height) {
     44                         const float viewport_width = (float)gd.height * ((float)tilemap.width/tilemap.height);
     45                         tilemap_offset_x = (tilemap_screen_width - viewport_width) / 2;
     46                         tilemap_offset_y = 0;
     47                         tilemap_screen_width = viewport_width;
     48                         tilemap_screen_height = gd.height;
     49                 } else {
     50                         tilemap_offset_x = 0;
     51                         tilemap_offset_y = (gd.height - viewport_height) / 2;
     52                         tilemap_screen_height = viewport_height;
     53                 }
     54 
     55                 tilemap_offset_x += 200;
     56         }
     57 }
     58 
     59 static inline void init()
     60 {
     61         hs_init(&gd, "Anders tale room editor (mfw Johan anderstaler istendefor å game)", framebuffer_size_callback, 0);
     62 
     63         ctx = nk_glfw3_init(&glfw, gd.window, NK_GLFW3_INSTALL_CALLBACKS);
     64         /* Load Fonts: if none of these are loaded a default font will be used  */
     65         /* Load Cursor: if you uncomment cursor loading please hide the cursor */
     66         {
     67                 struct nk_font_atlas *atlas;
     68                 nk_glfw3_font_stash_begin(&glfw, &atlas);
     69                 nk_glfw3_font_stash_end(&glfw);
     70         }
     71 }
     72 
     73 static inline void
     74 init_new_room(const uint32_t room_width, const uint32_t room_height)
     75 {
     76         hs_tilemap_init(&tilemap, 0);
     77         framebuffer_size_callback(gd.window, gd.width, gd.height);
     78 
     79         if (current_room.data)
     80                 free(current_room.data);
     81         if (undo_room.data)
     82                 free(undo_room.data);
     83         uint8_t* room_data = calloc(1, sizeof(uint8_t) * room_width * room_height);
     84         assert(room_data);
     85 
     86         uint8_t* undo_data = calloc(1, sizeof(uint8_t) * room_width * room_height);
     87         assert(undo_data);
     88 
     89         current_room = (hs_aroom){
     90                 .width = room_width,
     91                 .height = room_height,
     92                 .layers = 1,
     93                 .data = room_data,
     94         };
     95         undo_room = (hs_aroom){
     96                 .width = room_width,
     97                 .height = room_height,
     98                 .layers = 1,
     99                 .data = undo_data,
    100         };
    101 }
    102 
    103 static inline uint32_t
    104 popup_create_new_room()
    105 {
    106         uint32_t new_tilemap = false;
    107         if (nk_popup_begin(ctx, NK_POPUP_STATIC, "Create new room",
    108                            NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_TITLE,
    109                            nk_rect(200, 10, 320, 140))) {
    110                 static int room_width = 20;
    111                 static int room_height = 20;
    112 
    113                 nk_layout_row_dynamic(ctx, 30, 1);
    114                 nk_property_int(ctx, "Room width:", 0, &room_width, 60, 1, 1);
    115                 nk_property_int(ctx, "Room height:", 0, &room_height, 60, 1, 1);
    116 
    117                 nk_layout_row_dynamic(ctx, 30, 2);
    118                 if (nk_button_label(ctx, "Create room")) {
    119                         tilemap.width = room_width;
    120                         tilemap.height = room_height;
    121                         tilemap.tile_width = 1.0f/tilemap.width;
    122                         tilemap.tile_height = 1.0f/tilemap.height;
    123 
    124                         init_new_room(room_width, room_height);
    125 
    126                         popup_new_room_on = false;
    127                         new_tilemap = true;
    128                         nk_popup_close(ctx);
    129                 }
    130 
    131                 if (nk_button_label(ctx, "Quit")) {
    132                         popup_new_room_on = false;
    133                         nk_popup_close(ctx);
    134                 }
    135                 nk_popup_end(ctx);
    136         }
    137         return new_tilemap;
    138 }
    139 
    140 static struct nk_image*
    141 create_tiles(const char* filename, const uint32_t width, const uint32_t height)
    142 {
    143         int tileset_width, tileset_height;
    144         tilemap.tex = hs_tex2d_create_size_info_pixel(filename, GL_RGBA, &tileset_width, &tileset_height);
    145         struct nk_image tileset = nk_image_id(tilemap.tex);
    146         struct nk_image* tiles = (struct nk_image*)malloc(sizeof(struct nk_image) * width * height);
    147         assert(tiles);
    148 
    149         const int tile_width = tileset_width / width;
    150         const int tile_height = tileset_height / height;
    151 
    152         uint32_t i = 0;
    153         for (uint32_t y = 0; y < height; y++) {
    154                 const uint32_t ypos = y * tile_height;
    155                 for (uint32_t x = 0; x < width; x++) {
    156                         const uint32_t xpos = x * tile_width;
    157                         tiles[i++] = nk_subimage_handle(tileset.handle, tileset_width, tileset_height,
    158                                                         nk_rect(xpos, ypos, tile_width, tile_height));
    159                 }
    160         }
    161 
    162         return tiles;
    163 }
    164 
    165 char* set_and_get_filename_short(char* file_path_src, char** file_path_dest) {
    166         if (*file_path_dest) {
    167                 free(*file_path_dest);
    168                 *file_path_dest = NULL;
    169         }
    170         *file_path_dest = malloc(strlen(file_path_src)+1);
    171         assert(*file_path_dest);
    172         strcpy(*file_path_dest, file_path_src);
    173 
    174         char* last_slash_pos = file_path_src;
    175         for (char* f = *file_path_dest; f[1] != '\0'; f++)
    176                 if (f[0] == '/' || f[0] == '\\')
    177                         last_slash_pos = f + 1;
    178         return last_slash_pos;
    179 }
    180 
    181 static inline void
    182 side_bar()
    183 {
    184         if (nk_begin(ctx, "Side bar", nk_rect(0, 0, 200, gd.height), 0)) {
    185                 const char* const tileset_default_name = "No tileset selected";
    186                 static char* tileset_filename = NULL;
    187                 static char* tileset_filename_short = (char*)tileset_default_name;
    188 
    189                 const char* const tilemap_default_name = "No tilemap selected";
    190                 const char* const tilemap_new = "New tilemap";
    191                 static char* tilemap_filename = NULL;
    192                 static char* tilemap_filename_short = (char*)tilemap_default_name;
    193 
    194                 static struct nk_image* tileset_images = NULL;
    195                 static uint32_t tileset_image_count = 0;
    196                 static uint32_t tileset_popup_on = 0;
    197 
    198                 nk_layout_row_dynamic(ctx, 10, 1);
    199                 nk_label(ctx, tilemap_filename_short, NK_TEXT_CENTERED);
    200 
    201                 nk_layout_row_dynamic(ctx, 30, 1);
    202                 if (nk_button_label(ctx, "New room")) {
    203                         popup_new_room_on = true;
    204                 }
    205 
    206                 nk_layout_row_dynamic(ctx, 30, 1);
    207                 if (nk_button_label(ctx, "Load room")) {
    208                         const char *filename = sfd_open_dialog(&anders_tale_room);
    209                         if (filename) {
    210                                 tilemap_filename_short = set_and_get_filename_short((char*)filename, &tilemap_filename);
    211 
    212                                 undo_room = hs_aroom_from_file(filename);
    213                                 current_room = hs_aroom_from_file(filename);
    214                                 tilemap.tile_width = 1.0f/current_room.width;
    215                                 tilemap.tile_height = 1.0f/current_room.height;
    216 
    217                                 hs_aroom_to_tilemap(current_room, &tilemap, 1);
    218                                 framebuffer_size_callback(gd.window, gd.width, gd.height);
    219                         }
    220                 }
    221                 if (current_room.data &&
    222                     nk_button_label(ctx, "Save room")) {
    223                         const char *filename = sfd_save_dialog(&anders_tale_room);
    224                         if (filename) {
    225                                 hs_aroom_write_to_file(filename, current_room);
    226                                 tilemap_filename_short = set_and_get_filename_short((char*)filename, &tilemap_filename);
    227                         }
    228                 }
    229                 if (popup_new_room_on) {
    230                         if (popup_create_new_room()) {
    231                                 tilemap_filename_short = (char*)tilemap_new;
    232                         }
    233                 }
    234 
    235                 nk_layout_row_dynamic(ctx, 10, 1);
    236                 nk_layout_row_dynamic(ctx, 10, 1);
    237                 nk_label(ctx, tileset_filename_short, NK_TEXT_CENTERED);
    238 
    239                 nk_layout_row_dynamic(ctx, 30, 1);
    240                 if (nk_button_label(ctx, "Choose new tileset")) {
    241                         char* filename = (char*)sfd_open_dialog(&(sfd_Options){
    242                                         .title = "Choose a tileset",
    243                                         .filter_name = "PNG image",
    244                                         .filter = "*.png",
    245                                 });
    246 
    247                         if (filename && filename[0] != '\0') {
    248                                 tileset_popup_on = true;
    249                                 tileset_filename_short = set_and_get_filename_short((char*)filename, &tileset_filename);
    250                         } else {
    251                                 tileset_filename_short = (char*)tileset_default_name;
    252                         }
    253                 }
    254 
    255                 if (tileset_popup_on &&
    256                     nk_popup_begin(ctx, NK_POPUP_STATIC, "Select tileset size",
    257                                    NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_TITLE,
    258                                    nk_rect(200, 30, 320, 140))) {
    259                         static int tileset_width = 5;
    260                         static int tileset_height = 3;
    261 
    262                         nk_layout_row_dynamic(ctx, 30, 1);
    263                         nk_property_int(ctx, "Tileset width (in tiles):", 1, &tileset_width, 20, 1, 0.4f);
    264                         nk_property_int(ctx, "Tileset height (in tiles):", 1, &tileset_height, 50, 1, 0.5f);
    265 
    266                         nk_layout_row_dynamic(ctx, 30, 2);
    267                         if (nk_button_label(ctx, "Confirm")) {
    268                                 if (tileset_images) {
    269                                         free(tileset_images);
    270                                         glDeleteTextures(1, &tilemap.tex);
    271                                         tilemap.tex= 0;
    272                                 }
    273 
    274                                 tileset_images = create_tiles(tileset_filename, tileset_width, tileset_height);
    275                                 tileset_image_count = tileset_width * tileset_height;
    276 
    277                                 tilemap.tileset_width = tileset_width;
    278                                 tilemap.tileset_height = tileset_height;
    279 
    280                                 if (tilemap.width) {
    281                                         hs_aroom_set_tilemap(current_room, &tilemap, 1);
    282                                 }
    283 
    284                                 tileset_popup_on = false;
    285                                 tileset_chosen = true;
    286                                 nk_popup_close(ctx);
    287                         }
    288                         if (nk_button_label(ctx, "Quit")) {
    289                                 tileset_popup_on = false;
    290                                 nk_popup_close(ctx);
    291                         }
    292 
    293                         nk_popup_end(ctx);
    294                 }
    295 
    296                 if (tileset_chosen) {
    297                         nk_layout_row_dynamic(ctx, 20, 1);
    298                         nk_label(ctx, "Selected tile:", NK_TEXT_CENTERED);
    299 
    300                         nk_layout_row_dynamic(ctx, 190, 1);
    301                         nk_image(ctx, tileset_images[selected_tile]);
    302 
    303                         nk_layout_row_dynamic(ctx, 60, 3);
    304                         for (uint32_t i = 0; i < tileset_image_count; i++) {
    305                                 if (nk_button_image(ctx, tileset_images[i]))
    306                                         selected_tile = i;
    307                         }
    308                 }
    309         }
    310         nk_end(ctx);
    311 }
    312 
    313 static inline void loop()
    314 {
    315         glViewport(0, 0, gd.height, gd.width);
    316         hs_clear(0.2, 0.2, 0.2, 1.0, 0);
    317         glEnable(GL_BLEND);
    318 
    319         if (tilemap.vertices && gd.width > 200) {
    320                 glViewport(tilemap_offset_x, tilemap_offset_y, tilemap_screen_width, tilemap_screen_height);
    321 
    322                 hs_tex2d_activate(tilemap.tex, GL_TEXTURE0);
    323                 hs_tilemap_draw(tilemap);
    324 
    325                 if (tileset_chosen) {
    326                         static uint32_t undo_buffer_update = true;
    327 
    328                         if (hs_get_key_toggle(gd, undo_key) == HS_KEY_PRESSED) {
    329                                 undo_buffer_update = false;
    330                                 memcpy(current_room.data, undo_room.data, current_room.width * current_room.height);
    331                                 hs_aroom_set_tilemap(current_room, &tilemap, 1);
    332                         } else if (glfwGetMouseButton(gd.window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) {
    333                                 double xpos, ypos;
    334                                 glfwGetCursorPos(gd.window, &xpos, &ypos);
    335 
    336                                 // reverse y axis
    337                                 ypos -= gd.height;
    338                                 ypos *= -1;
    339                                 int32_t tilex = (xpos - tilemap_offset_x) / (tilemap.tile_width * tilemap_screen_width);
    340                                 int32_t tiley = (ypos - tilemap_offset_y) / (tilemap.tile_height * tilemap_screen_height);
    341                                 if (hs_aroom_get_xy(current_room, tilex, tiley) != selected_tile &&
    342                                     xpos > tilemap_offset_x && xpos < tilemap_offset_x + tilemap_screen_width &&
    343                                     ypos > tilemap_offset_y && ypos < tilemap_offset_y + tilemap_screen_height) {
    344                                         if (undo_buffer_update) {
    345                                                 undo_buffer_update = false;
    346                                                 memcpy(undo_room.data, current_room.data, undo_room.width * undo_room.height);
    347                                         }
    348                                         hs_aroom_set_xy(&current_room, tilex, tiley, selected_tile);
    349 
    350                                         hs_tilemap_set_xy(&tilemap, tilex, tiley, selected_tile);
    351                                         hs_tilemap_update_vbo(tilemap);
    352                                 }
    353                         } else if (!undo_buffer_update) {
    354                                 undo_buffer_update = true;
    355                         }
    356                 }
    357         }
    358 
    359         side_bar();
    360 
    361         /* IMPORTANT: `nk_glfw_render` modifies some global OpenGL state
    362          * with blending, scissor, face culling, depth test and viewport and
    363          * defaults everything back into a default state.
    364          * Make sure to either a.) save and restore or b.) reset your own state after
    365          * rendering the UI. */
    366         nk_glfw3_render(&glfw, NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER);
    367 }
    368 
    369 int main()
    370 {
    371         init();
    372         hs_loop(gd, nk_glfw3_new_frame(&glfw); loop());
    373 
    374         nk_glfw3_shutdown(&glfw);
    375         hs_exit();
    376         return 0;
    377 }