From 58a1e8b63ff2e504feb0f103c84775656b77314f Mon Sep 17 00:00:00 2001 From: Jakub Marcowski Date: Thu, 29 Jan 2026 13:07:49 +0100 Subject: [PATCH] ufbx: Update to 0.21.2 --- thirdparty/README.md | 2 +- thirdparty/ufbx/ufbx.c | 111 +++++++++++++++++++++-------------------- thirdparty/ufbx/ufbx.h | 6 +-- 3 files changed, 61 insertions(+), 58 deletions(-) diff --git a/thirdparty/README.md b/thirdparty/README.md index 40bf2af4c2a..294b36ac2e3 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -1076,7 +1076,7 @@ Patches: ## ufbx - Upstream: https://github.com/ufbx/ufbx -- Version: 0.20.0 (a63ff0a47485328880b3300e7bcdf01413343a45, 2025) +- Version: 0.21.2 (6ecd6177af59c82ec363356ac36c3a4245b85321, 2025) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/ufbx/ufbx.c b/thirdparty/ufbx/ufbx.c index cd7273d88ac..3b67f3d7e54 100644 --- a/thirdparty/ufbx/ufbx.c +++ b/thirdparty/ufbx/ufbx.c @@ -40,7 +40,6 @@ // UFBX_STATIC_ANALYSIS Enable static analysis augmentation // UFBX_DEBUG_BINARY_SEARCH Force using binary search for debugging // UFBX_EXTENSIVE_THREADING Use threads for small inputs -// UFBX_POINTER_SIZE Allow specifying sizeof(void*) as a preprocessor constant // UFBX_MAXIMUM_ALIGNMENT Maximum alignment used for allocation #if defined(UFBX_CONFIG_SOURCE) @@ -543,7 +542,8 @@ extern "C" { #pragma GCC diagnostic ignored "-Wfloat-conversion" #endif // `-Warray-bounds` results in warnings if UBsan is enabled and pre-GCC-14 has no way of detecting it.. - #if UFBXI_GNUC_VERSION >= ufbx_pack_version(4, 3, 0) && UFBXI_GNUC_VERSION < ufbx_pack_version(14, 0, 0) + // The workaround for this uses an assert, but if the user has both UBsan and asserts disabled we need to silence the warning. + #if (UFBXI_GNUC_VERSION >= ufbx_pack_version(4, 3, 0) && UFBXI_GNUC_VERSION < ufbx_pack_version(14, 0, 0)) || defined(NDEBUG) #pragma GCC diagnostic ignored "-Warray-bounds" #endif #endif @@ -859,22 +859,22 @@ ufbx_static_assert(sizeof_f64, sizeof(double) == 8); enum { UFBX_MAXIMUM_ALIGNMENT = sizeof(void*) > 8 ? sizeof(void*) : 8 }; #endif -#if !defined(UFBX_POINTER_SIZE) && !defined(UFBX_STANDARD_C) - #if (defined(_M_X64) || defined(__x86_64__) || defined(_M_ARM64) || defined(__aarch64__)) && !defined(__CHERI__) - #define UFBX_POINTER_SIZE 8 - #elif defined(__wasm__) || defined(__EMSCRIPTEN__) - #define UFBX_POINTER_SIZE 4 +#if !defined(UFBXI_UINTPTR_SIZE) && !defined(__CHERI__) // CHERI lies about UINTPTR_MAX + #if UINTPTR_MAX == UINT64_MAX + #define UFBXI_UINTPTR_SIZE 8 + #elif UINTPTR_MAX == UINT32_MAX + #define UFBXI_UINTPTR_SIZE 4 #endif #endif -#if defined(UFBX_POINTER_SIZE) - ufbx_static_assert(pointer_size, UFBX_POINTER_SIZE == sizeof(void*)); +#if defined(UFBXI_UINTPTR_SIZE) + ufbx_static_assert(pointer_size, UFBXI_UINTPTR_SIZE == sizeof(uintptr_t)); #else - #define UFBX_POINTER_SIZE 0 + #define UFBXI_UINTPTR_SIZE 0 #endif // -- Version -#define UFBX_SOURCE_VERSION ufbx_pack_version(0, 20, 0) +#define UFBX_SOURCE_VERSION ufbx_pack_version(0, 21, 2) ufbx_abi_data_def const uint32_t ufbx_source_version = UFBX_SOURCE_VERSION; ufbx_static_assert(source_header_version, UFBX_SOURCE_VERSION/1000u == UFBX_HEADER_VERSION/1000u); @@ -1082,7 +1082,7 @@ ufbx_static_assert(source_header_version, UFBX_SOURCE_VERSION/1000u == UFBX_HEAD // -- Utility #if defined(UFBX_UBSAN) - static void ufbxi_assert_zero(size_t offset) { ufbx_assert(offset == 0); } + static void ufbxi_assert_zero(size_t offset) { (void)offset; ufbx_assert(offset == 0); } #define ufbxi_add_ptr(ptr, offset) ((ptr) ? (ptr) + (offset) : (ufbxi_assert_zero((size_t)(offset)), (ptr))) #define ufbxi_sub_ptr(ptr, offset) ((ptr) ? (ptr) - (offset) : (ufbxi_assert_zero((size_t)(offset)), (ptr))) #else @@ -1884,6 +1884,9 @@ static const uint8_t ufbxi_deflate_code_length_permutation[] = { #define UFBXI_HUFF_MAX_EXTRA_SYMS 32 +#define UFBXI_HUFF_CODELEN_SYMS 19 +#define UFBXI_HUFF_MAX_COMBINED_SYMS 320 + typedef struct { // Number of bytes left to read from `read_fn()` @@ -2212,6 +2215,11 @@ ufbxi_bit_copy_bytes(void *dst, ufbxi_bit_stream *s, size_t len) s->left -= 8; } + // Copied fully from buffer + if (len == 0) { + return 1; + } + // We need to clear the top bits as there may be data // read ahead past `s->left` in some cases s->bits = 0; @@ -2529,34 +2537,21 @@ static ufbxi_noinline void ufbxi_init_static_huff(ufbxi_trees *trees, const ufbx ufbx_assert(err == 0); } -// 0: Success -// -1: Huffman Overfull -// -2: Huffman Underfull -// -3: Code 16 repeat overflow -// -4: Code 17 repeat overflow -// -5: Code 18 repeat overflow -// -6: Bad length code -// -7: Cancelled -static ufbxi_noinline ptrdiff_t ufbxi_init_dynamic_huff_tree(ufbxi_deflate_context *dc, const ufbxi_huff_tree *huff_code_length, ufbxi_huff_tree *tree, - uint32_t num_symbols, const uint32_t *sym_extra, uint32_t sym_extra_offset, uint32_t fast_bits) +static ufbxi_noinline ptrdiff_t ufbxi_decode_dynamic_huff_bits(ufbxi_deflate_context *dc, const ufbxi_huff_tree *huff_code_length, uint8_t *code_lengths, uint32_t num_symbols) { - uint8_t code_lengths[UFBXI_HUFF_MAX_VALUE]; // ufbxi_uninit - ufbx_assert(num_symbols <= UFBXI_HUFF_MAX_VALUE); - uint64_t bits = dc->stream.bits; size_t left = dc->stream.left; const char *data = dc->stream.chunk_ptr; - uint32_t bits_counts[UFBXI_HUFF_MAX_BITS]; // ufbxi_uninit - memset(bits_counts, 0, sizeof(bits_counts)); uint32_t symbol_index = 0; uint8_t prev = 0; while (symbol_index < num_symbols) { ufbxi_bit_refill(&bits, &left, &data, &dc->stream); - if (dc->stream.cancelled) return -7; + if (dc->stream.cancelled) return -28; ufbxi_huff_sym sym = ufbxi_huff_decode_bits(huff_code_length, bits, UFBXI_HUFF_CODELEN_FAST_BITS, UFBXI_HUFF_CODELEN_FAST_MASK); ufbxi_regression_assert(sym != UFBXI_HUFF_UNINITIALIZED_SYM); + if (sym == UFBXI_HUFF_ERROR_SYM) return -21; uint32_t inst = ufbxi_huff_sym_value(sym); uint32_t sym_len = ufbxi_huff_sym_total_bits(sym); @@ -2568,44 +2563,37 @@ static ufbxi_noinline ptrdiff_t ufbxi_init_dynamic_huff_tree(ufbxi_deflate_conte // "0 - 15: Represent code lengths of 0 - 15" prev = (uint8_t)inst; code_lengths[symbol_index++] = (uint8_t)inst; - bits_counts[(int32_t)inst]++; } else if (inst == 16) { // "16: Copy the previous code length 3 - 6 times. The next 2 bits indicate repeat length." uint32_t num = 3 + ((uint32_t)bits & 0x3); bits >>= 2; left -= 2; - if (symbol_index + num > num_symbols) return -3; + if (symbol_index + num > num_symbols) return -18; memset(code_lengths + symbol_index, prev, num); symbol_index += num; - bits_counts[(int32_t)prev] += num; } else if (inst == 17) { // "17: Repeat a code length of 0 for 3 - 10 times. (3 bits of length)" uint32_t num = 3 + ((uint32_t)bits & 0x7); bits >>= 3; left -= 3; - if (symbol_index + num > num_symbols) return -4; + if (symbol_index + num > num_symbols) return -19; memset(code_lengths + symbol_index, 0, num); symbol_index += num; prev = 0; - bits_counts[0] += num; } else if (inst == 18) { // "18: Repeat a code length of 0 for 11 - 138 times (7 bits of length)" uint32_t num = 11 + ((uint32_t)bits & 0x7f); bits >>= 7; left -= 7; - if (symbol_index + num > num_symbols) return -5; + if (symbol_index + num > num_symbols) return -20; memset(code_lengths + symbol_index, 0, num); symbol_index += num; prev = 0; - bits_counts[0] += num; } else { return -6; } } - ptrdiff_t err = ufbxi_huff_build_imp(tree, code_lengths, num_symbols, sym_extra, sym_extra_offset, fast_bits, bits_counts); - if (err != 0) return err; - dc->stream.bits = bits; dc->stream.left = left; dc->stream.chunk_ptr = data; @@ -2631,11 +2619,13 @@ ufbxi_init_dynamic_huff(ufbxi_deflate_context *dc, ufbxi_trees *trees) bits >>= 14; left -= 14; + uint8_t code_lengths[UFBXI_HUFF_MAX_COMBINED_SYMS]; // ufbxi_uninit + // Code lengths for the "code length" Huffman tree are represented literally // 3 bits in order of: 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 up to // `num_code_lengths`, rest of the code lengths are 0 (unused) - uint8_t code_lengths[19]; // ufbxi_uninit - memset(code_lengths, 0, sizeof(code_lengths)); + memset(code_lengths, 0, UFBXI_HUFF_CODELEN_SYMS); + for (size_t len_i = 0; len_i < num_code_lengths; len_i++) { if (len_i == 14) { ufbxi_bit_refill(&bits, &left, &data, &dc->stream); @@ -2656,11 +2646,16 @@ ufbxi_init_dynamic_huff(ufbxi_deflate_context *dc, ufbxi_trees *trees) // Build the temporary "code length" Huffman tree used to encode the actual // trees used to compress the data. Use that to build the literal/length and // distance trees. - err = ufbxi_huff_build(&huff_code_length, code_lengths, ufbxi_arraycount(code_lengths), NULL, INT32_MAX, UFBXI_HUFF_CODELEN_FAST_BITS); + err = ufbxi_huff_build(&huff_code_length, code_lengths, UFBXI_HUFF_CODELEN_SYMS, NULL, INT32_MAX, UFBXI_HUFF_CODELEN_FAST_BITS); if (err) return -14 + 1 + err; - err = ufbxi_init_dynamic_huff_tree(dc, &huff_code_length, &trees->lit_length, num_lit_lengths, ufbxi_deflate_length_lut, 256, dc->fast_bits); + + err = ufbxi_decode_dynamic_huff_bits(dc, &huff_code_length, code_lengths, num_lit_lengths + num_dists); + if (err) return err; + + err = ufbxi_huff_build(&trees->lit_length, code_lengths, num_lit_lengths, ufbxi_deflate_length_lut, 256, dc->fast_bits); if (err) return err == -7 ? -28 : -16 + 1 + err; - err = ufbxi_init_dynamic_huff_tree(dc, &huff_code_length, &trees->dist, num_dists, ufbxi_deflate_dist_lut, 0, dc->fast_bits); + + err = ufbxi_huff_build(&trees->dist, code_lengths + num_lit_lengths, num_dists, ufbxi_deflate_dist_lut, 0, dc->fast_bits); if (err) return err == -7 ? -28 : -22 + 1 + err; return 0; @@ -3122,10 +3117,17 @@ static void ufbxi_inflate_init_retain(ufbx_inflate_retain *retain) // -13: Bad lit/length code // -14: Codelen Huffman Overfull // -15: Codelen Huffman Underfull -// -16 - -21: Litlen Huffman: Overfull / Underfull / Repeat 16/17/18 overflow / Bad length code -// -22 - -27: Distance Huffman: Overfull / Underfull / Repeat 16/17/18 overflow / Bad length code +// -16: Litlen Huffman Overfull +// -17: Litlen Huffman Underfull +// -18: Repeat 16 overflow +// -19: Repeat 17 overflow +// -20: Repeat 18 overflow +// -21: Bad codelen code +// -22: Distance Huffman: Overfull +// -23: Distance Huffman: Underfull // -28: Cancelled // -29: Invalid ufbx_inflate_input.internal_fast_bits value +// -30: Bad window size (ZLIB header) ufbxi_extern_c ptrdiff_t ufbx_inflate(void *dst, size_t dst_size, const ufbx_inflate_input *input, ufbx_inflate_retain *retain) { ufbxi_inflate_retain_imp *ret_imp = (ufbxi_inflate_retain_imp*)retain; @@ -3161,6 +3163,7 @@ ufbxi_extern_c ptrdiff_t ufbx_inflate(void *dst, size_t dst_size, const ufbx_inf if ((cmf & 0xf) != 0x8) return -1; if ((flg & 0x20) != 0) return -2; if ((cmf << 8 | flg) % 31u != 0) return -3; + if ((cmf >> 4) > 7) return -30; } for (;;) { @@ -4797,9 +4800,9 @@ static ufbxi_unused ufbxi_forceinline uint32_t ufbxi_hash64(uint64_t x) static ufbxi_forceinline uint32_t ufbxi_hash_uptr(uintptr_t ptr) { -#if UFBX_POINTER_SIZE == 8 +#if UFBXI_UINTPTR_SIZE == 8 return ufbxi_hash64((uint64_t)ptr); -#elif UFBX_POINTER_SIZE == 4 +#elif UFBXI_UINTPTR_SIZE == 4 return ufbxi_hash32((uint32_t)ptr); #else if (sizeof(ptr) == 8) return ufbxi_hash64((uint64_t)ptr); @@ -15366,7 +15369,7 @@ ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_take_anim_channel(ufbxi_con // automatic tangents for now as they're the least likely to break with // objectionable artifacts. We need to defer the automatic tangent resolve // until we have read the next time/value. - // TODO: Solve what this is more throroughly + // TODO: Solve what this is more thoroughly auto_slope = true; if (uc->version == 5000) { num_weights = 0; @@ -24910,7 +24913,7 @@ static ufbxi_forceinline double ufbxi_find_cubic_bezier_t(double p1, double p2, double t = x0; double x1, t2, t3; - // Manually unroll three iterations of Newton-Rhapson, this is enough + // Manually unroll three iterations of Newton-Raphson, this is enough // for most tangents t2 = t*t; t3 = t2*t; x1 = a*t3 + b*t2 + c*t - x0; t -= x1 / (a_3*t2 + b_2*t + c); @@ -25721,7 +25724,7 @@ static ufbxi_noinline void ufbxi_evaluate_connected_prop(ufbx_prop *prop, const // Found a non-cyclic connection if (conn && !ufbxi_find_prop_connection(conn->src, conn->src_prop.data)) { - ufbx_prop ep = ufbx_evaluate_prop_len_flags(anim, conn->src, conn->src_prop.data, conn->src_prop.length, time, flags); + ufbx_prop ep = ufbx_evaluate_prop_flags_len(anim, conn->src, conn->src_prop.data, conn->src_prop.length, time, flags); prop->value_vec4 = ep.value_vec4; prop->value_int = ep.value_int; prop->value_str = ep.value_str; @@ -27413,7 +27416,7 @@ ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_anim_prop(ufbxi_bake_contex for (size_t i = 0; i < times.count; i++) { ufbxi_bake_time bake_time = times.data[i]; double eval_time = ufbxi_bake_time_sample_time(bake_time); - ufbx_prop prop = ufbx_evaluate_prop_len_flags(bc->anim, element, name.data, name.length, eval_time, bc->opts.evaluate_flags); + ufbx_prop prop = ufbx_evaluate_prop_flags_len(bc->anim, element, name.data, name.length, eval_time, bc->opts.evaluate_flags); keys.data[i].time = bake_time.time; keys.data[i].value = prop.value_vec3; keys.data[i].flags = (ufbx_baked_key_flags)bake_time.flags; @@ -30838,10 +30841,10 @@ ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_evaluate_anim_value_vec3_flags(const ufbx ufbx_abi ufbxi_noinline ufbx_prop ufbx_evaluate_prop_len(const ufbx_anim *anim, const ufbx_element *element, const char *name, size_t name_len, double time) { - return ufbx_evaluate_prop_len_flags(anim, element, name, name_len, time, 0); + return ufbx_evaluate_prop_flags_len(anim, element, name, name_len, time, 0); } -ufbx_abi ufbxi_noinline ufbx_prop ufbx_evaluate_prop_len_flags(const ufbx_anim *anim, const ufbx_element *element, const char *name, size_t name_len, double time, uint32_t flags) +ufbx_abi ufbxi_noinline ufbx_prop ufbx_evaluate_prop_flags_len(const ufbx_anim *anim, const ufbx_element *element, const char *name, size_t name_len, double time, uint32_t flags) { ufbx_prop result; @@ -33041,7 +33044,7 @@ ufbx_abi ufbx_anim_stack *ufbx_find_anim_stack(const ufbx_scene *scene, const ch ufbx_abi ufbx_material *ufbx_find_material(const ufbx_scene *scene, const char *name) { return ufbx_find_material_len(scene, name, strlen(name)); } ufbx_abi ufbx_anim_prop *ufbx_find_anim_prop(const ufbx_anim_layer *layer, const ufbx_element *element, const char *prop) { return ufbx_find_anim_prop_len(layer, element, prop, strlen(prop)); } ufbx_abi ufbx_prop ufbx_evaluate_prop(const ufbx_anim *anim, const ufbx_element *element, const char *name, double time) { return ufbx_evaluate_prop_len(anim, element, name, strlen(name), time); } -ufbx_abi ufbx_prop ufbx_evaluate_prop_flags(const ufbx_anim *anim, const ufbx_element *element, const char *name, double time, uint32_t flags) { return ufbx_evaluate_prop_len_flags(anim, element, name, strlen(name), time, flags); } +ufbx_abi ufbx_prop ufbx_evaluate_prop_flags(const ufbx_anim *anim, const ufbx_element *element, const char *name, double time, uint32_t flags) { return ufbx_evaluate_prop_flags_len(anim, element, name, strlen(name), time, flags); } ufbx_abi ufbx_texture *ufbx_find_prop_texture(const ufbx_material *material, const char *name) { return ufbx_find_prop_texture_len(material, name, strlen(name)); } ufbx_abi ufbx_string ufbx_find_shader_prop(const ufbx_shader *shader, const char *name) { return ufbx_find_shader_prop_len(shader, name, strlen(name)); } ufbx_abi ufbx_shader_prop_binding_list ufbx_find_shader_prop_bindings(const ufbx_shader *shader, const char *name) { return ufbx_find_shader_prop_bindings_len(shader, name, strlen(name)); } diff --git a/thirdparty/ufbx/ufbx.h b/thirdparty/ufbx/ufbx.h index 9078a9608f7..939952f5aaa 100644 --- a/thirdparty/ufbx/ufbx.h +++ b/thirdparty/ufbx/ufbx.h @@ -267,7 +267,7 @@ struct ufbx_converter { }; // `ufbx_source_version` contains the version of the corresponding source file. // HINT: The version can be compared numerically to the result of `ufbx_pack_version()`, // for example `#if UFBX_VERSION >= ufbx_pack_version(0, 12, 0)`. -#define UFBX_HEADER_VERSION ufbx_pack_version(0, 20, 0) +#define UFBX_HEADER_VERSION ufbx_pack_version(0, 21, 2) #define UFBX_VERSION UFBX_HEADER_VERSION // -- Basic types @@ -1584,7 +1584,7 @@ struct ufbx_camera { ufbx_vec2 field_of_view_deg; // Component-wise `tan(field_of_view_deg)`, also represents the size of the - // proection frustum slice at distance of 1. + // frustum slice at distance of 1. // Valid if `projection_mode == UFBX_PROJECTION_MODE_PERSPECTIVE`. ufbx_vec2 field_of_view_tan; @@ -5427,7 +5427,7 @@ ufbx_abi ufbx_vec3 ufbx_evaluate_anim_value_vec3_flags(const ufbx_anim_value *an // NOTE: If the property is not found it will have the flag `UFBX_PROP_FLAG_NOT_FOUND`. ufbx_abi ufbx_prop ufbx_evaluate_prop_len(const ufbx_anim *anim, const ufbx_element *element, const char *name, size_t name_len, double time); ufbx_abi ufbx_prop ufbx_evaluate_prop(const ufbx_anim *anim, const ufbx_element *element, const char *name, double time); -ufbx_abi ufbx_prop ufbx_evaluate_prop_len_flags(const ufbx_anim *anim, const ufbx_element *element, const char *name, size_t name_len, double time, uint32_t flags); +ufbx_abi ufbx_prop ufbx_evaluate_prop_flags_len(const ufbx_anim *anim, const ufbx_element *element, const char *name, size_t name_len, double time, uint32_t flags); ufbx_abi ufbx_prop ufbx_evaluate_prop_flags(const ufbx_anim *anim, const ufbx_element *element, const char *name, double time, uint32_t flags); // Evaluate all _animated_ properties of `element`.