From 2d604425277f56c98a5c8351914178480f68bc23 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 6 Mar 2026 13:30:21 -0800 Subject: Move contrib libraries to contrib repo --- contrib/cgltf-tangents/cgltf_tangents.c | 618 -------------------------------- 1 file changed, 618 deletions(-) delete mode 100644 contrib/cgltf-tangents/cgltf_tangents.c (limited to 'contrib/cgltf-tangents/cgltf_tangents.c') diff --git a/contrib/cgltf-tangents/cgltf_tangents.c b/contrib/cgltf-tangents/cgltf_tangents.c deleted file mode 100644 index 80b1e56..0000000 --- a/contrib/cgltf-tangents/cgltf_tangents.c +++ /dev/null @@ -1,618 +0,0 @@ -/* -Copyright 2022 Marc Sunet - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -#include "cgltf_tangents.h" -#include "cgltf.h" - -#include "MikkTSpace/mikktspace.h" - -#include -#include -#include -#include -#include - -#ifdef CGLTF_TANGENTS_DEBUG -#include -#define DLOG printf -#else -#define DLOG(...) -#endif - -#include // TODO: Remove me. - -#define CGLTF_OPTIONS_MALLOC(size) \ - options->memory.alloc(options->memory.user_data, size) - -#define CGLTF_OPTIONS_FREE(ptr) \ - options->memory.free(options->memory.user_data, ptr) - -static void* cgltf_default_alloc(void* user, cgltf_size size) { - (void)user; - return malloc(size); -} - -static void cgltf_default_free(void* user, void* ptr) { - (void)user; - free(ptr); -} - -static const cgltf_size NUM_TANGENT_COMPONENTS = 4; // X,Y,Z,fSign - -static float normalize_i8(int8_t x) { return (float)x / 128.0; } -static float normalize_u8(uint8_t x) { return (float)x / 255.0; } -static float normalize_i16(int16_t x) { return (float)x / 32768.0; } -static float normalize_u16(uint16_t x) { return (float)x / 65535.0; } -static float normalize_u32(uint32_t x) { return (float)x / 4294967295.0; } - -static cgltf_size num_vertex_attrib_components(cgltf_type type) { - switch (type) { - case cgltf_type_scalar: - return 1; - case cgltf_type_vec2: - return 2; - case cgltf_type_vec3: - return 3; - case cgltf_type_vec4: - return 4; - default: - assert(false); - return 0; - } -} - -static cgltf_size cgltf_component_type_size_bytes(cgltf_component_type type) { - switch (type) { - case cgltf_component_type_r_8: - return 1; - case cgltf_component_type_r_8u: - return 1; - case cgltf_component_type_r_16: - return 2; - case cgltf_component_type_r_16u: - return 2; - case cgltf_component_type_r_32u: - return 4; - case cgltf_component_type_r_32f: - return 4; - default: - assert(false); - return 0; - } -} - -static cgltf_size default_stride(cgltf_type type, - cgltf_component_type component_type) { - return num_vertex_attrib_components(type) * - cgltf_component_type_size_bytes(component_type); -} - -// ----------------------------------------------------------------------------- -// MikkTSpace interface - -// An array of values for a given vertex attribute or for vertex indices. -// For positions and normals, glTF mandates floats. -// Texcoords and indices can be different types and vary in size: 8-bit, 16-bit, -// or 32-bit. -// We store void* pointers so that we can do byte pointer arithmetic. -typedef struct Buffer { - const void* start; // X-coordinate of the first attribute. - const void* end; // One byte past the end of the buffer. - cgltf_size stride_bytes; // Stride in bytes between each value. - cgltf_component_type type; // Type of each value in the buffer. -} Buffer; - -// User data for mesh processing. -// See: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes -// Buffer pointers have the accessor + view offsets baked in so that we do the -// addition only once. -typedef struct SMikkUserData { - const cgltf_primitive* primitive; - // Index buffer may be empty (mesh primitive has no indices). - Buffer indices; - // Vertex attributes. - Buffer positions; - Buffer normals; - Buffer texcoords; - // Output tangents. - void* tangents; -} SMikkUserData; - -static cgltf_size get_vertex_index(const SMikkUserData* data, cgltf_size iFace, - cgltf_size iVert) { - const cgltf_primitive* primitive = data->primitive; - - // First compute a vertex index as if the mesh primitive had no indices. - cgltf_size vertex_idx = 0; - switch (primitive->type) { - case cgltf_primitive_type_triangles: - vertex_idx = iFace * 3 + iVert; - break; - case cgltf_primitive_type_triangle_strip: - // For triangle strips: - // face 0 -> verts 0, 1, 2 - // face 1 -> verts 1, 3, 2 (1, 2, 3 flipped) - // face 2 -> verts 2, 3, 4 - // face 3 -> verts 3, 5, 4 (3, 4, 5 flipped) - // ... - // face N=2k -> verts N, N+1, N+2 - // face N=2k+1 -> verts N, N+2, N+1 - if (iFace & 1) { - // Flip the winding of odd faces so that the is consistent with the even - // ones. - // iVert = 0 -> vert 0 - // iVert = 1 -> vert 2 - // iVert = 2 -> vert 1 - vertex_idx = iFace + (2 - iVert); - } else { - vertex_idx = iFace + iVert; - } - break; - case cgltf_primitive_type_triangle_fan: - // For triangle fans: - // face 0 -> verts 0, 1, 2 - // face 1 -> verts 0, 2, 3 - // face 2 -> verts 0, 3, 4 - // face 3 -> verts 0, 4, 5 - // ... - // face N -> verts 0, N=1, N=2 - if (iVert == 0) { - vertex_idx = 0; - } else { - vertex_idx = iFace + iVert; - } - break; - default: - assert(false); - break; - } - - // If the mesh primitive has vertex indices, then vertex_idx is actually the - // index of the index. Index the index buffer with vertex_idx to find the - // real vertex index. - if (primitive->indices != NULL) { - const void* p_idx = - data->indices.start + vertex_idx * data->indices.stride_bytes; - switch (data->indices.type) { - case cgltf_component_type_r_8: - vertex_idx = *((int8_t*)p_idx); - break; - case cgltf_component_type_r_8u: - vertex_idx = *((uint8_t*)p_idx); - break; - case cgltf_component_type_r_16: - vertex_idx = *((int16_t*)p_idx); - break; - case cgltf_component_type_r_16u: - vertex_idx = *((uint16_t*)p_idx); - break; - case cgltf_component_type_r_32u: - vertex_idx = *((uint32_t*)p_idx); - break; - default: - assert(false); - break; - } - } - - return vertex_idx; -} - -static const void* get_vertex(const Buffer* buffer, cgltf_size index) { - // Stride is the offset in bytes between vertex attributes. - const void* vertex = buffer->start + buffer->stride_bytes * index; - assert(vertex < buffer->end); - return vertex; -} - -static const void* get_position(const SMikkUserData* data, cgltf_size index) { - return get_vertex(&data->positions, index); -} - -static const void* get_normal(const SMikkUserData* data, cgltf_size index) { - return get_vertex(&data->normals, index); -} - -static const void* get_texcoord(const SMikkUserData* data, cgltf_size index) { - return get_vertex(&data->texcoords, index); -} - -static float* get_tangent(void* buffer, cgltf_size index) { - // Tangents are tightly packed. - return (float*)(buffer) + NUM_TANGENT_COMPONENTS * index; -} - -static int SMikk_get_num_faces(const SMikkTSpaceContext* pContext) { - SMikkUserData* data = (SMikkUserData*)pContext->m_pUserData; - const cgltf_primitive* primitive = data->primitive; - - // Find the number of effective vertices (vertices or indices) in the mesh - // primitive. - // - // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes - // - // "All attribute accessors for a given primitive MUST have the same count. - // When indices property is not defined, attribute accessors' count indicates - // the number of vertices to render; when indices property is defined, it - // indicates the upper (exclusive) bound on the index values in the indices - // accessor, i.e., all index values MUST be less than attribute accessors' - // count." - const cgltf_size num_verts = (primitive->indices != NULL) - ? primitive->indices->count - : primitive->attributes_count; - - // Determine the number of faces given the number of vertices. - // - // We use the fact that glTF only supports triangles for faces. - // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes - switch (primitive->type) { - case cgltf_primitive_type_triangles: - return (int)num_verts / 3; - case cgltf_primitive_type_triangle_strip: - case cgltf_primitive_type_triangle_fan: - return (int)num_verts - 2; - default: - return 0; - } -} - -int SMikk_get_num_vertices_of_face(const SMikkTSpaceContext* pContext, - const int iFace) { - // Triangles are the only faces supported by glTF. - // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes - return 3; -} - -void SMikk_get_position(const SMikkTSpaceContext* pContext, float fvPosOut[], - const int iFace, const int iVert) { - const SMikkUserData* data = (SMikkUserData*)pContext->m_pUserData; - const cgltf_primitive* primitive = data->primitive; - - const cgltf_size idx = get_vertex_index(data, iFace, iVert); - const float* coord = get_position(data, idx); - fvPosOut[0] = *coord++; - fvPosOut[1] = *coord++; - fvPosOut[2] = *coord; - DLOG("Position (face: %d, vert: %d): %f, %f, %f; idx: %lu\n", iFace, iVert, - fvPosOut[0], fvPosOut[1], fvPosOut[2], idx); -} - -void SMikk_get_normal(const SMikkTSpaceContext* pContext, float fvNormOut[], - const int iFace, const int iVert) { - const SMikkUserData* data = (SMikkUserData*)pContext->m_pUserData; - const cgltf_primitive* primitive = data->primitive; - - const cgltf_size idx = get_vertex_index(data, iFace, iVert); - const float* coord = get_normal(data, idx); - fvNormOut[0] = *coord++; - fvNormOut[1] = *coord++; - fvNormOut[2] = *coord; - DLOG("Normal (face: %d, vert: %d): %f, %f, %f\n", iFace, iVert, fvNormOut[0], - fvNormOut[1], fvNormOut[2]); -} - -void SMikk_get_texcoord(const SMikkTSpaceContext* pContext, float fvTexcOut[], - const int iFace, const int iVert) { - const SMikkUserData* data = (SMikkUserData*)pContext->m_pUserData; - const cgltf_primitive* primitive = data->primitive; - - const cgltf_size idx = get_vertex_index(data, iFace, iVert); - const void* coord = get_texcoord(data, idx); - switch (data->texcoords.type) { - case cgltf_component_type_r_8: { - const int8_t* c = coord; - fvTexcOut[0] = normalize_i8(*c++); - fvTexcOut[1] = normalize_i8(*c); - break; - } - case cgltf_component_type_r_8u: { - const uint8_t* c = coord; - fvTexcOut[0] = normalize_u8(*c++); - fvTexcOut[1] = normalize_u8(*c); - break; - } - case cgltf_component_type_r_16: { - const int16_t* c = coord; - fvTexcOut[0] = normalize_i16(*c++); - fvTexcOut[1] = normalize_i16(*c); - break; - } - case cgltf_component_type_r_16u: { - const uint16_t* c = coord; - fvTexcOut[0] = normalize_u16(*c++); - fvTexcOut[1] = normalize_u16(*c); - break; - } - case cgltf_component_type_r_32u: { - const uint32_t* c = coord; - fvTexcOut[0] = normalize_u32(*c++); - fvTexcOut[1] = normalize_u32(*c); - break; - } - case cgltf_component_type_r_32f: { - const float* c = coord; - fvTexcOut[0] = *c++; - fvTexcOut[1] = *c; - break; - } - default: - assert(false); - break; - } - DLOG("Texcoord (face: %d, vert: %d): %f, %f\n", iFace, iVert, fvTexcOut[0], - fvTexcOut[1]); -} - -void SMikk_set_TSpace_basic(const SMikkTSpaceContext* pContext, - const float fvTangent[], const float fSign, - const int iFace, const int iVert) { - SMikkUserData* data = (SMikkUserData*)pContext->m_pUserData; - const cgltf_primitive* primitive = data->primitive; - - const cgltf_size idx = get_vertex_index(data, iFace, iVert); - float* coord = get_tangent(data->tangents, idx); - *coord++ = fvTangent[0]; - *coord++ = fvTangent[1]; - *coord++ = fvTangent[2]; - *coord = fSign; - DLOG("Tangent (face: %d, vert: %d): %f, %f, %f; sign: %f\n", iFace, iVert, - fvTangent[0], fvTangent[1], fvTangent[2], fSign); -} - -// ----------------------------------------------------------------------------- - -static bool has_normal_map(const cgltf_primitive* primitive) { - return (primitive->material != NULL) && - (primitive->material->normal_texture.texture != NULL); -} - -static const cgltf_attribute* find_attribute(const cgltf_primitive* primitive, - cgltf_attribute_type type) { - for (cgltf_size i = 0; i < primitive->attributes_count; ++i) { - const cgltf_attribute* attrib = &primitive->attributes[i]; - if (attrib->type == type) { - return attrib; - } - } - return NULL; -} - -static bool has_attribute(const cgltf_primitive* primitive, - cgltf_attribute_type type) { - return find_attribute(primitive, type) != NULL; -} - -static bool has_positions3d(const cgltf_primitive* primitive) { - const cgltf_attribute* attrib = - find_attribute(primitive, cgltf_attribute_type_position); - if (attrib) { - return attrib->data->type == cgltf_type_vec3; - } - return false; -} - -static bool has_normals(const cgltf_primitive* primitive) { - return has_attribute(primitive, cgltf_attribute_type_normal); -} - -static bool has_texcoords(const cgltf_primitive* primitive) { - return has_attribute(primitive, cgltf_attribute_type_texcoord); -} - -static bool has_tangents(const cgltf_primitive* primitive) { - return has_attribute(primitive, cgltf_attribute_type_tangent); -} - -static bool has_indices(const cgltf_primitive* primitive) { - return primitive->indices != 0; -} - -static cgltfTangentBuffer compute_tangents(const cgltf_options* options, - const cgltf_data* data, - cgltf_primitive* primitive) { - cgltfTangentBuffer buffer = {0}; - SMikkUserData user = {0}; - cgltf_size num_verts = 0; - - user.primitive = primitive; - - if (primitive->indices != NULL) { - const cgltf_accessor* accessor = primitive->indices; - const cgltf_buffer_view* view = accessor->buffer_view; - const cgltf_size offset_bytes = accessor->offset + view->offset; - const void* buffer_data = view->buffer->data + offset_bytes; - const void* buffer_end = view->buffer->data + view->offset + view->size; - - user.indices.start = buffer_data; - user.indices.end = buffer_end; - // Indices are tightly packed, stride 0. - user.indices.stride_bytes = - default_stride(accessor->type, accessor->component_type); - user.indices.type = accessor->component_type; - } - - for (cgltf_size i = 0; i < primitive->attributes_count; ++i) { - const cgltf_attribute* attrib = &primitive->attributes[i]; - - if ((attrib->type == cgltf_attribute_type_position) || - (attrib->type == cgltf_attribute_type_normal) || - (attrib->type == cgltf_attribute_type_texcoord)) { - const cgltf_accessor* accessor = attrib->data; - const cgltf_buffer_view* view = accessor->buffer_view; - const cgltf_buffer* buffer = view->buffer; - const cgltf_size offset_bytes = accessor->offset + view->offset; - const cgltf_size stride_bytes = - view->stride > 0 - ? view->stride - : default_stride(accessor->type, accessor->component_type); - // const cgltf_size size_bytes = view->size; - const void* buffer_data = view->buffer->data + offset_bytes; - const void* buffer_end = view->buffer->data + view->offset + view->size; - - Buffer* attrib_buffer = 0; - - if (attrib->type == cgltf_attribute_type_position) { - // glTF currently mandates vec3 for positions. Caller should ensure - // this. - assert(accessor->type == cgltf_type_vec3); - num_verts = attrib->data->count; - attrib_buffer = &user.positions; - } else if (attrib->type == cgltf_attribute_type_normal) { - attrib_buffer = &user.normals; - } else if (attrib->type == cgltf_attribute_type_texcoord) { - attrib_buffer = &user.texcoords; - } - - attrib_buffer->start = buffer_data; - attrib_buffer->end = buffer_end; - attrib_buffer->stride_bytes = stride_bytes; - attrib_buffer->type = accessor->component_type; - } - } - - assert(user.positions.start); - assert(user.positions.end); - assert(user.normals.start); - assert(user.normals.end); - assert(user.texcoords.start); - assert(user.texcoords.end); - assert(num_verts > 0); - - const cgltf_size tangents_size_bytes = - num_verts * NUM_TANGENT_COMPONENTS * sizeof(float); - - user.tangents = CGLTF_OPTIONS_MALLOC(tangents_size_bytes); - if (!user.tangents) { - return buffer; - } - - SMikkTSpaceInterface interface = (SMikkTSpaceInterface){ - .m_getNumFaces = SMikk_get_num_faces, - .m_getNumVerticesOfFace = SMikk_get_num_vertices_of_face, - .m_getPosition = SMikk_get_position, - .m_getNormal = SMikk_get_normal, - .m_getTexCoord = SMikk_get_texcoord, - .m_setTSpaceBasic = SMikk_set_TSpace_basic, - }; - const SMikkTSpaceContext context = (SMikkTSpaceContext){ - .m_pInterface = &interface, - .m_pUserData = &user, - }; - if (!genTangSpaceDefault(&context)) { - return buffer; - } - - buffer.data = user.tangents; - buffer.size_bytes = tangents_size_bytes; - buffer.primitive = primitive; - - return buffer; -} - -static void process_primitive(const cgltf_options* options, - const cgltf_data* data, - cgltf_primitive* primitive, - cgltfTangentBuffer* tangent_buffers, - cgltf_size* num_tangent_buffers) { - DLOG("Processing primitive\n"); - cgltf_size cur_buffer = 0; - // TODO: MikkTSpace should not be used with models with vertex indices. One - // workaround is to unindex the mesh, compute tangents, and then re-index it. - if (((primitive->type == cgltf_primitive_type_triangle_fan) || - (primitive->type == cgltf_primitive_type_triangle_strip) || - (primitive->type == cgltf_primitive_type_triangles)) && - has_normal_map(primitive) && !has_tangents(primitive) && - has_positions3d(primitive) && has_normals(primitive) && - has_texcoords(primitive) && !has_indices(primitive)) { - *num_tangent_buffers += 1; - if (tangent_buffers) { - DLOG("Model with normal map missing tangents detected\n"); - tangent_buffers[cur_buffer] = compute_tangents(options, data, primitive); - if (tangent_buffers[cur_buffer].data) { - DLOG("Tangents computed\n"); - } - cur_buffer++; - } - } -} - -cgltf_result cgltf_compute_tangents(const cgltf_options* input_options, - const cgltf_data* data, - cgltfTangentBuffer** tangent_buffers, - cgltf_size* num_tangent_buffers) { - if ((input_options == NULL) || (data == NULL)) { - return cgltf_result_invalid_options; - } - - DLOG("cgltf_compute_tangents\n"); - - cgltf_options options = *input_options; - if (options.memory.alloc == NULL) { - options.memory.alloc = &cgltf_default_alloc; - } - if (options.memory.free == NULL) { - options.memory.free = &cgltf_default_free; - } - - // First pass: compute the number of tangent buffers to be created. - *num_tangent_buffers = 0; - for (cgltf_size mesh_idx = 0; mesh_idx < data->meshes_count; ++mesh_idx) { - const cgltf_mesh* mesh = &data->meshes[mesh_idx]; - - for (cgltf_size prim_idx = 0; prim_idx < mesh->primitives_count; - ++prim_idx) { - // Pass in null for the tangent buffers to just compute the number of - // buffers. - process_primitive(&options, data, &mesh->primitives[prim_idx], 0, - num_tangent_buffers); - } - } - DLOG("Number of primitives to be patched: %lu\n", *num_tangent_buffers); - - // Second pass: compute the tangents. - if (*num_tangent_buffers > 0) { - *tangent_buffers = - options.memory.alloc(options.memory.user_data, - *num_tangent_buffers * sizeof(cgltfTangentBuffer)); - if (!*tangent_buffers) { - return cgltf_result_out_of_memory; - } - - cgltf_size tangent_buffers_computed = 0; - - for (cgltf_size mesh_idx = 0; mesh_idx < data->meshes_count; ++mesh_idx) { - const cgltf_mesh* mesh = &data->meshes[mesh_idx]; - - for (cgltf_size prim_idx = 0; prim_idx < mesh->primitives_count; - ++prim_idx) { - process_primitive(&options, data, &mesh->primitives[prim_idx], - *tangent_buffers, &tangent_buffers_computed); - } - } - - assert(tangent_buffers_computed == *num_tangent_buffers); - } - - return cgltf_result_success; -} -- cgit v1.2.3