change tiles from a texture array to a texture atlas

This commit is contained in:
Sven Balzer
2025-02-27 14:22:03 +01:00
parent 2d583d8f72
commit 1c8a65e8ad
7 changed files with 794 additions and 317 deletions
+110 -79
View File
@@ -6,6 +6,7 @@
#include <imgui_impl_sdl3.h>
#include <imgui_impl_sdlgpu3.h>
#include "smol-atlas.h"
#include "defer.h"
#include "log.h"
#include "m_string.h"
@@ -31,8 +32,8 @@ SDL_GPUBuffer *index_buffer;
SDL_GPUBuffer *tiles_instance_buffer;
SDL_GPUBuffer *player_instance_buffer;
Sint16 window_width;
Sint16 window_height;
Sint32 window_width;
Sint32 window_height;
bool Running = true;
@@ -68,14 +69,10 @@ 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}};
struct Tile {
Uint32 type;
};
Uint32 map_width = view_width;
Uint32 map_height = view_height;
Tile* map_tiles;
Uint32* map_tiles;
struct Player {
Sint64 pos_x;
@@ -94,6 +91,24 @@ struct PerFrame {
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" },
{ 0x0200, "../assets/tiles/ground_1.png" },
{ 0x0300, "../assets/tiles/water_1.png" },
};
int tile_atlas_size = 256;
smol_atlas_t *tile_atlas;
void save_map() {
log("Save file is under construction.");
FILE* file = fopen("../assets/map/map.sv", "wb");
@@ -115,7 +130,8 @@ void save_map() {
}
for (int i = 0; i < map_width * map_height; i++) {
if (fwrite(&map_tiles[i], sizeof(Tile), 1, file) != 1) {
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;
}
@@ -142,17 +158,26 @@ void load_map() {
if (map_tiles)
free(map_tiles);
map_tiles = (Tile*)malloc(map_width * map_height * sizeof(Tile));
map_tiles = (Uint32*)malloc(map_width * map_height * sizeof(Uint32));
for (int i = 0; i < map_width * map_height; i++) {
map_tiles[i] = read<Tile>(file);
Uint32 type = read<Uint32>(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) {
Tile* old_map = map_tiles;
Uint32* old_map = map_tiles;
auto old_map_width = map_width;
auto old_map_height = map_height;
@@ -200,7 +225,7 @@ void change_map_size(char direction, int amount) {
to_fill_y_offset = old_map_height;
}
map_tiles = (Tile*)malloc(map_width * map_height * sizeof(Tile));
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++) {
@@ -210,7 +235,7 @@ void change_map_size(char direction, int amount) {
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)].type = 0;
map_tiles[(y + to_fill_y_offset) * map_width + (x + to_fill_x_offset)] = 0;
}
}
@@ -406,27 +431,27 @@ SDL_GPUTexture *create_shader_texture(const char *path) {
return texture;
}
SDL_GPUTexture* create_shader_texture_array(Uint32 num_textures, const char **texture_paths, const char *debug_name) {
if (!num_textures) return NULL;
int width = 0, height = 0, channels = 0;
stbi_uc *data = stbi_load(texture_paths[0], &width, &height, &channels, 4);
if (!data) {
log_error("Failed to load texture (\"%s\").", texture_paths[0]);
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, debug_name);
if (properties) SDL_SetStringProperty(properties, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING, name);
SDL_GPUTextureCreateInfo texture_info = {
.type = SDL_GPU_TEXTURETYPE_2D_ARRAY,
.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
.type = SDL_GPU_TEXTURETYPE_2D,
.format = format,
.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER,
.width = (Uint32)width,
.height = (Uint32)height,
.layer_count_or_depth = (Uint32)num_textures,
.layer_count_or_depth = 1,
.num_levels = 1,
.props = properties,
@@ -435,23 +460,20 @@ SDL_GPUTexture* create_shader_texture_array(Uint32 num_textures, const char **te
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 * 4;
Uint32 total_upload_size = upload_size * num_textures;
Uint32 upload_size = width * height * channels;
SDL_GPUTransferBufferCreateInfo transfer_buffer_info = {
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
.size = total_upload_size,
.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;
}
@@ -460,30 +482,23 @@ SDL_GPUTexture* create_shader_texture_array(Uint32 num_textures, const char **te
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);
stbi_image_free(data);
for (int i = 1; i < num_textures; i++) {
int width2 = 0, height2 = 0;
stbi_uc *data = stbi_load(texture_paths[i], &width2, &height2, &channels, 4);
if (!data || width != width2 || height != height2) {
log_error("Failed to load texture (\"%s\").", texture_paths[i]);
SDL_UnmapGPUTransferBuffer(device, transfer_buffer);
SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
SDL_ReleaseGPUTexture(device, texture);
return NULL;
}
memcpy((void *)((char *)transfer_data + i * upload_size), data, upload_size);
stbi_image_free(data);
}
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());
@@ -493,26 +508,7 @@ SDL_GPUTexture* create_shader_texture_array(Uint32 num_textures, const char **te
}
SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(command_buffer);
for (int i = 0; i < num_textures; i++) {
SDL_GPUTextureTransferInfo transfer_info = {
.transfer_buffer = transfer_buffer,
.offset = i * upload_size,
.pixels_per_row = (Uint32)width,
.rows_per_layer = (Uint32)height,
};
SDL_GPUTextureRegion tetxure_region = {
.texture = texture,
.layer = (Uint32)i,
.w = (Uint32)width,
.h = (Uint32)height,
.d = 1,
};
SDL_UploadToGPUTexture(copy_pass, &transfer_info, &tetxure_region, false);
}
SDL_UploadToGPUTexture(copy_pass, &transfer_info, &tetxure_region, false);
SDL_EndGPUCopyPass(copy_pass);
if (!SDL_SubmitGPUCommandBuffer(command_buffer)) {
@@ -527,6 +523,11 @@ SDL_GPUTexture* create_shader_texture_array(Uint32 num_textures, const char **te
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);
}
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());
@@ -684,17 +685,35 @@ int main(int argc, char **argv) {
}
}
const char *player_texture_array[] = { "../assets/decorations/strawberry.png" };
SDL_GPUTexture *player_texture = create_shader_texture_array(SDL_arraysize(player_texture_array), player_texture_array, "player_textures");
SDL_GPUTexture *player_texture = create_shader_texture("../assets/decorations/strawberry.png");
if (!player_texture) {
log_error("Failed to create shader texture. Exiting.");
return 1;
}
const char *tile_texture_array[] = { "../assets/tiles/empty.png", "../assets/tiles/grass_1.png" , "../assets/tiles/ground_1.png" , "../assets/tiles/water_1.png", "../assets/tiles/error.png" };
SDL_GPUTexture *tile_texture = create_shader_texture_array(SDL_arraysize(tile_texture_array), tile_texture_array, "tile_textures");
if (!player_texture) {
log_error("Failed to create shader texture array. Exiting.");
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;
}
@@ -755,6 +774,9 @@ int main(int argc, char **argv) {
bool show_demo_window = true;
bool first_frame = true;
SDL_GetWindowSizeInPixels(window, &window_width, &window_height);
// MSG Message;
while (Running) {
ImGui_ImplSDLGPU3_NewFrame();
@@ -861,7 +883,7 @@ int main(int argc, char **argv) {
if(0 <= tile_x && tile_x < map_width &&
0 <= tile_y && tile_y < map_height)
map_tiles[tile_x + map_width * tile_y].type = (map_tiles[tile_x + map_width * tile_y].type + 1) % 4;
map_tiles[tile_x + map_width * tile_y] = (map_tiles[tile_x + map_width * tile_y] + 1) % 4;
SDL_Keymod modifiers = SDL_GetModState();
if (modifiers & SDL_KMOD_SHIFT && tile_x == -1) {
@@ -900,10 +922,19 @@ int main(int argc, char **argv) {
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].type;
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 = 4;
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 };
}
}
@@ -942,7 +973,7 @@ int main(int argc, char **argv) {
{ .buffer = tiles_instance_buffer, .offset = 0 },
};
SDL_GPUTextureSamplerBinding texture_bindings[] = {
{ .texture = tile_texture, .sampler = point_sampler },
{ .texture = tile_atlas_texture, .sampler = point_sampler },
};
SDL_BindGPUGraphicsPipeline(render_pass, basic_graphics_pipeline);
@@ -987,4 +1018,4 @@ int main(int argc, char **argv) {
}
}
return 0;
}
}