#include #include #include #include #include #include #include #include "smol-atlas.h" #include "defer.h" #include "log.h" #include "m_string.h" #include "math_graphics.h" #include "load_entire_file.h" #include "stb_image.h" #include "../assets/shader/basic_vertex_shader.h" #include "../assets/shader/basic_pixel_shader.h" using namespace M; SDL_GPUDevice *device; SDL_Window *window; SDL_GPUShader *basic_vertex_shader; SDL_GPUShader *basic_pixel_shader; SDL_GPUGraphicsPipeline *basic_graphics_pipeline; SDL_GPUSampler *point_sampler; SDL_GPUBuffer *vertex_buffer; SDL_GPUBuffer *index_buffer; SDL_GPUBuffer *tiles_instance_buffer; SDL_GPUBuffer *player_instance_buffer; Sint32 window_width; Sint32 window_height; bool Running = true; #define view_width 16 #define view_height 9 struct Vertex { V4 pos; V2 uv0uv1; }; Vertex vertices[] = { {{ -1, 1, 1, 1 }, {0, 0}}, {{ 1, -1, 1, 1 }, {1, 1}}, {{ -1, -1, 1, 1 }, {0, 1}}, {{ 1, 1, 1, 1 }, {1, 0}}, }; Uint16 indices[] = { 0, 1, 2, 0, 3, 1, }; struct Instance { V4 pos_size; Uint32 tile_type; V4 uv0uv1; }; Instance tiles_instances[view_width * view_height] = { }; Instance player_instance = { { 0.5f + 0.5f / view_width, 0.5f, 1.0f / view_width, 1.0f / view_height}, 0, {0, 0, 1, 1}}; Uint32 map_width = view_width; Uint32 map_height = view_height; Uint32* map_tiles; struct Player { Sint64 pos_x; Sint64 pos_y; }; Player player = { .pos_x = 8, .pos_y = 6, }; struct PerFrame { float aspect_ratio; float empty[3]; }; PerFrame per_frame = {1}; typedef struct { Uint32 type; const char *asset_path; smol_atlas_item_t *atlas_item; } TileInfo; TileInfo tile_infos[] = { { 0x0001, "../assets/tiles/error.png" }, { 0x0000, "../assets/tiles/empty.png" }, { 0x0100, "../assets/tiles/grass_1.png" }, { 0x0101, "../assets/tiles/grass_2.png" }, { 0x0102, "../assets/tiles/grass_3.png" }, { 0x0200, "../assets/tiles/ground_1.png" }, { 0x0300, "../assets/tiles/water_1.png" }, { 0x0301, "../assets/tiles/water_2.png" }, }; int tile_atlas_size = 256; smol_atlas_t *tile_atlas; Sint32 selected_tile = -1; void save_map() { log("Save file is under construction."); FILE* file = fopen("../assets/map/map.sv", "wb"); if (!file) { log_error("Save file creation has failed."); return; } defer(fclose(file)); if (fwrite(&map_width, sizeof(Uint32), 1, file) != 1) { log_error("fwrite for map_width has failed."); return; } if (fwrite(&map_height, sizeof(Uint32), 1, file) != 1) { log_error("fwrite for map_height has failed."); return; } for (int i = 0; i < map_width * map_height; i++) { Uint32 type = tile_infos[map_tiles[i]].type; if (fwrite(&type, sizeof(Uint32), 1, file) != 1) { log_error("fwrite for tile_type at tile number %d has failed.", i); return; } } log("Saving map was successful."); } void load_map() { log("Load save file."); String file = load_entire_file("../assets/map/map.sv"); if (!file.length) { log_error("Loading save file has failed."); return; } map_width = read(file); map_height = read(file); if (file.length != map_width * map_height * sizeof(Uint32)) { log_error("Incorrect file.length."); return; } if (map_tiles) free(map_tiles); map_tiles = (Uint32*)malloc(map_width * map_height * sizeof(Uint32)); for (int i = 0; i < map_width * map_height; i++) { Uint32 type = read(file); Uint32 kind = 0; for (int i = 0; i < SDL_arraysize(tile_infos); i++) { if (tile_infos[i].type == type) { kind = i; break; } } map_tiles[i] = kind; } log("Loading map was successful."); } void change_map_size(char direction, int amount) { Uint32* old_map = map_tiles; auto old_map_width = map_width; auto old_map_height = map_height; Sint32 new_x_offset = 0; Sint32 new_y_offset = 0; Sint32 old_x_offset = 0; Sint32 old_y_offset = 0; Sint32 to_fill_width = map_width; Sint32 to_fill_height = map_height; Sint32 to_fill_x_offset = 0; Sint32 to_fill_y_offset = 0; if (direction == 'W') { player.pos_x = player.pos_x + amount; map_width += amount; to_fill_width = amount; if (amount < 0) old_x_offset = -amount; else new_x_offset = amount; } if (direction == 'N') { player.pos_y = player.pos_y + amount; map_height += amount; to_fill_height = amount; if (amount < 0) old_y_offset = -amount; else new_y_offset = amount; } if (direction == 'E') { map_width += amount; to_fill_width = amount; to_fill_x_offset = old_map_width; } if (direction == 'S') { map_height += amount; to_fill_height = amount; to_fill_y_offset = old_map_height; } map_tiles = (Uint32*)malloc(map_width * map_height * sizeof(Uint32)); for (int y = 0; y < min(old_map_height, map_height); y++) { for (int x = 0; x < min(old_map_width, map_width); x++) { map_tiles[(y + new_y_offset) * map_width + (x + new_x_offset)] = old_map[(y + old_y_offset) * old_map_width + (x + old_x_offset)]; } } for (int y = 0; y < to_fill_height; y++) { for (int x = 0; x < to_fill_width; x++) { map_tiles[(y + to_fill_y_offset) * map_width + (x + to_fill_x_offset)] = 0; } } player.pos_x = clamp(0, player.pos_x, map_width - 1); player.pos_y = clamp(0, player.pos_y, map_height - 1); free(old_map); } bool update_buffer(SDL_GPUBuffer *buffer, Uint32 offset, Uint32 num_bytes, void *data) { SDL_GPUTransferBufferCreateInfo transfer_buffer_info = { .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = num_bytes, }; SDL_GPUTransferBuffer *transfer_buffer = SDL_CreateGPUTransferBuffer(device, &transfer_buffer_info); if (!transfer_buffer) { log_error("Failed to create gpu transfer buffer (%s).", SDL_GetError()); return false; } void *transfer_data = SDL_MapGPUTransferBuffer(device, transfer_buffer, true); if (!transfer_data) { log_error("Failed to map gpu transfer buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); return false; } memcpy(transfer_data, data, num_bytes); SDL_UnmapGPUTransferBuffer(device, transfer_buffer); SDL_GPUCommandBuffer *command_buffer = SDL_AcquireGPUCommandBuffer(device); if (!command_buffer) { log_error("Failed to acquire gpu command buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); return false; } SDL_GPUTransferBufferLocation transfer_info = { .transfer_buffer = transfer_buffer, .offset = 0, }; SDL_GPUBufferRegion buffer_region = { .buffer = buffer, .offset = offset, .size = num_bytes, }; SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(command_buffer); SDL_UploadToGPUBuffer(copy_pass, &transfer_info, &buffer_region, true); SDL_EndGPUCopyPass(copy_pass); if (!SDL_SubmitGPUCommandBuffer(command_buffer)) { log_error("Failed to submit gpu command buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); SDL_CancelGPUCommandBuffer(command_buffer); return false; } SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); return true; } SDL_GPUBuffer *create_buffer(SDL_GPUBufferUsageFlags usage, Uint32 num_bytes, void *data = NULL, const char *name = NULL) { SDL_PropertiesID properties = 0; if (name) { properties = SDL_CreateProperties(); if (properties) SDL_SetStringProperty(properties, SDL_PROP_GPU_BUFFER_CREATE_NAME_STRING, name); } SDL_GPUBufferCreateInfo buffer_info = { .usage = usage, .size = num_bytes, .props = properties, }; SDL_GPUBuffer *buffer = SDL_CreateGPUBuffer(device, &buffer_info); if (properties) SDL_DestroyProperties(properties); if (data) { if (!update_buffer(buffer, 0, num_bytes, data)) { log_error("Failed to update buffer (%s).", SDL_GetError()); SDL_ReleaseGPUBuffer(device, buffer); return NULL; } } return buffer; } SDL_GPUTexture *create_shader_texture(const char *path) { int width = 0, height = 0, channels = 0; stbi_uc *data = stbi_load(path, &width, &height, &channels, 0); if (!data) { log_error("Failed to load texture (\"%s\").", path); return NULL; } SDL_GPUTextureFormat format = SDL_GPU_TEXTUREFORMAT_INVALID; if (channels == 1) format = SDL_GPU_TEXTUREFORMAT_A8_UNORM; if (channels == 4) format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; if (format == SDL_GPU_TEXTUREFORMAT_INVALID) { log_error("Failed to find texture format for texture (\"%s\").", path); stbi_image_free(data); return NULL; } SDL_PropertiesID properties = SDL_CreateProperties(); if (properties) SDL_SetStringProperty(properties, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING, path); SDL_GPUTextureCreateInfo texture_info = { .type = SDL_GPU_TEXTURETYPE_2D, .format = format, .usage = SDL_GPU_TEXTUREUSAGE_SAMPLER, .width = (Uint32)width, .height = (Uint32)height, .layer_count_or_depth = 1, .num_levels = 1, .props = properties, }; SDL_GPUTexture *texture = SDL_CreateGPUTexture(device, &texture_info); if (!texture) { log_error("Failed to create gpu texture (%s).", SDL_GetError()); stbi_image_free(data); return NULL; } if (properties) SDL_DestroyProperties(properties); Uint32 upload_size = width * height * channels; SDL_GPUTransferBufferCreateInfo transfer_buffer_info = { .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = upload_size, }; SDL_GPUTransferBuffer *transfer_buffer = SDL_CreateGPUTransferBuffer(device, &transfer_buffer_info); if (!transfer_buffer) { log_error("Failed to create gpu transfer buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTexture(device, texture); stbi_image_free(data); return NULL; } void *transfer_data = SDL_MapGPUTransferBuffer(device, transfer_buffer, false); if (!transfer_data) { log_error("Failed to map gpu transfer buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); SDL_ReleaseGPUTexture(device, texture); stbi_image_free(data); return NULL; } memcpy(transfer_data, data, upload_size); SDL_UnmapGPUTransferBuffer(device, transfer_buffer); stbi_image_free(data); SDL_GPUTextureTransferInfo transfer_info = { .transfer_buffer = transfer_buffer, .pixels_per_row = (Uint32)width, }; SDL_GPUTextureRegion tetxure_region = { .texture = texture, .w = (Uint32)width, .h = (Uint32)height, .d = 1, }; SDL_GPUCommandBuffer *command_buffer = SDL_AcquireGPUCommandBuffer(device); if (!command_buffer) { log_error("Failed to acquire gpu command buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); SDL_ReleaseGPUTexture(device, texture); return NULL; } SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(command_buffer); SDL_UploadToGPUTexture(copy_pass, &transfer_info, &tetxure_region, false); SDL_EndGPUCopyPass(copy_pass); if (!SDL_SubmitGPUCommandBuffer(command_buffer)) { log_error("Failed to submit gpu command buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); SDL_ReleaseGPUTexture(device, texture); SDL_CancelGPUCommandBuffer(command_buffer); return NULL; } SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); return texture; } SDL_GPUTexture *create_shader_texture(const char *name, const char *data, int width, int height, int channels) { SDL_GPUTextureFormat format = SDL_GPU_TEXTUREFORMAT_INVALID; if (channels == 1) format = SDL_GPU_TEXTUREFORMAT_A8_UNORM; if (channels == 4) format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; if (format == SDL_GPU_TEXTUREFORMAT_INVALID) { log_error("Failed to find texture format for texture."); return NULL; } SDL_PropertiesID properties = SDL_CreateProperties(); if (properties) SDL_SetStringProperty(properties, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING, name); SDL_GPUTextureCreateInfo texture_info = { .type = SDL_GPU_TEXTURETYPE_2D, .format = format, .usage = SDL_GPU_TEXTUREUSAGE_SAMPLER, .width = (Uint32)width, .height = (Uint32)height, .layer_count_or_depth = 1, .num_levels = 1, .props = properties, }; SDL_GPUTexture *texture = SDL_CreateGPUTexture(device, &texture_info); if (!texture) { log_error("Failed to create gpu texture (%s).", SDL_GetError()); return NULL; } if (properties) SDL_DestroyProperties(properties); Uint32 upload_size = width * height * channels; SDL_GPUTransferBufferCreateInfo transfer_buffer_info = { .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = upload_size, }; SDL_GPUTransferBuffer *transfer_buffer = SDL_CreateGPUTransferBuffer(device, &transfer_buffer_info); if (!transfer_buffer) { log_error("Failed to create gpu transfer buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTexture(device, texture); return NULL; } void *transfer_data = SDL_MapGPUTransferBuffer(device, transfer_buffer, false); if (!transfer_data) { log_error("Failed to map gpu transfer buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); SDL_ReleaseGPUTexture(device, texture); return NULL; } memcpy(transfer_data, data, upload_size); SDL_UnmapGPUTransferBuffer(device, transfer_buffer); SDL_GPUTextureTransferInfo transfer_info = { .transfer_buffer = transfer_buffer, .pixels_per_row = (Uint32)width, }; SDL_GPUTextureRegion tetxure_region = { .texture = texture, .w = (Uint32)width, .h = (Uint32)height, .d = 1, }; SDL_GPUCommandBuffer *command_buffer = SDL_AcquireGPUCommandBuffer(device); if (!command_buffer) { log_error("Failed to acquire gpu command buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); SDL_ReleaseGPUTexture(device, texture); return NULL; } SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(command_buffer); SDL_UploadToGPUTexture(copy_pass, &transfer_info, &tetxure_region, false); SDL_EndGPUCopyPass(copy_pass); if (!SDL_SubmitGPUCommandBuffer(command_buffer)) { log_error("Failed to submit gpu command buffer (%s).", SDL_GetError()); SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); SDL_ReleaseGPUTexture(device, texture); SDL_CancelGPUCommandBuffer(command_buffer); return NULL; } SDL_ReleaseGPUTransferBuffer(device, transfer_buffer); return texture; } void blit(char *dst, Sint32 dst_width, Sint32 dst_x, Sint32 dst_y, char *src, Sint32 width, Sint32 height, int components = 4) { for (Sint32 y = 0; y < height; y++) memmove(&dst[((dst_y + y) * dst_width + dst_x) * components], &src[y * width * components], width * components); } bool SelectableImage(const char *label, bool selected, SDL_GPUTextureSamplerBinding *image, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1)) { const ImGuiContext *context = ImGui::GetCurrentContext(); const ImVec2 padding = context->Style.FramePadding; bool pressed = ImGui::Selectable(label, selected, 0, image_size + padding * 2.0f); ImVec2 min = ImGui::GetItemRectMin(); ImVec2 max = ImGui::GetItemRectMax(); if (image) context->CurrentWindow->DrawList->AddImage((ImTextureID)image, min + padding, max - padding, uv0, uv1); return pressed; } int main(int argc, char **argv) { if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { log_error("Failed to initialize SDL (%s). Exiting.", SDL_GetError()); return 1; } device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV, true, NULL); if (!device) { log_error("Failed to create gpu device (%s). Exiting.", SDL_GetError()); return 1; } SDL_SetGPUAllowedFramesInFlight(device, 1); window = SDL_CreateWindow("Mikemon", 1280, 720, SDL_WINDOW_RESIZABLE); if (!window) { log_error("Failed to create window (%s). Exiting.", SDL_GetError()); return 1; } if (!SDL_ClaimWindowForGPUDevice(device, window)) { log_error("Failed to claim window for gpu device (%s). Exiting.", SDL_GetError()); return 1; } SDL_SetGPUSwapchainParameters(device, window, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC); SDL_GPUShaderCreateInfo basic_vertex_shader_info = { .code_size = SDL_arraysize(SPIRV_basic_vertex_shader), .code = SPIRV_basic_vertex_shader, .entrypoint = "main", .format = SDL_GPU_SHADERFORMAT_SPIRV, .stage = SDL_GPU_SHADERSTAGE_VERTEX, .num_uniform_buffers = 1, }; basic_vertex_shader = SDL_CreateGPUShader(device, &basic_vertex_shader_info); if (!basic_vertex_shader) { log_error("Failed to create basic vertex shader. Exiting."); return 1; } SDL_GPUShaderCreateInfo basic_pixel_shader_info = { .code_size = SDL_arraysize(SPIRV_basic_pixel_shader), .code = SPIRV_basic_pixel_shader, .entrypoint = "main", .format = SDL_GPU_SHADERFORMAT_SPIRV, .stage = SDL_GPU_SHADERSTAGE_FRAGMENT, .num_samplers = 1, }; basic_pixel_shader = SDL_CreateGPUShader(device, &basic_pixel_shader_info); if (!basic_pixel_shader) { log_error("Failed to create basic pixel shader. Exiting."); return 1; } SDL_GPUVertexBufferDescription vertex_buffer_descriptions[] = { { .slot = 0, .pitch = sizeof(Vertex), .input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX, }, { .slot = 1, .pitch = sizeof(Instance), .input_rate = SDL_GPU_VERTEXINPUTRATE_INSTANCE, .instance_step_rate = 1, }, }; SDL_GPUVertexAttribute vertex_attributes[] = { { .location = 0, .buffer_slot = 0, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4, .offset = 0, }, { .location = 1, .buffer_slot = 0, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, .offset = 16, }, { .location = 2, .buffer_slot = 1, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4, .offset = 0, }, { .location = 3, .buffer_slot = 1, .format = SDL_GPU_VERTEXELEMENTFORMAT_UINT, .offset = 16, }, { .location = 4, .buffer_slot = 1, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4, .offset = 20, }, }; SDL_GPUColorTargetDescription color_target_descriptions[] = { { .format = SDL_GetGPUSwapchainTextureFormat(device, window), .blend_state = { .src_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE, .dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, .color_blend_op = SDL_GPU_BLENDOP_ADD, .src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE, .dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, .alpha_blend_op = SDL_GPU_BLENDOP_ADD, .enable_blend = true, }, }, }; SDL_GPUGraphicsPipelineCreateInfo basic_graphics_pipeline_info = { .vertex_shader = basic_vertex_shader, .fragment_shader = basic_pixel_shader, .vertex_input_state = { .vertex_buffer_descriptions = vertex_buffer_descriptions, .num_vertex_buffers = SDL_arraysize(vertex_buffer_descriptions), .vertex_attributes = vertex_attributes, .num_vertex_attributes = SDL_arraysize(vertex_attributes), }, .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, .rasterizer_state = { .fill_mode = SDL_GPU_FILLMODE_FILL, .cull_mode = SDL_GPU_CULLMODE_BACK, .front_face = SDL_GPU_FRONTFACE_CLOCKWISE, }, .target_info = { .color_target_descriptions = color_target_descriptions, .num_color_targets = SDL_arraysize(color_target_descriptions), }, }; basic_graphics_pipeline = SDL_CreateGPUGraphicsPipeline(device, &basic_graphics_pipeline_info); if (!basic_graphics_pipeline) { log_error("Failed to create basic graphics pipeline. Exiting."); return 1; } for (int y = 0; y < view_height; y++) { for (int x = 0; x < view_width; x++) { tiles_instances[x + y * view_width].pos_size.zw = { 1.0f / view_width, 1.0f / view_height }; tiles_instances[x + y * view_width].pos_size.xy = { (float)x / view_width, (float)y / (float)view_height }; tiles_instances[x + y * view_width].pos_size.xy += tiles_instances[x + y * view_width].pos_size.zw * 0.5f; tiles_instances[x + y * view_width].uv0uv1 = { 0, 0, 1, 1 }; } } SDL_GPUTexture *player_texture = create_shader_texture("../assets/decorations/strawberry.png"); if (!player_texture) { log_error("Failed to create shader texture. Exiting."); return 1; } tile_atlas = sma_atlas_create(tile_atlas_size, tile_atlas_size); char *tile_atlas_texture_cpu = (char *)calloc(1, tile_atlas_size * tile_atlas_size * 4); for (int i = 0; i < SDL_arraysize(tile_infos); i++) { int width = 0, height = 0; stbi_uc *data = stbi_load(tile_infos[i].asset_path, &width, &height, NULL, 4); if (!data) { log_error("Failed to load texture (\"%s\"). Exiting.", tile_infos[i].asset_path); return 1; } tile_infos[i].atlas_item = sma_item_add(tile_atlas, width, height); if (!tile_infos[i].atlas_item) { log_error("Failed to add tile texture to atlas. Exiting."); return 1; } blit(tile_atlas_texture_cpu, tile_atlas_size, sma_item_x(tile_infos[i].atlas_item), sma_item_y(tile_infos[i].atlas_item), (char *)data, width, height); stbi_image_free(data); } SDL_GPUTexture *tile_atlas_texture = create_shader_texture("tile_atlas", tile_atlas_texture_cpu, tile_atlas_size, tile_atlas_size, 4); free(tile_atlas_texture_cpu); if (!tile_atlas_texture) { log_error("Failed to create tile atlas texture. Exiting."); return 1; } SDL_GPUSamplerCreateInfo point_sampler_info = { .min_filter = SDL_GPU_FILTER_NEAREST, .mag_filter = SDL_GPU_FILTER_NEAREST, .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST, .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, }; point_sampler = SDL_CreateGPUSampler(device, &point_sampler_info); vertex_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_VERTEX, sizeof(vertices), vertices, "vertex_buffer"); if (!vertex_buffer) { log_error("Failed to create buffer. Exiting."); return 1; } index_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_INDEX, sizeof(indices), indices, "index_buffer"); if (!index_buffer) { log_error("Failed to create buffer. Exiting."); return 1; } tiles_instance_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_VERTEX, sizeof(tiles_instances), tiles_instances, "tiles_instance_buffer"); if (!tiles_instance_buffer) { log_error("Failed to create buffer. Exiting."); return 1; } player_instance_buffer = create_buffer(SDL_GPU_BUFFERUSAGE_VERTEX, sizeof(player_instance), &player_instance, "player_instance_buffer"); if (!player_instance_buffer) { log_error("Failed to create buffer. Exiting."); return 1; } load_map(); IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO &io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; ImGui::StyleColorsDark(); ImGui_ImplSDL3_InitForSDLGPU(window); ImGui_ImplSDLGPU3_InitInfo imgui_init_info = { .Device = device, .ColorTargetFormat = SDL_GetGPUSwapchainTextureFormat(device, window), .MSAASamples = SDL_GPU_SAMPLECOUNT_1, }; ImGui_ImplSDLGPU3_Init(&imgui_init_info); bool show_demo_window = true; bool first_frame = true; SDL_GetWindowSizeInPixels(window, &window_width, &window_height); // MSG Message; while (Running) { ImGui_ImplSDLGPU3_NewFrame(); ImGui_ImplSDL3_NewFrame(); ImGui::NewFrame(); ImGui::SetNextWindowPos(ImVec2(0, 0)); ImGui::SetNextWindowSizeConstraints(ImVec2(0, window_height), ImVec2(window_width, window_height)); ImGui::SetNextWindowSize(ImVec2(0.2 * window_width, window_height), ImGuiCond_FirstUseEver); if (ImGui::Begin("Tile Picker", NULL, ImGuiWindowFlags_NoFocusOnAppearing)) { if (SelectableImage("##tile", selected_tile == -1, NULL, ImVec2(32, 32))) { selected_tile = -1; } for (int i = 0; i < SDL_arraysize(tile_infos); i++) { ImGui::PushID(i); SDL_GPUTextureSamplerBinding texture_binding = { .texture = tile_atlas_texture, .sampler = point_sampler }; ImVec2 uv0 = ImVec2(sma_item_x(tile_infos[i].atlas_item) / (float)tile_atlas_size, sma_item_y(tile_infos[i].atlas_item) / (float)tile_atlas_size); ImVec2 uv1 = ImVec2(uv0.x + sma_item_width(tile_infos[i].atlas_item) / (float)tile_atlas_size, uv0.y + sma_item_height(tile_infos[i].atlas_item) / (float)tile_atlas_size); float available = ImGui::GetContentRegionAvail().x - ImGui::GetItemRectMax().x; if (available >= 32) ImGui::SameLine(); if (SelectableImage("##tile", selected_tile == i, &texture_binding, ImVec2(32, 32), uv0, uv1)) { selected_tile = i; } ImGui::PopID(); } } ImGui::End(); if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); if (first_frame) { ImGui::SetWindowFocus(NULL); first_frame = false; } SDL_GPUCommandBuffer *command_buffer = SDL_AcquireGPUCommandBuffer(device); if (!command_buffer) { log_error("Failed to acquire gpu command buffer (%s). Exiting.", SDL_GetError()); return 1; } Uint32 width = 0, height = 0; SDL_GPUTexture *swapchain_texture = NULL; if (!SDL_WaitAndAcquireGPUSwapchainTexture(command_buffer, window, &swapchain_texture, &width, &height)) { log_error("Failed to acquire swapchain texture (%s). Exiting.", SDL_GetError()); SDL_CancelGPUCommandBuffer(command_buffer); return 1; } SDL_Event event; while (SDL_PollEvent(&event)) { ImGui_ImplSDL3_ProcessEvent(&event); switch (event.type) { case SDL_EVENT_QUIT: { Running = false; } break; case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: { window_width = event.window.data1; window_height = event.window.data2; } break; case SDL_EVENT_KEY_DOWN: { if (io.WantCaptureKeyboard) continue; if (event.key.key == SDLK_UP || event.key.key == SDLK_W) { player.pos_y = clamp(0, player.pos_y - 1, map_height - 1); } if (event.key.key == SDLK_LEFT || event.key.key == SDLK_A) { player.pos_x = clamp(0, player.pos_x - 1, map_width - 1); } if (event.key.key == SDLK_DOWN || event.key.key == SDLK_S) { player.pos_y = clamp(0, player.pos_y + 1, map_height - 1); } if (event.key.key == SDLK_RIGHT || event.key.key == SDLK_D) { player.pos_x = clamp(0, player.pos_x + 1, map_width - 1); } if (event.key.key == SDLK_F1) { save_map(); } if (event.key.key == SDLK_F4) { load_map(); } } break; case SDL_EVENT_MOUSE_BUTTON_DOWN: { if (io.WantCaptureMouse) continue; if (selected_tile == -1) continue; float mouse_x = event.button.x; float mouse_y = event.button.y; V2 X = V2{ -1, 1 }; V2 Y = V2{ -1, 1 }; float correction_factor = (window_width / (float)window_height) / (16.0 / 9.0); if (correction_factor < 1) Y *= correction_factor; else X /= correction_factor; float m_x = remap(0, window_width, -1, 1, mouse_x); float m_y = remap(0, window_height, -1, 1, mouse_y); mouse_x = remap(X.x, X.y, 0, window_width, m_x); mouse_y = remap(Y.x, Y.y, 0, window_height, m_y); float tile_width = window_width / (float) view_width; float tile_height = window_height / (float) view_height; int tile_x = (int)(mouse_x / tile_width) + player.pos_x - (view_width / 2); int tile_y = (int)(mouse_y / tile_height) + player.pos_y - (view_height / 2); if (mouse_x < 0) tile_x = -1; if (mouse_y< 0) tile_y = -1; if(0 <= tile_x && tile_x < map_width && 0 <= tile_y && tile_y < map_height) map_tiles[tile_x + map_width * tile_y] = selected_tile; SDL_Keymod modifiers = SDL_GetModState(); if (modifiers & SDL_KMOD_SHIFT && tile_x == -1) { if(modifiers & SDL_KMOD_CTRL) change_map_size('W', -1); else change_map_size('W', 2); } if (modifiers & SDL_KMOD_SHIFT && tile_x == map_width) { if (modifiers & SDL_KMOD_CTRL) change_map_size('E', -1); else change_map_size('E', 2); } if (modifiers & SDL_KMOD_SHIFT && tile_y == -1) { if (modifiers & SDL_KMOD_CTRL) change_map_size('N', -1); else change_map_size('N', 2); } if (modifiers & SDL_KMOD_SHIFT && tile_y == map_height) { if (modifiers & SDL_KMOD_CTRL) change_map_size('S', -1); else change_map_size('S', 2); } } break; } } //Tiles updaten for (int y = 0; y < view_height; y++) { for (int x = 0; x < view_width; x++) { if (x + player.pos_x - view_width / 2 >= 0 && x + player.pos_x - view_width / 2 < map_width && y + player.pos_y - view_height / 2 >= 0 && y + player.pos_y - view_height / 2 < map_height) { tiles_instances[x + y * view_width].tile_type = map_tiles[(x + player.pos_x - view_width / 2) + (y + player.pos_y - view_height / 2) * map_width]; } else { tiles_instances[x + y * view_width].tile_type = 0; } Uint32 type = tiles_instances[x + y * view_width].tile_type; float x0 = sma_item_x(tile_infos[type].atlas_item); float y0 = sma_item_y(tile_infos[type].atlas_item); float x1 = x0 + sma_item_width(tile_infos[type].atlas_item); float y1 = y0 + sma_item_height(tile_infos[type].atlas_item); tiles_instances[x + y * view_width].uv0uv1 = { x0 / tile_atlas_size, y0 / tile_atlas_size, x1 / tile_atlas_size, y1 / tile_atlas_size }; } } if (!swapchain_texture) { SDL_CancelGPUCommandBuffer(command_buffer); ImGui::Render(); ImDrawData *draw_data = ImGui::GetDrawData(); continue; } per_frame.aspect_ratio = (float) window_width / (float) window_height; SDL_PushGPUVertexUniformData(command_buffer, 0, &per_frame, sizeof(per_frame)); if (!update_buffer(tiles_instance_buffer, 0, sizeof(tiles_instances), tiles_instances)) { log_error("Failed to update buffer. Exiting."); return 1; } SDL_GPUColorTargetInfo color_target_info = { .texture = swapchain_texture, .clear_color = { .r = 1.0f, .g = 0.0f, .b = 1.0f, .a = 1.0f }, .load_op = SDL_GPU_LOADOP_CLEAR, .store_op = SDL_GPU_STOREOP_STORE, }; SDL_GPURenderPass *render_pass = SDL_BeginGPURenderPass(command_buffer, &color_target_info, 1, NULL); if (!render_pass) { log_error("Failed to begin render pass. Exiting."); return 1; } { // Draw Map SDL_GPUBufferBinding index_buffer_binding = { .buffer = index_buffer, .offset = 0 }; SDL_GPUBufferBinding vertex_buffers[] = { { .buffer = vertex_buffer, .offset = 0 }, { .buffer = tiles_instance_buffer, .offset = 0 }, }; SDL_GPUTextureSamplerBinding texture_bindings[] = { { .texture = tile_atlas_texture, .sampler = point_sampler }, }; SDL_BindGPUGraphicsPipeline(render_pass, basic_graphics_pipeline); SDL_BindGPUIndexBuffer(render_pass, &index_buffer_binding, SDL_GPU_INDEXELEMENTSIZE_16BIT); SDL_BindGPUVertexBuffers(render_pass, 0, vertex_buffers, SDL_arraysize(vertex_buffers)); SDL_BindGPUFragmentSamplers(render_pass, 0, texture_bindings, SDL_arraysize(texture_bindings)); SDL_DrawGPUIndexedPrimitives(render_pass, 6, view_width * view_height, 0, 0, 0); } { // Draw Player SDL_GPUBufferBinding vertex_buffers[] = { { .buffer = vertex_buffer, .offset = 0 }, { .buffer = player_instance_buffer, .offset = 0 }, }; SDL_GPUTextureSamplerBinding texture_bindings[] = { { .texture = player_texture, .sampler = point_sampler }, }; SDL_BindGPUGraphicsPipeline(render_pass, basic_graphics_pipeline); SDL_BindGPUVertexBuffers(render_pass, 0, vertex_buffers, SDL_arraysize(vertex_buffers)); SDL_BindGPUFragmentSamplers(render_pass, 0, texture_bindings, SDL_arraysize(texture_bindings)); SDL_DrawGPUIndexedPrimitives(render_pass, 6, 1, 0, 0, 0); } SDL_EndGPURenderPass(render_pass); ImGui::Render(); ImDrawData *draw_data = ImGui::GetDrawData(); Imgui_ImplSDLGPU3_PrepareDrawData(draw_data, command_buffer); SDL_GPUColorTargetInfo imgui_color_target_info = { .texture = swapchain_texture, .load_op = SDL_GPU_LOADOP_LOAD, .store_op = SDL_GPU_STOREOP_STORE, }; SDL_GPURenderPass *imgui_render_pass = SDL_BeginGPURenderPass(command_buffer, &imgui_color_target_info, 1, NULL); ImGui_ImplSDLGPU3_RenderDrawData(draw_data, command_buffer, imgui_render_pass); SDL_EndGPURenderPass(imgui_render_pass); if (!SDL_SubmitGPUCommandBuffer(command_buffer)) { log_error("Failed to submit gpu command buffer (%s). Exiting.", SDL_GetError()); return 1; } } return 0; }