From bd57f345ed9dbed1d81683e48199626de2ea9044 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 27 Jun 2025 10:18:39 -0700 Subject: Restructure project --- src/asset/texture.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 src/asset/texture.c (limited to 'src/asset/texture.c') diff --git a/src/asset/texture.c b/src/asset/texture.c new file mode 100644 index 0000000..c790394 --- /dev/null +++ b/src/asset/texture.c @@ -0,0 +1,177 @@ +#include "texture.h" + +#include "gfx/core.h" + +#include "error.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#include + +static void flip_horizontally( + unsigned char* pixels, int width, int height, int components) { + assert(pixels); + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width / 2; ++x) { + unsigned char* p1 = &pixels[(y * width + x) * components]; + unsigned char* p2 = &pixels[(y * width + (width - x - 1)) * components]; + + for (int c = 0; c < components; ++c) { + unsigned char tmp = *p1; + *p1 = *p2; + *p2 = tmp; + p1++; + p2++; + } + } + } +} + +// Note that the cubemap coordinate system uses the one in RenderMan: +// +// https://www.khronos.org/opengl/wiki/Cubemap_Texture +// +// This is what happens: +// +// - Cubemaps follow a left-handed coordinate system. Say, +X is right, +Y is +// up, and +Z is forward. +// - The texture coordinate system follow's DirectX's, so +V goes down, not up +// like it does in OpenGL. +// +// For this reason, we do X and Y flips when doing cubemap textures so that we +// can sample cubemaps as if they were given in the usual OpenGL coordinate +// system. +Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { + assert(gfxcore); + assert(cmd); + assert(cmd->origin == AssetFromFile || cmd->origin == AssetFromMemory); + assert(cmd->type == LoadTexture || cmd->type == LoadCubemap); + + int width, height, components, old_components; + unsigned char* pixels[6] = {0}; + + switch (cmd->origin) { + case AssetFromFile: + switch (cmd->type) { + case LoadTexture: { + const char* filepath = mstring_cstr(&cmd->data.texture.filepath); + stbi_set_flip_vertically_on_load(0); + pixels[0] = stbi_load(filepath, &width, &height, &components, 0); + if (!pixels[0]) { + log_error("Failed to load texture file: %s", filepath); + } + break; + } + case LoadCubemap: + for (int i = 0; i < 6; ++i) { + // Flip +Y and -Y textures vertically. + stbi_set_flip_vertically_on_load(((i == 2) || (i == 3)) ? 1 : 0); + const char* filepath = + mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x + i); + stbi_uc* image_pixels = + stbi_load(filepath, &width, &height, &components, 0); + if (!image_pixels) { + log_error("Failed to load texture file: %s", filepath); + break; + } + if (i > 0 && components != old_components) { + log_error("All textures in a cubemap must have the same number of " + "components"); + break; + } + if ((i != 2) && (i != 3)) { + flip_horizontally(image_pixels, width, height, components); + } + pixels[i] = image_pixels; + old_components = components; + } + break; + } + break; + case AssetFromMemory: + // TODO: Load textures from memory. + log_error("Loading textures from memory is not yet implemented"); + return 0; + } + + // Error out if we failed to load a texture. + if (!pixels[0] || + (cmd->type == LoadCubemap && + (!pixels[1] || !pixels[2] || !pixels[3] || !pixels[4] || !pixels[5]))) { + for (int i = 0; i < 6; ++i) { + if (pixels[i]) { + stbi_image_free(pixels[i]); + } + } + return 0; + } + + TextureDesc desc = (TextureDesc){0}; + desc.width = width; + desc.height = height; + + switch (cmd->type) { + case LoadTexture: + desc.dimension = Texture2D; + break; + case LoadCubemap: + desc.dimension = TextureCubeMap; + break; + } + + switch (components) { + case 3: + switch (cmd->colour_space) { + case LinearColourSpace: + desc.format = TextureRGB8; + break; + case sRGB: + desc.format = TextureSRGB8; + break; + default: + log_error("Unsupported texture colour space: %d", cmd->colour_space); + return 0; + } + break; + case 4: + switch (cmd->colour_space) { + case LinearColourSpace: + desc.format = TextureRGBA8; + break; + case sRGB: + desc.format = TextureSRGBA8; + break; + default: + log_error("Unsupported texture colour space: %d", cmd->colour_space); + return 0; + } + break; + default: + log_error("Unsupported number of texture components: %d", components); + return 0; + } + + desc.filtering = cmd->filtering; + desc.mipmaps = cmd->mipmaps; + + switch (cmd->type) { + case LoadTexture: + desc.data.pixels = pixels[0]; + break; + case LoadCubemap: + for (int i = 0; i < 6; ++i) { + *(&desc.data.cubemap.pixels_pos_x + i) = pixels[i]; + } + break; + } + + Texture* texture = gfx_make_texture(gfxcore, &desc); + for (int i = 0; i < 6; ++i) { + if (pixels[i]) { + stbi_image_free(pixels[i]); + } + } + return texture; +} -- cgit v1.2.3