From a0bb0d0114c2b228f2f1715b6fc53d99901b0193 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sun, 29 Jun 2025 16:43:36 -0700 Subject: Add support for R8 linear textures to render baked AO --- include/gfx/core.h | 1 + src/asset/model.c | 111 ++++++++++++++++++++++++++-------------------------- src/asset/texture.c | 32 ++++++++++++--- src/core/texture.c | 10 +++++ 4 files changed, 94 insertions(+), 60 deletions(-) diff --git a/include/gfx/core.h b/include/gfx/core.h index 44509c9..2a29003 100644 --- a/include/gfx/core.h +++ b/include/gfx/core.h @@ -219,6 +219,7 @@ typedef enum { Texture2D, TextureCubeMap } TextureDimension; /// Texture data format. typedef enum { TextureDepth, + TextureR8, TextureRG16, TextureRG16F, TextureRGB8, diff --git a/src/asset/model.c b/src/asset/model.c index 25f2780..402b2e1 100644 --- a/src/asset/model.c +++ b/src/asset/model.c @@ -578,11 +578,10 @@ static bool load_buffers( const cgltf_buffer* buffer = &data->buffers[i]; assert(buffer->data); buffers[i] = gfx_make_buffer( - gfxcore, &(BufferDesc){ - .usage = BufferStatic, - .type = BufferUntyped, - .data.data = buffer->data, - .data.count = buffer->size}); + gfxcore, &(BufferDesc){.usage = BufferStatic, + .type = BufferUntyped, + .data.data = buffer->data, + .data.count = buffer->size}); if (!buffers[i]) { return false; } @@ -604,11 +603,10 @@ static bool load_tangent_buffers( const cgltfTangentBuffer* buffer = &cgltf_tangent_buffers[i]; assert(buffer->data); tangent_buffers[i] = gfx_make_buffer( - gfxcore, &(BufferDesc){ - .usage = BufferStatic, - .type = BufferUntyped, - .data.data = buffer->data, - .data.count = buffer->size_bytes}); + gfxcore, &(BufferDesc){.usage = BufferStatic, + .type = BufferUntyped, + .data.data = buffer->data, + .data.count = buffer->size_bytes}); if (!tangent_buffers[i]) { return false; } @@ -682,14 +680,13 @@ static void load_textures_lazy( mstring fullpath = mstring_concat_path(mstring_make(directory), mstring_make(image->uri)); - load_texture_cmds[i] = (LoadTextureCmd){ - .origin = AssetFromFile, - .type = LoadTexture, - .colour_space = sRGB, - .filtering = filtering, - .wrap = wrap, - .mipmaps = mipmaps, - .data.texture.filepath = fullpath}; + load_texture_cmds[i] = (LoadTextureCmd){.origin = AssetFromFile, + .type = LoadTexture, + .colour_space = LinearColourSpace, + .filtering = filtering, + .wrap = wrap, + .mipmaps = mipmaps, + .data.texture.filepath = fullpath}; } } @@ -717,8 +714,12 @@ static bool load_texture_and_uniform( // not be used as albedo and vice versa. if (!textures[texture_index]) { LoadTextureCmd* cmd = &load_texture_cmds[texture_index]; - // TODO: Check for colour textures and default to LinearColourSpace instead. - if (texture_type == NormalMap) { + // Albedo and emissive use sRGB. Other textures use a linear colour space. + // See the notes on the spec. + if ((texture_type == BaseColorTexture) || + (texture_type == EmissiveTexture)) { + cmd->colour_space = sRGB; + } else { cmd->colour_space = LinearColourSpace; } @@ -778,22 +779,22 @@ static bool load_materials( .value.vec4 = vec4_from_array(pbr->base_color_factor)}; assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); - desc.uniforms[next_uniform++] = (ShaderUniform){ - .name = sstring_make(UNIFORM_METALLIC_FACTOR), - .type = UniformFloat, - .value.scalar = pbr->metallic_factor}; + desc.uniforms[next_uniform++] = + (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), + .type = UniformFloat, + .value.scalar = pbr->metallic_factor}; assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); - desc.uniforms[next_uniform++] = (ShaderUniform){ - .name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), - .type = UniformFloat, - .value.scalar = pbr->roughness_factor}; + desc.uniforms[next_uniform++] = + (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), + .type = UniformFloat, + .value.scalar = pbr->roughness_factor}; assert(next_uniform < GFX_MAX_UNIFORMS_PER_MATERIAL); - desc.uniforms[next_uniform++] = (ShaderUniform){ - .name = sstring_make(UNIFORM_EMISSIVE_FACTOR), - .type = UniformVec3, - .value.vec3 = vec3_from_array(mat->emissive_factor)}; + desc.uniforms[next_uniform++] = + (ShaderUniform){.name = sstring_make(UNIFORM_EMISSIVE_FACTOR), + .type = UniformVec3, + .value.vec3 = vec3_from_array(mat->emissive_factor)}; if (pbr->base_color_texture.texture) { if (!load_texture_and_uniform( @@ -854,28 +855,28 @@ static Material* make_default_material() { MaterialDesc desc = (MaterialDesc){0}; assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); - desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ - .name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), - .type = UniformVec4, - .value.vec4 = vec4_make(1, 1, 1, 1)}; + desc.uniforms[desc.num_uniforms++] = + (ShaderUniform){.name = sstring_make(UNIFORM_BASE_COLOR_FACTOR), + .type = UniformVec4, + .value.vec4 = vec4_make(1, 1, 1, 1)}; assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); - desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ - .name = sstring_make(UNIFORM_METALLIC_FACTOR), - .type = UniformFloat, - .value.scalar = 0}; + desc.uniforms[desc.num_uniforms++] = + (ShaderUniform){.name = sstring_make(UNIFORM_METALLIC_FACTOR), + .type = UniformFloat, + .value.scalar = 0}; assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); - desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ - .name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), - .type = UniformFloat, - .value.scalar = 1}; + desc.uniforms[desc.num_uniforms++] = + (ShaderUniform){.name = sstring_make(UNIFORM_ROUGHNESS_FACTOR), + .type = UniformFloat, + .value.scalar = 1}; assert(desc.num_uniforms < GFX_MAX_UNIFORMS_PER_MATERIAL); - desc.uniforms[desc.num_uniforms++] = (ShaderUniform){ - .name = sstring_make(UNIFORM_EMISSIVE_FACTOR), - .type = UniformVec3, - .value.vec3 = vec3_make(0, 0, 0)}; + desc.uniforms[desc.num_uniforms++] = + (ShaderUniform){.name = sstring_make(UNIFORM_EMISSIVE_FACTOR), + .type = UniformVec3, + .value.vec3 = vec3_make(0, 0, 0)}; return gfx_make_material(&desc); } @@ -1192,10 +1193,10 @@ static bool load_meshes( shader ? shader : make_shader_permutation(gfxcore, perm); assert(mesh_shader); - meshes[next_mesh] = gfx_make_mesh(&(MeshDesc){ - .geometry = geometries[next_mesh], - .material = material, - .shader = mesh_shader}); + meshes[next_mesh] = + gfx_make_mesh(&(MeshDesc){.geometry = geometries[next_mesh], + .material = material, + .shader = mesh_shader}); if (!meshes[next_mesh]) { return false; @@ -1432,9 +1433,9 @@ static void load_animations( const cgltf_animation* animation = &data->animations[a]; AnimationDesc* animation_desc = &anima_desc->animations[a]; - *animation_desc = (AnimationDesc){ - .name = sstring_make(animation->name), - .num_channels = animation->channels_count}; + *animation_desc = + (AnimationDesc){.name = sstring_make(animation->name), + .num_channels = animation->channels_count}; assert(animation->channels_count <= GFX_MAX_NUM_CHANNELS); for (cgltf_size c = 0; c < animation->channels_count; ++c) { diff --git a/src/asset/texture.c b/src/asset/texture.c index c790394..fb423cc 100644 --- a/src/asset/texture.c +++ b/src/asset/texture.c @@ -49,7 +49,7 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { assert(cmd->origin == AssetFromFile || cmd->origin == AssetFromMemory); assert(cmd->type == LoadTexture || cmd->type == LoadCubemap); - int width, height, components, old_components; + int width, height, components; unsigned char* pixels[6] = {0}; switch (cmd->origin) { @@ -64,7 +64,8 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { } break; } - case LoadCubemap: + case LoadCubemap: { + int old_components = 0; 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); @@ -76,9 +77,10 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { 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"); + 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)) { @@ -89,6 +91,7 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { } break; } + } break; case AssetFromMemory: // TODO: Load textures from memory. @@ -122,6 +125,25 @@ Texture* gfx_texture_load(GfxCore* gfxcore, const LoadTextureCmd* cmd) { } switch (components) { + case 1: + switch (cmd->colour_space) { + case LinearColourSpace: + desc.format = TextureR8; + break; + case sRGB: + // TODO: Gamma single-channel textures are not implemented yet. + // The caller should convert the single-channel to RGB and pass it down + // as sRGB. This is why the ChronographWatch currently appears red on the + // back. + log_error("Gamma colour space is not supported for 1-channel textures"); + // return 0; + desc.format = TextureR8; + break; + default: + log_error("Unsupported texture colour space: %d", cmd->colour_space); + return 0; + } + break; case 3: switch (cmd->colour_space) { case LinearColourSpace: diff --git a/src/core/texture.c b/src/core/texture.c index 89f7ec0..372f9e6 100644 --- a/src/core/texture.c +++ b/src/core/texture.c @@ -37,6 +37,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { gfx_del_texture(texture); return false; } + ASSERT_GL; texture->format = to_GL_format(desc->format); texture->type = to_GL_type(desc->format); @@ -50,6 +51,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { // Mipmaps. if (desc->mipmaps) { glGenerateMipmap(texture->target); + ASSERT_GL; } // Texture filtering. @@ -60,6 +62,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { GLenum mag = linear ? GL_LINEAR : GL_NEAREST; glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); + ASSERT_GL; // Texture wrapping. GLenum wrap = GL_INVALID_ENUM; @@ -74,6 +77,7 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { glTexParameteri(texture->target, GL_TEXTURE_WRAP_R, wrap); glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, wrap); glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, wrap); + ASSERT_GL; glBindTexture(texture->target, 0); return true; @@ -119,6 +123,7 @@ void gfx_update_texture(Texture* texture, const TextureDataDesc* desc) { FAIL("Unhandled texture dimension"); break; } + ASSERT_GL; glBindTexture(texture->target, 0); } @@ -139,6 +144,8 @@ GLenum to_GL_internal_format(TextureFormat format) { switch (format) { case TextureDepth: return GL_DEPTH_COMPONENT; + case TextureR8: + return GL_R8; case TextureRG16: return GL_RG16; case TextureRG16F: @@ -163,6 +170,8 @@ GLenum to_GL_format(TextureFormat format) { switch (format) { case TextureDepth: return GL_DEPTH_COMPONENT; + case TextureR8: + return GL_RED; case TextureRG16: case TextureRG16F: return GL_RG; @@ -185,6 +194,7 @@ GLenum to_GL_type(TextureFormat format) { case TextureRG16F: case TextureR11G11B10F: return GL_FLOAT; + case TextureR8: case TextureRG16: case TextureRGB8: case TextureRGBA8: -- cgit v1.2.3