Lumenarium/src_v2/lumenarium_texture_atlas.cpp

189 lines
4.8 KiB
C++
Raw Normal View History

/* date = March 31st 2022 8:30 pm */
#ifndef LUMENARIUM_TEXTURE_ATLAS_CPP
#define LUMENARIUM_TEXTURE_ATLAS_CPP
struct Texture_Atlas_Sprite
{
u16 min_x;
u16 min_y;
u16 max_x;
u16 max_y;
2022-04-07 12:02:12 +00:00
v2 draw_offset;
};
struct Texture_Atlas
{
u8* pixels;
2022-04-07 12:02:12 +00:00
u32 width;
u32 height;
2022-04-07 12:02:12 +00:00
u32 next_x;
u32 next_y;
u32 y_used;
u32* ids;
Texture_Atlas_Sprite* sprites;
u32 cap;
u32 used;
};
internal Texture_Atlas
texture_atlas_create(u32 width, u32 height, u32 cap, Allocator* allocator)
{
Texture_Atlas result = {};
result.pixels = allocator_alloc_array(allocator, u8, width * height * 4);
result.width = (u16)width;
result.height = (u16)height;
for (u32 i = 0; i < width * height; i++) {
u8* base = result.pixels + (i * 4);
2022-04-07 12:02:12 +00:00
*(u32*)base = 0x00FFFFFF;
}
result.ids = allocator_alloc_array(allocator, u32, cap);
result.sprites = allocator_alloc_array(allocator, Texture_Atlas_Sprite, cap);
result.cap = cap;
hash_table_init(result.ids, cap);
return result;
}
2022-04-07 12:02:12 +00:00
enum Texture_Atlas_Registration_Flags
{
TextureAtlasRegistration_None = 0,
TextureAtlasRegistration_PixelFormat_RGBA = 1,
TextureAtlasRegistration_PixelFormat_Alpha = 2,
};
internal void
2022-04-07 12:02:12 +00:00
texture_atlas_register(Texture_Atlas* ta, u8* pixels, u32 width, u32 height, u32 id, v2 draw_offset, u32 flags)
{
2022-04-07 12:02:12 +00:00
if (ta->next_x > ta->width || (ta->next_x + width + 2) > ta->width)
{
ta->next_x = 0;
ta->next_y = ta->y_used;
}
2022-04-07 12:02:12 +00:00
u32 min_x = ta->next_x + 1;
u32 min_y = ta->next_y + 1;
u32 max_x = min_x + width;
u32 max_y = min_y + height;
// copy the data
2022-04-07 12:02:12 +00:00
if (has_flag(flags, TextureAtlasRegistration_PixelFormat_RGBA))
{
2022-04-07 12:02:12 +00:00
for (u32 y = 0; y < height; y++)
{
2022-04-07 12:02:12 +00:00
u32 src_row = (y * width) * 4;
u32 dst_row = (((y + min_y) * ta->width) + min_x) * 4;
for (u32 x = 0; x < width; x++)
{
ta->pixels[dst_row++] = pixels[src_row++];
ta->pixels[dst_row++] = pixels[src_row++];
ta->pixels[dst_row++] = pixels[src_row++];
ta->pixels[dst_row++] = pixels[src_row++];
}
}
}
2022-04-07 12:02:12 +00:00
else if (has_flag(flags, TextureAtlasRegistration_PixelFormat_Alpha))
{
for (u32 y = 0; y < height; y++)
{
u32 src_row = y * width;
u32 dst_row = (((y + min_y) * ta->width) + min_x) * 4;
for (u32 x = 0; x < width; x++)
{
ta->pixels[dst_row++] = 0xFF;
ta->pixels[dst_row++] = 0xFF;
ta->pixels[dst_row++] = 0xFF;
ta->pixels[dst_row++] = pixels[src_row++];
}
}
}
// copy nearest pixels to the border
u32 pi_width = ta->width;
u32 pi_stride = 4;
#define PIXEL_INDEX(x,y) ((((y) * pi_width) + (x)) * pi_stride)
#define COPY_PIXEL(db,sb) \
ta->pixels[(db) + 0] = ta->pixels[(sb) + 0]; \
ta->pixels[(db) + 1] = ta->pixels[(sb) + 1]; \
ta->pixels[(db) + 2] = ta->pixels[(sb) + 2]; \
ta->pixels[(db) + 3] = ta->pixels[(sb) + 3];
for (u32 x = 0; x < width; x++)
{
u32 top = PIXEL_INDEX(min_x + x, min_y - 1);
u32 top_near = PIXEL_INDEX(min_x + x, min_y);
u32 bot = PIXEL_INDEX(min_x + x, max_y);
u32 bot_near = PIXEL_INDEX(min_x + x, max_y - 1);
COPY_PIXEL(top, top_near);
COPY_PIXEL(bot, bot_near);
}
for (u32 y = 0; y < height + 2; y++)
{
u32 left = PIXEL_INDEX(min_x - 1, min_y + y - 1);
u32 left_near = PIXEL_INDEX(min_x, min_y + y - 1);
u32 right = PIXEL_INDEX(max_x, min_y + y - 1);
u32 right_near = PIXEL_INDEX(max_x - 1, min_y + y - 1);
COPY_PIXEL(left, left_near);
COPY_PIXEL(right, right_near);
}
#undef PIXEL_INDEX
#undef COPY_PIXEL
// register a new slot
u32 index = hash_table_register(ta->ids, ta->cap, id);
Texture_Atlas_Sprite* sprite = ta->sprites + index;
2022-04-07 12:02:12 +00:00
sprite->min_x = (u16)min_x;
sprite->min_y = (u16)min_y;
sprite->max_x = (u16)max_x;
sprite->max_y = (u16)max_y;
sprite->draw_offset = draw_offset;
// Prepare for next registration
if (max_y > ta->y_used)
{
2022-04-07 12:02:12 +00:00
ta->y_used = max_y + 2;
}
2022-04-07 12:02:12 +00:00
ta->next_x = max_x + 2;
}
internal Texture_Atlas_Sprite
texture_atlas_sprite_get(Texture_Atlas* ta, u32 id)
{
Texture_Atlas_Sprite result = {};
u32 index = hash_table_find(ta->ids, ta->cap, id);
if (index == ta->cap) return result;
result = ta->sprites[index];
return result;
}
internal v4
2022-04-07 12:02:12 +00:00
texture_atlas_sprite_get_uvs(Texture_Atlas* ta, Texture_Atlas_Sprite sprite)
{
v4 result = {};
// uv min
result.x = (r32)sprite.min_x / (r32)ta->width;
result.y = (r32)sprite.min_y / (r32)ta->height;
// uv max
result.z = (r32)sprite.max_x / (r32)ta->width;
result.w = (r32)sprite.max_y / (r32)ta->height;
return result;
}
2022-04-07 12:02:12 +00:00
internal v4
texture_atlas_sprite_get_uvs(Texture_Atlas* ta, u32 id)
{
Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(ta, id);
return texture_atlas_sprite_get_uvs(ta, sprite);
}
#endif //LUMENARIUM_TEXTURE_ATLAS_CPP