Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 66 additions & 55 deletions native/viewer/ufbx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -407,8 +406,10 @@ extern "C" {

#if defined(__GNUC__)
#define UFBXI_GNUC __GNUC__
#define UFBXI_GNUC_VERSION ufbx_pack_version(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#else
#define UFBXI_GNUC 0
#define UFBXI_GNUC_VERSION 0
#endif

#if !defined(UFBX_STANDARD_C) && defined(_MSC_VER)
Expand Down Expand Up @@ -540,6 +541,11 @@ extern "C" {
#if defined(__MINGW32__)
#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..
// 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

#if !defined(ufbx_static_assert)
Expand Down Expand Up @@ -853,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, 19, 0)
#define UFBX_SOURCE_VERSION ufbx_pack_version(0, 21, 4)
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);
Expand Down Expand Up @@ -1076,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
Expand Down Expand Up @@ -1878,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()`
Expand Down Expand Up @@ -2206,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;
Expand Down Expand Up @@ -2523,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)
{
uint8_t code_lengths[UFBXI_HUFF_MAX_VALUE]; // ufbxi_uninit
ufbx_assert(num_symbols <= UFBXI_HUFF_MAX_VALUE);

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)
{
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);
Expand All @@ -2562,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;
Expand All @@ -2625,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);
Expand All @@ -2650,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;
Expand Down Expand Up @@ -3116,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;
Expand Down Expand Up @@ -3155,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 (;;) {
Expand Down Expand Up @@ -4791,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);
Expand Down Expand Up @@ -15360,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;
Expand Down Expand Up @@ -19383,6 +19392,7 @@ static const ufbxi_shader_mapping ufbxi_obj_fbx_mapping[] = {
{ UFBX_MATERIAL_FBX_NORMAL_MAP, 0, 0, ufbxi_mat_string("norm") },
{ UFBX_MATERIAL_FBX_DISPLACEMENT, 0, 0, ufbxi_mat_string("disp") },
{ UFBX_MATERIAL_FBX_BUMP, 0, 0, ufbxi_mat_string("bump") },
{ UFBX_MATERIAL_FBX_BUMP, 0, 0, ufbxi_mat_string("Bump") },
};

static const ufbxi_shader_mapping ufbxi_fbx_lambert_shader_pbr_mapping[] = {
Expand Down Expand Up @@ -19728,6 +19738,7 @@ static const ufbxi_shader_mapping ufbxi_obj_pbr_mapping[] = {
{ UFBX_MATERIAL_PBR_TRANSMISSION_COLOR, UFBXI_SHADER_MAPPING_DEFAULT_W_1|UFBXI_SHADER_MAPPING_WIDEN_TO_RGB, 0, ufbxi_mat_string("Tf") },
{ UFBX_MATERIAL_PBR_DISPLACEMENT_MAP, 0, 0, ufbxi_mat_string("disp") },
{ UFBX_MATERIAL_PBR_NORMAL_MAP, 0, 0, ufbxi_mat_string("bump") },
{ UFBX_MATERIAL_PBR_NORMAL_MAP, 0, 0, ufbxi_mat_string("Bump") },
{ UFBX_MATERIAL_PBR_NORMAL_MAP, 0, 0, ufbxi_mat_string("norm") },
{ UFBX_MATERIAL_PBR_SHEEN_COLOR, UFBXI_SHADER_MAPPING_DEFAULT_W_1|UFBXI_SHADER_MAPPING_WIDEN_TO_RGB, 0, ufbxi_mat_string("Ps") },
{ UFBX_MATERIAL_PBR_COAT_FACTOR, 0, 0, ufbxi_mat_string("Pc") },
Expand Down Expand Up @@ -24904,7 +24915,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);
Expand Down Expand Up @@ -25715,7 +25726,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;
Expand Down Expand Up @@ -27066,7 +27077,7 @@ ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_postprocess_quat(ufbxi_bake
ufbx_quat ref = src.data[0].value;
for (size_t i = 1; i < src.count; i++) {
ufbx_quat v = src.data[i].value;
if (v.x != ref.x || v.y != ref.y || v.z != ref.z || v.y != ref.w) {
if (v.x != ref.x || v.y != ref.y || v.z != ref.z || v.w != ref.w) {
constant = false;
break;
}
Expand Down Expand Up @@ -27407,7 +27418,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;
Expand Down Expand Up @@ -30832,10 +30843,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;

Expand Down Expand Up @@ -33035,7 +33046,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)); }
Expand Down
Loading
Loading