#ifndef UFBX_UFBX_C_INCLUDED
#define UFBX_UFBX_C_INCLUDED
#if defined(UFBX_HEADER_PATH)
#include UFBX_HEADER_PATH
#else
#include "ufbx.h"
#endif
#if defined(UFBX_CONFIG_SOURCE)
#include UFBX_CONFIG_SOURCE
#endif
#define UFBXI_MAX_NON_ARRAY_VALUES …
#define UFBXI_MAX_NODE_DEPTH …
#define UFBXI_MAX_XML_DEPTH …
#define UFBXI_MAX_SKIP_SIZE …
#define UFBXI_MAP_MAX_SCAN …
#define UFBXI_KD_FAST_DEPTH …
#define UFBXI_HUGE_MAX_SCAN …
#define UFBXI_MIN_FILE_FORMAT_LOOKAHEAD …
#define UFBXI_FACE_GROUP_HASH_BITS …
#define UFBXI_MIN_THREADED_DEFLATE_BYTES …
#define UFBXI_MIN_THREADED_ASCII_VALUES …
#define UFBXI_GEOMETRY_CACHE_BUFFER_SIZE …
#ifndef UFBXI_MAX_NURBS_ORDER
#define UFBXI_MAX_NURBS_ORDER …
#endif
#ifndef UFBX_EPSILON
#define UFBX_EPSILON …
#endif
#if !defined(UFBX_MINIMAL)
#if !defined(UFBX_NO_SUBDIVISION)
#define UFBXI_FEATURE_SUBDIVISION …
#endif
#if !defined(UFBX_NO_TESSELLATION)
#define UFBXI_FEATURE_TESSELLATION …
#endif
#if !defined(UFBX_NO_GEOMETRY_CACHE)
#define UFBXI_FEATURE_GEOMETRY_CACHE …
#endif
#if !defined(UFBX_NO_SCENE_EVALUATION)
#define UFBXI_FEATURE_SCENE_EVALUATION …
#endif
#if !defined(UFBX_NO_SKINNING_EVALUATION)
#define UFBXI_FEATURE_SKINNING_EVALUATION …
#endif
#if !defined(UFBX_NO_ANIMATION_BAKING)
#define UFBXI_FEATURE_ANIMATION_BAKING …
#endif
#if !defined(UFBX_NO_TRIANGULATION)
#define UFBXI_FEATURE_TRIANGULATION …
#endif
#if !defined(UFBX_NO_INDEX_GENERATION)
#define UFBXI_FEATURE_INDEX_GENERATION …
#endif
#if !defined(UFBX_NO_FORMAT_OBJ)
#define UFBXI_FEATURE_FORMAT_OBJ …
#endif
#endif
#if defined(UFBX_DEV)
#if !defined(UFBX_NO_ERROR_STACK)
#define UFBXI_FEATURE_ERROR_STACK …
#endif
#endif
#if !defined(UFBXI_FEATURE_SUBDIVISION) && defined(UFBX_ENABLE_SUBDIVISION)
#define UFBXI_FEATURE_SUBDIVISION …
#endif
#if !defined(UFBXI_FEATURE_TESSELLATION) && defined(UFBX_ENABLE_TESSELLATION)
#define UFBXI_FEATURE_TESSELLATION …
#endif
#if !defined(UFBXI_FEATURE_GEOMETRY_CACHE) && defined(UFBX_ENABLE_GEOMETRY_CACHE)
#define UFBXI_FEATURE_GEOMETRY_CACHE …
#endif
#if !defined(UFBXI_FEATURE_SCENE_EVALUATION) && defined(UFBX_ENABLE_SCENE_EVALUATION)
#define UFBXI_FEATURE_SCENE_EVALUATION …
#endif
#if !defined(UFBXI_FEATURE_SKINNING_EVALUATION) && defined(UFBX_ENABLE_SKINNING_EVALUATION)
#define UFBXI_FEATURE_SKINNING_EVALUATION …
#endif
#if !defined(UFBXI_FEATURE_ANIMATION_BAKING) && defined(UFBX_ENABLE_ANIMATION_BAKING)
#define UFBXI_FEATURE_ANIMATION_BAKING …
#endif
#if !defined(UFBXI_FEATURE_TRIANGULATION) && defined(UFBX_ENABLE_TRIANGULATION)
#define UFBXI_FEATURE_TRIANGULATION …
#endif
#if !defined(UFBXI_FEATURE_INDEX_GENERATION) && defined(UFBX_ENABLE_INDEX_GENERATION)
#define UFBXI_FEATURE_INDEX_GENERATION …
#endif
#if !defined(UFBXI_FEATURE_FORMAT_OBJ) && defined(UFBX_ENABLE_FORMAT_OBJ)
#define UFBXI_FEATURE_FORMAT_OBJ …
#endif
#if !defined(UFBXI_FEATURE_ERROR_STACK) && defined(UFBX_ENABLE_ERROR_STACK)
#define UFBXI_FEATURE_ERROR_STACK …
#endif
#if !defined(UFBXI_FEATURE_SUBDIVISION)
#define UFBXI_FEATURE_SUBDIVISION …
#endif
#if !defined(UFBXI_FEATURE_TESSELLATION)
#define UFBXI_FEATURE_TESSELLATION …
#endif
#if !defined(UFBXI_FEATURE_GEOMETRY_CACHE)
#define UFBXI_FEATURE_GEOMETRY_CACHE …
#endif
#if !defined(UFBXI_FEATURE_SCENE_EVALUATION)
#define UFBXI_FEATURE_SCENE_EVALUATION …
#endif
#if !defined(UFBXI_FEATURE_SKINNING_EVALUATION)
#define UFBXI_FEATURE_SKINNING_EVALUATION …
#endif
#if !defined(UFBXI_FEATURE_ANIMATION_BAKING)
#define UFBXI_FEATURE_ANIMATION_BAKING …
#endif
#if !defined(UFBXI_FEATURE_TRIANGULATION)
#define UFBXI_FEATURE_TRIANGULATION …
#endif
#if !defined(UFBXI_FEATURE_INDEX_GENERATION)
#define UFBXI_FEATURE_INDEX_GENERATION …
#endif
#if !defined(UFBXI_FEATURE_FORMAT_OBJ)
#define UFBXI_FEATURE_FORMAT_OBJ …
#endif
#if !defined(UFBXI_FEATURE_ERROR_STACK)
#define UFBXI_FEATURE_ERROR_STACK …
#endif
#if UFBXI_FEATURE_GEOMETRY_CACHE
#define UFBXI_FEATURE_XML …
#else
#define UFBXI_FEATURE_XML …
#endif
#if UFBXI_FEATURE_TRIANGULATION
#define UFBXI_FEATURE_KD …
#else
#define UFBXI_FEATURE_KD …
#endif
#if !UFBXI_FEATURE_SUBDIVISION || !UFBXI_FEATURE_TESSELLATION || !UFBXI_FEATURE_GEOMETRY_CACHE || !UFBXI_FEATURE_SCENE_EVALUATION || !UFBXI_FEATURE_SKINNING_EVALUATION || !UFBXI_FEATURE_ANIMATION_BAKING || !UFBXI_FEATURE_TRIANGULATION || !UFBXI_FEATURE_INDEX_GENERATION || !UFBXI_FEATURE_XML || !UFBXI_FEATURE_KD || !UFBXI_FEATURE_FORMAT_OBJ
#define UFBXI_PARTIAL_FEATURES …
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <locale.h>
#include <float.h>
#if !defined(UFBX_NO_MATH_H)
#include <math.h>
#define UFBX_INFINITY …
#define UFBX_NAN …
#endif
#if !defined(UFBX_MATH_PREFIX)
#define UFBX_MATH_PREFIX
#endif
#define ufbxi_math_cat2(a, b) …
#define ufbxi_math_cat(a, b) …
#define ufbxi_math_fn(name) …
#if !defined(UFBX_NO_MATH_DEFINES)
#define ufbx_sqrt …
#define ufbx_fabs …
#define ufbx_pow …
#define ufbx_sin …
#define ufbx_cos …
#define ufbx_tan …
#define ufbx_asin …
#define ufbx_acos …
#define ufbx_atan …
#define ufbx_atan2 …
#define ufbx_copysign …
#define ufbx_fmin …
#define ufbx_fmax …
#define ufbx_nextafter …
#define ufbx_rint …
#define ufbx_ceil …
#define ufbx_isnan …
#endif
#if defined(UFBX_NO_MATH_H) && !defined(UFBX_NO_MATH_DECLARATIONS)
double ufbx_sqrt(double x);
double ufbx_sin(double x);
double ufbx_cos(double x);
double ufbx_tan(double x);
double ufbx_asin(double x);
double ufbx_acos(double x);
double ufbx_atan(double x);
double ufbx_atan2(double y, double x);
double ufbx_pow(double x, double y);
double ufbx_fmin(double a, double b);
double ufbx_fmax(double a, double b);
double ufbx_fabs(double x);
double ufbx_copysign(double x, double y);
double ufbx_nextafter(double x, double y);
double ufbx_rint(double x);
double ufbx_ceil(double x);
int ufbx_isnan(double x);
#endif
#if !defined(UFBX_INFINITY)
#define UFBX_INFINITY …
#endif
#if !defined(UFBX_NAN)
#define UFBX_NAN …
#endif
#if defined(_MSC_VER)
#define UFBXI_MSC_VER …
#else
#define UFBXI_MSC_VER …
#endif
#if defined(__GNUC__)
#define UFBXI_GNUC …
#else
#define UFBXI_GNUC …
#endif
#if !defined(UFBX_STANDARD_C) && defined(_MSC_VER)
#define ufbxi_noinline …
#define ufbxi_forceinline …
#define ufbxi_restrict …
#if defined(_Check_return_)
#define ufbxi_nodiscard …
#else
#define ufbxi_nodiscard
#endif
#define ufbxi_unused
#define ufbxi_unlikely …
#elif !defined(UFBX_STANDARD_C) && (defined(__GNUC__) || defined(__clang__))
#define ufbxi_noinline …
#define ufbxi_forceinline …
#define ufbxi_restrict …
#define ufbxi_nodiscard …
#define ufbxi_unused …
#define ufbxi_unlikely(cond) …
#else
#define ufbxi_noinline
#define ufbxi_forceinline
#define ufbxi_nodiscard
#define ufbxi_restrict
#define ufbxi_unused
#define ufbxi_unlikely …
#endif
#if !defined(UFBX_STANDARD_C) && defined(__clang__)
#define ufbxi_nounroll …
#elif !defined(UFBX_STANDARD_C) && UFBXI_GNUC >= 8
#define ufbxi_nounroll …
#elif !defined(UFBX_STANDARD_C) && defined(_MSC_VER)
#define ufbxi_nounroll …
#else
#define ufbxi_nounroll
#endif
#if defined(__GNUC__) && !defined(__clang__)
#define ufbxi_ignore …
#else
#define ufbxi_ignore(cond) …
#endif
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4061)
#pragma warning(disable: 4200)
#pragma warning(disable: 4201)
#pragma warning(disable: 4210)
#pragma warning(disable: 4127)
#pragma warning(disable: 4706)
#pragma warning(disable: 4789)
#pragma warning(disable: 4820)
#if defined(UFBX_STANDARD_C)
#pragma warning(disable: 4996)
#endif
#if defined(UFBXI_PARTIAL_FEATURES)
#pragma warning(disable: 4100)
#pragma warning(disable: 4505)
#endif
#endif
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#pragma clang diagnostic ignored "-Wmissing-braces"
#pragma clang diagnostic ignored "-Wdouble-promotion"
#pragma clang diagnostic ignored "-Wpedantic"
#pragma clang diagnostic ignored "-Wcast-qual"
#pragma clang diagnostic ignored "-Wcast-align"
#pragma clang diagnostic ignored "-Wcovered-switch-default"
#pragma clang diagnostic ignored "-Wpadded"
#pragma clang diagnostic ignored "-Wswitch-enum"
#pragma clang diagnostic ignored "-Wfloat-equal"
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#if __has_warning("-Watomic-implicit-seq-cst")
#pragma clang diagnostic ignored "-Watomic-implicit-seq-cst"
#endif
#if defined(UFBX_STANDARD_C)
#pragma clang diagnostic ignored "-Wunused-function"
#endif
#if defined(UFBXI_PARTIAL_FEATURES)
#pragma clang diagnostic ignored "-Wunused-function"
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif
#if defined(__cplusplus)
#pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#else
#pragma clang diagnostic ignored "-Wdeclaration-after-statement"
#pragma clang diagnostic ignored "-Wbad-function-cast"
#endif
#endif
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#pragma GCC diagnostic ignored "-Wmissing-braces"
#pragma GCC diagnostic ignored "-Wdouble-promotion"
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wcast-align"
#pragma GCC diagnostic ignored "-Wpadded"
#pragma GCC diagnostic ignored "-Wswitch-enum"
#pragma GCC diagnostic ignored "-Wfloat-equal"
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#pragma GCC diagnostic ignored "-Wlong-long"
#if defined(UFBX_STANDARD_C)
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#if defined(UFBXI_PARTIAL_FEATURES)
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#if defined(__cplusplus)
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
#else
#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
#pragma GCC diagnostic ignored "-Wbad-function-cast"
#if __GNUC__ >= 5
#pragma GCC diagnostic ignored "-Wc90-c99-compat"
#pragma GCC diagnostic ignored "-Wc99-c11-compat"
#endif
#endif
#endif
#if !defined(ufbx_static_assert)
#if defined(__cplusplus) && __cplusplus >= 201103
#define ufbx_static_assert …
#else
#define ufbx_static_assert(desc, cond) …
#endif
#endif
#if defined(__has_feature)
#if __has_feature(undefined_behavior_sanitizer) && !defined(UFBX_UBSAN)
#define UFBX_UBSAN …
#endif
#endif
#if defined(__SANITIZE_UNDEFINED__) && !defined(UFBX_UBSAN)
#define UFBX_UBSAN …
#endif
#if defined(UFBX_UBSAN) && !defined(UFBX_NO_UNALIGNED_LOADS)
#define UFBX_NO_UNALIGNED_LOADS
#endif
#if defined(__clang_analyzer__) && !defined(UFBX_STATIC_ANALYSIS)
#define UFBX_STATIC_ANALYSIS …
#endif
#if defined(UFBX_STATIC_ANALYSIS)
bool ufbxi_analysis_opaque;
#define ufbxi_maybe_null …
#else
#define ufbxi_maybe_null(ptr) …
#endif
#if !defined(ufbxi_trace)
#if defined(UFBX_TRACE)
#define ufbxi_trace …
#else
#define ufbxi_trace(desc) …
#endif
#endif
#ifndef UFBX_PATH_SEPARATOR
#if defined(_WIN32)
#define UFBX_PATH_SEPARATOR …
#else
#define UFBX_PATH_SEPARATOR …
#endif
#endif
#if !defined(UFBX_STANDARD_C) && defined(_POSIX_C_SOURCE)
#if _POSIX_C_SOURCE >= 200112l
#ifndef UFBX_HAS_FTELLO
#define UFBX_HAS_FTELLO
#endif
#endif
#endif
#if !defined(UFBX_STANDARD_C) && (defined(_MSC_VER) && defined(_M_X64)) || ((defined(__GNUC__) || defined(__clang__)) && defined(__x86_64__)) || defined(UFBX_USE_SSE)
#define UFBXI_HAS_SSE …
#include <xmmintrin.h>
#include <emmintrin.h>
#else
#define UFBXI_HAS_SSE …
#endif
#if !defined(UFBX_LITTLE_ENDIAN)
#if !defined(UFBX_STANDARD_C) && (defined(_M_IX86) || defined(__i386__) || defined(_M_X64) || defined(__x86_64__) || defined(_M_ARM64) || defined(__aarch64__) || defined(__wasm__) || defined(__EMSCRIPTEN__))
#define UFBX_LITTLE_ENDIAN …
#else
#define UFBX_LITTLE_ENDIAN …
#endif
#endif
#define ufbxi_read_u8(ptr) …
#if !defined(UFBX_STANDARD_C) && (defined(__clang__) && defined(__APPLE__))
#if __clang_major__ >= 5
#define UFBXI_HAS_ATTRIBUTE_ALIGNED …
#endif
#elif !defined(UFBX_STANDARD_C) && defined(__clang__)
#if (__clang_major__ >= 4) || (__clang_major__ == 3 && __clang_minor__ >= 3)
#define UFBXI_HAS_ATTRIBUTE_ALIGNED …
#endif
#elif !defined(UFBX_STANDARD_C) && defined(__GNUC__)
#if __GNUC__ >= 5
#define UFBXI_HAS_ATTRIBUTE_ALIGNED …
#endif
#endif
#if defined(UFBXI_HAS_ATTRIBUTE_ALIGNED)
#define UFBXI_HAS_UNALIGNED …
#define ufbxi_unaligned
ufbxi_unaligned_u16;
ufbxi_unaligned_u32;
ufbxi_unaligned_u64;
ufbxi_unaligned_f32;
ufbxi_unaligned_f64;
#elif !defined(UFBX_STANDARD_C) && defined(_MSC_VER)
#define UFBXI_HAS_UNALIGNED …
#if defined(_M_IX86)
#define ufbxi_unaligned
#else
#define ufbxi_unaligned …
#endif
typedef uint16_t ufbxi_unaligned_u16;
typedef uint32_t ufbxi_unaligned_u32;
typedef uint64_t ufbxi_unaligned_u64;
typedef float ufbxi_unaligned_f32;
typedef double ufbxi_unaligned_f64;
#endif
#if (defined(UFBXI_HAS_UNALIGNED) && UFBX_LITTLE_ENDIAN && !defined(UFBX_NO_UNALIGNED_LOADS)) || defined(UFBX_USE_UNALIGNED_LOADS)
#define ufbxi_read_u16(ptr) …
#define ufbxi_read_u32(ptr) …
#define ufbxi_read_u64(ptr) …
#define ufbxi_read_f32(ptr) …
#define ufbxi_read_f64(ptr) …
#else
static ufbxi_forceinline uint16_t ufbxi_read_u16(const void *ptr) {
const char *p = (const char*)ptr;
return (uint16_t)(
(unsigned)(uint8_t)p[0] << 0u |
(unsigned)(uint8_t)p[1] << 8u );
}
static ufbxi_forceinline uint32_t ufbxi_read_u32(const void *ptr) {
const char *p = (const char*)ptr;
return (uint32_t)(
(unsigned)(uint8_t)p[0] << 0u |
(unsigned)(uint8_t)p[1] << 8u |
(unsigned)(uint8_t)p[2] << 16u |
(unsigned)(uint8_t)p[3] << 24u );
}
static ufbxi_forceinline uint64_t ufbxi_read_u64(const void *ptr) {
const char *p = (const char*)ptr;
return (uint64_t)(
(uint64_t)(uint8_t)p[0] << 0u |
(uint64_t)(uint8_t)p[1] << 8u |
(uint64_t)(uint8_t)p[2] << 16u |
(uint64_t)(uint8_t)p[3] << 24u |
(uint64_t)(uint8_t)p[4] << 32u |
(uint64_t)(uint8_t)p[5] << 40u |
(uint64_t)(uint8_t)p[6] << 48u |
(uint64_t)(uint8_t)p[7] << 56u );
}
static ufbxi_forceinline float ufbxi_read_f32(const void *ptr) {
uint32_t u = ufbxi_read_u32(ptr);
float f;
memcpy(&f, &u, 4);
return f;
}
static ufbxi_forceinline double ufbxi_read_f64(const void *ptr) {
uint64_t u = ufbxi_read_u64(ptr);
double f;
memcpy(&f, &u, 8);
return f;
}
#endif
#define ufbxi_read_i8(ptr) …
#define ufbxi_read_i16(ptr) …
#define ufbxi_read_i32(ptr) …
#define ufbxi_read_i64(ptr) …
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
#define UFBX_SOURCE_VERSION …
ufbx_abi_data_def const uint32_t ufbx_source_version = …;
ufbx_static_assert(…);
#if UFBXI_HAS_SSE
#define ufbxi_copy_16_bytes(dst, src) …
#elif defined(UFBXI_HAS_UNALIGNED)
#define ufbxi_copy_16_bytes …
#else
#define ufbxi_copy_16_bytes …
#endif
#if !defined(UFBX_STANDARD_C) && (defined(__wasm__) || defined(__EMSCRIPTEN__)) && !defined(UFBX_WASM_32BIT)
typedef uint64_t ufbxi_fast_uint;
#else
ufbxi_fast_uint;
#endif
#if !defined(UFBX_STANDARD_C) && defined(_MSC_VER) && defined(_M_X64)
#define ufbxi_wrap_shr64 …
#else
#define ufbxi_wrap_shr64(a, b) …
#endif
#define UFBXI_THREAD_SAFE …
#if defined(__cplusplus)
#define ufbxi_extern_c …
#else
#define ufbxi_extern_c
#endif
#if !defined(UFBX_STANDARD_C) && (defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER))
ufbxi_atomic_counter;
#define ufbxi_atomic_counter_init(ptr) …
#define ufbxi_atomic_counter_free(ptr) …
#define ufbxi_atomic_counter_inc(ptr) …
#define ufbxi_atomic_counter_dec(ptr) …
#define ufbxi_atomic_counter_load(ptr) …
#elif !defined(UFBX_STANDARD_C) && defined(_MSC_VER)
#if defined(_M_X64) || defined(_M_ARM64)
ufbxi_extern_c __int64 _InterlockedIncrement64(__int64 volatile * lpAddend);
ufbxi_extern_c __int64 _InterlockedDecrement64(__int64 volatile * lpAddend);
ufbxi_extern_c __int64 _InterlockedExchangeAdd64(__int64 volatile * lpAddend, __int64 Value);
typedef volatile __int64 ufbxi_atomic_counter;
#define ufbxi_atomic_counter_init …
#define ufbxi_atomic_counter_free …
#define ufbxi_atomic_counter_inc …
#define ufbxi_atomic_counter_dec …
#define ufbxi_atomic_counter_load …
#else
ufbxi_extern_c long __cdecl _InterlockedIncrement(long volatile * lpAddend);
ufbxi_extern_c long __cdecl _InterlockedDecrement(long volatile * lpAddend);
ufbxi_extern_c long __cdecl _InterlockedExchangeAdd(long volatile * lpAddend, long Value);
typedef volatile long ufbxi_atomic_counter;
#define ufbxi_atomic_counter_init …
#define ufbxi_atomic_counter_free …
#define ufbxi_atomic_counter_inc …
#define ufbxi_atomic_counter_dec …
#define ufbxi_atomic_counter_load …
#endif
#elif !defined(UFBX_STANDARD_C) && defined(__TINYC__)
#if defined(__x86_64__) || defined(_AMD64_)
static size_t ufbxi_tcc_atomic_add(volatile size_t *dst, size_t value) {
__asm__ __volatile__("lock; xaddq %0, %1;" : "+r" (value), "=m" (*dst) : "m" (dst));
return value;
}
#elif defined(__i386__) || defined(_X86_)
static size_t ufbxi_tcc_atomic_add(volatile size_t *dst, size_t value) {
__asm__ __volatile__("lock; xaddl %0, %1;" : "+r" (value), "=m" (*dst) : "m" (dst));
return value;
}
#else
#error Unexpected TCC architecture
#endif
typedef volatile size_t ufbxi_atomic_counter;
#define ufbxi_atomic_counter_init …
#define ufbxi_atomic_counter_free …
#define ufbxi_atomic_counter_inc …
#define ufbxi_atomic_counter_dec …
#define ufbxi_atomic_counter_load …
#elif defined(__cplusplus) && (__cplusplus >= 201103L)
#include <new>
#include <atomic>
typedef struct { alignas(std::atomic_size_t) char data[sizeof(std::atomic_size_t)]; } ufbxi_atomic_counter;
#define ufbxi_atomic_counter_init …
#define ufbxi_atomic_counter_free …
#define ufbxi_atomic_counter_inc …
#define ufbxi_atomic_counter_dec …
#define ufbxi_atomic_counter_load …
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)
#include <stdatomic.h>
typedef volatile atomic_size_t ufbxi_atomic_counter;
#define ufbxi_atomic_counter_init …
#define ufbxi_atomic_counter_free …
#define ufbxi_atomic_counter_inc …
#define ufbxi_atomic_counter_dec …
#define ufbxi_atomic_counter_load …
#else
typedef volatile size_t ufbxi_atomic_counter;
#define ufbxi_atomic_counter_init …
#define ufbxi_atomic_counter_free …
#define ufbxi_atomic_counter_inc …
#define ufbxi_atomic_counter_dec …
#define ufbxi_atomic_counter_load …
#undef UFBXI_THREAD_SAFE
#define UFBXI_THREAD_SAFE …
#endif
#if !defined(UFBX_STANDARD_C) && defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
ufbxi_extern_c unsigned char _BitScanReverse(unsigned long * _Index, unsigned long _Mask);
ufbxi_extern_c unsigned char _BitScanReverse64(unsigned long * _Index, unsigned __int64 _Mask);
static ufbxi_forceinline ufbxi_unused uint32_t ufbxi_lzcnt64(uint64_t v) {
unsigned long index;
#if defined(_M_X64)
_BitScanReverse64(&index, (unsigned __int64)v);
#else
uint32_t hi = (uint32_t)(v >> 32u);
uint32_t hi_nonzero = hi != 0 ? 1 : 0;
uint32_t part = hi_nonzero ? hi : (uint32_t)v;
_BitScanReverse(&index, (unsigned long)part);
index += hi_nonzero * 32u;
#endif
return 63 - (uint32_t)index;
}
#elif !defined(UFBX_STANDARD_C) && (defined(__GNUC__) || defined(__clang__))
#define ufbxi_lzcnt64(v) …
#else
static const uint8_t ufbxi_lzcnt_table[] = {
63, 16, 62, 7, 15, 36, 61, 3, 6, 14, 22, 26, 35, 47, 60, 2, 9, 5, 28, 11, 13, 21, 42,
19, 25, 31, 34, 40, 46, 52, 59, 1, 17, 8, 37, 4, 23, 27, 48, 10, 29, 12, 43, 20, 32, 41,
53, 18, 38, 24, 49, 30, 44, 33, 54, 39, 50, 45, 55, 51, 56, 57, 58, 0,
};
static ufbxi_noinline ufbxi_unused uint32_t ufbxi_lzcnt64(uint64_t v) {
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
return ufbxi_lzcnt_table[(v * UINT64_C(0x03f79d71b4cb0a89)) >> 58];
}
#endif
#if defined(UFBX_DEBUG_BINARY_SEARCH) || defined(UFBX_REGRESSION)
#define ufbxi_clamp_linear_threshold …
#else
#define ufbxi_clamp_linear_threshold(v) …
#endif
#if defined(UFBX_REGRESSION)
#undef UFBXI_MAX_SKIP_SIZE
#define UFBXI_MAX_SKIP_SIZE …
#undef UFBXI_MAP_MAX_SCAN
#define UFBXI_MAP_MAX_SCAN …
#undef UFBXI_KD_FAST_DEPTH
#define UFBXI_KD_FAST_DEPTH …
#undef UFBXI_FACE_GROUP_HASH_BITS
#define UFBXI_FACE_GROUP_HASH_BITS …
#endif
#if defined(UFBX_REGRESSION) || defined(UFBX_EXTENSIVE_THREADING)
#undef UFBXI_MIN_THREADED_DEFLATE_BYTES
#define UFBXI_MIN_THREADED_DEFLATE_BYTES …
#undef UFBXI_MIN_THREADED_ASCII_VALUES
#define UFBXI_MIN_THREADED_ASCII_VALUES …
#endif
#if defined(UFBX_REGRESSION)
#define ufbxi_regression_assert …
#else
#define ufbxi_regression_assert(cond) …
#endif
#if defined(UFBX_REGRESSION) || defined(UFBX_DEV)
#define ufbxi_dev_assert …
#else
#define ufbxi_dev_assert(cond) …
#endif
#define ufbxi_unreachable(reason) …
#if defined(UFBX_REGRESSION)
#define UFBXI_IS_REGRESSION …
#else
#define UFBXI_IS_REGRESSION …
#endif
#if defined(_MSC_VER)
#define ufbxi_thread_local …
#elif defined(__GNUC__) || defined(__clang__)
#define ufbxi_thread_local …
#elif UFBXI_HAS_CPP11
#define ufbxi_thread_local …
#elif UFBX_STDC >= 201112L
#define ufbxi_thread_local …
#endif
#if defined(UFBXI_ANALYSIS_RECURSIVE)
#define ufbxi_recursive_function …
#define ufbxi_recursive_function_void …
#elif UFBXI_IS_REGRESSION && defined(ufbxi_thread_local)
#define ufbxi_recursive_function …
#define ufbxi_recursive_function_void …
#else
#define ufbxi_recursive_function(m_ret, m_name, m_args, m_max_depth, m_params) …
#define ufbxi_recursive_function_void(m_name, m_args, m_max_depth, m_params) …
#endif
#if defined(UFBX_UBSAN)
static void ufbxi_assert_zero(size_t offset) { ufbx_assert(offset == 0); }
#define ufbxi_add_ptr …
#define ufbxi_sub_ptr …
#else
#define ufbxi_add_ptr(ptr, offset) …
#define ufbxi_sub_ptr(ptr, offset) …
#endif
#define ufbxi_arraycount(arr) …
#define ufbxi_for(m_type, m_name, m_begin, m_num) …
#define ufbxi_for_ptr(m_type, m_name, m_begin, m_num) …
#define ufbxi_for_list(m_type, m_name, m_list) …
#define ufbxi_for_ptr_list(m_type, m_name, m_list) …
#define ufbxi_string_literal(str) …
static ufbxi_forceinline uint32_t ufbxi_min32(uint32_t a, uint32_t b) { … }
static ufbxi_forceinline uint32_t ufbxi_max32(uint32_t a, uint32_t b) { … }
static ufbxi_forceinline uint64_t ufbxi_min64(uint64_t a, uint64_t b) { … }
static ufbxi_forceinline uint64_t ufbxi_max64(uint64_t a, uint64_t b) { … }
static ufbxi_forceinline size_t ufbxi_min_sz(size_t a, size_t b) { … }
static ufbxi_forceinline size_t ufbxi_max_sz(size_t a, size_t b) { … }
static ufbxi_forceinline ufbx_real ufbxi_min_real(ufbx_real a, ufbx_real b) { … }
static ufbxi_forceinline ufbx_real ufbxi_max_real(ufbx_real a, ufbx_real b) { … }
static ufbxi_forceinline int32_t ufbxi_f64_to_i32(double value)
{ … }
static ufbxi_forceinline int64_t ufbxi_f64_to_i64(double value)
{ … }
#if defined(UFBX_REGRESSION)
static size_t ufbxi_to_size(ptrdiff_t delta) {
ufbx_assert(delta >= 0);
return (size_t)delta;
}
#else
#define ufbxi_to_size(delta) …
#endif
#define ufbxi_macro_stable_sort(m_type, m_linear_size, m_data, m_tmp, m_size, m_cmp_lambda) …
#define ufbxi_macro_lower_bound_eq(m_type, m_linear_size, m_result_ptr, m_data, m_begin, m_size, m_cmp_lambda, m_eq_lambda) …
#define ufbxi_macro_upper_bound_eq(m_type, m_linear_size, m_result_ptr, m_data, m_begin, m_size, m_eq_lambda) …
ufbxi_less_fn;
static ufbxi_noinline void ufbxi_stable_sort(size_t stride, size_t linear_size, void *in_data, void *in_tmp, size_t size, ufbxi_less_fn *less_fn, void *less_user)
{ … }
static ufbxi_forceinline void ufbxi_swap(void *a, void *b, size_t size)
{ … }
static ufbxi_noinline void ufbxi_unstable_sort(void *in_data, size_t size, size_t stride, ufbxi_less_fn *less_fn, void *less_user)
{ … }
#if !defined(UFBX_STANDARD_C) && UFBXI_MSC_VER >= 1920 && defined(_M_X64) && !defined(__clang__)
ufbxi_extern_c extern unsigned __int64 __cdecl _udiv128(unsigned __int64 highdividend,
unsigned __int64 lowdividend, unsigned __int64 divisor, unsigned __int64 *remainder);
#define ufbxi_div128 …
#elif !defined(UFBX_STANDARD_C) && (defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(_M_X64))
static ufbxi_forceinline uint64_t ufbxi_div128(uint64_t a_hi, uint64_t a_lo, uint64_t b, uint64_t *p_rem) { … }
#else
static ufbxi_forceinline uint64_t ufbxi_div128(uint64_t a_hi, uint64_t a_lo, uint64_t b, uint64_t *p_rem) {
uint32_t shift = ufbxi_lzcnt64(b);
a_hi = (a_hi << shift) | (shift ? a_lo >> (64 - shift) : 0);
a_lo <<= shift;
b <<= shift;
uint64_t v = b;
uint32_t v1 = (uint32_t)(v >> 32);
uint32_t v0 = (uint32_t)(v);
uint64_t q1, q0, r;
{
uint64_t u2_u1 = a_hi;
uint32_t u0 = (uint32_t)(a_lo >> 32u);
uint64_t qh = u2_u1 / v1;
uint64_t rh = u2_u1 % v1;
uint64_t rh_u0 = rh << 32u | u0;
uint64_t v0qh = v0 * qh;
uint32_t c = rh_u0 < v0qh ? 1 : 0;
c += c & (v0qh - rh_u0 > v ? 1 : 0);
q1 = qh - c;
r = rh_u0 - v0qh + v*c;
}
{
uint64_t u2_u1 = r;
uint32_t u0 = (uint32_t)a_lo;
uint64_t qh = u2_u1 / v1;
uint64_t rh = u2_u1 % v1;
uint64_t rh_u0 = rh << 32u | u0;
uint64_t v0qh = v0 * qh;
uint32_t c = rh_u0 < v0qh ? 1 : 0;
c += c & (v0qh - rh_u0 > v ? 1 : 0);
q0 = qh - c;
r = rh_u0 - v0qh + v*c;
}
*p_rem = r >> shift;
return q1 << 32u | q0;
}
#endif
ufbxi_parse_double_flag;
static const uint64_t ufbxi_pow5_tab[] = …;
static const int8_t ufbxi_pow2_tab[] = …;
static const double ufbxi_pow10_tab_f64[] = …;
static ufbxi_noinline uint32_t ufbxi_parse_double_init_flags()
{ … }
static ufbxi_noinline double ufbxi_parse_double_slow(const char *str, char **end)
{ … }
static ufbxi_noinline double ufbxi_parse_double(const char *str, size_t max_length, char **end, uint32_t flags)
{ … }
static ufbxi_forceinline int64_t ufbxi_parse_int64(const char *str, char **end)
{ … }
#if !defined(ufbx_inflate)
static const uint32_t ufbxi_deflate_length_lut[] = …;
static const uint32_t ufbxi_deflate_dist_lut[] = …;
static const uint8_t ufbxi_deflate_code_length_permutation[] = …;
#define UFBXI_INFLATE_FAST_MIN_IN …
#define UFBXI_INFLATE_FAST_MIN_OUT …
#define UFBXI_HUFF_MAX_BITS …
#define UFBXI_HUFF_MAX_VALUE …
#define UFBXI_HUFF_FAST_BITS …
#define UFBXI_HUFF_FAST_SIZE …
#define UFBXI_HUFF_FAST_MASK …
#define UFBXI_HUFF_MAX_LONG_BITS …
#define UFBXI_HUFF_MAX_LONG_SYMS …
#define UFBXI_HUFF_CODELEN_FAST_BITS …
#define UFBXI_HUFF_CODELEN_FAST_MASK …
#define UFBXI_HUFF_MAX_EXTRA_SYMS …
ufbxi_bit_stream;
ufbxi_huff_sym;
#define ufbxi_huff_sym_total_bits(sym) …
#define ufbxi_huff_sym_long_mask(sym) …
#define ufbxi_huff_sym_long_offset(sym) …
#define ufbxi_huff_sym_value(sym) …
enum { … };
#define UFBXI_HUFF_ERROR_SYM …
#define UFBXI_HUFF_UNINITIALIZED_SYM …
ufbxi_huff_tree;
ufbxi_trees;
ufbxi_inflate_retain_imp;
ufbx_static_assert(…);
ufbxi_deflate_context;
static ufbxi_forceinline uint32_t
ufbxi_bit_reverse(uint32_t mask, uint32_t num_bits)
{ … }
static ufbxi_noinline const char *
ufbxi_bit_chunk_refill(ufbxi_bit_stream *s, const char *ptr)
{ … }
static ufbxi_noinline void ufbxi_bit_stream_init(ufbxi_bit_stream *s, const ufbx_inflate_input *input)
{ … }
static ufbxi_noinline const char *
ufbxi_bit_yield(ufbxi_bit_stream *s, const char *ptr)
{ … }
static ufbxi_forceinline void
ufbxi_bit_refill(uint64_t *p_bits, size_t *p_left, const char **p_data, ufbxi_bit_stream *s)
{ … }
#define ufbxi_macro_bit_refill_fast(m_bits, m_left, m_data, m_refill_bits) …
static ufbxi_noinline int
ufbxi_bit_copy_bytes(void *dst, ufbxi_bit_stream *s, size_t len)
{ … }
static ufbxi_noinline ptrdiff_t
ufbxi_huff_build_imp(ufbxi_huff_tree *tree, uint8_t *sym_bits, uint32_t sym_count, const uint32_t *sym_extra, uint32_t sym_extra_offset, uint32_t fast_bits, uint32_t *bits_counts)
{ … }
static ufbxi_noinline ptrdiff_t
ufbxi_huff_build(ufbxi_huff_tree *tree, uint8_t *sym_bits, uint32_t sym_count, const uint32_t *sym_extra, uint32_t sym_extra_offset, uint32_t fast_bits)
{ … }
static ufbxi_forceinline ufbxi_huff_sym
ufbxi_huff_decode_bits(const ufbxi_huff_tree *tree, uint64_t bits, uint32_t fast_bits, uint32_t fast_mask)
{ … }
static ufbxi_noinline void ufbxi_init_static_huff(ufbxi_trees *trees, const ufbx_inflate_input *input)
{ … }
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_init_dynamic_huff(ufbxi_deflate_context *dc, ufbxi_trees *trees)
{ … }
static ufbxi_noinline uint32_t ufbxi_adler32(const void *data, size_t size)
{ … }
static ufbxi_noinline int
ufbxi_inflate_block_slow(ufbxi_deflate_context *dc, ufbxi_trees *trees, size_t max_symbols)
{ … }
ufbx_static_assert(…);
ufbx_static_assert(…);
static ufbxi_noinline int
ufbxi_inflate_block_fast(ufbxi_deflate_context *dc, ufbxi_trees *trees)
{ … }
static void ufbxi_inflate_init_retain(ufbx_inflate_retain *retain)
{ … }
ufbxi_extern_c ptrdiff_t ufbx_inflate(void *dst, size_t dst_size, const ufbx_inflate_input *input, ufbx_inflate_retain *retain)
{ … }
#endif
static const char ufbxi_empty_char[1] = …;
static ufbxi_noinline int ufbxi_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list args)
{ … }
static ufbxi_noinline int ufbxi_snprintf(char *buf, size_t buf_size, const char *fmt, ...)
{ … }
static ufbxi_noinline void ufbxi_panicf_imp(ufbx_panic *panic, const char *fmt, ...)
{ … }
#define ufbxi_panicf(panic, cond, ...) …
#define ufbxi_error_msg(cond, msg) …
static ufbxi_noinline int ufbxi_fail_imp_err(ufbx_error *err, const char *cond, const char *func, uint32_t line)
{ … }
ufbxi_nodiscard static ufbxi_noinline size_t ufbxi_utf8_valid_length(const char *str, size_t length)
{ … }
static ufbxi_noinline void ufbxi_clean_string_utf8(char *str, size_t length)
{ … }
static ufbxi_noinline void ufbxi_set_err_info(ufbx_error *err, const char *data, size_t length)
{ … }
static ufbxi_noinline void ufbxi_fmt_err_info(ufbx_error *err, const char *fmt, ...)
{ … }
static ufbxi_noinline void ufbxi_clear_error(ufbx_error *err)
{ … }
#if UFBXI_FEATURE_ERROR_STACK
#define ufbxi_function …
#define ufbxi_line …
#define ufbxi_cond_str …
#else
#define ufbxi_function …
#define ufbxi_line …
#define ufbxi_cond_str(cond) …
#endif
#define ufbxi_check_err(err, cond) …
#define ufbxi_check_return_err(err, cond, ret) …
#define ufbxi_fail_err(err, desc) …
#define ufbxi_check_err_msg(err, cond, msg) …
#define ufbxi_check_return_err_msg(err, cond, ret, msg) …
#define ufbxi_fail_err_msg(err, desc, msg) …
#define ufbxi_report_err_msg(err, desc, msg) …
static ufbxi_noinline void ufbxi_fix_error_type(ufbx_error *error, const char *default_desc)
{ … }
#if defined(UFBX_REGRESSION)
static const char ufbxi_zero_size_buffer[4096] = { 0 };
#else
static const char ufbxi_zero_size_buffer[64] = …;
#endif
static ufbxi_forceinline size_t ufbxi_align_to_mask(size_t value, size_t align_mask)
{ … }
static ufbxi_forceinline size_t ufbxi_size_align_mask(size_t size)
{ … }
ufbxi_allocator;
static ufbxi_forceinline bool ufbxi_does_overflow(size_t total, size_t a, size_t b)
{ … }
static ufbxi_noinline void *ufbxi_alloc_size(ufbxi_allocator *ator, size_t size, size_t n)
{ … }
static void ufbxi_free_size(ufbxi_allocator *ator, size_t size, void *ptr, size_t n);
static ufbxi_noinline void *ufbxi_realloc_size(ufbxi_allocator *ator, size_t size, void *old_ptr, size_t old_n, size_t n)
{ … }
static ufbxi_noinline void ufbxi_free_size(ufbxi_allocator *ator, size_t size, void *ptr, size_t n)
{ … }
ufbxi_noinline ufbxi_nodiscard static bool ufbxi_grow_array_size(ufbxi_allocator *ator, size_t size, void *p_ptr, size_t *p_cap, size_t n)
{ … }
static ufbxi_noinline void ufbxi_free_ator(ufbxi_allocator *ator)
{ … }
#define ufbxi_alloc(ator, type, n) …
#define ufbxi_alloc_zero(ator, type, n) …
#define ufbxi_realloc(ator, type, old_ptr, old_n, n) …
#define ufbxi_realloc_zero(ator, type, old_ptr, old_n, n) …
#define ufbxi_free(ator, type, ptr, n) …
#define ufbxi_grow_array(ator, p_ptr, p_cap, n) …
#define UFBXI_SCENE_IMP_MAGIC …
#define UFBXI_MESH_IMP_MAGIC …
#define UFBXI_LINE_CURVE_IMP_MAGIC …
#define UFBXI_CACHE_IMP_MAGIC …
#define UFBXI_ANIM_IMP_MAGIC …
#define UFBXI_BAKED_ANIM_IMP_MAGIC …
#define UFBXI_REFCOUNT_IMP_MAGIC …
#define UFBXI_BUF_CHUNK_IMP_MAGIC …
ufbxi_buf_padding;
ufbxi_buf_chunk;
struct ufbxi_buf_padding { … };
struct ufbxi_buf_chunk { … };
ufbx_static_assert(…);
ufbxi_buf;
ufbxi_buf_state;
static ufbxi_noinline void *ufbxi_push_size_new_block(ufbxi_buf *b, size_t size)
{ … }
static ufbxi_noinline void *ufbxi_push_size(ufbxi_buf *b, size_t size, size_t n)
{ … }
static ufbxi_forceinline void *ufbxi_push_size_fast(ufbxi_buf *b, size_t size, size_t n)
{ … }
static ufbxi_noinline void *ufbxi_push_size_zero(ufbxi_buf *b, size_t size, size_t n)
{ … }
ufbxi_nodiscard static ufbxi_noinline void *ufbxi_push_size_copy(ufbxi_buf *b, size_t size, size_t n, const void *data)
{ … }
ufbxi_nodiscard static ufbxi_forceinline void *ufbxi_push_size_copy_fast(ufbxi_buf *b, size_t size, size_t n, const void *data)
{ … }
static ufbxi_noinline void ufbxi_buf_free_unused(ufbxi_buf *b)
{ … }
static ufbxi_noinline void ufbxi_pop_size(ufbxi_buf *b, size_t size, size_t n, void *dst, bool peek)
{ … }
static ufbxi_noinline void *ufbxi_push_pop_size(ufbxi_buf *dst, ufbxi_buf *src, size_t size, size_t n)
{ … }
static ufbxi_noinline void *ufbxi_push_peek_size(ufbxi_buf *dst, ufbxi_buf *src, size_t size, size_t n)
{ … }
static ufbxi_noinline void ufbxi_buf_free(ufbxi_buf *buf)
{ … }
static ufbxi_noinline void ufbxi_buf_clear(ufbxi_buf *buf)
{ … }
#define ufbxi_push(b, type, n) …
#define ufbxi_push_zero(b, type, n) …
#define ufbxi_push_copy(b, type, n, data) …
#define ufbxi_push_copy_fast(b, type, n, data) …
#define ufbxi_push_fast(b, type, n) …
#define ufbxi_pop(b, type, n, dst) …
#define ufbxi_peek(b, type, n, dst) …
#define ufbxi_push_pop(dst, src, type, n) …
#define ufbxi_push_peek(dst, src, type, n) …
ufbxi_aa_node;
ufbxi_cmp_fn;
struct ufbxi_aa_node { … };
ufbxi_map;
static ufbxi_noinline void ufbxi_map_init(ufbxi_map *map, ufbxi_allocator *ator, ufbxi_cmp_fn *cmp_fn, void *cmp_user)
{ … }
static ufbxi_noinline void ufbxi_map_free(ufbxi_map *map)
{ … }
static ufbxi_noinline ufbxi_aa_node *ufbxi_aa_tree_insert(ufbxi_map *map, ufbxi_aa_node *node, const void *value, uint32_t index, size_t item_size)
ufbxi_recursive_function(ufbxi_aa_node *, ufbxi_aa_tree_insert, (map, node, value, index, item_size), 59,
(ufbxi_map *map, ufbxi_aa_node *node, const void *value, uint32_t index, size_t item_size))
{ … }
static ufbxi_noinline void *ufbxi_aa_tree_find(ufbxi_map *map, const void *value, size_t item_size)
{ … }
static ufbxi_noinline bool ufbxi_map_grow_size_imp(ufbxi_map *map, size_t item_size, size_t min_size)
{ … }
static ufbxi_forceinline bool ufbxi_map_grow_size(ufbxi_map *map, size_t size, size_t min_size)
{ … }
static ufbxi_noinline void *ufbxi_map_find_size(ufbxi_map *map, size_t size, uint32_t hash, const void *value)
{ … }
static ufbxi_noinline void *ufbxi_map_insert_size(ufbxi_map *map, size_t size, uint32_t hash, const void *value)
{ … }
#define ufbxi_map_grow(map, type, min_size) …
#define ufbxi_map_find(map, type, hash, value) …
#define ufbxi_map_insert(map, type, hash, value) …
static int ufbxi_map_cmp_uint64(void *user, const void *va, const void *vb)
{ … }
static int ufbxi_map_cmp_const_char_ptr(void *user, const void *va, const void *vb)
{ … }
static int ufbxi_map_cmp_uintptr(void *user, const void *va, const void *vb)
{ … }
static ufbxi_noinline uint32_t ufbxi_hash_string(const char *str, size_t length)
{ … }
static ufbxi_noinline uint32_t ufbxi_hash_string_check_ascii(const char *str, size_t length, bool *p_non_ascii)
{ … }
static ufbxi_forceinline uint32_t ufbxi_hash32(uint32_t x)
{ … }
static ufbxi_forceinline uint32_t ufbxi_hash64(uint64_t x)
{ … }
static ufbxi_forceinline uint32_t ufbxi_hash_uptr(uintptr_t ptr)
{ … }
#define ufbxi_hash_ptr(ptr) …
ufbxi_warnings;
ufbxi_nodiscard static ufbxi_noinline int ufbxi_vwarnf_imp(ufbxi_warnings *ws, ufbx_warning_type type, uint32_t element_id, const char *fmt, va_list args)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_warnf_imp(ufbxi_warnings *ws, ufbx_warning_type type, uint32_t element_id, const char *fmt, ...)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_pop_warnings(ufbxi_warnings *ws, ufbx_warning_list *warnings, bool *p_has_warning)
{ … }
ufbxi_string_pool;
ufbxi_sanitized_string;
static ufbxi_forceinline bool ufbxi_str_equal(ufbx_string a, ufbx_string b)
{ … }
static ufbxi_forceinline bool ufbxi_str_less(ufbx_string a, ufbx_string b)
{ … }
static ufbxi_forceinline int ufbxi_str_cmp(ufbx_string a, ufbx_string b)
{ … }
static ufbxi_forceinline ufbx_string ufbxi_str_c(const char *str)
{ … }
static ufbxi_noinline uint32_t ufbxi_get_concat_key(const ufbx_string *parts, size_t num_parts)
{ … }
static ufbxi_noinline int ufbxi_concat_str_cmp(const ufbx_string *ref, const ufbx_string *parts, size_t num_parts)
{ … }
static ufbxi_forceinline bool ufbxi_starts_with(ufbx_string str, ufbx_string prefix)
{ … }
static ufbxi_forceinline bool ufbxi_ends_with(ufbx_string str, ufbx_string suffix)
{ … }
static ufbxi_noinline bool ufbxi_remove_prefix_len(ufbx_string *str, const char *prefix, size_t prefix_len)
{ … }
static ufbxi_noinline bool ufbxi_remove_suffix_len(ufbx_string *str, const char *suffix, size_t suffix_len)
{ … }
static ufbxi_forceinline bool ufbxi_remove_prefix_str(ufbx_string *str, ufbx_string prefix)
{ … }
static ufbxi_forceinline bool ufbxi_remove_suffix_c(ufbx_string *str, const char *suffix)
{ … }
static int ufbxi_map_cmp_string(void *user, const void *va, const void *vb)
{ … }
static ufbxi_forceinline ufbx_string ufbxi_safe_string(const char *data, size_t length)
{ … }
static void ufbxi_string_pool_temp_free(ufbxi_string_pool *pool)
{ … }
ufbxi_nodiscard static size_t ufbxi_add_replacement_char(ufbxi_string_pool *pool, char *dst, char c)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_sanitize_string(ufbxi_string_pool *pool, ufbxi_sanitized_string *sanitized, const char *str, size_t length, size_t valid_length, bool push_both)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_push_sanitized_string(ufbxi_string_pool *pool, ufbxi_sanitized_string *sanitized, const char *str, size_t length, uint32_t hash, bool raw)
{ … }
ufbxi_nodiscard static ufbxi_noinline const char *ufbxi_push_string_imp(ufbxi_string_pool *pool, const char *str, size_t length, size_t *p_out_length, bool copy, bool raw)
{ … }
ufbxi_nodiscard static ufbxi_forceinline const char *ufbxi_push_string(ufbxi_string_pool *pool, const char *str, size_t length, size_t *p_out_length, bool raw)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_push_string_place(ufbxi_string_pool *pool, const char **p_str, size_t *p_length, bool raw)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_push_string_place_str(ufbxi_string_pool *pool, ufbx_string *p_str, bool raw)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_push_string_place_blob(ufbxi_string_pool *pool, ufbx_blob *p_blob, bool raw)
{ … }
static const char ufbxi_AllSame[] = …;
static const char ufbxi_Alphas[] = …;
static const char ufbxi_AmbientColor[] = …;
static const char ufbxi_AnimationCurveNode[] = …;
static const char ufbxi_AnimationCurve[] = …;
static const char ufbxi_AnimationLayer[] = …;
static const char ufbxi_AnimationStack[] = …;
static const char ufbxi_ApertureFormat[] = …;
static const char ufbxi_ApertureMode[] = …;
static const char ufbxi_AreaLightShape[] = …;
static const char ufbxi_AspectH[] = …;
static const char ufbxi_AspectHeight[] = …;
static const char ufbxi_AspectRatioMode[] = …;
static const char ufbxi_AspectW[] = …;
static const char ufbxi_AspectWidth[] = …;
static const char ufbxi_Audio[] = …;
static const char ufbxi_AudioLayer[] = …;
static const char ufbxi_BaseLayer[] = …;
static const char ufbxi_BinaryData[] = …;
static const char ufbxi_BindPose[] = …;
static const char ufbxi_BindingTable[] = …;
static const char ufbxi_Binormals[] = …;
static const char ufbxi_BinormalsIndex[] = …;
static const char ufbxi_BinormalsW[] = …;
static const char ufbxi_BlendMode[] = …;
static const char ufbxi_BlendModes[] = …;
static const char ufbxi_BlendShapeChannel[] = …;
static const char ufbxi_BlendShape[] = …;
static const char ufbxi_BlendWeights[] = …;
static const char ufbxi_BoundaryRule[] = …;
static const char ufbxi_Boundary[] = …;
static const char ufbxi_ByEdge[] = …;
static const char ufbxi_ByPolygonVertex[] = …;
static const char ufbxi_ByPolygon[] = …;
static const char ufbxi_ByVertex[] = …;
static const char ufbxi_ByVertice[] = …;
static const char ufbxi_Cache[] = …;
static const char ufbxi_CameraProjectionType[] = …;
static const char ufbxi_CameraStereo[] = …;
static const char ufbxi_CameraSwitcher[] = …;
static const char ufbxi_Camera[] = …;
static const char ufbxi_CastLight[] = …;
static const char ufbxi_CastShadows[] = …;
static const char ufbxi_Channel[] = …;
static const char ufbxi_Character[] = …;
static const char ufbxi_Children[] = …;
static const char ufbxi_Cluster[] = …;
static const char ufbxi_CollectionExclusive[] = …;
static const char ufbxi_Collection[] = …;
static const char ufbxi_ColorIndex[] = …;
static const char ufbxi_Color[] = …;
static const char ufbxi_Colors[] = …;
static const char ufbxi_Cone_angle[] = …;
static const char ufbxi_ConeAngle[] = …;
static const char ufbxi_Connections[] = …;
static const char ufbxi_Constraint[] = …;
static const char ufbxi_Content[] = …;
static const char ufbxi_CoordAxisSign[] = …;
static const char ufbxi_CoordAxis[] = …;
static const char ufbxi_Count[] = …;
static const char ufbxi_Creator[] = …;
static const char ufbxi_CurrentTextureBlendMode[] = …;
static const char ufbxi_CurrentTimeMarker[] = …;
static const char ufbxi_CustomFrameRate[] = …;
static const char ufbxi_DecayType[] = …;
static const char ufbxi_DefaultCamera[] = …;
static const char ufbxi_Default[] = …;
static const char ufbxi_Definitions[] = …;
static const char ufbxi_DeformPercent[] = …;
static const char ufbxi_Deformer[] = …;
static const char ufbxi_DiffuseColor[] = …;
static const char ufbxi_Dimension[] = …;
static const char ufbxi_Dimensions[] = …;
static const char ufbxi_DisplayLayer[] = …;
static const char ufbxi_Document[] = …;
static const char ufbxi_Documents[] = …;
static const char ufbxi_EdgeCrease[] = …;
static const char ufbxi_EdgeIndexArray[] = …;
static const char ufbxi_Edges[] = …;
static const char ufbxi_EmissiveColor[] = …;
static const char ufbxi_Entry[] = …;
static const char ufbxi_FBXHeaderExtension[] = …;
static const char ufbxi_FBXVersion[] = …;
static const char ufbxi_FKEffector[] = …;
static const char ufbxi_FarPlane[] = …;
static const char ufbxi_FbxPropertyEntry[] = …;
static const char ufbxi_FbxSemanticEntry[] = …;
static const char ufbxi_FieldOfViewX[] = …;
static const char ufbxi_FieldOfViewY[] = …;
static const char ufbxi_FieldOfView[] = …;
static const char ufbxi_FileName[] = …;
static const char ufbxi_Filename[] = …;
static const char ufbxi_FilmHeight[] = …;
static const char ufbxi_FilmSqueezeRatio[] = …;
static const char ufbxi_FilmWidth[] = …;
static const char ufbxi_FlipNormals[] = …;
static const char ufbxi_FocalLength[] = …;
static const char ufbxi_Form[] = …;
static const char ufbxi_Freeze[] = …;
static const char ufbxi_FrontAxisSign[] = …;
static const char ufbxi_FrontAxis[] = …;
static const char ufbxi_FullWeights[] = …;
static const char ufbxi_GateFit[] = …;
static const char ufbxi_GeometricRotation[] = …;
static const char ufbxi_GeometricScaling[] = …;
static const char ufbxi_GeometricTranslation[] = …;
static const char ufbxi_GeometryUVInfo[] = …;
static const char ufbxi_Geometry[] = …;
static const char ufbxi_GlobalSettings[] = …;
static const char ufbxi_Hole[] = …;
static const char ufbxi_HotSpot[] = …;
static const char ufbxi_IKEffector[] = …;
static const char ufbxi_ImageData[] = …;
static const char ufbxi_Implementation[] = …;
static const char ufbxi_Indexes[] = …;
static const char ufbxi_InheritType[] = …;
static const char ufbxi_InnerAngle[] = …;
static const char ufbxi_Intensity[] = …;
static const char ufbxi_IsTheNodeInSet[] = …;
static const char ufbxi_KeyAttrDataFloat[] = …;
static const char ufbxi_KeyAttrFlags[] = …;
static const char ufbxi_KeyAttrRefCount[] = …;
static const char ufbxi_KeyCount[] = …;
static const char ufbxi_KeyTime[] = …;
static const char ufbxi_KeyValueFloat[] = …;
static const char ufbxi_Key[] = …;
static const char ufbxi_KnotVectorU[] = …;
static const char ufbxi_KnotVectorV[] = …;
static const char ufbxi_KnotVector[] = …;
static const char ufbxi_LayerElementBinormal[] = …;
static const char ufbxi_LayerElementColor[] = …;
static const char ufbxi_LayerElementEdgeCrease[] = …;
static const char ufbxi_LayerElementHole[] = …;
static const char ufbxi_LayerElementMaterial[] = …;
static const char ufbxi_LayerElementNormal[] = …;
static const char ufbxi_LayerElementPolygonGroup[] = …;
static const char ufbxi_LayerElementSmoothing[] = …;
static const char ufbxi_LayerElementTangent[] = …;
static const char ufbxi_LayerElementUV[] = …;
static const char ufbxi_LayerElementVertexCrease[] = …;
static const char ufbxi_LayerElementVisibility[] = …;
static const char ufbxi_LayerElement[] = …;
static const char ufbxi_Layer[] = …;
static const char ufbxi_LayeredTexture[] = …;
static const char ufbxi_Lcl_Rotation[] = …;
static const char ufbxi_Lcl_Scaling[] = …;
static const char ufbxi_Lcl_Translation[] = …;
static const char ufbxi_LeftCamera[] = …;
static const char ufbxi_LightType[] = …;
static const char ufbxi_Light[] = …;
static const char ufbxi_LimbLength[] = …;
static const char ufbxi_LimbNode[] = …;
static const char ufbxi_Limb[] = …;
static const char ufbxi_Line[] = …;
static const char ufbxi_Link[] = …;
static const char ufbxi_LocalStart[] = …;
static const char ufbxi_LocalStop[] = …;
static const char ufbxi_LocalTime[] = …;
static const char ufbxi_LodGroup[] = …;
static const char ufbxi_MappingInformationType[] = …;
static const char ufbxi_Marker[] = …;
static const char ufbxi_MaterialAssignation[] = …;
static const char ufbxi_Material[] = …;
static const char ufbxi_Materials[] = …;
static const char ufbxi_Matrix[] = …;
static const char ufbxi_Media[] = …;
static const char ufbxi_Mesh[] = …;
static const char ufbxi_Model[] = …;
static const char ufbxi_Name[] = …;
static const char ufbxi_NearPlane[] = …;
static const char ufbxi_NodeAttributeName[] = …;
static const char ufbxi_NodeAttribute[] = …;
static const char ufbxi_Node[] = …;
static const char ufbxi_Normals[] = …;
static const char ufbxi_NormalsIndex[] = …;
static const char ufbxi_NormalsW[] = …;
static const char ufbxi_Null[] = …;
static const char ufbxi_NurbsCurve[] = …;
static const char ufbxi_NurbsSurfaceOrder[] = …;
static const char ufbxi_NurbsSurface[] = …;
static const char ufbxi_Nurbs[] = …;
static const char ufbxi_OO[] = …;
static const char ufbxi_OP[] = …;
static const char ufbxi_ObjectMetaData[] = …;
static const char ufbxi_ObjectType[] = …;
static const char ufbxi_Objects[] = …;
static const char ufbxi_Order[] = …;
static const char ufbxi_OriginalUnitScaleFactor[] = …;
static const char ufbxi_OriginalUpAxis[] = …;
static const char ufbxi_OriginalUpAxisSign[] = …;
static const char ufbxi_OrthoZoom[] = …;
static const char ufbxi_OuterAngle[] = …;
static const char ufbxi_PO[] = …;
static const char ufbxi_PP[] = …;
static const char ufbxi_PointsIndex[] = …;
static const char ufbxi_Points[] = …;
static const char ufbxi_PolygonGroup[] = …;
static const char ufbxi_PolygonIndexArray[] = …;
static const char ufbxi_PolygonVertexIndex[] = …;
static const char ufbxi_PoseNode[] = …;
static const char ufbxi_Pose[] = …;
static const char ufbxi_PostRotation[] = …;
static const char ufbxi_PreRotation[] = …;
static const char ufbxi_PreviewDivisionLevels[] = …;
static const char ufbxi_Properties60[] = …;
static const char ufbxi_Properties70[] = …;
static const char ufbxi_PropertyTemplate[] = …;
static const char ufbxi_R[] = …;
static const char ufbxi_ReferenceStart[] = …;
static const char ufbxi_ReferenceStop[] = …;
static const char ufbxi_ReferenceTime[] = …;
static const char ufbxi_RelativeFileName[] = …;
static const char ufbxi_RelativeFilename[] = …;
static const char ufbxi_RenderDivisionLevels[] = …;
static const char ufbxi_RightCamera[] = …;
static const char ufbxi_RootNode[] = …;
static const char ufbxi_Root[] = …;
static const char ufbxi_RotationAccumulationMode[] = …;
static const char ufbxi_RotationOffset[] = …;
static const char ufbxi_RotationOrder[] = …;
static const char ufbxi_RotationPivot[] = …;
static const char ufbxi_Rotation[] = …;
static const char ufbxi_S[] = …;
static const char ufbxi_ScaleAccumulationMode[] = …;
static const char ufbxi_ScalingOffset[] = …;
static const char ufbxi_ScalingPivot[] = …;
static const char ufbxi_Scaling[] = …;
static const char ufbxi_SceneInfo[] = …;
static const char ufbxi_SelectionNode[] = …;
static const char ufbxi_SelectionSet[] = …;
static const char ufbxi_ShadingModel[] = …;
static const char ufbxi_Shape[] = …;
static const char ufbxi_Shininess[] = …;
static const char ufbxi_Show[] = …;
static const char ufbxi_Size[] = …;
static const char ufbxi_Skin[] = …;
static const char ufbxi_SkinningType[] = …;
static const char ufbxi_Smoothing[] = …;
static const char ufbxi_Smoothness[] = …;
static const char ufbxi_SnapOnFrameMode[] = …;
static const char ufbxi_SpecularColor[] = …;
static const char ufbxi_Step[] = …;
static const char ufbxi_SubDeformer[] = …;
static const char ufbxi_T[] = …;
static const char ufbxi_Take[] = …;
static const char ufbxi_Takes[] = …;
static const char ufbxi_Tangents[] = …;
static const char ufbxi_TangentsIndex[] = …;
static const char ufbxi_TangentsW[] = …;
static const char ufbxi_Texture[] = …;
static const char ufbxi_Texture_alpha[] = …;
static const char ufbxi_TextureId[] = …;
static const char ufbxi_TextureRotationPivot[] = …;
static const char ufbxi_TextureScalingPivot[] = …;
static const char ufbxi_TextureUV[] = …;
static const char ufbxi_TextureUVVerticeIndex[] = …;
static const char ufbxi_Thumbnail[] = …;
static const char ufbxi_TimeMarker[] = …;
static const char ufbxi_TimeMode[] = …;
static const char ufbxi_TimeProtocol[] = …;
static const char ufbxi_TimeSpanStart[] = …;
static const char ufbxi_TimeSpanStop[] = …;
static const char ufbxi_TransformLink[] = …;
static const char ufbxi_Transform[] = …;
static const char ufbxi_Translation[] = …;
static const char ufbxi_TrimNurbsSurface[] = …;
static const char ufbxi_Type[] = …;
static const char ufbxi_TypedIndex[] = …;
static const char ufbxi_UVIndex[] = …;
static const char ufbxi_UVSet[] = …;
static const char ufbxi_UVSwap[] = …;
static const char ufbxi_UV[] = …;
static const char ufbxi_UnitScaleFactor[] = …;
static const char ufbxi_UpAxisSign[] = …;
static const char ufbxi_UpAxis[] = …;
static const char ufbxi_Version5[] = …;
static const char ufbxi_VertexCacheDeformer[] = …;
static const char ufbxi_VertexCrease[] = …;
static const char ufbxi_VertexCreaseIndex[] = …;
static const char ufbxi_VertexIndexArray[] = …;
static const char ufbxi_Vertices[] = …;
static const char ufbxi_Video[] = …;
static const char ufbxi_Visibility[] = …;
static const char ufbxi_Weight[] = …;
static const char ufbxi_Weights[] = …;
static const char ufbxi_WrapModeU[] = …;
static const char ufbxi_WrapModeV[] = …;
static const char ufbxi_X[] = …;
static const char ufbxi_Y[] = …;
static const char ufbxi_Z[] = …;
static const char ufbxi_d_X[] = …;
static const char ufbxi_d_Y[] = …;
static const char ufbxi_d_Z[] = …;
static const ufbx_string ufbxi_strings[] = …;
static const ufbx_vec3 ufbxi_one_vec3 = …;
#define UFBXI_PI …
#define UFBXI_DPI …
#define UFBXI_DEG_TO_RAD …
#define UFBXI_RAD_TO_DEG …
#define UFBXI_DEG_TO_RAD_DOUBLE …
#define UFBXI_RAD_TO_DEG_DOUBLE …
#define UFBXI_MM_TO_INCH …
ufbx_inline ufbx_vec3 ufbxi_add3(ufbx_vec3 a, ufbx_vec3 b) { … }
ufbx_inline ufbx_vec3 ufbxi_sub3(ufbx_vec3 a, ufbx_vec3 b) { … }
ufbx_inline ufbx_vec3 ufbxi_mul3(ufbx_vec3 a, ufbx_real b) { … }
ufbx_inline ufbx_vec3 ufbxi_lerp3(ufbx_vec3 a, ufbx_vec3 b, ufbx_real t) { … }
ufbx_inline ufbx_real ufbxi_dot3(ufbx_vec3 a, ufbx_vec3 b) { … }
ufbx_inline ufbx_real ufbxi_length3(ufbx_vec3 v)
{ … }
ufbx_inline ufbx_real ufbxi_min3(ufbx_vec3 v)
{ … }
ufbx_inline ufbx_vec3 ufbxi_cross3(ufbx_vec3 a, ufbx_vec3 b) { … }
ufbx_inline ufbx_vec3 ufbxi_normalize3(ufbx_vec3 a) { … }
ufbx_inline ufbx_real ufbxi_distsq2(ufbx_vec2 a, ufbx_vec2 b) { … }
static ufbxi_noinline ufbx_vec3 ufbxi_slow_normalize3(const ufbx_vec3 *a) { … }
static ufbxi_noinline ufbx_vec3 ufbxi_slow_normalized_cross3(const ufbx_vec3 *a, const ufbx_vec3 *b) { … }
ufbxi_task;
ufbxi_thread;
ufbxi_thread_pool;
ufbxi_task_fn;
struct ufbxi_task { … };
ufbxi_task_imp;
ufbxi_task_group;
struct ufbxi_thread_pool { … };
static void ufbxi_thread_pool_execute(ufbxi_thread_pool *pool, uint32_t index)
{ … }
ufbxi_noinline static void ufbxi_thread_pool_update_finished(ufbxi_thread_pool *pool, uint32_t max_index)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_thread_pool_wait_imp(ufbxi_thread_pool *pool, uint32_t group, bool can_fail)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_thread_pool_wait_group(ufbxi_thread_pool *pool)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_thread_pool_wait_all(ufbxi_thread_pool *pool)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_thread_pool_init(ufbxi_thread_pool *pool, ufbx_error *error, ufbxi_allocator *ator, const ufbx_thread_opts *opts)
{ … }
ufbxi_noinline static void ufbxi_thread_pool_free(ufbxi_thread_pool *pool)
{ … }
ufbxi_nodiscard ufbxi_noinline static uint32_t ufbxi_thread_pool_available_tasks(ufbxi_thread_pool *pool)
{ … }
ufbxi_noinline static void ufbxi_thread_pool_flush_group(ufbxi_thread_pool *pool)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbxi_task *ufbxi_thread_pool_create_task(ufbxi_thread_pool *pool, ufbxi_task_fn *fn)
{ … }
static void ufbxi_thread_pool_run_task(ufbxi_thread_pool *pool, ufbxi_task *task)
{ … }
ufbxi_node;
ufbxi_value_type;
ufbxi_value;
ufbxi_value_array;
struct ufbxi_node { … };
ufbxi_refcount;
struct ufbxi_refcount { … };
static ufbxi_noinline void ufbxi_init_ref(ufbxi_refcount *refcount, uint32_t magic, ufbxi_refcount *parent);
static ufbxi_noinline void ufbxi_retain_ref(ufbxi_refcount *refcount);
#define ufbxi_get_imp(type, ptr) …
ufbxi_scene_imp;
ufbx_static_assert(…);
ufbxi_mesh_imp;
ufbx_static_assert(…);
ufbxi_ascii_token;
ufbxi_ascii;
ufbxi_template;
ufbxi_fbx_id_entry;
ufbxi_fbx_attr_entry;
ufbxi_tmp_connection;
ufbxi_element_info;
ufbxi_tmp_bone_pose;
ufbxi_tmp_mesh_texture;
ufbxi_mesh_extra;
ufbxi_tmp_material_texture;
ufbxi_texture_extra;
ufbxi_obj_attrib;
#define UFBXI_OBJ_NUM_ATTRIBS …
#define UFBXI_OBJ_NUM_ATTRIBS_EXT …
ufbxi_obj_index_range;
ufbxi_obj_mesh;
ufbxi_obj_group_entry;
ufbxi_obj_fast_indices;
ufbxi_tmp_anim_stack;
ufbxi_file_content;
ufbxi_obj_context;
ufbxi_context;
static ufbxi_noinline int ufbxi_fail_imp(ufbxi_context *uc, const char *cond, const char *func, uint32_t line)
{ … }
#define ufbxi_check(cond) …
#define ufbxi_check_return(cond, ret) …
#define ufbxi_fail(desc) …
#define ufbxi_fail_return(desc, ret) …
#define ufbxi_check_msg(cond, msg) …
#define ufbxi_check_return_msg(cond, ret, msg) …
#define ufbxi_fail_msg(desc, msg) …
#define ufbxi_warnf(type, ...) …
#define ufbxi_warnf_tag(type, element_id, ...) …
static ufbxi_forceinline uint64_t ufbxi_get_read_offset(ufbxi_context *uc)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_report_progress(ufbxi_context *uc)
{ … }
ufbxi_unused ufbxi_nodiscard static ufbxi_forceinline int ufbxi_progress(ufbxi_context *uc, size_t work_units)
{ … }
static ufbxi_noinline const char *ufbxi_refill(ufbxi_context *uc, size_t size, bool require_size)
{ … }
static ufbxi_forceinline void ufbxi_pause_progress(ufbxi_context *uc)
{ … }
static ufbxi_noinline int ufbxi_resume_progress(ufbxi_context *uc)
{ … }
static ufbxi_noinline const char *ufbxi_yield(ufbxi_context *uc, size_t size)
{ … }
static ufbxi_forceinline const char *ufbxi_peek_bytes(ufbxi_context *uc, size_t size)
{ … }
static ufbxi_forceinline const char *ufbxi_read_bytes(ufbxi_context *uc, size_t size)
{ … }
static ufbxi_forceinline void ufbxi_consume_bytes(ufbxi_context *uc, size_t size)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_skip_bytes(ufbxi_context *uc, uint64_t size)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_read_to(ufbxi_context *uc, void *dst, size_t size)
{ … }
static ufbxi_noinline void ufbxi_init_ator(ufbx_error *error, ufbxi_allocator *ator, const ufbx_allocator_opts *opts, const char *name)
{ … }
static ufbxi_noinline FILE *ufbxi_fopen(const char *path, size_t path_len, ufbxi_allocator *tmp_ator)
{ … }
static uint64_t ufbxi_ftell(FILE *file)
{ … }
static size_t ufbxi_file_read(void *user, void *data, size_t max_size)
{ … }
static bool ufbxi_file_skip(void *user, size_t size)
{ … }
static void ufbxi_file_close(void *user)
{ … }
ufbxi_memory_stream;
static size_t ufbxi_memory_read(void *user, void *data, size_t max_size)
{ … }
static bool ufbxi_memory_skip(void *user, size_t size)
{ … }
static void ufbxi_memory_close(void *user)
{ … }
#if UFBXI_FEATURE_XML
typedef struct ufbxi_xml_tag ufbxi_xml_tag;
typedef struct ufbxi_xml_attrib ufbxi_xml_attrib;
typedef struct ufbxi_xml_document ufbxi_xml_document;
struct ufbxi_xml_attrib {
ufbx_string name;
ufbx_string value;
};
struct ufbxi_xml_tag {
ufbx_string name;
ufbx_string text;
ufbxi_xml_attrib *attribs;
size_t num_attribs;
ufbxi_xml_tag *children;
size_t num_children;
};
struct ufbxi_xml_document {
ufbxi_xml_tag *root;
ufbxi_buf buf;
};
typedef struct {
ufbx_error error;
ufbxi_allocator *ator;
ufbxi_buf tmp_stack;
ufbxi_buf result;
ufbxi_xml_document *doc;
ufbx_read_fn *read_fn;
void *read_user;
char *tok;
size_t tok_cap;
size_t tok_len;
const char *pos, *pos_end;
char data[4096];
bool io_error;
} ufbxi_xml_context;
enum {
UFBXI_XML_CTYPE_WHITESPACE = 0x1,
UFBXI_XML_CTYPE_SINGLE_QUOTE = 0x2,
UFBXI_XML_CTYPE_DOUBLE_QUOTE = 0x4,
UFBXI_XML_CTYPE_NAME_END = 0x8,
UFBXI_XML_CTYPE_TAG_START = 0x10,
UFBXI_XML_CTYPE_END_OF_FILE = 0x20,
};
static const uint8_t ufbxi_xml_ctype[256] = {
32,0,0,0,0,0,0,0,0,9,9,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
9,0,12,0,0,0,0,10,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,16,8,8,8,
};
static ufbxi_noinline void ufbxi_xml_refill(ufbxi_xml_context *xc)
{
size_t num = xc->read_fn(xc->read_user, xc->data, sizeof(xc->data));
if (num == SIZE_MAX || num < sizeof(xc->data)) xc->io_error = true;
if (num < sizeof(xc->data)) {
xc->data[num++] = '\0';
}
xc->pos = xc->data;
xc->pos_end = xc->data + num;
}
static ufbxi_forceinline void ufbxi_xml_advance(ufbxi_xml_context *xc)
{
if (++xc->pos == xc->pos_end) ufbxi_xml_refill(xc);
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_xml_push_token_char(ufbxi_xml_context *xc, char c)
{
if (xc->tok_len == xc->tok_cap || UFBXI_IS_REGRESSION) {
ufbxi_check_err(&xc->error, ufbxi_grow_array(xc->ator, &xc->tok, &xc->tok_cap, xc->tok_len + 1));
}
xc->tok[xc->tok_len++] = c;
return 1;
}
static ufbxi_noinline int ufbxi_xml_accept(ufbxi_xml_context *xc, char ch)
{
if (*xc->pos == ch) {
ufbxi_xml_advance(xc);
return 1;
} else {
return 0;
}
}
static ufbxi_noinline void ufbxi_xml_skip_while(ufbxi_xml_context *xc, uint32_t ctypes)
{
while (ufbxi_xml_ctype[(uint8_t)*xc->pos] & ctypes) {
ufbxi_xml_advance(xc);
}
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_xml_skip_until_string(ufbxi_xml_context *xc, ufbx_string *dst, const char *suffix)
{
xc->tok_len = 0;
size_t match_len = 0, ix = 0, suffix_len = strlen(suffix);
char buf[16] = { 0 };
size_t wrap_mask = sizeof(buf) - 1;
ufbx_assert(suffix_len < sizeof(buf));
for (;;) {
char c = *xc->pos;
ufbxi_check_err_msg(&xc->error, c != 0, "Truncated file");
ufbxi_xml_advance(xc);
if (ix >= suffix_len) {
ufbxi_check_err(&xc->error, ufbxi_xml_push_token_char(xc, buf[(ix - suffix_len) & wrap_mask]));
}
buf[ix++ & wrap_mask] = c;
for (match_len = 0; match_len < suffix_len; match_len++) {
if (buf[(ix - suffix_len + match_len) & wrap_mask] != suffix[match_len]) {
break;
}
}
if (match_len == suffix_len) break;
}
ufbxi_check_err(&xc->error, ufbxi_xml_push_token_char(xc, '\0'));
if (dst) {
dst->length = xc->tok_len - 1;
dst->data = ufbxi_push_copy(&xc->result, char, xc->tok_len, xc->tok);
ufbxi_check_err(&xc->error, dst->data);
}
return 1;
}
static ufbxi_noinline int ufbxi_xml_read_until(ufbxi_xml_context *xc, ufbx_string *dst, uint32_t ctypes)
{
xc->tok_len = 0;
for (;;) {
char c = *xc->pos;
if (c == '&') {
size_t entity_begin = xc->tok_len;
for (;;) {
ufbxi_xml_advance(xc);
c = *xc->pos;
ufbxi_check_err(&xc->error, c != '\0');
if (c == ';') break;
ufbxi_check_err(&xc->error, ufbxi_xml_push_token_char(xc, c));
}
ufbxi_xml_advance(xc);
ufbxi_check_err(&xc->error, ufbxi_xml_push_token_char(xc, '\0'));
char *entity = xc->tok + entity_begin;
xc->tok_len = entity_begin;
if (entity[0] == '#') {
unsigned long code = 0;
if (entity[1] == 'x') {
code = strtoul(entity + 2, NULL, 16);
} else {
code = strtoul(entity + 1, NULL, 10);
}
char bytes[5] = { 0 };
if (code < 0x80) {
bytes[0] = (char)code;
} else if (code < 0x800) {
bytes[0] = (char)(0xc0 | (code>>6));
bytes[1] = (char)(0x80 | (code & 0x3f));
} else if (code < 0x10000) {
bytes[0] = (char)(0xe0 | (code>>12));
bytes[1] = (char)(0x80 | ((code>>6) & 0x3f));
bytes[2] = (char)(0x80 | (code & 0x3f));
} else {
bytes[0] = (char)(0xf0 | (code>>18));
bytes[1] = (char)(0x80 | ((code>>12) & 0x3f));
bytes[2] = (char)(0x80 | ((code>>6) & 0x3f));
bytes[3] = (char)(0x80 | (code & 0x3f));
}
for (char *b = bytes; *b; b++) {
ufbxi_check_err(&xc->error, ufbxi_xml_push_token_char(xc, *b));
}
} else {
char ch = '\0';
if (!strcmp(entity, "lt")) ch = '<';
else if (!strcmp(entity, "quot")) ch = '"';
else if (!strcmp(entity, "amp")) ch = '&';
else if (!strcmp(entity, "apos")) ch = '\'';
else if (!strcmp(entity, "gt")) ch = '>';
if (ch) {
ufbxi_check_err(&xc->error, ufbxi_xml_push_token_char(xc, ch));
}
}
} else {
if ((ufbxi_xml_ctype[(uint8_t)c] & ctypes) != 0) break;
ufbxi_check_err_msg(&xc->error, c != 0, "Truncated file");
ufbxi_check_err(&xc->error, ufbxi_xml_push_token_char(xc, c));
ufbxi_xml_advance(xc);
}
}
ufbxi_check_err(&xc->error, ufbxi_xml_push_token_char(xc, '\0'));
if (dst) {
dst->length = xc->tok_len - 1;
dst->data = ufbxi_push_copy(&xc->result, char, xc->tok_len, xc->tok);
ufbxi_check_err(&xc->error, dst->data);
}
return 1;
}
static ufbxi_noinline int ufbxi_xml_parse_tag(ufbxi_xml_context *xc, size_t depth, bool *p_closing, const char *opening)
ufbxi_recursive_function(int, ufbxi_xml_parse_tag, (xc, depth, p_closing, opening), UFBXI_MAX_XML_DEPTH + 1,
(ufbxi_xml_context *xc, size_t depth, bool *p_closing, const char *opening))
{
ufbxi_check_err(&xc->error, depth < UFBXI_MAX_XML_DEPTH);
if (!ufbxi_xml_accept(xc, '<')) {
if (*xc->pos == '\0') {
*p_closing = true;
} else {
ufbxi_check_err(&xc->error, ufbxi_xml_read_until(xc, NULL, UFBXI_XML_CTYPE_TAG_START | UFBXI_XML_CTYPE_END_OF_FILE));
bool has_text = false;
for (size_t i = 0; i < xc->tok_len; i++) {
if ((ufbxi_xml_ctype[(uint8_t)xc->tok[i]] & UFBXI_XML_CTYPE_WHITESPACE) == 0) {
has_text = true;
break;
}
}
if (has_text) {
ufbxi_xml_tag *tag = ufbxi_push_zero(&xc->tmp_stack, ufbxi_xml_tag, 1);
ufbxi_check_err(&xc->error, tag);
tag->name.data = ufbxi_empty_char;
tag->text.length = xc->tok_len - 1;
tag->text.data = ufbxi_push_copy(&xc->result, char, xc->tok_len, xc->tok);
ufbxi_check_err(&xc->error, tag->text.data);
}
}
return 1;
}
if (ufbxi_xml_accept(xc, '/')) {
ufbxi_check_err(&xc->error, ufbxi_xml_read_until(xc, NULL, UFBXI_XML_CTYPE_NAME_END));
ufbxi_check_err(&xc->error, opening && !strcmp(xc->tok, opening));
ufbxi_xml_skip_while(xc, UFBXI_XML_CTYPE_WHITESPACE);
if (!ufbxi_xml_accept(xc, '>')) return 0;
*p_closing = true;
return 1;
} else if (ufbxi_xml_accept(xc, '!')) {
if (ufbxi_xml_accept(xc, '[')) {
for (const char *ch = "CDATA["; *ch; ch++) {
if (!ufbxi_xml_accept(xc, *ch)) return 0;
}
ufbxi_xml_tag *tag = ufbxi_push_zero(&xc->tmp_stack, ufbxi_xml_tag, 1);
ufbxi_check_err(&xc->error, tag);
ufbxi_check_err(&xc->error, ufbxi_xml_skip_until_string(xc, &tag->text, "]]>"));
tag->name.data = ufbxi_empty_char;
} else if (ufbxi_xml_accept(xc, '-')) {
if (!ufbxi_xml_accept(xc, '-')) return 0;
ufbxi_check_err(&xc->error, ufbxi_xml_skip_until_string(xc, NULL, "-->"));
} else {
ufbxi_check_err(&xc->error, ufbxi_xml_skip_until_string(xc, NULL, ">"));
}
return 1;
} else if (ufbxi_xml_accept(xc, '?')) {
ufbxi_check_err(&xc->error, ufbxi_xml_skip_until_string(xc, NULL, "?>"));
return 1;
}
ufbxi_xml_tag *tag = ufbxi_push_zero(&xc->tmp_stack, ufbxi_xml_tag, 1);
ufbxi_check_err(&xc->error, tag);
ufbxi_check_err(&xc->error, ufbxi_xml_read_until(xc, &tag->name, UFBXI_XML_CTYPE_NAME_END));
tag->text.data = ufbxi_empty_char;
bool has_children = false;
size_t num_attribs = 0;
for (;;) {
ufbxi_xml_skip_while(xc, UFBXI_XML_CTYPE_WHITESPACE);
if (ufbxi_xml_accept(xc, '/')) {
if (!ufbxi_xml_accept(xc, '>')) return 0;
break;
} else if (ufbxi_xml_accept(xc, '>')) {
has_children = true;
break;
} else {
ufbxi_xml_attrib *attrib = ufbxi_push_zero(&xc->tmp_stack, ufbxi_xml_attrib, 1);
ufbxi_check_err(&xc->error, attrib);
ufbxi_check_err(&xc->error, ufbxi_xml_read_until(xc, &attrib->name, UFBXI_XML_CTYPE_NAME_END));
ufbxi_xml_skip_while(xc, UFBXI_XML_CTYPE_WHITESPACE);
if (!ufbxi_xml_accept(xc, '=')) return 0;
ufbxi_xml_skip_while(xc, UFBXI_XML_CTYPE_WHITESPACE);
uint32_t quote_ctype = 0;
if (ufbxi_xml_accept(xc, '"')) {
quote_ctype = UFBXI_XML_CTYPE_DOUBLE_QUOTE;
} else if (ufbxi_xml_accept(xc, '\'')) {
quote_ctype = UFBXI_XML_CTYPE_SINGLE_QUOTE;
} else {
ufbxi_fail_err(&xc->error, "Bad attrib value");
}
ufbxi_check_err(&xc->error, ufbxi_xml_read_until(xc, &attrib->value, quote_ctype));
ufbxi_xml_advance(xc);
num_attribs++;
}
}
tag->num_attribs = num_attribs;
tag->attribs = ufbxi_push_pop(&xc->result, &xc->tmp_stack, ufbxi_xml_attrib, num_attribs);
ufbxi_check_err(&xc->error, tag->attribs);
if (has_children) {
size_t children_begin = xc->tmp_stack.num_items;
for (;;) {
bool closing = false;
ufbxi_check_err(&xc->error, ufbxi_xml_parse_tag(xc, depth + 1, &closing, tag->name.data));
if (closing) break;
}
tag->num_children = xc->tmp_stack.num_items - children_begin;
tag->children = ufbxi_push_pop(&xc->result, &xc->tmp_stack, ufbxi_xml_tag, tag->num_children);
ufbxi_check_err(&xc->error, tag->children);
}
return 1;
}
static ufbxi_noinline int ufbxi_xml_parse_root(ufbxi_xml_context *xc)
{
ufbxi_xml_tag *tag = ufbxi_push_zero(&xc->result, ufbxi_xml_tag, 1);
ufbxi_check_err(&xc->error, tag);
tag->name.data = ufbxi_empty_char;
tag->text.data = ufbxi_empty_char;
for (;;) {
bool closing = false;
ufbxi_check_err(&xc->error, ufbxi_xml_parse_tag(xc, 0, &closing, NULL));
if (closing) break;
}
tag->num_children = xc->tmp_stack.num_items;
tag->children = ufbxi_push_pop(&xc->result, &xc->tmp_stack, ufbxi_xml_tag, tag->num_children);
ufbxi_check_err(&xc->error, tag->children);
xc->doc = ufbxi_push(&xc->result, ufbxi_xml_document, 1);
ufbxi_check_err(&xc->error, xc->doc);
xc->doc->root = tag;
xc->doc->buf = xc->result;
return 1;
}
typedef struct {
ufbxi_allocator *ator;
ufbx_read_fn *read_fn;
void *read_user;
const char *prefix;
size_t prefix_length;
} ufbxi_xml_load_opts;
static ufbxi_noinline ufbxi_xml_document *ufbxi_load_xml(ufbxi_xml_load_opts *opts, ufbx_error *error)
{
ufbxi_xml_context xc = { UFBX_ERROR_NONE };
xc.ator = opts->ator;
xc.read_fn = opts->read_fn;
xc.read_user = opts->read_user;
xc.tmp_stack.ator = xc.ator;
xc.result.ator = xc.ator;
xc.result.unordered = true;
if (opts->prefix_length > 0) {
xc.pos = opts->prefix;
xc.pos_end = opts->prefix + opts->prefix_length;
} else {
ufbxi_xml_refill(&xc);
}
int ok = ufbxi_xml_parse_root(&xc);
ufbxi_buf_free(&xc.tmp_stack);
ufbxi_free(xc.ator, char, xc.tok, xc.tok_cap);
if (ok) {
return xc.doc;
} else {
ufbxi_buf_free(&xc.result);
if (error) {
*error = xc.error;
}
return NULL;
}
}
static ufbxi_noinline void ufbxi_free_xml(ufbxi_xml_document *doc)
{
ufbxi_buf buf = doc->buf;
ufbxi_buf_free(&buf);
}
static ufbxi_noinline ufbxi_xml_tag *ufbxi_xml_find_child(ufbxi_xml_tag *tag, const char *name)
{
ufbxi_for(ufbxi_xml_tag, child, tag->children, tag->num_children) {
if (!strcmp(child->name.data, name)) {
return child;
}
}
return NULL;
}
static ufbxi_noinline ufbxi_xml_attrib *ufbxi_xml_find_attrib(ufbxi_xml_tag *tag, const char *name)
{
ufbxi_for(ufbxi_xml_attrib, attrib, tag->attribs, tag->num_attribs) {
if (!strcmp(attrib->name.data, name)) {
return attrib;
}
}
return NULL;
}
#endif
static char ufbxi_normalize_array_type(char type, char bool_type) { … }
static ufbxi_noinline size_t ufbxi_array_type_size(char type)
{ … }
static ufbxi_noinline ufbxi_node *ufbxi_find_child(ufbxi_node *node, const char *name)
{ … }
ufbxi_nodiscard ufbxi_forceinline static int ufbxi_get_val_at(ufbxi_node *node, size_t ix, char fmt, void *v)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbxi_value_array *ufbxi_get_array(ufbxi_node *node, char fmt)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_get_val1(ufbxi_node *node, const char *fmt, void *v0)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_get_val2(ufbxi_node *node, const char *fmt, void *v0, void *v1)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_get_val3(ufbxi_node *node, const char *fmt, void *v0, void *v1, void *v2)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_get_val4(ufbxi_node *node, const char *fmt, void *v0, void *v1, void *v2, void *v3)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_get_val5(ufbxi_node *node, const char *fmt, void *v0, void *v1, void *v2, void *v3, void *v4)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_find_val1(ufbxi_node *node, const char *name, const char *fmt, void *v0)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_find_val2(ufbxi_node *node, const char *name, const char *fmt, void *v0, void *v1)
{ … }
ufbxi_nodiscard static ufbxi_noinline ufbxi_value_array *ufbxi_find_array(ufbxi_node *node, const char *name, char fmt)
{ … }
static ufbxi_node *ufbxi_find_child_strcmp(ufbxi_node *node, const char *name)
{ … }
ufbxi_nodiscard static ufbxi_noinline void *ufbxi_push_element_extra_size(ufbxi_context *uc, uint32_t id, size_t size)
{ … }
static ufbxi_noinline void *ufbxi_get_element_extra(ufbxi_context *uc, uint32_t id)
{ … }
#define ufbxi_push_element_extra(uc, id, type) …
ufbxi_parse_state;
ufbxi_array_flags;
ufbxi_array_info;
static ufbxi_noinline ufbxi_parse_state ufbxi_update_parse_state(ufbxi_parse_state parent, const char *name)
{ … }
static bool ufbxi_is_array_node(ufbxi_context *uc, ufbxi_parse_state parent, const char *name, ufbxi_array_info *info)
{ … }
static ufbxi_noinline bool ufbxi_is_raw_string(ufbxi_context *uc, ufbxi_parse_state parent, const char *name, size_t index)
{ … }
ufbxi_nodiscard static ufbxi_noinline char *ufbxi_swap_endian(ufbxi_context *uc, const void *src, size_t count, size_t elem_size)
{ … }
ufbxi_nodiscard static ufbxi_noinline const char *ufbxi_swap_endian_array(ufbxi_context *uc, const void *src, size_t count, char type)
{ … }
ufbxi_nodiscard static ufbxi_noinline const char *ufbxi_swap_endian_value(ufbxi_context *uc, const void *src, char type)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_binary_convert_array(ufbxi_context *maybe_uc, char src_type, char dst_type, const void *src, void *dst, size_t size)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_binary_parse_multivalue_array(ufbxi_context *uc, char dst_type, void *dst, size_t size, ufbxi_buf *tmp_buf)
{ … }
ufbxi_nodiscard ufbxi_noinline static void *ufbxi_push_array_data(ufbxi_context *uc, const ufbxi_array_info *info, size_t size, ufbxi_buf *tmp_buf)
{ … }
ufbxi_noinline static void ufbxi_postprocess_bool_array(char *data, size_t size)
{ … }
ufbxi_deflate_task;
static bool ufbxi_deflate_task_fn(ufbxi_task *task)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_binary_parse_node(ufbxi_context *uc, uint32_t depth, ufbxi_parse_state parent_state, bool *p_end, ufbxi_buf *tmp_buf, bool recursive)
ufbxi_recursive_function(int, ufbxi_binary_parse_node, (uc, depth, parent_state, p_end, tmp_buf, recursive), UFBXI_MAX_NODE_DEPTH + 1,
(ufbxi_context *uc, uint32_t depth, ufbxi_parse_state parent_state, bool *p_end, ufbxi_buf *tmp_buf, bool recursive))
{ … }
#define UFBXI_BINARY_MAGIC_SIZE …
#define UFBXI_BINARY_HEADER_SIZE …
static const char ufbxi_binary_magic[] = …;
#define UFBXI_ASCII_END …
#define UFBXI_ASCII_NAME …
#define UFBXI_ASCII_BARE_WORD …
#define UFBXI_ASCII_INT …
#define UFBXI_ASCII_FLOAT …
#define UFBXI_ASCII_STRING …
static ufbxi_noinline char ufbxi_ascii_refill(ufbxi_context *uc)
{ … }
static ufbxi_noinline char ufbxi_ascii_yield(ufbxi_context *uc)
{ … }
static ufbxi_forceinline char ufbxi_ascii_peek(ufbxi_context *uc)
{ … }
static ufbxi_forceinline char ufbxi_ascii_next(ufbxi_context *uc)
{ … }
static ufbxi_noinline uint32_t ufbxi_ascii_parse_version(ufbxi_context *uc)
{ … }
static const uint32_t ufbxi_space_mask = … ;
ufbx_static_assert(…);
static ufbxi_forceinline bool ufbxi_is_space(char c)
{ … }
static ufbxi_noinline char ufbxi_ascii_skip_whitespace(ufbxi_context *uc)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_ascii_push_token_char(ufbxi_context *uc, ufbxi_ascii_token *token, char c)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_ascii_push_token_string(ufbxi_context *uc, ufbxi_ascii_token *token, const char *data, size_t length)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_ascii_skip_until(ufbxi_context *uc, char dst)
{ … }
ufbxi_ascii_span;
ufbxi_nodiscard ufbxi_noinline static int ufbxi_ascii_store_array(ufbxi_context *uc, ufbxi_buf *tmp_buf)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_ascii_try_ignore_string(ufbxi_context *uc, ufbxi_ascii_token *token)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_ascii_next_token(ufbxi_context *uc, ufbxi_ascii_token *token)
{ … }
ufbxi_nodiscard static int ufbxi_ascii_accept(ufbxi_context *uc, char type)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_ascii_read_int_array(ufbxi_context *uc, char type, size_t *p_num_read)
{ … }
ufbxi_ascii_array_task;
ufbxi_noinline static const char *ufbxi_ascii_array_task_parse_floats(ufbxi_ascii_array_task *t, const char *src, const char *src_end, uint32_t parse_flags)
{ … }
ufbxi_noinline static const char *ufbxi_ascii_array_task_parse_ints(ufbxi_ascii_array_task *t, const char *src, const char *src_end)
{ … }
ufbxi_noinline static const char *ufbxi_ascii_array_task_parse(ufbxi_ascii_array_task *t, const char *src, const char *src_end)
{ … }
ufbxi_ascii_scan_state;
ufbxi_noinline static bool ufbxi_ascii_array_task_imp(ufbxi_ascii_array_task *t)
{ … }
ufbxi_noinline static bool ufbxi_ascii_array_task_fn(ufbxi_task *task)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_ascii_read_float_array(ufbxi_context *uc, char type, size_t *p_num_read)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_ascii_parse_node(ufbxi_context *uc, uint32_t depth, ufbxi_parse_state parent_state, bool *p_end, ufbxi_buf *tmp_buf, bool recursive)
ufbxi_recursive_function(int, ufbxi_ascii_parse_node, (uc, depth, parent_state, p_end, tmp_buf, recursive), UFBXI_MAX_NODE_DEPTH + 1,
(ufbxi_context *uc, uint32_t depth, ufbxi_parse_state parent_state, bool *p_end, ufbxi_buf *tmp_buf, bool recursive))
{ … }
ufbxi_dom_mapping;
ufbxi_nodiscard static ufbxi_noinline ufbx_dom_node *ufbxi_get_dom_node_imp(ufbxi_context *uc, ufbxi_node *node)
{ … }
ufbxi_nodiscard static ufbxi_forceinline ufbx_dom_node *ufbxi_get_dom_node(ufbxi_context *uc, ufbxi_node *node)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_retain_dom_node(ufbxi_context *uc, ufbxi_node *node, ufbx_dom_node **p_dom_node)
ufbxi_recursive_function(int, ufbxi_retain_dom_node, (uc, node, p_dom_node), UFBXI_MAX_NODE_DEPTH + 1,
(ufbxi_context *uc, ufbxi_node *node, ufbx_dom_node **p_dom_node))
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_retain_toplevel(ufbxi_context *uc, ufbxi_node *node)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_retain_toplevel_child(ufbxi_context *uc, ufbxi_node *child)
{ … }
static ufbxi_noinline bool ufbxi_next_line(ufbx_string *line, ufbx_string *buf, bool skip_space)
{ … }
static ufbxi_noinline const char *ufbxi_match_skip(const char *fmt, bool alternation)
ufbxi_recursive_function(const char *, ufbxi_match_skip, (fmt, alternation), 4,
(const char *fmt, bool alternation))
{ … }
static ufbxi_noinline bool ufbxi_match_imp(const char **p_str, const char *end, const char **p_fmt)
ufbxi_recursive_function(bool, ufbxi_match_imp, (p_str, end, p_fmt), 4,
(const char **p_str, const char *end, const char **p_fmt))
{ … }
static ufbxi_noinline bool ufbxi_match(const ufbx_string *str, const char *fmt)
{ … }
static ufbxi_noinline bool ufbxi_is_format(const char *data, size_t size, ufbx_file_format format)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_determine_format(ufbxi_context *uc)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_begin_parse(ufbxi_context *uc)
{ … }
ufbxi_nodiscard static int ufbxi_parse_toplevel_child_imp(ufbxi_context *uc, ufbxi_parse_state state, ufbxi_buf *buf, bool *p_end)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_parse_toplevel(ufbxi_context *uc, const char *name)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_parse_toplevel_child(ufbxi_context *uc, ufbxi_node **p_node, ufbxi_buf *tmp_buf)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_parse_legacy_toplevel(ufbxi_context *uc)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_load_strings(ufbxi_context *uc)
{ … }
ufbxi_prop_type_name;
static const ufbxi_prop_type_name ufbxi_prop_type_names[] = …;
static ufbx_prop_type ufbxi_get_prop_type(ufbxi_context *uc, const char *name)
{ … }
static ufbxi_noinline ufbx_prop *ufbxi_find_prop_with_key(const ufbx_props *props, const char *name, uint32_t key)
{ … }
ufbxi_texture_file_entry;
#define ufbxi_find_prop(props, name) …
static ufbxi_forceinline ufbx_real ufbxi_find_real(const ufbx_props *props, const char *name, ufbx_real def)
{ … }
static ufbxi_forceinline ufbx_vec3 ufbxi_find_vec3(const ufbx_props *props, const char *name, ufbx_real def_x, ufbx_real def_y, ufbx_real def_z)
{ … }
static ufbxi_forceinline int64_t ufbxi_find_int(const ufbx_props *props, const char *name, int64_t def)
{ … }
static ufbxi_forceinline int64_t ufbxi_find_enum(const ufbx_props *props, const char *name, int64_t def, int64_t max_value)
{ … }
ufbxi_noinline static bool ufbxi_matrix_all_zero(const ufbx_matrix *matrix)
{ … }
static ufbxi_forceinline bool ufbxi_is_vec3_zero(ufbx_vec3 v)
{ … }
static ufbxi_forceinline bool ufbxi_is_vec4_zero(ufbx_vec4 v)
{ … }
static ufbxi_forceinline bool ufbxi_is_vec3_one(ufbx_vec3 v)
{ … }
static ufbxi_forceinline bool ufbxi_is_quat_identity(ufbx_quat v)
{ … }
static ufbxi_noinline bool ufbxi_is_transform_identity(const ufbx_transform *t)
{ … }
static ufbxi_forceinline uint32_t ufbxi_get_name_key(const char *name, size_t len)
{ … }
static ufbxi_forceinline uint32_t ufbxi_get_name_key_c(const char *name)
{ … }
static ufbxi_forceinline bool ufbxi_name_key_less(ufbx_prop *prop, const char *data, size_t name_len, uint32_t key)
{ … }
static const char *const ufbxi_node_prop_names[] = …;
ufbxi_nodiscard static ufbxi_noinline int ufbxi_init_node_prop_names(ufbxi_context *uc)
{ … }
static bool ufbxi_is_node_property(ufbxi_context *uc, const char *name)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_load_maps(ufbxi_context *uc)
{ … }
ufbxi_noinline static void ufbxi_decode_base64(char *dst, const char *src, size_t src_length)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_embedded_blob(ufbxi_context *uc, ufbx_blob *dst_blob, ufbxi_node *node)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_read_property(ufbxi_context *uc, ufbxi_node *node, ufbx_prop *prop, int version)
{ … }
static ufbxi_forceinline bool ufbxi_prop_less(ufbx_prop *a, ufbx_prop *b)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_properties(ufbxi_context *uc, ufbx_prop *props, size_t count)
{ … }
ufbxi_noinline static void ufbxi_deduplicate_properties(ufbx_prop_list *list)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_read_properties(ufbxi_context *uc, ufbxi_node *parent, ufbx_props *props)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_read_thumbnail(ufbxi_context *uc, ufbxi_node *node, ufbx_thumbnail *thumbnail)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_read_scene_info(ufbxi_context *uc, ufbxi_node *node)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_read_header_extension(ufbxi_context *uc)
{ … }
static bool ufbxi_match_version_string(const char *fmt, ufbx_string str, uint32_t *p_version)
{ … }
ufbxi_nodiscard static int ufbxi_match_exporter(ufbxi_context *uc)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_read_document(ufbxi_context *uc)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_read_definitions(ufbxi_context *uc)
{ … }
ufbxi_nodiscard static ufbx_props *ufbxi_find_template(ufbxi_context *uc, const char *name, const char *sub_type)
{ … }
#define UFBXI_SYNTHETIC_ID_BIT …
ufbx_static_assert(…);
static ufbxi_forceinline uint64_t ufbxi_synthetic_id_from_pointer(const void *ptr)
{ … }
static ufbxi_forceinline uint64_t ufbxi_synthetic_id_from_string(const char *str)
{ … }
static ufbxi_noinline int ufbxi_push_synthetic_id(ufbxi_context *uc, uint64_t *p_dst)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_split_type_and_name(ufbxi_context *uc, ufbx_string type_and_name, ufbx_string *type, ufbx_string *name)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_insert_fbx_id(ufbxi_context *uc, uint64_t fbx_id, uint32_t element_id)
{ … }
static ufbxi_noinline ufbxi_fbx_id_entry *ufbxi_find_fbx_id(ufbxi_context *uc, uint64_t fbx_id)
{ … }
static ufbxi_forceinline bool ufbxi_fbx_id_exists(ufbxi_context *uc, uint64_t fbx_id)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_insert_fbx_attr(ufbxi_context *uc, uint64_t fbx_id, uint64_t attrib_fbx_id)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbx_element *ufbxi_push_element_size(ufbxi_context *uc, ufbxi_element_info *info, size_t size, ufbx_element_type type)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbx_element *ufbxi_push_synthetic_element_size(ufbxi_context *uc, uint64_t *p_fbx_id, ufbxi_node *node, const char *name, size_t size, ufbx_element_type type)
{ … }
#define ufbxi_push_element(uc, info, type_name, type_enum) …
#define ufbxi_push_synthetic_element(uc, p_fbx_id, node, name, type_name, type_enum) …
ufbxi_nodiscard ufbxi_noinline static int ufbxi_connect_oo(ufbxi_context *uc, uint64_t src, uint64_t dst)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_connect_op(ufbxi_context *uc, uint64_t src, uint64_t dst, ufbx_string prop)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_connect_pp(ufbxi_context *uc, uint64_t src, uint64_t dst, ufbx_string src_prop, ufbx_string dst_prop)
{ … }
ufbxi_noinline static void ufbxi_init_synthetic_int_prop(ufbx_prop *dst, const char *name, int64_t value, ufbx_prop_type type)
{ … }
ufbxi_noinline static void ufbxi_init_synthetic_real_prop(ufbx_prop *dst, const char *name, ufbx_real value, ufbx_prop_type type)
{ … }
ufbxi_noinline static void ufbxi_init_synthetic_vec3_prop(ufbx_prop *dst, const char *name, const ufbx_vec3 *value, ufbx_prop_type type)
{ … }
ufbxi_noinline static void ufbxi_set_own_prop_vec3_uniform(ufbx_props *props, const char *name, ufbx_real value)
{ … }
ufbxi_node_extra;
ufbxi_nodiscard ufbxi_noinline static int ufbxi_setup_geometry_transform_helper(ufbxi_context *uc, ufbx_node *node, uint64_t node_fbx_id)
{ … }
ufbxi_scale_helper_prop;
static const ufbxi_scale_helper_prop ufbxi_scale_helper_props[] = …;
ufbxi_nodiscard ufbxi_noinline static int ufbxi_setup_scale_helper(ufbxi_context *uc, ufbx_node *node, uint64_t node_fbx_id)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_model(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_element(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info, size_t size, ufbx_element_type type)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_unknown(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *element, ufbx_string type, ufbx_string sub_type, const char *node_name)
{ … }
ufbxi_tangent_layer;
static ufbx_real ufbxi_zero_element[8] = …;
static const uint32_t ufbxi_sentinel_index_zero[1] = …;
static const uint32_t ufbxi_sentinel_index_consecutive[1] = …;
ufbxi_noinline static int ufbxi_fix_index(ufbxi_context *uc, uint32_t *p_dst, uint32_t index, size_t one_past_max_val)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_check_indices(ufbxi_context *uc, uint32_t **p_dst, uint32_t *indices, bool owns_indices, size_t num_indices, size_t num_indexers, size_t num_elems)
{ … }
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbxi_nodiscard ufbxi_noinline static int ufbxi_warn_polygon_mapping(ufbxi_context *uc, const char *data_name, const char *mapping)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_vertex_element(ufbxi_context *uc, ufbx_mesh *mesh, ufbxi_node *node,
ufbx_vertex_attrib *attrib, const char *data_name, const char *index_name, const char *w_name, char data_type, size_t num_components)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_truncated_array(ufbxi_context *uc, void *p_data, size_t *p_count, ufbxi_node *node, const char *name, char fmt, size_t size)
{ … }
ufbxi_noinline static bool ufbxi_uv_set_less(void *user, const void *va, const void *vb)
{ … }
ufbxi_noinline static bool ufbxi_color_set_less(void *user, const void *va, const void *vb)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_uv_sets(ufbxi_context *uc, ufbx_uv_set *sets, size_t count)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_color_sets(ufbxi_context *uc, ufbx_color_set *sets, size_t count)
{ … }
ufbxi_blend_offset;
static ufbxi_noinline bool ufbxi_blend_offset_less(void *user, const void *va, const void *vb)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_blend_offsets(ufbxi_context *uc, ufbxi_blend_offset *offsets, size_t count)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_shape(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_synthetic_blend_shapes(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_process_indices(ufbxi_context *uc, ufbx_mesh *mesh, uint32_t *index_data)
{ … }
ufbxi_noinline static void ufbxi_patch_mesh_reals(ufbx_mesh *mesh)
{ … }
ufbxi_id_group;
static bool ufbxi_less_int32(void *user, const void *va, const void *vb)
{ … }
ufbx_static_assert(…);
ufbx_static_assert(…);
static ufbxi_forceinline void ufbxi_mesh_part_add_face(ufbx_mesh_part *part, uint32_t num_indices)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_assign_face_groups(ufbxi_buf *buf, ufbx_error *error, ufbx_mesh *mesh, size_t *p_consecutive_indices, bool retain_parts)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_update_face_groups(ufbxi_buf *buf, ufbx_error *error, ufbx_mesh *mesh, bool need_copy)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_mesh(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_noinline static ufbx_nurbs_topology ufbxi_read_nurbs_topology(const char *form)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_nurbs_curve(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_nurbs_surface(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_line(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_noinline static void ufbxi_read_transform_matrix(ufbx_matrix *m, ufbx_real *data)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_bone(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info, const char *sub_type)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_marker(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info, const char *sub_type, ufbx_marker_type type)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_skin(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_skin_cluster(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_blend_channel(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_key_flags;
static ufbxi_noinline float ufbxi_solve_auto_tangent(ufbxi_context *uc, double prev_time, double time, double next_time, ufbx_real prev_value, ufbx_real value, ufbx_real next_value, float weight_left, float weight_right, float auto_bias, uint32_t flags)
{ … }
static float ufbxi_solve_auto_tangent_left(ufbxi_context *uc, double prev_time, double time, ufbx_real prev_value, ufbx_real value, float weight_left, float auto_bias, uint32_t flags)
{ … }
static float ufbxi_solve_auto_tangent_right(ufbxi_context *uc, double time, double next_time, ufbx_real value, ufbx_real next_value, float weight_right, float auto_bias, uint32_t flags)
{ … }
static void ufbxi_solve_tcb(float *p_slope_left, float *p_slope_right, double tension, double continuity, double bias, double slope_left, double slope_right, bool edge)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_animation_curve(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_material(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_texture(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_layered_texture(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_video(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_anim_stack(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_pose(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info, const char *sub_type)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_shader_prop_bindings(ufbxi_context *uc, ufbx_shader_prop_binding *bindings, size_t count)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_binding_table(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_selection_set(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_noinline static void ufbxi_find_uint32_list(ufbx_uint32_list *dst, ufbxi_node *node, const char *name)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_selection_node(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_character(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_audio_clip(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_constraint_type;
static const ufbxi_constraint_type ufbxi_constraint_types[] = …;
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_constraint(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_synthetic_attribute(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info, ufbx_string type_str, const char *sub_type, const char *super_type)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_global_settings(ufbxi_context *uc, ufbxi_node *node)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_object(ufbxi_context *uc, ufbxi_node *node)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_objects(ufbxi_context *uc)
{ … }
ufbxi_object_batch;
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_objects_threaded(ufbxi_context *uc)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_connections(ufbxi_context *uc)
{ … }
ufbxi_forceinline static char ufbxi_double_to_char(double value)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_take_anim_channel(ufbxi_context *uc, ufbxi_node *node, uint64_t value_fbx_id, const char *name, ufbx_real *p_default)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_take_prop_channel(ufbxi_context *uc, ufbxi_node *node, uint64_t target_fbx_id, uint64_t layer_fbx_id, ufbx_string name)
ufbxi_recursive_function(int, ufbxi_read_take_prop_channel, (uc, node, target_fbx_id, layer_fbx_id, name), 2,
(ufbxi_context *uc, ufbxi_node *node, uint64_t target_fbx_id, uint64_t layer_fbx_id, ufbx_string name))
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_take_object(ufbxi_context *uc, ufbxi_node *node, uint64_t layer_fbx_id)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_take(ufbxi_context *uc, ufbxi_node *node)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_takes(ufbxi_context *uc)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_legacy_settings(ufbxi_context *uc, ufbxi_node *node)
{ … }
ufbxi_noinline static ufbx_matrix ufbxi_unscaled_transform_to_matrix(const ufbx_transform *t)
{ … }
ufbxi_noinline static void ufbxi_setup_root_node(ufbxi_context *uc, ufbx_node *root)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_root(ufbxi_context *uc)
{ … }
ufbxi_legacy_prop;
static const ufbxi_legacy_prop ufbxi_legacy_light_props[] = …;
static const ufbxi_legacy_prop ufbxi_legacy_camera_props[] = …;
static const ufbxi_legacy_prop ufbxi_legacy_bone_props[] = …;
static const ufbxi_legacy_prop ufbxi_legacy_material_props[] = …;
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_legacy_prop(ufbxi_node *node, ufbx_prop *prop, const ufbxi_legacy_prop *legacy_prop)
{ … }
ufbxi_nodiscard ufbxi_noinline static size_t ufbxi_read_legacy_props(ufbxi_node *node, ufbx_prop *props, const ufbxi_legacy_prop *legacy_props, size_t num_legacy)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_legacy_material(ufbxi_context *uc, ufbxi_node *node, uint64_t *p_fbx_id, const char *name)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_legacy_link(ufbxi_context *uc, ufbxi_node *node, uint64_t *p_fbx_id, const char *name)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_legacy_light(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_legacy_camera(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_legacy_limb_node(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_legacy_mesh(ufbxi_context *uc, ufbxi_node *node, ufbxi_element_info *info)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_legacy_media(ufbxi_context *uc, ufbxi_node *node)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_read_legacy_model(ufbxi_context *uc, ufbxi_node *node)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_read_legacy_root(ufbxi_context *uc)
{ … }
ufbxi_nodiscard ufbxi_noinline static size_t ufbxi_trim_delimiters(ufbxi_context *uc, const char *data, size_t length)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_init_file_paths(ufbxi_context *uc)
{ … }
ufbxi_strblob;
static ufbxi_noinline void ufbxi_strblob_set(ufbxi_strblob *dst, const char *data, size_t length, bool raw)
{ … }
static ufbxi_forceinline const char *ufbxi_strblob_data(const ufbxi_strblob *strblob, bool raw)
{ … }
static ufbxi_forceinline size_t ufbxi_strblob_length(const ufbxi_strblob *strblob, bool raw)
{ … }
ufbxi_nodiscard ufbxi_noinline static bool ufbxi_is_absolute_path(const char *path, size_t length)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_resolve_relative_filename(ufbxi_context *uc, ufbxi_strblob *p_dst, const ufbxi_strblob *p_src, bool raw)
{ … }
static void *ufbxi_ator_alloc(void *user, size_t size)
{ … }
static void *ufbxi_ator_realloc(void *user, void *old_ptr, size_t old_size, size_t new_size)
{ … }
static void ufbxi_ator_free(void *user, void *ptr, size_t size)
{ … }
static ufbxi_noinline void ufbxi_setup_ator_allocator(ufbx_allocator *allocator, ufbxi_allocator *ator)
{ … }
static ufbxi_noinline bool ufbxi_open_file(const ufbx_open_file_cb *cb, ufbx_stream *stream, const char *path, size_t path_len, const ufbx_blob *original_filename, ufbxi_allocator *ator, ufbx_open_file_type type)
{ … }
#define ufbxi_patch_zero(dst, src) …
static void ufbxi_update_vertex_first_index(ufbx_mesh *mesh)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_finalize_mesh(ufbxi_buf *buf, ufbx_error *error, ufbx_mesh *mesh)
{ … }
#if UFBXI_FEATURE_FORMAT_OBJ
static const uint8_t ufbxi_obj_attrib_stride[] = {
3, 2, 3, 4,
};
ufbx_static_assert(obj_attrib_strides, ufbxi_arraycount(ufbxi_obj_attrib_stride) == UFBXI_OBJ_NUM_ATTRIBS_EXT);
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_pop_props(ufbxi_context *uc, ufbx_prop_list *dst, size_t count)
{
ufbx_prop_list props;
props.count = count;
props.data = ufbxi_push_pop(&uc->result, &uc->obj.tmp_props, ufbx_prop, count);
ufbxi_check(props.data);
ufbxi_for_list(ufbx_prop, prop, props) {
prop->_internal_key = ufbxi_get_name_key(prop->name.data, prop->name.length);
if (prop->value_str.length == 0) {
prop->value_str.data = ufbxi_empty_char;
}
if (!prop->value_int) {
prop->value_int = ufbxi_f64_to_i64(prop->value_real);
}
if (prop->value_blob.size == 0 && prop->value_str.length > 0) {
prop->value_blob.data = prop->value_str.data;
prop->value_blob.size = prop->value_str.length;
}
}
if (props.count > 1) {
ufbxi_check(ufbxi_sort_properties(uc, props.data, props.count));
ufbxi_deduplicate_properties(&props);
}
*dst = props;
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_push_mesh(ufbxi_context *uc)
{
ufbxi_obj_mesh *mesh = ufbxi_push_zero(&uc->obj.tmp_meshes, ufbxi_obj_mesh, 1);
ufbxi_check(mesh);
uc->obj.mesh = mesh;
ufbxi_nounroll for (size_t i = 0; i < UFBXI_OBJ_NUM_ATTRIBS; i++) {
mesh->vertex_range[i].min_ix = UINT64_MAX;
}
const char *name = "";
if (uc->opts.obj_split_groups && uc->obj.group.length > 0) {
name = uc->obj.group.data;
} else if (!uc->opts.obj_merge_objects && uc->obj.object.length > 0) {
name = uc->obj.object.data;
} else if (!uc->opts.obj_merge_groups && uc->obj.group.length > 0) {
name = uc->obj.group.data;
}
mesh->fbx_node = ufbxi_push_synthetic_element(uc, &mesh->fbx_node_id, NULL, name, ufbx_node, UFBX_ELEMENT_NODE);
mesh->fbx_mesh = ufbxi_push_synthetic_element(uc, &mesh->fbx_mesh_id, NULL, name, ufbx_mesh, UFBX_ELEMENT_MESH);
ufbxi_check(mesh->fbx_node && mesh->fbx_mesh);
mesh->fbx_mesh->vertex_position.unique_per_vertex = true;
ufbxi_check(ufbxi_push_copy(&uc->tmp_node_ids, uint32_t, 1, &mesh->fbx_node->element_id));
uc->obj.face_material = UFBX_NO_INDEX;
uc->obj.face_group = 0;
uc->obj.face_group_dirty = true;
uc->obj.material_dirty = true;
ufbxi_check(ufbxi_connect_oo(uc, mesh->fbx_mesh_id, mesh->fbx_node_id));
ufbxi_check(ufbxi_connect_oo(uc, mesh->fbx_node_id, 0));
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_flush_mesh(ufbxi_context *uc)
{
if (!uc->obj.mesh) return 1;
size_t num_props = uc->obj.tmp_props.num_items;
ufbxi_check(ufbxi_obj_pop_props(uc, &uc->obj.mesh->fbx_mesh->props.props, num_props));
size_t num_groups = uc->obj.tmp_face_group_infos.num_items;
ufbx_face_group *groups = ufbxi_push_pop(&uc->result, &uc->obj.tmp_face_group_infos, ufbx_face_group, num_groups);
ufbxi_check(groups);
uc->obj.mesh->fbx_mesh->face_groups.data = groups;
uc->obj.mesh->fbx_mesh->face_groups.count = num_groups;
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_init(ufbxi_context *uc)
{
uc->from_ascii = true;
uc->obj.initialized = true;
ufbxi_nounroll for (size_t i = 0; i < UFBXI_OBJ_NUM_ATTRIBS_EXT; i++) {
uc->obj.tmp_vertices[i].ator = &uc->ator_tmp;
uc->obj.tmp_indices[i].ator = &uc->ator_tmp;
}
uc->obj.tmp_color_valid.ator = &uc->ator_tmp;
uc->obj.tmp_faces.ator = &uc->ator_tmp;
uc->obj.tmp_face_material.ator = &uc->ator_tmp;
uc->obj.tmp_face_smoothing.ator = &uc->ator_tmp;
uc->obj.tmp_face_group.ator = &uc->ator_tmp;
uc->obj.tmp_face_group_infos.ator = &uc->ator_tmp;
uc->obj.tmp_meshes.ator = &uc->ator_tmp;
uc->obj.tmp_props.ator = &uc->ator_tmp;
uc->data_size += uc->yield_size;
uc->obj.object.data = ufbxi_empty_char;
uc->obj.group.data = ufbxi_empty_char;
ufbxi_map_init(&uc->obj.group_map, &uc->ator_tmp, ufbxi_map_cmp_const_char_ptr, NULL);
{
ufbxi_element_info root_info = { uc->root_id };
root_info.name = ufbx_empty_string;
ufbx_node *root = ufbxi_push_element(uc, &root_info, ufbx_node, UFBX_ELEMENT_NODE);
ufbxi_check(root);
ufbxi_setup_root_node(uc, root);
ufbxi_check(ufbxi_push_copy(&uc->tmp_node_ids, uint32_t, 1, &root->element.element_id));
}
return 1;
}
static ufbxi_noinline void ufbxi_obj_free(ufbxi_context *uc)
{
if (!uc->obj.initialized) return;
ufbxi_nounroll for (size_t i = 0; i < UFBXI_OBJ_NUM_ATTRIBS_EXT; i++) {
ufbxi_buf_free(&uc->obj.tmp_vertices[i]);
ufbxi_buf_free(&uc->obj.tmp_indices[i]);
}
ufbxi_buf_free(&uc->obj.tmp_color_valid);
ufbxi_buf_free(&uc->obj.tmp_faces);
ufbxi_buf_free(&uc->obj.tmp_face_material);
ufbxi_buf_free(&uc->obj.tmp_face_smoothing);
ufbxi_buf_free(&uc->obj.tmp_face_group);
ufbxi_buf_free(&uc->obj.tmp_face_group_infos);
ufbxi_buf_free(&uc->obj.tmp_meshes);
ufbxi_buf_free(&uc->obj.tmp_props);
ufbxi_map_free(&uc->obj.group_map);
ufbxi_free(&uc->ator_tmp, ufbx_string, uc->obj.tokens, uc->obj.tokens_cap);
ufbxi_free(&uc->ator_tmp, ufbx_material*, uc->obj.tmp_materials, uc->obj.tmp_materials_cap);
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_read_line(ufbxi_context *uc)
{
ufbxi_dev_assert(!uc->obj.eof);
size_t offset = 0;
for (;;) {
const char *begin = ufbxi_add_ptr(uc->data, offset);
const char *end = begin ? (const char*)memchr(begin, '\n', uc->data_size - offset) : NULL;
if (!end) {
if (uc->eof) {
offset = uc->data_size;
uc->obj.eof = true;
break;
} else {
size_t new_cap = ufbxi_max_sz(1, uc->data_size * 2);
ufbxi_check(ufbxi_refill(uc, new_cap, false));
continue;
}
}
offset += ufbxi_to_size(end - begin) + 1;
const char *esc = end;
if (esc > begin && esc[-1] == '\r') esc--;
if (esc > begin && esc[-1] == '\\') {
continue;
}
break;
}
size_t line_len = offset;
uc->obj.line.data = uc->data;
uc->obj.line.length = line_len;
uc->data += line_len;
uc->data_size -= line_len;
uc->obj.read_progress += line_len;
if (uc->obj.read_progress >= uc->progress_interval) {
ufbxi_check(ufbxi_report_progress(uc));
uc->obj.read_progress %= uc->progress_interval;
}
if (uc->obj.eof) {
char *new_data = ufbxi_push(&uc->tmp, char, line_len + 1);
ufbxi_check(new_data);
memcpy(new_data, uc->obj.line.data, line_len);
new_data[line_len] = '\n';
uc->obj.line.data = new_data;
uc->obj.line.length++;
}
return 1;
}
static ufbxi_noinline ufbx_string ufbxi_obj_span_token(ufbxi_context *uc, size_t start_token, size_t end_token)
{
ufbx_assert(start_token < uc->obj.num_tokens);
end_token = ufbxi_min_sz(end_token, uc->obj.num_tokens - 1);
ufbx_assert(start_token <= end_token);
ufbx_string start = uc->obj.tokens[start_token];
ufbx_string end = uc->obj.tokens[end_token];
size_t num_between = ufbxi_to_size(end.data - start.data);
ufbx_string result;
result.data = start.data;
result.length = num_between + end.length;
return result;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_tokenize(ufbxi_context *uc)
{
const char *ptr = uc->obj.line.data, *end = ptr + uc->obj.line.length;
uc->obj.num_tokens = 0;
for (;;) {
char c;
for (;;) {
c = *ptr;
if (c == ' ' || c == '\t' || c == '\r') {
ptr++;
continue;
}
if (c == '\\') {
const char *p = ptr + 1;
if (*p == '\r') p++;
if (*p == '\n' && p < end - 1) {
ptr = p + 1;
continue;
}
}
break;
}
c = *ptr;
if (c == '\n') break;
if (c == '#' && uc->obj.num_tokens > 0) break;
size_t index = uc->obj.num_tokens++;
ufbxi_check(ufbxi_grow_array(&uc->ator_tmp, &uc->obj.tokens, &uc->obj.tokens_cap, index + 1));
ufbx_string *tok = &uc->obj.tokens[index];
tok->data = ptr;
if (c == '#') {
ptr++;
tok->length = 1;
continue;
}
for (;;) {
c = *++ptr;
if (ufbxi_is_space(c)) {
break;
}
if (c == '\\') {
const char *p = ptr + 1;
if (*p == '\r') p++;
if (*p == '\n' && p < end - 1) {
break;
}
}
}
tok->length = ufbxi_to_size(ptr - tok->data);
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_tokenize_line(ufbxi_context *uc)
{
ufbxi_check(ufbxi_obj_read_line(uc));
ufbxi_check(ufbxi_obj_tokenize(uc));
return 1;
}
static ufbxi_noinline int ufbxi_obj_parse_vertex(ufbxi_context *uc, ufbxi_obj_attrib attrib, size_t offset)
{
if (uc->opts.ignore_geometry) return 1;
ufbxi_buf *dst = &uc->obj.tmp_vertices[attrib];
size_t num_values = ufbxi_obj_attrib_stride[attrib];
uc->obj.vertex_count[attrib]++;
size_t read_values = num_values;
if (attrib == UFBXI_OBJ_ATTRIB_COLOR) {
if (offset + read_values > uc->obj.num_tokens) {
read_values = 3;
}
}
ufbxi_check(offset + read_values <= uc->obj.num_tokens);
uint32_t parse_flags = uc->double_parse_flags;
ufbx_real *vals = ufbxi_push_fast(dst, ufbx_real, num_values);
ufbxi_check(vals);
for (size_t i = 0; i < read_values; i++) {
ufbx_string str = uc->obj.tokens[offset + i];
char *end;
double val = ufbxi_parse_double(str.data, str.length, &end, parse_flags);
ufbxi_check(end == str.data + str.length);
vals[i] = (ufbx_real)val;
}
if (read_values < num_values) {
ufbx_assert(read_values + 1 == num_values);
ufbx_assert(attrib == UFBXI_OBJ_ATTRIB_COLOR);
vals[read_values] = 1.0f;
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_index(ufbxi_context *uc, ufbx_string *s, uint32_t attrib)
{
const char *ptr = s->data, *end = ptr + s->length;
bool negative = false;
if (*ptr == '-') {
negative = true;
ptr++;
}
uint64_t index = 0;
for (; ptr != end; ptr++) {
char c = *ptr;
if (c >= '0' && c <= '9') {
ufbxi_check(index < UINT64_MAX / 10 - 10);
index = index * 10 + (uint64_t)(c - '0');
} else if (c == '/') {
ptr++;
break;
}
}
if (negative) {
size_t count = uc->obj.vertex_count[attrib];
index = index <= count ? count - index : UINT64_MAX;
} else {
index -= 1;
}
ufbxi_obj_fast_indices *fast_indices = &uc->obj.fast_indices[attrib];
if (fast_indices->num_left == 0) {
size_t num_push = 128;
uint64_t *dst = ufbxi_push(&uc->obj.tmp_indices[attrib], uint64_t, num_push);
ufbxi_check(dst);
uc->obj.fast_indices[attrib].indices = dst;
uc->obj.fast_indices[attrib].num_left = num_push;
}
*fast_indices->indices++ = index;
fast_indices->num_left--;
ufbxi_obj_mesh *mesh = uc->obj.mesh;
if (index != UINT64_MAX) {
ufbxi_obj_index_range *range = &mesh->vertex_range[attrib];
range->min_ix = ufbxi_min64(range->min_ix, index);
range->max_ix = ufbxi_max64(range->max_ix, index);
}
s->data = ptr;
s->length = ufbxi_to_size(end - ptr);
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_indices(ufbxi_context *uc, size_t token_begin, size_t num_tokens)
{
bool flush_mesh = false;
if (uc->obj.object_dirty) {
if (!uc->opts.obj_merge_objects) {
flush_mesh = true;
}
uc->obj.object_dirty = false;
}
if (uc->obj.group_dirty) {
if (((uc->obj.object.length == 0 || uc->opts.obj_merge_objects) && !uc->opts.obj_merge_groups) || uc->opts.obj_split_groups) {
flush_mesh = true;
}
uc->obj.group_dirty = false;
uc->obj.face_group_dirty = true;
}
if (!uc->obj.mesh || flush_mesh) {
ufbxi_check(ufbxi_obj_flush_mesh(uc));
ufbxi_check(ufbxi_obj_push_mesh(uc));
}
ufbxi_obj_mesh *mesh = uc->obj.mesh;
if (uc->obj.material_dirty) {
if (uc->obj.usemtl_fbx_id != 0) {
ufbxi_fbx_id_entry *entry = ufbxi_find_fbx_id(uc, uc->obj.usemtl_fbx_id);
ufbx_assert(entry);
if (mesh->usemtl_base == 0 || entry->user_id < mesh->usemtl_base) {
ufbxi_check(ufbxi_connect_oo(uc, uc->obj.usemtl_fbx_id, mesh->fbx_node_id));
uint32_t index = ++uc->obj.usemtl_index;
ufbxi_check(index < UINT32_MAX);
entry->user_id = index;
if (mesh->usemtl_base == 0) {
mesh->usemtl_base = index;
}
uc->obj.face_material = index - mesh->usemtl_base;
}
uc->obj.face_material = entry->user_id - mesh->usemtl_base;
}
}
if (uc->opts.ignore_geometry) return 1;
if (num_tokens == 0 && !uc->opts.allow_empty_faces) {
ufbxi_check(ufbxi_warnf(UFBX_WARNING_EMPTY_FACE_REMOVED, "Empty face has been removed"));
return 1;
}
if (uc->obj.face_group_dirty) {
ufbx_string name = ufbx_empty_string;
if (uc->obj.group.length > 0 && (uc->obj.object.length > 0 || uc->opts.obj_merge_groups) && !uc->opts.obj_split_groups) {
name = uc->obj.group;
}
uint32_t hash = ufbxi_hash_ptr(name.data);
ufbxi_obj_group_entry *entry = ufbxi_map_find(&uc->obj.group_map, ufbxi_obj_group_entry, hash, &name.data);
if (!entry) {
entry = ufbxi_map_insert(&uc->obj.group_map, ufbxi_obj_group_entry, hash, &name.data);
ufbxi_check(entry);
entry->name = name.data;
entry->mesh_id = 0;
entry->local_id = 0;
}
uint32_t mesh_id = mesh->fbx_mesh->element_id;
if (entry->mesh_id != mesh_id) {
uint32_t id = mesh->num_groups++;
entry->mesh_id = mesh_id;
entry->local_id = id;
ufbx_face_group *group = ufbxi_push_zero(&uc->obj.tmp_face_group_infos, ufbx_face_group, 1);
ufbxi_check(group);
group->id = 0;
group->name = name;
}
uc->obj.face_group = entry->local_id;
if (!uc->obj.has_face_group) {
uc->obj.has_face_group = true;
ufbxi_check(ufbxi_push_zero(&uc->obj.tmp_face_group, uint32_t, uc->obj.tmp_faces.num_items));
}
uc->obj.face_group_dirty = false;
}
size_t num_indices = num_tokens;
ufbxi_check(UINT32_MAX - mesh->num_indices >= num_indices);
ufbx_face *face = ufbxi_push_fast(&uc->obj.tmp_faces, ufbx_face, 1);
ufbxi_check(face);
face->index_begin = (uint32_t)mesh->num_indices;
face->num_indices = (uint32_t)num_indices;
mesh->num_faces++;
mesh->num_indices += num_indices;
uint32_t *p_face_mat = ufbxi_push_fast(&uc->obj.tmp_face_material, uint32_t, 1);
ufbxi_check(p_face_mat);
*p_face_mat = uc->obj.face_material;
if (uc->obj.has_face_smoothing) {
bool *p_face_smooth = ufbxi_push_fast(&uc->obj.tmp_face_smoothing, bool, 1);
ufbxi_check(p_face_smooth);
*p_face_smooth = uc->obj.face_smoothing;
}
if (uc->obj.has_face_group) {
uint32_t *p_face_group = ufbxi_push_fast(&uc->obj.tmp_face_group, uint32_t, 1);
ufbxi_check(p_face_group);
*p_face_group = uc->obj.face_group;
}
for (size_t ix = 0; ix < num_indices; ix++) {
ufbx_string tok = uc->obj.tokens[token_begin + ix];
for (uint32_t attrib = 0; attrib < UFBXI_OBJ_NUM_ATTRIBS; attrib++) {
ufbxi_check(ufbxi_obj_parse_index(uc, &tok, attrib));
}
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_multi_indices(ufbxi_context *uc, size_t window)
{
for (size_t begin = 1; begin + window <= uc->obj.num_tokens; begin++) {
ufbxi_check(ufbxi_obj_parse_indices(uc, begin, window));
}
return 1;
}
static ufbxi_noinline uint32_t ufbxi_parse_hex(const char *digits, size_t length)
{
uint32_t value = 0;
for (size_t i = 0; i < length; i++) {
char c = digits[i];
uint32_t v = 0;
if (c >= '0' && c <= '9') {
v = (uint32_t)(c - '0');
} else if (c >= 'A' && c <= 'F') {
v = (uint32_t)(c - 'A') + 10;
} else if (c >= 'a' && c <= 'f') {
v = (uint32_t)(c - 'a') + 10;
}
value = (value << 4) | v;
}
return value;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_comment(ufbxi_context *uc)
{
if (uc->obj.num_tokens >= 3 && ufbxi_str_equal(uc->obj.tokens[1], ufbxi_str_c("MRGB"))) {
size_t num_color = uc->obj.vertex_count[UFBXI_OBJ_ATTRIB_COLOR];
if (num_color > uc->obj.mrgb_vertex_count) {
size_t num_pop = num_color - uc->obj.mrgb_vertex_count;
ufbxi_pop(&uc->obj.tmp_color_valid, bool, num_pop, NULL);
ufbxi_pop(&uc->obj.tmp_vertices[UFBXI_OBJ_ATTRIB_COLOR], ufbx_real, num_pop * 4, NULL);
uc->obj.vertex_count[UFBXI_OBJ_ATTRIB_COLOR] -= num_pop;
}
ufbx_string mrgb = uc->obj.tokens[2];
for (size_t i = 0; i + 8 <= mrgb.length; i += 8) {
ufbx_real *p_rgba = ufbxi_push(&uc->obj.tmp_vertices[UFBXI_OBJ_ATTRIB_COLOR], ufbx_real, 4);
bool *p_valid = ufbxi_push(&uc->obj.tmp_color_valid, bool, 1);
ufbxi_check(p_rgba && p_valid);
*p_valid = true;
uint32_t hex = ufbxi_parse_hex(mrgb.data + i, 8);
p_rgba[0] = (ufbx_real)((hex >> 16u) & 0xff) / 255.0f;
p_rgba[1] = (ufbx_real)((hex >> 8u) & 0xff) / 255.0f;
p_rgba[2] = (ufbx_real)((hex >> 0u) & 0xff) / 255.0f;
p_rgba[3] = (ufbx_real)((hex >> 24u) & 0xff) / 255.0f;
}
uc->obj.has_vertex_color = true;
}
if (!uc->opts.disable_quirks) {
if (ufbxi_match(&uc->obj.line, "\\s*#\\s*File exported by ZBrush.*")) {
if (!uc->obj.mesh) {
uc->opts.obj_merge_groups = true;
}
}
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_material(ufbxi_context *uc)
{
ufbxi_check(uc->obj.num_tokens >= 2);
ufbx_string name = ufbxi_obj_span_token(uc, 1, SIZE_MAX);
ufbxi_check(ufbxi_push_string_place_str(&uc->string_pool, &name, false));
uint64_t fbx_id = ufbxi_synthetic_id_from_string(name.data);
ufbxi_fbx_id_entry *entry = ufbxi_find_fbx_id(uc, fbx_id);
uc->obj.usemtl_fbx_id = fbx_id;
uc->obj.usemtl_name = name;
if (!entry) {
ufbxi_element_info info = { 0 };
info.fbx_id = fbx_id;
info.name = name;
ufbx_material *material = ufbxi_push_element(uc, &info, ufbx_material, UFBX_ELEMENT_MATERIAL);
ufbxi_check(material);
material->shader_type = UFBX_SHADER_WAVEFRONT_MTL;
material->shading_model_name.data = ufbxi_empty_char;
material->shader_prop_prefix.data = ufbxi_empty_char;
size_t id = material->element_id;
ufbxi_check(ufbxi_grow_array(&uc->ator_tmp, &uc->obj.tmp_materials, &uc->obj.tmp_materials_cap, id + 1));
uc->obj.tmp_materials[id] = material;
}
uc->obj.material_dirty = true;
return 1;
}
#define ufbxi_obj_cmd1 …
#define ufbxi_obj_cmd2 …
#define ufbxi_obj_cmd3 …
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_pop_vertices(ufbxi_context *uc, ufbx_real_list *dst, uint32_t attrib, uint64_t min_index)
{
size_t stride = ufbxi_obj_attrib_stride[attrib];
ufbxi_check(min_index < uc->obj.tmp_vertices[attrib].num_items / stride);
size_t count = uc->obj.tmp_vertices[attrib].num_items - (size_t)min_index * stride;
ufbx_real *data = ufbxi_push(&uc->result, ufbx_real, count + 4);
ufbxi_check(data);
data[0] = 0.0f;
data[1] = 0.0f;
data[2] = 0.0f;
data[3] = 0.0f;
data += 4;
ufbxi_pop(&uc->obj.tmp_vertices[attrib], ufbx_real, count, data);
dst->data = data;
dst->count = count;
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_setup_attrib(ufbxi_context *uc, ufbxi_obj_mesh *mesh, uint64_t *tmp_indices,
ufbx_vertex_attrib *dst, const ufbx_real_list *p_data, uint32_t attrib, bool non_disjoint, bool required)
{
ufbx_real_list data = *p_data;
size_t num_indices = mesh->num_indices;
size_t stride = ufbxi_obj_attrib_stride[attrib];
size_t num_values = data.count / stride;
uint64_t mesh_min_ix = mesh->vertex_range[attrib].min_ix;
if (num_indices == 0 || num_values == 0 || mesh_min_ix == UINT64_MAX) {
ufbxi_check(num_indices == 0 || !required);
ufbxi_pop(&uc->obj.tmp_indices[attrib], uint64_t, num_indices, NULL);
return 1;
}
uint64_t min_index = non_disjoint ? 0 : mesh_min_ix;
ufbxi_pop(&uc->obj.tmp_indices[attrib], uint64_t, num_indices, tmp_indices);
uint32_t *dst_indices = ufbxi_push(&uc->result, uint32_t, num_indices);
ufbxi_check(dst_indices);
dst->exists = true;
dst->values.data = data.data;
dst->values.count = num_values;
dst->indices.data = dst_indices;
dst->indices.count = num_indices;
ufbxi_nounroll for (size_t i = 0; i < num_indices; i++) {
uint64_t ix = tmp_indices[i];
if (ix != UINT64_MAX) {
ix -= min_index;
ufbxi_check(ix < UINT32_MAX);
}
if (ix < num_values) {
dst_indices[i] = (uint32_t)ix;
} else {
ufbxi_check(ufbxi_fix_index(uc, &dst_indices[i], (uint32_t)ix, num_values));
}
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_pad_colors(ufbxi_context *uc, size_t num_vertices)
{
if (uc->opts.ignore_geometry) return 1;
size_t num_colors = uc->obj.vertex_count[UFBXI_OBJ_ATTRIB_COLOR];
if (num_vertices > num_colors) {
size_t num_pad = num_vertices - num_colors;
ufbxi_check(ufbxi_push_zero(&uc->obj.tmp_vertices[UFBXI_OBJ_ATTRIB_COLOR], ufbx_real, num_pad * 4));
ufbxi_check(ufbxi_push_zero(&uc->obj.tmp_color_valid, bool, num_pad));
uc->obj.vertex_count[UFBXI_OBJ_ATTRIB_COLOR] += num_pad;
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_pop_meshes(ufbxi_context *uc)
{
size_t num_meshes = uc->obj.tmp_meshes.num_items;
ufbxi_obj_mesh *meshes = ufbxi_push_pop(&uc->tmp, &uc->obj.tmp_meshes, ufbxi_obj_mesh, num_meshes);
ufbxi_check(meshes);
if (uc->obj.has_vertex_color) {
ufbxi_check(ufbxi_obj_pad_colors(uc, uc->obj.vertex_count[UFBXI_OBJ_ATTRIB_POSITION]));
}
for (size_t i = 0; i < UFBXI_OBJ_NUM_ATTRIBS; i++) {
ufbxi_pop(&uc->obj.tmp_indices[i], uint64_t, uc->obj.fast_indices[i].num_left, NULL);
}
bool non_disjoint[UFBXI_OBJ_NUM_ATTRIBS] = { 0 };
uint64_t next_min[UFBXI_OBJ_NUM_ATTRIBS] = { 0 };
ufbx_real_list vertices[UFBXI_OBJ_NUM_ATTRIBS_EXT] = { 0 };
bool *color_valid = NULL;
size_t max_indices = 0;
for (size_t i = 0; i < num_meshes; i++) {
ufbxi_obj_mesh *mesh = &meshes[i];
max_indices = ufbxi_max_sz(max_indices, mesh->num_indices);
ufbxi_nounroll for (uint32_t attrib = 0; attrib < UFBXI_OBJ_NUM_ATTRIBS; attrib++) {
ufbxi_obj_index_range range = mesh->vertex_range[attrib];
if (range.min_ix > range.max_ix) continue;
if (range.min_ix < next_min[attrib]) {
non_disjoint[attrib] = true;
}
next_min[attrib] = range.max_ix + 1;
}
}
uint64_t *tmp_indices = ufbxi_push(&uc->tmp, uint64_t, max_indices);
ufbxi_check(tmp_indices);
ufbxi_nounroll for (uint32_t attrib = 0; attrib < UFBXI_OBJ_NUM_ATTRIBS; attrib++) {
if (!non_disjoint[attrib]) continue;
ufbxi_check(ufbxi_obj_pop_vertices(uc, &vertices[attrib], attrib, 0));
}
if (uc->obj.has_vertex_color && non_disjoint[UFBXI_OBJ_ATTRIB_POSITION]) {
ufbxi_check(ufbxi_obj_pop_vertices(uc, &vertices[UFBXI_OBJ_ATTRIB_COLOR], UFBXI_OBJ_ATTRIB_COLOR, 0));
color_valid = ufbxi_push_pop(&uc->tmp, &uc->obj.tmp_color_valid, bool, vertices[UFBXI_OBJ_ATTRIB_COLOR].count / 4);
ufbxi_check(color_valid);
}
for (size_t i = num_meshes; i > 0; i--) {
ufbxi_obj_mesh *mesh = &meshes[i - 1];
ufbx_mesh *fbx_mesh = mesh->fbx_mesh;
size_t num_faces = mesh->num_faces;
if (!uc->opts.ignore_geometry) {
ufbxi_nounroll for (uint32_t attrib = 0; attrib < UFBXI_OBJ_NUM_ATTRIBS; attrib++) {
if (non_disjoint[attrib]) continue;
uint64_t min_ix = mesh->vertex_range[attrib].min_ix;
if (min_ix < UINT64_MAX) {
ufbxi_check(ufbxi_obj_pop_vertices(uc, &vertices[attrib], attrib, min_ix));
}
}
if (uc->obj.has_vertex_color && !non_disjoint[UFBXI_OBJ_ATTRIB_POSITION]) {
uint64_t min_ix = mesh->vertex_range[UFBXI_OBJ_ATTRIB_POSITION].min_ix;
ufbxi_check(min_ix < UINT64_MAX);
ufbxi_check(ufbxi_obj_pop_vertices(uc, &vertices[UFBXI_OBJ_ATTRIB_COLOR], UFBXI_OBJ_ATTRIB_COLOR, min_ix));
color_valid = ufbxi_push_pop(&uc->tmp, &uc->obj.tmp_color_valid, bool, vertices[UFBXI_OBJ_ATTRIB_COLOR].count / 4);
ufbxi_check(color_valid);
}
fbx_mesh->faces.count = num_faces;
fbx_mesh->face_material.count = num_faces;
fbx_mesh->faces.data = ufbxi_push_pop(&uc->result, &uc->obj.tmp_faces, ufbx_face, num_faces);
fbx_mesh->face_material.data = ufbxi_push_pop(&uc->result, &uc->obj.tmp_face_material, uint32_t, num_faces);
ufbxi_check(fbx_mesh->faces.data);
ufbxi_check(fbx_mesh->face_material.data);
if (uc->obj.has_face_smoothing) {
fbx_mesh->face_smoothing.count = num_faces;
fbx_mesh->face_smoothing.data = ufbxi_push_pop(&uc->result, &uc->obj.tmp_face_smoothing, bool, num_faces);
ufbxi_check(fbx_mesh->face_smoothing.data);
}
if (uc->obj.has_face_group) {
if (mesh->num_groups > 1) {
fbx_mesh->face_group.count = num_faces;
fbx_mesh->face_group.data = ufbxi_push_pop(&uc->result, &uc->obj.tmp_face_group, uint32_t, num_faces);
ufbxi_check(fbx_mesh->face_group.data);
} else {
ufbxi_pop(&uc->obj.tmp_face_group, uint32_t, num_faces, NULL);
}
}
ufbxi_check(ufbxi_obj_setup_attrib(uc, mesh, tmp_indices, (ufbx_vertex_attrib*)&fbx_mesh->vertex_position,
&vertices[UFBXI_OBJ_ATTRIB_POSITION], UFBXI_OBJ_ATTRIB_POSITION, non_disjoint[UFBXI_OBJ_ATTRIB_POSITION], true));
ufbxi_check(ufbxi_obj_setup_attrib(uc, mesh, tmp_indices, (ufbx_vertex_attrib*)&fbx_mesh->vertex_uv,
&vertices[UFBXI_OBJ_ATTRIB_UV], UFBXI_OBJ_ATTRIB_UV, non_disjoint[UFBXI_OBJ_ATTRIB_UV], false));
ufbxi_check(ufbxi_obj_setup_attrib(uc, mesh, tmp_indices, (ufbx_vertex_attrib*)&fbx_mesh->vertex_normal,
&vertices[UFBXI_OBJ_ATTRIB_NORMAL], UFBXI_OBJ_ATTRIB_NORMAL, non_disjoint[UFBXI_OBJ_ATTRIB_NORMAL], false));
if (uc->obj.has_vertex_color) {
ufbx_assert(color_valid);
bool has_color = false;
bool all_valid = true;
size_t max_index = fbx_mesh->vertex_position.values.count;
ufbxi_for_list(uint32_t, p_ix, fbx_mesh->vertex_position.indices) {
if (*p_ix < max_index) {
if (color_valid[*p_ix]) {
has_color = true;
} else {
all_valid = false;
}
}
}
if (has_color) {
fbx_mesh->vertex_color.exists = true;
fbx_mesh->vertex_color.values.data = (ufbx_vec4*)vertices[UFBXI_OBJ_ATTRIB_COLOR].data;
fbx_mesh->vertex_color.values.count = vertices[UFBXI_OBJ_ATTRIB_COLOR].count / 4;
fbx_mesh->vertex_color.indices = fbx_mesh->vertex_position.indices;
fbx_mesh->vertex_color.unique_per_vertex = true;
if (!all_valid) {
uint32_t *indices = fbx_mesh->vertex_color.indices.data;
indices = ufbxi_push_copy(&uc->result, uint32_t, mesh->num_indices, indices);
ufbxi_check(indices);
size_t num_values = fbx_mesh->vertex_color.values.count;
ufbxi_for(uint32_t, p_ix, indices, mesh->num_indices) {
if (*p_ix >= num_values || !color_valid[*p_ix]) {
ufbxi_check(ufbxi_fix_index(uc, p_ix, *p_ix, num_values));
}
}
fbx_mesh->vertex_color.indices.data = indices;
}
}
}
}
ufbxi_check(ufbxi_finalize_mesh(&uc->result, &uc->error, fbx_mesh));
if (uc->retain_mesh_parts) {
fbx_mesh->face_group_parts.count = mesh->num_groups;
fbx_mesh->face_group_parts.data = ufbxi_push_zero(&uc->result, ufbx_mesh_part, mesh->num_groups);
ufbxi_check(fbx_mesh->face_group_parts.data);
}
if (mesh->num_groups > 1) {
ufbxi_check(ufbxi_update_face_groups(&uc->result, &uc->error, fbx_mesh, false));
} else if (mesh->num_groups == 1) {
fbx_mesh->face_group.data = (uint32_t*)ufbxi_sentinel_index_zero;
fbx_mesh->face_group.count = num_faces;
if (fbx_mesh->face_group_parts.count > 0) {
ufbx_mesh_part *part = &fbx_mesh->face_group_parts.data[0];
part->num_faces = fbx_mesh->num_faces;
part->num_faces = num_faces;
part->num_empty_faces = fbx_mesh->num_empty_faces;
part->num_point_faces = fbx_mesh->num_point_faces;
part->num_line_faces = fbx_mesh->num_line_faces;
part->num_triangles = fbx_mesh->num_triangles;
part->face_indices.data = (uint32_t*)ufbxi_sentinel_index_consecutive;
part->face_indices.count = num_faces;
}
}
uc->max_zero_indices = ufbxi_max_sz(uc->max_zero_indices, num_faces);
uc->max_consecutive_indices = ufbxi_max_sz(uc->max_consecutive_indices, num_faces);
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_file(ufbxi_context *uc)
{
while (!uc->obj.eof) {
ufbxi_check(ufbxi_obj_tokenize_line(uc));
size_t num_tokens = uc->obj.num_tokens;
if (num_tokens == 0) continue;
ufbx_string cmd = uc->obj.tokens[0];
uint32_t key = ufbxi_get_name_key(cmd.data, cmd.length);
if (key == ufbxi_obj_cmd1('v')) {
ufbxi_check(ufbxi_obj_parse_vertex(uc, UFBXI_OBJ_ATTRIB_POSITION, 1));
if (num_tokens >= 7) {
size_t num_vertices = uc->obj.vertex_count[UFBXI_OBJ_ATTRIB_POSITION];
uc->obj.has_vertex_color = true;
ufbxi_check(ufbxi_obj_pad_colors(uc, num_vertices - 1));
if (uc->obj.vertex_count[UFBXI_OBJ_ATTRIB_COLOR] < num_vertices) {
ufbx_assert(uc->obj.vertex_count[UFBXI_OBJ_ATTRIB_COLOR] == num_vertices - 1);
ufbxi_check(ufbxi_obj_parse_vertex(uc, UFBXI_OBJ_ATTRIB_COLOR, 4));
bool *valid = ufbxi_push(&uc->obj.tmp_color_valid, bool, 1);
ufbxi_check(valid);
*valid = true;
}
}
} else if (key == ufbxi_obj_cmd2('v','t')) {
ufbxi_check(ufbxi_obj_parse_vertex(uc, UFBXI_OBJ_ATTRIB_UV, 1));
} else if (key == ufbxi_obj_cmd2('v','n')) {
ufbxi_check(ufbxi_obj_parse_vertex(uc, UFBXI_OBJ_ATTRIB_NORMAL, 1));
} else if (key == ufbxi_obj_cmd1('f')) {
ufbxi_check(ufbxi_obj_parse_indices(uc, 1, uc->obj.num_tokens - 1));
} else if (key == ufbxi_obj_cmd1('p')) {
ufbxi_check(ufbxi_obj_parse_multi_indices(uc, 1));
} else if (key == ufbxi_obj_cmd1('l')) {
ufbxi_check(ufbxi_obj_parse_multi_indices(uc, 2));
} else if (key == ufbxi_obj_cmd1('s')) {
if (num_tokens >= 2) {
uc->obj.has_face_smoothing = true;
uc->obj.face_smoothing = !ufbxi_str_equal(uc->obj.tokens[1], ufbxi_str_c("off"));
if (uc->obj.tmp_face_smoothing.num_items == 0 && uc->obj.tmp_faces.num_items > 0) {
ufbxi_check(ufbxi_push_zero(&uc->obj.tmp_face_smoothing, bool, uc->obj.tmp_faces.num_items));
}
}
} else if (key == ufbxi_obj_cmd1('o')) {
if (num_tokens >= 2) {
uc->obj.object = ufbxi_obj_span_token(uc, 1, SIZE_MAX);
ufbxi_check(ufbxi_push_string_place_str(&uc->string_pool, &uc->obj.object, false));
uc->obj.object_dirty = true;
}
} else if (key == ufbxi_obj_cmd1('g')) {
if (num_tokens >= 2) {
uc->obj.group = ufbxi_obj_span_token(uc, 1, SIZE_MAX);
ufbxi_check(ufbxi_push_string_place_str(&uc->string_pool, &uc->obj.group, false));
uc->obj.group_dirty = true;
} else {
uc->obj.group = ufbx_empty_string;
uc->obj.group_dirty = true;
}
} else if (key == ufbxi_obj_cmd1('#')) {
ufbxi_check(ufbxi_obj_parse_comment(uc));
} else if (ufbxi_str_equal(cmd, ufbxi_str_c("mtllib"))) {
ufbxi_check(uc->obj.num_tokens >= 2);
ufbx_string lib = ufbxi_obj_span_token(uc, 1, SIZE_MAX);
lib.data = ufbxi_push_copy(&uc->tmp, char, lib.length + 1, lib.data);
ufbxi_check(lib.data);
uc->obj.mtllib_relative_path.data = lib.data;
uc->obj.mtllib_relative_path.size = lib.length;
} else if (ufbxi_str_equal(cmd, ufbxi_str_c("usemtl"))) {
ufbxi_check(ufbxi_obj_parse_material(uc));
} else {
ufbxi_check(ufbxi_warnf(UFBX_WARNING_UNKNOWN_OBJ_DIRECTIVE, "Unknown .obj directive, skipped line"));
}
}
ufbxi_check(ufbxi_obj_flush_mesh(uc));
ufbxi_check(ufbxi_obj_pop_meshes(uc));
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_flush_material(ufbxi_context *uc)
{
if (uc->obj.usemtl_fbx_id == 0) return 1;
ufbxi_fbx_id_entry *entry = ufbxi_find_fbx_id(uc, uc->obj.usemtl_fbx_id);
ufbx_assert(entry);
ufbx_material *material = uc->obj.tmp_materials[entry->element_id];
size_t num_props = uc->obj.tmp_props.num_items;
ufbxi_check(ufbxi_obj_pop_props(uc, &material->props.props, num_props));
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_prop(ufbxi_context *uc, ufbx_string name, size_t start, bool include_rest, size_t *p_next)
{
if (start >= uc->obj.num_tokens) {
if (p_next) {
*p_next = start;
}
return 1;
}
ufbx_prop *prop = ufbxi_push_zero(&uc->obj.tmp_props, ufbx_prop, 1);
ufbxi_check(prop);
prop->name = name;
ufbxi_check(ufbxi_push_string_place_str(&uc->string_pool, &prop->name, false));
uint32_t flags = UFBX_PROP_FLAG_VALUE_STR;
size_t num_reals = 0;
for (; num_reals < 4; num_reals++) {
if (start + num_reals >= uc->obj.num_tokens) break;
ufbx_string tok = uc->obj.tokens[start + num_reals];
char *end;
double val = ufbxi_parse_double(tok.data, tok.length, &end, uc->double_parse_flags);
if (end != tok.data + tok.length) break;
prop->value_real_arr[num_reals] = (ufbx_real)val;
if (num_reals == 0) {
prop->value_int = ufbxi_f64_to_i64(val);
flags |= UFBX_PROP_FLAG_VALUE_INT;
}
}
size_t num_args = 0;
if (!include_rest) {
for (; start + num_args < uc->obj.num_tokens - 1; num_args++) {
if (ufbxi_match(&uc->obj.tokens[start + num_args], "-[A-Za-z][\\-A-Za-z0-9_]*")) break;
}
}
if (num_args > 0 || include_rest) {
ufbx_string span = ufbxi_obj_span_token(uc, start, include_rest ? SIZE_MAX : start + num_args - 1);
prop->value_str = span;
prop->value_blob.data = span.data;
prop->value_blob.size = span.length;
ufbxi_check(ufbxi_push_string_place_str(&uc->string_pool, &prop->value_str, false));
ufbxi_check(ufbxi_push_string_place_blob(&uc->string_pool, &prop->value_blob, true));
} else {
prop->value_str.data = ufbxi_empty_char;
}
if (num_reals > 0) {
flags = (uint32_t)UFBX_PROP_FLAG_VALUE_REAL << (num_reals - 1);
} else {
if (!strcmp(prop->value_str.data, "on")) {
prop->value_int = 1;
prop->value_real = 1.0f;
flags |= UFBX_PROP_FLAG_VALUE_INT;
} else if (!strcmp(prop->value_str.data, "off")) {
prop->value_int = 0;
prop->value_real = 0.0f;
flags |= UFBX_PROP_FLAG_VALUE_INT;
}
}
prop->flags = (ufbx_prop_flags)flags;
if (p_next) {
*p_next = start + num_args;
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_mtl_map(ufbxi_context *uc, size_t prefix_len)
{
if (uc->obj.num_tokens < 2) return 1;
size_t num_props = 1;
ufbxi_check(ufbxi_obj_parse_prop(uc, ufbxi_str_c("obj|args"), 1, true, NULL));
size_t start = 1;
for (; start + 1 < uc->obj.num_tokens; ) {
ufbx_string tok = uc->obj.tokens[start];
if (ufbxi_match(&tok, "-[A-Za-z][\\-A-Za-z0-9_]*")) {
tok.data += 1;
tok.length -= 1;
ufbxi_check(ufbxi_obj_parse_prop(uc, tok, start + 1, false, &start));
num_props++;
} else {
break;
}
}
ufbx_string tex_str = ufbxi_obj_span_token(uc, start, SIZE_MAX);
ufbx_blob tex_raw = { tex_str.data, tex_str.length };
ufbxi_check(ufbxi_push_string_place_str(&uc->string_pool, &tex_str, false));
ufbxi_check(ufbxi_push_string_place_blob(&uc->string_pool, &tex_raw, true));
uint64_t fbx_id = 0;
ufbx_texture *texture = ufbxi_push_synthetic_element(uc, &fbx_id, NULL, "", ufbx_texture, UFBX_ELEMENT_TEXTURE);
ufbxi_check(texture);
texture->filename.data = ufbxi_empty_char;
texture->absolute_filename.data = ufbxi_empty_char;
texture->uv_set.data = ufbxi_empty_char;
texture->relative_filename = tex_str;
texture->raw_relative_filename = tex_raw;
ufbxi_check(ufbxi_obj_pop_props(uc, &texture->props.props, num_props));
ufbx_string prop = uc->obj.tokens[0];
ufbx_assert(prop.length >= prefix_len);
prop.data += prefix_len;
prop.length -= prefix_len;
ufbxi_check(ufbxi_push_string_place_str(&uc->string_pool, &prop, false));
if (uc->obj.usemtl_fbx_id != 0) {
ufbxi_check(ufbxi_connect_op(uc, fbx_id, uc->obj.usemtl_fbx_id, prop));
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_mtl(ufbxi_context *uc)
{
uc->obj.mesh = NULL;
uc->obj.usemtl_fbx_id = 0;
while (!uc->obj.eof) {
ufbxi_check(ufbxi_obj_tokenize_line(uc));
size_t num_tokens = uc->obj.num_tokens;
if (num_tokens == 0) continue;
ufbx_string cmd = uc->obj.tokens[0];
if (ufbxi_str_equal(cmd, ufbxi_str_c("newmtl"))) {
ufbxi_check(ufbxi_obj_flush_material(uc));
ufbxi_check(ufbxi_obj_parse_material(uc));
} else if (cmd.length > 4 && !memcmp(cmd.data, "map_", 4)) {
ufbxi_check(ufbxi_obj_parse_mtl_map(uc, 4));
} else if (cmd.length == 4 && (!memcmp(cmd.data, "bump", 4) || !memcmp(cmd.data, "disp", 4) || !memcmp(cmd.data, "norm", 4))) {
ufbxi_check(ufbxi_obj_parse_mtl_map(uc, 0));
} else if (cmd.length == 1 && cmd.data[0] == '#') {
} else {
ufbxi_check(ufbxi_obj_parse_prop(uc, uc->obj.tokens[0], 1, true, NULL));
}
}
ufbxi_check(ufbxi_obj_flush_material(uc));
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_load_mtl(ufbxi_context *uc)
{
if (uc->close_fn) {
uc->close_fn(uc->read_user);
}
uc->read_fn = NULL;
uc->close_fn = NULL;
uc->read_user = NULL;
uc->data_begin = NULL;
uc->data = NULL;
uc->data_size = 0;
uc->yield_size = 0;
uc->eof = false;
uc->obj.eof = false;
if (uc->opts.obj_mtl_data.size > 0) {
uc->data_begin = uc->data = (const char*)uc->opts.obj_mtl_data.data;
uc->data_size = uc->opts.obj_mtl_data.size;
ufbxi_check(ufbxi_obj_parse_mtl(uc));
return 1;
}
ufbx_stream stream = { 0 };
bool has_stream = false;
bool needs_stream = false;
ufbx_blob stream_path = { 0 };
if (uc->opts.open_file_cb.fn) {
if (uc->opts.obj_mtl_path.length > 0) {
has_stream = ufbxi_open_file(&uc->opts.open_file_cb, &stream, uc->opts.obj_mtl_path.data, uc->opts.obj_mtl_path.length, NULL, &uc->ator_tmp, UFBX_OPEN_FILE_OBJ_MTL);
stream_path.data = uc->opts.obj_mtl_path.data;
stream_path.size = uc->opts.obj_mtl_path.length;
needs_stream = true;
if (!has_stream) {
ufbxi_check(ufbxi_warnf(UFBX_WARNING_MISSING_EXTERNAL_FILE, "Could not open .mtl file: %s", uc->opts.obj_mtl_path.data));
}
}
if (!has_stream && uc->opts.load_external_files && uc->obj.mtllib_relative_path.size > 0) {
ufbx_blob dst;
ufbxi_check(ufbxi_resolve_relative_filename(uc, (ufbxi_strblob*)&dst, (const ufbxi_strblob*)&uc->obj.mtllib_relative_path, true));
has_stream = ufbxi_open_file(&uc->opts.open_file_cb, &stream, (const char*)dst.data, dst.size, &uc->obj.mtllib_relative_path, &uc->ator_tmp, UFBX_OPEN_FILE_OBJ_MTL);
stream_path = uc->obj.mtllib_relative_path;
needs_stream = true;
if (!has_stream) {
ufbxi_check(ufbxi_warnf(UFBX_WARNING_MISSING_EXTERNAL_FILE, "Could not open .mtl file: %s", dst.data));
}
}
ufbx_string path = uc->scene.metadata.filename;
if (!has_stream && uc->opts.load_external_files && uc->opts.obj_search_mtl_by_filename && path.length > 4) {
ufbx_string ext = { path.data + path.length - 4, 4 };
if (ufbxi_match(&ext, "\\c.obj")) {
char *copy = ufbxi_push_copy(&uc->tmp, char, path.length + 1, path.data);
ufbxi_check(copy);
copy[path.length - 3] = copy[path.length - 3] == 'O' ? 'M' : 'm';
copy[path.length - 2] = copy[path.length - 2] == 'B' ? 'T' : 't';
copy[path.length - 1] = copy[path.length - 1] == 'J' ? 'L' : 'l';
has_stream = ufbxi_open_file(&uc->opts.open_file_cb, &stream, copy, path.length, NULL, &uc->ator_tmp, UFBX_OPEN_FILE_OBJ_MTL);
if (has_stream) {
ufbxi_check(ufbxi_warnf(UFBX_WARNING_IMPLICIT_MTL, "Opened .mtl file derived from .obj filename: %s", copy));
}
}
}
}
if (has_stream) {
uc->read_fn = stream.read_fn;
uc->close_fn = stream.close_fn;
uc->read_user = stream.user;
int ok = ufbxi_obj_parse_mtl(uc);
if (uc->close_fn) {
uc->close_fn(uc->read_user);
}
uc->read_fn = NULL;
uc->close_fn = NULL;
uc->read_user = NULL;
ufbxi_check(ok);
} else if (needs_stream && !uc->opts.ignore_missing_external_files) {
ufbxi_set_err_info(&uc->error, (const char*)stream_path.data, stream_path.size);
ufbxi_fail_msg("ufbxi_obj_load_mtl()", "External file not found");
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_load(ufbxi_context *uc)
{
ufbxi_check(ufbxi_obj_init(uc));
ufbxi_check(ufbxi_obj_parse_file(uc));
ufbxi_check(ufbxi_init_file_paths(uc));
ufbxi_check(ufbxi_obj_load_mtl(uc));
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_mtl_load(ufbxi_context *uc)
{
ufbxi_check(ufbxi_obj_init(uc));
ufbxi_check(ufbxi_init_file_paths(uc));
ufbxi_check(ufbxi_obj_parse_mtl(uc));
return 1;
}
#else
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_obj_load(ufbxi_context *uc)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_mtl_load(ufbxi_context *uc)
{ … }
static ufbxi_forceinline void ufbxi_obj_free(ufbxi_context *uc)
{ … }
#endif
ufbxi_pre_connection;
ufbxi_pre_node;
ufbxi_pre_mesh;
ufbxi_pre_anim_value;
ufbxi_nodiscard ufbxi_noinline static int ufbxi_pre_finalize_scene(ufbxi_context *uc)
{ … }
static ufbxi_noinline ufbx_element *ufbxi_find_element_by_fbx_id(ufbxi_context *uc, uint64_t fbx_id)
{ … }
ufbxi_forceinline static bool ufbxi_cmp_name_element_less(const ufbx_name_element *a, const ufbx_name_element *b)
{ … }
ufbxi_forceinline static bool ufbxi_cmp_name_element_less_ref(const ufbx_name_element *a, ufbx_string name, ufbx_element_type type, uint32_t key)
{ … }
ufbxi_forceinline static bool ufbxi_cmp_prop_less_ref(const ufbx_prop *a, ufbx_string name, uint32_t key)
{ … }
ufbxi_forceinline static bool ufbxi_cmp_prop_less_concat(const ufbx_prop *a, const ufbx_string *parts, size_t num_parts, uint32_t key)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_name_elements(ufbxi_context *uc, ufbx_name_element *name_elems, size_t count)
{ … }
ufbxi_noinline static bool ufbxi_cmp_node_less(ufbx_node *a, ufbx_node *b)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_node_ptrs(ufbxi_context *uc, ufbx_node **nodes, size_t count)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_cmp_tmp_material_texture_less(const ufbxi_tmp_material_texture *a, const ufbxi_tmp_material_texture *b)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_tmp_material_textures(ufbxi_context *uc, ufbxi_tmp_material_texture *mat_texs, size_t count)
{ … }
ufbx_static_assert(…);
ufbxi_forceinline static bool ufbxi_cmp_connection_less(ufbx_connection *a, ufbx_connection *b, size_t index)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_connections(ufbxi_context *uc, ufbx_connection *connections, size_t count, size_t index)
{ … }
static uint64_t ufbxi_find_attribute_fbx_id(ufbxi_context *uc, uint64_t node_fbx_id)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_resolve_connections(ufbxi_context *uc)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_add_connections_to_elements(ufbxi_context *uc)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_linearize_nodes(ufbxi_context *uc)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbx_connection_list ufbxi_find_dst_connections(ufbx_element *element, const char *prop)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbx_connection_list ufbxi_find_src_connections(ufbx_element *element, const char *prop)
{ … }
ufbxi_nodiscard static ufbx_element *ufbxi_get_element_node(ufbx_element *element)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_fetch_dst_elements(ufbxi_context *uc, void *p_dst_list, ufbx_element *element, bool search_node, bool ignore_duplicates, const char *prop, ufbx_element_type src_type)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_fetch_src_elements(ufbxi_context *uc, void *p_dst_list, ufbx_element *element, bool search_node, bool ignore_duplicates, const char *prop, ufbx_element_type dst_type)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbx_element *ufbxi_fetch_dst_element(ufbx_element *element, bool search_node, const char *prop, ufbx_element_type src_type)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbx_element *ufbxi_fetch_src_element(ufbx_element *element, bool search_node, const char *prop, ufbx_element_type dst_type)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_fetch_textures(ufbxi_context *uc, ufbx_material_texture_list *list, ufbx_element *element, bool search_node)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_fetch_mesh_materials(ufbxi_context *uc, ufbx_material_list *list, ufbx_element *element, bool search_node)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_fetch_deformers(ufbxi_context *uc, ufbx_element_list *list, ufbx_element *element, bool search_node)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_fetch_blend_keyframes(ufbxi_context *uc, ufbx_blend_keyframe_list *list, ufbx_element *element)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_fetch_texture_layers(ufbxi_context *uc, ufbx_texture_layer_list *list, ufbx_element *element)
{ … }
static ufbxi_forceinline bool ufbxi_prop_connection_less(const ufbx_connection *a, const char *prop)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbx_connection *ufbxi_find_prop_connection(const ufbx_element *element, const char *prop)
{ … }
ufbxi_forceinline static void ufbxi_patch_index_pointer(ufbxi_context *uc, uint32_t **p_index)
{ … }
ufbxi_nodiscard static bool ufbxi_cmp_anim_prop_less(const ufbx_anim_prop *a, const ufbx_anim_prop *b)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_anim_props(ufbxi_context *uc, ufbx_anim_prop *aprops, size_t count)
{ … }
ufbxi_noinline static bool ufbxi_material_texture_less(void *user, const void *va, const void *vb)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_material_textures(ufbxi_context *uc, ufbx_material_texture *textures, size_t count)
{ … }
static ufbxi_noinline bool ufbxi_bone_pose_less(void *user, const void *va, const void *vb)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbx_anim_prop *ufbxi_find_anim_prop_start(ufbx_anim_layer *layer, const ufbx_element *element)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_bone_poses(ufbxi_context *uc, ufbx_pose *pose)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_skin_weights(ufbxi_context *uc, ufbx_skin_deformer *skin)
{ … }
ufbxi_noinline static bool ufbxi_blend_keyframe_less(void *user, const void *va, const void *vb)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_blend_keyframes(ufbxi_context *uc, ufbx_blend_keyframe *keyframes, size_t count)
{ … }
ufbxi_mat_transform_fn;
static void ufbxi_mat_transform_invert_x(ufbx_vec4 *v) { … }
static void ufbxi_mat_transform_unknown_shininess(ufbx_vec4 *v) { … }
static void ufbxi_mat_transform_blender_opacity(ufbx_vec4 *v) { … }
static void ufbxi_mat_transform_blender_shininess(ufbx_vec4 *v) { … }
ufbxi_mat_transform;
ufbxi_shader_mapping_flag;
ufbxi_shader_feature_flag;
static const ufbxi_mat_transform_fn ufbxi_mat_transform_fns[] = …;
ufbx_static_assert(…);
ufbxi_shader_mapping;
ufbxi_shader_mapping_list;
#define ufbxi_mat_string(str) …
static const ufbxi_shader_mapping ufbxi_base_fbx_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_obj_fbx_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_fbx_lambert_shader_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_fbx_phong_shader_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_osl_standard_shader_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_osl_standard_shader_features[] = …;
static const ufbxi_shader_mapping ufbxi_arnold_shader_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_arnold_shader_features[] = …;
static const ufbxi_shader_mapping ufbxi_3ds_max_physical_material_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_3ds_max_physical_material_features[] = …;
static const ufbxi_shader_mapping ufbxi_gltf_material_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_3ds_max_pbr_metal_rough_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_3ds_max_pbr_spec_gloss_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_3ds_max_pbr_features[] = …;
static const ufbxi_shader_mapping ufbxi_gltf_material_features[] = …;
static const ufbxi_shader_mapping ufbxi_shaderfx_graph_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_blender_phong_shader_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_obj_pbr_mapping[] = …;
static const ufbxi_shader_mapping ufbxi_obj_features[] = …;
enum { … };
static const ufbxi_shader_mapping_list ufbxi_shader_pbr_mappings[] = …;
ufbx_static_assert(…);
enum { … };
ufbxi_noinline static void ufbxi_fetch_mapping_maps(ufbx_material *material, ufbx_material_map *maps, ufbx_material_feature_info *features,
ufbx_shader *shader, const ufbxi_shader_mapping *mappings, size_t count, ufbx_string prefix, ufbx_string prefix2, ufbx_string suffix, uint32_t flags)
{ … }
ufbxi_noinline static void ufbxi_update_factor(ufbx_material_map *factor_map, ufbx_material_map *color_map)
{ … }
ufbxi_glossiness_remap;
static const ufbxi_glossiness_remap ufbxi_glossiness_remaps[] = …;
ufbxi_noinline static void ufbxi_fetch_maps(ufbx_scene *scene, ufbx_material *material)
{ … }
ufbxi_constraint_prop_type;
ufbxi_constraint_prop;
static const ufbxi_constraint_prop ufbxi_constraint_props[] = …;
ufbxi_nodiscard ufbxi_noinline static int ufbxi_add_constraint_prop(ufbxi_context *uc, ufbx_constraint *constraint, ufbx_node *node, const char *prop)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_finalize_nurbs_basis(ufbxi_context *uc, ufbx_nurbs_basis *basis)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_finalize_lod_group(ufbxi_context *uc, ufbx_lod_group *lod)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_generate_normals(ufbxi_context *uc, ufbx_mesh *mesh)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_push_prop_prefix(ufbxi_context *uc, ufbx_string *dst, ufbx_string prefix)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_shader_texture_find_prefix(ufbxi_context *uc, ufbx_texture *texture, ufbx_shader_texture *shader)
{ … }
ufbxi_file_shader;
static const ufbxi_file_shader ufbxi_file_shaders[] = …;
ufbxi_noinline static void ufbxi_update_shader_texture(ufbx_texture *texture, ufbx_shader_texture *shader)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_finalize_shader_texture(ufbxi_context *uc, ufbx_texture *texture)
{ … }
ufbxi_noinline static void ufbxi_propagate_main_textures(ufbx_scene *scene)
{ … }
#define ufbxi_patch_empty(m_dst, m_len, m_src) …
ufbxi_nodiscard ufbxi_noinline static int ufbxi_insert_texture_file(ufbxi_context *uc, ufbx_texture *texture)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_pop_texture_files(ufbxi_context *uc)
{ … }
ufbxi_ordered_texture;
ufbxi_noinline static bool ufbxi_ordered_texture_less_texture(void *user, const void *va, const void *vb)
{ … }
ufbxi_noinline static bool ufbxi_ordered_texture_less_order(void *user, const void *va, const void *vb)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_deduplicate_textures(ufbxi_context *uc, ufbxi_buf *dst_buf, ufbxi_ordered_texture **p_dst, size_t *p_dst_count, size_t count)
{ … }
ufbxi_file_texture_fetch_state;
ufbxi_nodiscard ufbxi_noinline static int ufbxi_fetch_file_textures(ufbxi_context *uc)
{ … }
ufbxi_nodiscard ufbxi_noinline static ufbx_node *ufbxi_get_geometry_transform_node(ufbx_element *element)
{ … }
ufbxi_noinline static void ufbxi_mirror_vec3_list(const void *v_list, ufbx_mirror_axis axis, size_t stride)
{ … }
ufbxi_noinline static void ufbxi_scale_vec3_list(const void *v_list, ufbx_real scale, size_t stride)
{ … }
ufbxi_noinline static void ufbxi_transform_vec3_list(const void *v_list, const ufbx_matrix *matrix, size_t stride)
{ … }
ufbxi_noinline static void ufbxi_normalize_vec3_list(const ufbx_vec3_list *list)
{ … }
ufbxi_noinline static ufbx_transform ufbxi_get_geometry_transform(const ufbx_props *props, ufbx_node *node);
ufbxi_nodiscard ufbxi_noinline static int ufbxi_flip_attrib_winding(ufbxi_context *uc, ufbx_mesh *mesh, ufbx_uint32_list *indices, bool is_position)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_flip_winding(ufbxi_context *uc, ufbx_mesh *mesh)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_modify_geometry(ufbxi_context *uc)
{ … }
ufbxi_noinline static void ufbxi_postprocess_scene(ufbxi_context *uc)
{ … }
ufbxi_noinline static size_t ufbxi_next_path_segment(const char *data, size_t begin, size_t length)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_absolute_to_relative_path(ufbxi_context *uc, ufbxi_strblob *p_dst, const ufbxi_strblob *p_rel, const ufbxi_strblob *p_src, bool raw)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_resolve_filenames(ufbxi_context *uc, ufbxi_strblob *filename, ufbxi_strblob *absolute_filename, ufbxi_strblob *relative_filename, bool raw)
{ … }
ufbxi_noinline static bool ufbxi_file_content_less(void *user, const void *va, const void *vb)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_sort_file_contents(ufbxi_context *uc, ufbxi_file_content *content, size_t count)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_push_file_content(ufbxi_context *uc, ufbx_string *p_filename, ufbx_blob *p_data)
{ … }
ufbxi_noinline static void ufbxi_fetch_file_content(ufbxi_context *uc, ufbx_string *p_filename, ufbx_blob *p_data)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_resolve_file_content(ufbxi_context *uc)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_validate_indices(ufbxi_context *uc, ufbx_uint32_list *indices, size_t max_index)
{ … }
static bool ufbxi_material_part_usage_less(void *user, const void *va, const void *vb)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_finalize_mesh_material(ufbxi_buf *buf, ufbx_error *error, ufbx_mesh *mesh)
{ … }
ufbxi_anim_imp;
ufbxi_nodiscard ufbxi_noinline static int ufbxi_push_anim(ufbxi_context *uc, ufbx_anim **p_anim, ufbx_anim_layer **layers, size_t num_layers)
{ … }
ufbxi_nodiscard ufbxi_noinline static int ufbxi_finalize_scene(ufbxi_context *uc)
{ … }
static ufbxi_forceinline void ufbxi_add_translate(ufbx_transform *t, ufbx_vec3 v)
{ … }
static ufbxi_forceinline void ufbxi_sub_translate(ufbx_transform *t, ufbx_vec3 v)
{ … }
static ufbxi_forceinline void ufbxi_mul_scale(ufbx_transform *t, ufbx_vec3 v)
{ … }
static ufbxi_forceinline void ufbxi_mul_scale_real(ufbx_transform *t, ufbx_real v)
{ … }
static ufbxi_noinline ufbx_quat ufbxi_mul_quat(ufbx_quat a, ufbx_quat b)
{ … }
static ufbxi_forceinline void ufbxi_add_weighted_vec3(ufbx_vec3 *r, ufbx_vec3 b, ufbx_real w)
{ … }
static ufbxi_forceinline void ufbxi_add_weighted_quat(ufbx_quat *r, ufbx_quat b, ufbx_real w)
{ … }
static ufbxi_noinline void ufbxi_add_weighted_mat(ufbx_matrix *r, const ufbx_matrix *b, ufbx_real w)
{ … }
static void ufbxi_mul_rotate(ufbx_transform *t, ufbx_vec3 v, ufbx_rotation_order order)
{ … }
static void ufbxi_mul_rotate_quat(ufbx_transform *t, ufbx_quat q)
{ … }
static void ufbxi_mul_inv_rotate(ufbx_transform *t, ufbx_vec3 v, ufbx_rotation_order order)
{ … }
ufbxi_forceinline static void ufbxi_mirror_translation(ufbx_vec3 *p_vec, ufbx_mirror_axis axis)
{ … }
ufbxi_forceinline static void ufbxi_mirror_rotation(ufbx_quat *p_quat, ufbx_mirror_axis axis)
{ … }
ufbxi_noinline static ufbx_transform ufbxi_get_geometry_transform(const ufbx_props *props, ufbx_node *node)
{ … }
ufbxi_noinline static ufbx_transform ufbxi_get_transform(const ufbx_props *props, ufbx_rotation_order order, const ufbx_node *node, const ufbx_vec3 *translation_scale)
{ … }
ufbxi_noinline static ufbx_quat ufbxi_get_rotation(const ufbx_props *props, ufbx_rotation_order order, const ufbx_node *node)
{ … }
ufbxi_noinline static ufbx_vec3 ufbxi_get_scale(const ufbx_props *props, const ufbx_node *node)
{ … }
ufbxi_noinline static ufbx_transform ufbxi_get_texture_transform(const ufbx_props *props)
{ … }
ufbxi_noinline static ufbx_transform ufbxi_get_constraint_transform(const ufbx_props *props)
{ … }
ufbxi_noinline static void ufbxi_update_node(ufbx_node *node, const ufbx_transform_override *overrides, size_t num_overrides)
{ … }
ufbxi_noinline static void ufbxi_update_light(ufbx_light *light)
{ … }
ufbxi_aperture_format;
static const ufbxi_aperture_format ufbxi_aperture_formats[] = …;
ufbxi_noinline static void ufbxi_update_camera(ufbx_scene *scene, ufbx_camera *camera)
{ … }
ufbxi_noinline static void ufbxi_update_bone(ufbx_scene *scene, ufbx_bone *bone)
{ … }
ufbxi_noinline static void ufbxi_update_line_curve(ufbx_line_curve *line)
{ … }
ufbxi_noinline static void ufbxi_update_pose(ufbx_pose *pose)
{ … }
ufbxi_noinline static void ufbxi_update_skin_cluster(ufbx_skin_cluster *cluster)
{ … }
ufbxi_noinline static void ufbxi_update_blend_channel(ufbx_blend_channel *channel)
{ … }
ufbxi_noinline static void ufbxi_update_material(ufbx_scene *scene, ufbx_material *material)
{ … }
ufbxi_noinline static void ufbxi_update_texture(ufbx_texture *texture)
{ … }
ufbxi_noinline static void ufbxi_update_anim_stack(ufbx_scene *scene, ufbx_anim_stack *stack)
{ … }
ufbxi_noinline static void ufbxi_update_display_layer(ufbx_display_layer *layer)
{ … }
ufbxi_noinline static void ufbxi_find_bool3(bool *dst, ufbx_props *props, const char *name, bool default_value)
{ … }
ufbxi_noinline static void ufbxi_update_constraint(ufbx_constraint *constraint)
{ … }
ufbxi_noinline static void ufbxi_update_anim(ufbx_scene *scene)
{ … }
static ufbxi_forceinline void ufbxi_mirror_matrix_dst(ufbx_matrix *m, ufbx_mirror_axis axis)
{ … }
static ufbxi_forceinline void ufbxi_mirror_matrix_src(ufbx_matrix *m, ufbx_mirror_axis axis)
{ … }
static ufbxi_noinline void ufbxi_mirror_matrix(ufbx_matrix *m, ufbx_mirror_axis axis)
{ … }
ufbxi_noinline static void ufbxi_update_initial_clusters(ufbx_scene *scene)
{ … }
ufbxi_noinline static ufbx_coordinate_axis ufbxi_find_axis(const ufbx_props *props, const char *axis_name, const char *sign_name)
{ … }
static const ufbx_real ufbxi_time_mode_fps[] = …;
static ufbxi_noinline bool ufbxi_axis_matrix(ufbx_matrix *mat, ufbx_coordinate_axes src, ufbx_coordinate_axes dst)
{ … }
ufbxi_noinline static void ufbxi_update_adjust_transforms(ufbxi_context *uc, ufbx_scene *scene)
{ … }
ufbxi_noinline static void ufbxi_update_scene(ufbx_scene *scene, bool initial, const ufbx_transform_override *transform_overrides, size_t num_transform_overrides)
{ … }
static ufbxi_noinline void ufbxi_update_scene_metadata(ufbx_metadata *metadata)
{ … }
static const ufbx_real ufbxi_pow10_targets[] = …;
static ufbxi_noinline ufbx_real ufbxi_round_if_near(const ufbx_real *targets, size_t num_targets, ufbx_real value)
{ … }
static ufbxi_noinline void ufbxi_update_scene_settings(ufbx_scene_settings *settings)
{ … }
static ufbxi_noinline void ufbxi_update_scene_settings_obj(ufbxi_context *uc)
{ … }
#if UFBXI_FEATURE_GEOMETRY_CACHE
typedef struct {
ufbxi_refcount refcount;
ufbx_geometry_cache cache;
uint32_t magic;
bool owned_by_scene;
ufbxi_buf string_buf;
} ufbxi_geometry_cache_imp;
ufbx_static_assert(geometry_cache_imp_offset, offsetof(ufbxi_geometry_cache_imp, cache) == sizeof(ufbxi_refcount));
typedef struct {
ufbx_string name;
ufbx_string interpretation;
uint32_t sample_rate;
uint32_t start_time;
uint32_t end_time;
uint32_t current_time;
uint32_t consecutive_fails;
bool try_load;
} ufbxi_cache_tmp_channel;
typedef enum {
UFBXI_CACHE_XML_TYPE_NONE,
UFBXI_CACHE_XML_TYPE_FILE_PER_FRAME,
UFBXI_CACHE_XML_TYPE_SINGLE_FILE,
} ufbxi_cache_xml_type;
typedef enum {
UFBXI_CACHE_XML_FORMAT_NONE,
UFBXI_CACHE_XML_FORMAT_MCC,
UFBXI_CACHE_XML_FORMAT_MCX,
} ufbxi_cache_xml_format;
typedef struct {
ufbx_error error;
ufbx_string filename;
bool owned_by_scene;
bool ignore_if_not_found;
ufbx_geometry_cache_opts opts;
ufbxi_allocator *ator_tmp;
ufbxi_allocator ator_result;
ufbxi_buf result;
ufbxi_buf tmp;
ufbxi_buf tmp_stack;
ufbxi_cache_tmp_channel *channels;
size_t num_channels;
char *tmp_arr;
size_t tmp_arr_size;
ufbxi_string_pool string_pool;
ufbx_open_file_cb open_file_cb;
double frames_per_second;
ufbx_string stream_filename;
ufbx_stream stream;
bool mc_for8;
ufbx_string xml_filename;
uint32_t xml_ticks_per_frame;
ufbxi_cache_xml_type xml_type;
ufbxi_cache_xml_format xml_format;
ufbx_string channel_name;
char *name_buf;
size_t name_cap;
uint64_t file_offset;
const char *pos, *pos_end;
ufbx_geometry_cache cache;
ufbxi_geometry_cache_imp *imp;
char buffer[128];
} ufbxi_cache_context;
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_read(ufbxi_cache_context *cc, void *dst, size_t size, bool allow_eof)
{
size_t buffered = ufbxi_min_sz(ufbxi_to_size(cc->pos_end - cc->pos), size);
memcpy(dst, cc->pos, buffered);
cc->pos += buffered;
size -= buffered;
cc->file_offset += buffered;
if (size == 0) return 1;
dst = (char*)dst + buffered;
if (size >= sizeof(cc->buffer)) {
size_t num_read = cc->stream.read_fn(cc->stream.user, dst, size);
ufbxi_check_err_msg(&cc->error, num_read <= size, "IO error");
if (!allow_eof) {
ufbxi_check_err_msg(&cc->error, num_read == size, "Truncated file");
}
cc->file_offset += num_read;
size -= num_read;
dst = (char*)dst + num_read;
} else {
size_t num_read = cc->stream.read_fn(cc->stream.user, cc->buffer, sizeof(cc->buffer));
ufbxi_check_err_msg(&cc->error, num_read <= sizeof(cc->buffer), "IO error");
if (!allow_eof) {
ufbxi_check_err_msg(&cc->error, num_read >= size, "Truncated file");
}
cc->pos = cc->buffer;
cc->pos_end = cc->buffer + sizeof(cc->buffer);
memcpy(dst, cc->pos, size);
cc->pos += size;
cc->file_offset += size;
size_t num_written = ufbxi_min_sz(size, num_read);
size -= num_written;
dst = (char*)dst + num_written;
}
if (size > 0) {
memset(dst, 0, size);
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_skip(ufbxi_cache_context *cc, uint64_t size)
{
cc->file_offset += size;
uint64_t buffered = ufbxi_min64((uint64_t)(cc->pos_end - cc->pos), size);
cc->pos += buffered;
size -= buffered;
if (cc->stream.skip_fn) {
while (size >= UFBXI_MAX_SKIP_SIZE) {
size -= UFBXI_MAX_SKIP_SIZE;
ufbxi_check_err_msg(&cc->error, cc->stream.skip_fn(cc->stream.user, UFBXI_MAX_SKIP_SIZE - 1), "Truncated file");
char single_byte[1];
size_t num_read = cc->stream.read_fn(cc->stream.user, single_byte, 1);
ufbxi_check_err_msg(&cc->error, num_read <= 1, "IO error");
ufbxi_check_err_msg(&cc->error, num_read == 1, "Truncated file");
}
if (size > 0) {
ufbxi_check_err_msg(&cc->error, cc->stream.skip_fn(cc->stream.user, (size_t)size), "Truncated file");
}
} else {
char skip_buf[2048];
while (size > 0) {
size_t to_skip = (size_t)ufbxi_min64(size, sizeof(skip_buf));
size -= to_skip;
ufbxi_check_err_msg(&cc->error, cc->stream.read_fn(cc->stream.user, skip_buf, to_skip), "Truncated file");
}
}
return 1;
}
#define ufbxi_cache_mc_tag …
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_mc_read_tag(ufbxi_cache_context *cc, uint32_t *p_tag)
{
char buf[4];
ufbxi_check_err(&cc->error, ufbxi_cache_read(cc, buf, 4, true));
*p_tag = (uint32_t)(uint8_t)buf[0]<<24u | (uint32_t)(uint8_t)buf[1]<<16 | (uint32_t)(uint8_t)buf[2]<<8u | (uint32_t)(uint8_t)buf[3];
if (*p_tag == ufbxi_cache_mc_tag('F','O','R','8')) {
cc->mc_for8 = true;
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_mc_read_u32(ufbxi_cache_context *cc, uint32_t *p_value)
{
char buf[4];
ufbxi_check_err(&cc->error, ufbxi_cache_read(cc, buf, 4, false));
*p_value = (uint32_t)(uint8_t)buf[0]<<24u | (uint32_t)(uint8_t)buf[1]<<16 | (uint32_t)(uint8_t)buf[2]<<8u | (uint32_t)(uint8_t)buf[3];
if (cc->mc_for8) {
ufbxi_check_err(&cc->error, ufbxi_cache_read(cc, buf, 4, false));
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_mc_read_u64(ufbxi_cache_context *cc, uint64_t *p_value)
{
if (!cc->mc_for8) {
uint32_t v32;
ufbxi_check_err(&cc->error, ufbxi_cache_mc_read_u32(cc, &v32));
*p_value = v32;
} else {
char buf[8];
ufbxi_check_err(&cc->error, ufbxi_cache_read(cc, buf, 8, false));
uint32_t hi = (uint32_t)(uint8_t)buf[0]<<24u | (uint32_t)(uint8_t)buf[1]<<16 | (uint32_t)(uint8_t)buf[2]<<8u | (uint32_t)(uint8_t)buf[3];
uint32_t lo = (uint32_t)(uint8_t)buf[4]<<24u | (uint32_t)(uint8_t)buf[5]<<16 | (uint32_t)(uint8_t)buf[6]<<8u | (uint32_t)(uint8_t)buf[7];
*p_value = (uint64_t)hi << 32u | (uint64_t)lo;
}
return 1;
}
static const uint8_t ufbxi_cache_data_format_size[] = {
0, 4, 12, 8, 24,
};
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_load_mc(ufbxi_cache_context *cc)
{
uint32_t version = 0, time_start = 0, time_end = 0;
uint32_t count = 0, time = 0;
char skip_buf[8];
for (;;) {
uint32_t tag;
uint64_t size;
ufbxi_check_err(&cc->error, ufbxi_cache_mc_read_tag(cc, &tag));
if (tag == 0) break;
if (tag == ufbxi_cache_mc_tag('C','A','C','H') || tag == ufbxi_cache_mc_tag('M','Y','C','H')) {
continue;
}
if (cc->mc_for8) {
ufbxi_check_err(&cc->error, ufbxi_cache_read(cc, skip_buf, 4, false));
}
ufbxi_check_err(&cc->error, ufbxi_cache_mc_read_u64(cc, &size));
uint64_t begin = cc->file_offset;
size_t alignment = cc->mc_for8 ? 8 : 4;
ufbx_cache_data_format format = UFBX_CACHE_DATA_FORMAT_UNKNOWN;
switch (tag) {
case ufbxi_cache_mc_tag('F','O','R','4'): cc->mc_for8 = false; break;
case ufbxi_cache_mc_tag('F','O','R','8'): cc->mc_for8 = true; break;
case ufbxi_cache_mc_tag('V','R','S','N'): ufbxi_check_err(&cc->error, ufbxi_cache_mc_read_u32(cc, &version)); break;
case ufbxi_cache_mc_tag('S','T','I','M'):
ufbxi_check_err(&cc->error, ufbxi_cache_mc_read_u32(cc, &time_start));
time = time_start;
break;
case ufbxi_cache_mc_tag('E','T','I','M'): ufbxi_check_err(&cc->error, ufbxi_cache_mc_read_u32(cc, &time_end)); break;
case ufbxi_cache_mc_tag('T','I','M','E'): ufbxi_check_err(&cc->error, ufbxi_cache_mc_read_u32(cc, &time)); break;
case ufbxi_cache_mc_tag('C','H','N','M'): {
ufbxi_check_err(&cc->error, size > 0 && size < SIZE_MAX);
size_t length = (size_t)size - 1;
size_t padded_length = ((size_t)size + alignment - 1) & ~(alignment - 1);
ufbxi_check_err(&cc->error, ufbxi_grow_array(cc->ator_tmp, &cc->name_buf, &cc->name_cap, padded_length));
ufbxi_check_err(&cc->error, ufbxi_cache_read(cc, cc->name_buf, padded_length, false));
cc->channel_name.data = cc->name_buf;
cc->channel_name.length = length;
ufbxi_check_err(&cc->error, ufbxi_push_string_place_str(&cc->string_pool, &cc->channel_name, false));
} break;
case ufbxi_cache_mc_tag('S','I','Z','E'): ufbxi_check_err(&cc->error, ufbxi_cache_mc_read_u32(cc, &count)); break;
case ufbxi_cache_mc_tag('F','V','C','A'): format = UFBX_CACHE_DATA_FORMAT_VEC3_FLOAT; break;
case ufbxi_cache_mc_tag('D','V','C','A'): format = UFBX_CACHE_DATA_FORMAT_VEC3_DOUBLE; break;
case ufbxi_cache_mc_tag('F','B','C','A'): format = UFBX_CACHE_DATA_FORMAT_REAL_FLOAT; break;
case ufbxi_cache_mc_tag('D','B','C','A'): format = UFBX_CACHE_DATA_FORMAT_REAL_DOUBLE; break;
case ufbxi_cache_mc_tag('D','B','L','A'): format = UFBX_CACHE_DATA_FORMAT_REAL_DOUBLE; break;
default: ufbxi_fail_err(&cc->error, "Unknown tag");
}
if (format != UFBX_CACHE_DATA_FORMAT_UNKNOWN) {
ufbx_cache_frame *frame = ufbxi_push_zero(&cc->tmp_stack, ufbx_cache_frame, 1);
ufbxi_check_err(&cc->error, frame);
uint32_t elem_size = ufbxi_cache_data_format_size[format];
uint64_t total_size = (uint64_t)elem_size * (uint64_t)count;
ufbxi_check_err(&cc->error, size >= elem_size * count);
frame->channel = cc->channel_name;
frame->time = (double)time * (1.0/6000.0);
frame->filename = cc->stream_filename;
frame->data_format = format;
frame->data_encoding = UFBX_CACHE_DATA_ENCODING_BIG_ENDIAN;
frame->data_offset = cc->file_offset;
frame->data_count = count;
frame->data_element_bytes = elem_size;
frame->data_total_bytes = total_size;
frame->file_format = UFBX_CACHE_FILE_FORMAT_MC;
uint64_t end = begin + ((size + alignment - 1) & ~(uint64_t)(alignment - 1));
ufbxi_check_err(&cc->error, end >= cc->file_offset);
uint64_t left = end - cc->file_offset;
ufbxi_check_err(&cc->error, ufbxi_cache_skip(cc, left));
}
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_load_pc2(ufbxi_cache_context *cc)
{
char header[32];
ufbxi_check_err(&cc->error, ufbxi_cache_read(cc, header, sizeof(header), false));
uint32_t version = ufbxi_read_u32(header + 12);
uint32_t num_points = ufbxi_read_u32(header + 16);
double start_frame = ufbxi_read_f32(header + 20);
double frames_per_sample = ufbxi_read_f32(header + 24);
uint32_t num_samples = ufbxi_read_u32(header + 28);
(void)version;
ufbx_cache_frame *frames = ufbxi_push_zero(&cc->tmp_stack, ufbx_cache_frame, num_samples);
ufbxi_check_err(&cc->error, frames);
uint64_t total_points = (uint64_t)num_points * (uint64_t)num_samples;
ufbxi_check_err(&cc->error, total_points < UINT64_MAX / 12);
uint64_t offset = cc->file_offset;
if (total_points > 0) {
char last_byte[1];
ufbxi_check_err(&cc->error, ufbxi_cache_skip(cc, total_points * 12 - 1));
ufbxi_check_err(&cc->error, ufbxi_cache_read(cc, last_byte, 1, false));
}
for (uint32_t i = 0; i < num_samples; i++) {
ufbx_cache_frame *frame = &frames[i];
double sample_frame = start_frame + (double)i * frames_per_sample;
frame->channel = cc->channel_name;
frame->time = sample_frame / cc->frames_per_second;
frame->filename = cc->stream_filename;
frame->data_format = UFBX_CACHE_DATA_FORMAT_VEC3_FLOAT;
frame->data_encoding = UFBX_CACHE_DATA_ENCODING_LITTLE_ENDIAN;
frame->data_offset = offset;
frame->data_count = num_points;
frame->data_element_bytes = 12;
frame->data_total_bytes = num_points * 12;
frame->file_format = UFBX_CACHE_FILE_FORMAT_PC2;
offset += num_points * 12;
}
return 1;
}
static ufbxi_noinline bool ufbxi_tmp_channel_less(void *user, const void *va, const void *vb)
{
(void)user;
const ufbxi_cache_tmp_channel *a = (const ufbxi_cache_tmp_channel *)va, *b = (const ufbxi_cache_tmp_channel *)vb;
return ufbxi_str_less(a->name, b->name);
}
static ufbxi_noinline int ufbxi_cache_sort_tmp_channels(ufbxi_cache_context *cc, ufbxi_cache_tmp_channel *channels, size_t count)
{
ufbxi_check_err(&cc->error, ufbxi_grow_array(cc->ator_tmp, &cc->tmp_arr, &cc->tmp_arr_size, count * sizeof(ufbxi_cache_tmp_channel)));
ufbxi_stable_sort(sizeof(ufbxi_cache_tmp_channel), 16, channels, cc->tmp_arr, count, &ufbxi_tmp_channel_less, NULL);
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_load_xml_imp(ufbxi_cache_context *cc, ufbxi_xml_document *doc)
{
cc->xml_ticks_per_frame = 250;
cc->xml_filename = cc->stream_filename;
ufbxi_xml_tag *tag_root = ufbxi_xml_find_child(doc->root, "Autodesk_Cache_File");
if (tag_root) {
ufbxi_xml_tag *tag_type = ufbxi_xml_find_child(tag_root, "cacheType");
ufbxi_xml_tag *tag_fps = ufbxi_xml_find_child(tag_root, "cacheTimePerFrame");
ufbxi_xml_tag *tag_channels = ufbxi_xml_find_child(tag_root, "Channels");
size_t num_extra = 0;
ufbxi_for(ufbxi_xml_tag, tag, tag_root->children, tag_root->num_children) {
if (tag->num_children != 1) continue;
if (strcmp(tag->name.data, "extra") != 0) continue;
ufbx_string *extra = ufbxi_push(&cc->tmp_stack, ufbx_string, 1);
ufbxi_check_err(&cc->error, extra);
*extra = tag->children[0].text;
ufbxi_check_err(&cc->error, ufbxi_push_string_place_str(&cc->string_pool, extra, false));
num_extra++;
}
cc->cache.extra_info.count = num_extra;
cc->cache.extra_info.data = ufbxi_push_pop(&cc->result, &cc->tmp_stack, ufbx_string, num_extra);
ufbxi_check_err(&cc->error, cc->cache.extra_info.data);
if (tag_type) {
ufbxi_xml_attrib *type = ufbxi_xml_find_attrib(tag_type, "Type");
ufbxi_xml_attrib *format = ufbxi_xml_find_attrib(tag_type, "Format");
if (type) {
if (!strcmp(type->value.data, "OneFilePerFrame")) {
cc->xml_type = UFBXI_CACHE_XML_TYPE_FILE_PER_FRAME;
} else if (!strcmp(type->value.data, "OneFile")) {
cc->xml_type = UFBXI_CACHE_XML_TYPE_SINGLE_FILE;
}
}
if (format) {
if (!strcmp(format->value.data, "mcc")) {
cc->xml_format = UFBXI_CACHE_XML_FORMAT_MCC;
} else if (!strcmp(format->value.data, "mcx")) {
cc->xml_format = UFBXI_CACHE_XML_FORMAT_MCX;
}
}
}
if (tag_fps) {
ufbxi_xml_attrib *fps = ufbxi_xml_find_attrib(tag_fps, "TimePerFrame");
if (fps) {
int value = atoi(fps->value.data);
if (value > 0) {
cc->xml_ticks_per_frame = (uint32_t)value;
}
}
}
if (tag_channels) {
cc->channels = ufbxi_push_zero(&cc->tmp, ufbxi_cache_tmp_channel, tag_channels->num_children);
ufbxi_check_err(&cc->error, cc->channels);
ufbxi_for(ufbxi_xml_tag, tag, tag_channels->children, tag_channels->num_children) {
ufbxi_xml_attrib *name = ufbxi_xml_find_attrib(tag, "ChannelName");
ufbxi_xml_attrib *type = ufbxi_xml_find_attrib(tag, "ChannelType");
ufbxi_xml_attrib *interpretation = ufbxi_xml_find_attrib(tag, "ChannelInterpretation");
if (!(name && type && interpretation)) continue;
ufbxi_cache_tmp_channel *channel = &cc->channels[cc->num_channels++];
channel->name = name->value;
channel->interpretation = interpretation->value;
ufbxi_check_err(&cc->error, ufbxi_push_string_place_str(&cc->string_pool, &channel->name, false));
ufbxi_check_err(&cc->error, ufbxi_push_string_place_str(&cc->string_pool, &channel->interpretation, false));
ufbxi_xml_attrib *sampling_rate = ufbxi_xml_find_attrib(tag, "SamplingRate");
ufbxi_xml_attrib *start_time = ufbxi_xml_find_attrib(tag, "StartTime");
ufbxi_xml_attrib *end_time = ufbxi_xml_find_attrib(tag, "EndTime");
if (sampling_rate && start_time && end_time) {
channel->sample_rate = (uint32_t)atoi(sampling_rate->value.data);
channel->start_time = (uint32_t)atoi(start_time->value.data);
channel->end_time = (uint32_t)atoi(end_time->value.data);
channel->current_time = channel->start_time;
channel->try_load = true;
}
}
}
}
ufbxi_check_err(&cc->error, ufbxi_cache_sort_tmp_channels(cc, cc->channels, cc->num_channels));
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_load_xml(ufbxi_cache_context *cc)
{
ufbxi_xml_load_opts opts = { 0 };
opts.ator = cc->ator_tmp;
opts.read_fn = cc->stream.read_fn;
opts.read_user = cc->stream.user;
opts.prefix = cc->pos;
opts.prefix_length = ufbxi_to_size(cc->pos_end - cc->pos);
ufbxi_xml_document *doc = ufbxi_load_xml(&opts, &cc->error);
ufbxi_check_err(&cc->error, doc);
int xml_ok = ufbxi_cache_load_xml_imp(cc, doc);
ufbxi_free_xml(doc);
ufbxi_check_err(&cc->error, xml_ok);
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_load_file(ufbxi_cache_context *cc, ufbx_string filename)
{
cc->stream_filename = filename;
ufbxi_check_err(&cc->error, ufbxi_push_string_place_str(&cc->string_pool, &cc->stream_filename, false));
size_t magic_len = cc->stream.read_fn(cc->stream.user, cc->buffer, 16);
ufbxi_check_err_msg(&cc->error, magic_len <= 16, "IO error");
ufbxi_check_err_msg(&cc->error, magic_len == 16, "Truncated file");
cc->pos = cc->buffer;
cc->pos_end = cc->buffer + 16;
cc->file_offset = 0;
if (!memcmp(cc->buffer, "POINTCACHE2", 11)) {
ufbxi_check_err(&cc->error, ufbxi_cache_load_pc2(cc));
} else if (!memcmp(cc->buffer, "FOR4", 4) || !memcmp(cc->buffer, "FOR8", 4)) {
ufbxi_check_err(&cc->error, ufbxi_cache_load_mc(cc));
} else {
ufbxi_check_err(&cc->error, ufbxi_cache_load_xml(cc));
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_try_open_file(ufbxi_cache_context *cc, ufbx_string filename, const ufbx_blob *original_filename, bool *p_found)
{
memset(&cc->stream, 0, sizeof(cc->stream));
ufbxi_regression_assert(strlen(filename.data) == filename.length);
if (!ufbxi_open_file(&cc->open_file_cb, &cc->stream, filename.data, filename.length, original_filename, cc->ator_tmp, UFBX_OPEN_FILE_GEOMETRY_CACHE)) {
return 1;
}
int ok = ufbxi_cache_load_file(cc, filename);
*p_found = true;
if (cc->stream.close_fn) {
cc->stream.close_fn(cc->stream.user);
}
return ok;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_cache_load_frame_files(ufbxi_cache_context *cc)
{
if (cc->xml_filename.length == 0) return 1;
const char *extension = NULL;
switch (cc->xml_format) {
case UFBXI_CACHE_XML_FORMAT_MCC: extension = "mc"; break;
case UFBXI_CACHE_XML_FORMAT_MCX: extension = "mcx"; break;
default: return 1;
}
size_t name_buf_len = cc->xml_filename.length + 64;
char *name_buf = ufbxi_push(&cc->tmp, char, name_buf_len);
ufbxi_check_err(&cc->error, name_buf);
size_t prefix_len = cc->xml_filename.length;
for (size_t i = prefix_len; i > 0; --i) {
if (cc->xml_filename.data[i - 1] == '.') {
prefix_len = i - 1;
break;
}
}
memcpy(name_buf, cc->xml_filename.data, prefix_len);
char *suffix_data = name_buf + prefix_len;
size_t suffix_len = name_buf_len - prefix_len;
ufbx_string filename;
filename.data = name_buf;
if (cc->xml_type == UFBXI_CACHE_XML_TYPE_SINGLE_FILE) {
filename.length = prefix_len + (size_t)ufbxi_snprintf(suffix_data, suffix_len, ".%s", extension);
bool found = false;
ufbxi_check_err(&cc->error, ufbxi_cache_try_open_file(cc, filename, NULL, &found));
} else if (cc->xml_type == UFBXI_CACHE_XML_TYPE_FILE_PER_FRAME) {
uint32_t lowest_time = 0;
for (;;) {
uint32_t time = UINT32_MAX;
ufbxi_for(ufbxi_cache_tmp_channel, chan, cc->channels, cc->num_channels) {
if (!chan->try_load || chan->consecutive_fails > 10) continue;
uint32_t sample_rate = chan->sample_rate ? chan->sample_rate : cc->xml_ticks_per_frame;
if (chan->current_time < lowest_time) {
uint32_t delta = (lowest_time - chan->current_time - 1) / sample_rate;
chan->current_time += delta * sample_rate;
if (UINT32_MAX - chan->current_time >= sample_rate) {
chan->current_time += sample_rate;
} else {
chan->try_load = false;
continue;
}
}
if (chan->current_time <= chan->end_time) {
time = ufbxi_min32(time, chan->current_time);
}
}
if (time == UINT32_MAX) break;
uint32_t frame = time / cc->xml_ticks_per_frame;
uint32_t tick = time % cc->xml_ticks_per_frame;
if (tick == 0) {
filename.length = prefix_len + (size_t)ufbxi_snprintf(suffix_data, suffix_len, "Frame%u.%s", frame, extension);
} else {
filename.length = prefix_len + (size_t)ufbxi_snprintf(suffix_data, suffix_len, "Frame%uTick%u.%s", frame, tick, extension);
}
bool found = false;
ufbxi_check_err(&cc->error, ufbxi_cache_try_open_file(cc, filename, NULL, &found));
ufbxi_for(ufbxi_cache_tmp_channel, chan, cc->channels, cc->num_channels) {
if (chan->current_time == time) {
chan->consecutive_fails = found ? 0 : chan->consecutive_fails + 1;
}
}
lowest_time = time + 1;
}
}
return 1;
}
static ufbxi_noinline bool ufbxi_cmp_cache_frame_less(void *user, const void *va, const void *vb)
{
(void)user;
const ufbx_cache_frame *a = (const ufbx_cache_frame *)va, *b = (const ufbx_cache_frame *)vb;
if (a->channel.data != b->channel.data) {
ufbxi_regression_assert(!ufbxi_str_equal(a->channel, b->channel));
return ufbxi_str_less(a->channel, b->channel);
}
return a->time < b->time;
}
static ufbxi_noinline int ufbxi_cache_sort_frames(ufbxi_cache_context *cc, ufbx_cache_frame *frames, size_t count)
{
ufbxi_check_err(&cc->error, ufbxi_grow_array(cc->ator_tmp, &cc->tmp_arr, &cc->tmp_arr_size, count * sizeof(ufbx_cache_frame)));
ufbxi_stable_sort(sizeof(ufbx_cache_frame), 16, frames, cc->tmp_arr, count, &ufbxi_cmp_cache_frame_less, NULL);
return 1;
}
typedef struct {
ufbx_cache_interpretation interpretation;
const char *pattern;
} ufbxi_cache_interpretation_name;
static const ufbxi_cache_interpretation_name ufbxi_cache_interpretation_names[] = {
{ UFBX_CACHE_INTERPRETATION_POINTS, "\\cpoints?" },
{ UFBX_CACHE_INTERPRETATION_VERTEX_POSITION, "\\cpositions?" },
{ UFBX_CACHE_INTERPRETATION_VERTEX_NORMAL, "\\cnormals?" },
};
static ufbxi_noinline int ufbxi_cache_setup_channels(ufbxi_cache_context *cc)
{
ufbxi_cache_tmp_channel *tmp_chan = cc->channels, *tmp_end = ufbxi_add_ptr(tmp_chan, cc->num_channels);
size_t begin = 0, num_channels = 0;
while (begin < cc->cache.frames.count) {
ufbx_cache_frame *frame = &cc->cache.frames.data[begin];
size_t end = begin + 1;
while (end < cc->cache.frames.count && cc->cache.frames.data[end].channel.data == frame->channel.data) {
end++;
}
ufbx_cache_channel *chan = ufbxi_push_zero(&cc->tmp_stack, ufbx_cache_channel, 1);
ufbxi_check_err(&cc->error, chan);
chan->name = frame->channel;
chan->interpretation_name = ufbx_empty_string;
chan->frames.data = frame;
chan->frames.count = end - begin;
while (tmp_chan < tmp_end && ufbxi_str_less(tmp_chan->name, chan->name)) {
tmp_chan++;
}
if (tmp_chan < tmp_end && ufbxi_str_equal(tmp_chan->name, chan->name)) {
chan->interpretation_name = tmp_chan->interpretation;
}
if (frame->file_format == UFBX_CACHE_FILE_FORMAT_PC2) {
chan->interpretation = UFBX_CACHE_INTERPRETATION_VERTEX_POSITION;
} else {
ufbxi_for(const ufbxi_cache_interpretation_name, name, ufbxi_cache_interpretation_names, ufbxi_arraycount(ufbxi_cache_interpretation_names)) {
if (ufbxi_match(&chan->interpretation_name, name->pattern)) {
chan->interpretation = name->interpretation;
break;
}
}
}
ufbx_mirror_axis mirror_axis = UFBX_MIRROR_AXIS_NONE;
ufbx_real scale_factor = 1.0f;
if (chan->interpretation != UFBX_CACHE_INTERPRETATION_UNKNOWN) {
mirror_axis = cc->opts.mirror_axis;
if (cc->opts.use_scale_factor) {
scale_factor = cc->opts.scale_factor;
}
}
chan->mirror_axis = mirror_axis;
chan->scale_factor = scale_factor;
ufbxi_for_list(ufbx_cache_frame, f, chan->frames) {
f->mirror_axis = mirror_axis;
f->scale_factor = scale_factor;
}
num_channels++;
begin = end;
}
cc->cache.channels.data = ufbxi_push_pop(&cc->result, &cc->tmp_stack, ufbx_cache_channel, num_channels);
ufbxi_check_err(&cc->error, cc->cache.channels.data);
cc->cache.channels.count = num_channels;
return 1;
}
static ufbxi_noinline int ufbxi_cache_load_imp(ufbxi_cache_context *cc, ufbx_string filename)
{
cc->tmp.ator = cc->ator_tmp;
cc->tmp_stack.ator = cc->ator_tmp;
cc->channel_name.data = ufbxi_empty_char;
if (!cc->open_file_cb.fn) {
cc->open_file_cb.fn = ufbx_default_open_file;
}
char *filename_data = ufbxi_push(&cc->tmp, char, filename.length + 1);
ufbxi_check_err(&cc->error, filename_data);
memcpy(filename_data, filename.data, filename.length);
filename_data[filename.length] = '\0';
ufbx_string filename_copy = { filename_data, filename.length };
bool found = false;
ufbxi_check_err(&cc->error, ufbxi_cache_try_open_file(cc, filename_copy, NULL, &found));
if (!found) {
ufbxi_set_err_info(&cc->error, filename.data, filename.length);
ufbxi_fail_err_msg(&cc->error, "open_file_fn()", "File not found");
}
cc->cache.root_filename = cc->stream_filename;
ufbxi_check_err(&cc->error, ufbxi_cache_load_frame_files(cc));
size_t num_frames = cc->tmp_stack.num_items;
cc->cache.frames.count = num_frames;
cc->cache.frames.data = ufbxi_push_pop(&cc->result, &cc->tmp_stack, ufbx_cache_frame, num_frames);
ufbxi_check_err(&cc->error, cc->cache.frames.data);
ufbxi_check_err(&cc->error, ufbxi_cache_sort_frames(cc, cc->cache.frames.data, cc->cache.frames.count));
ufbxi_check_err(&cc->error, ufbxi_cache_setup_channels(cc));
cc->imp = ufbxi_push(&cc->result, ufbxi_geometry_cache_imp, 1);
ufbxi_check_err(&cc->error, cc->imp);
ufbxi_init_ref(&cc->imp->refcount, UFBXI_CACHE_IMP_MAGIC, NULL);
cc->imp->cache = cc->cache;
cc->imp->magic = UFBXI_CACHE_IMP_MAGIC;
cc->imp->owned_by_scene = cc->owned_by_scene;
cc->imp->refcount.ator = cc->ator_result;
cc->imp->refcount.buf = cc->result;
cc->imp->refcount.buf.ator = &cc->imp->refcount.ator;
cc->imp->string_buf = cc->string_pool.buf;
cc->imp->string_buf.ator = &cc->imp->refcount.ator;
return 1;
}
ufbxi_noinline static ufbx_geometry_cache *ufbxi_cache_load(ufbxi_cache_context *cc, ufbx_string filename)
{
int ok = ufbxi_cache_load_imp(cc, filename);
ufbxi_buf_free(&cc->tmp);
ufbxi_buf_free(&cc->tmp_stack);
ufbxi_free(cc->ator_tmp, char, cc->name_buf, cc->name_cap);
ufbxi_free(cc->ator_tmp, char, cc->tmp_arr, cc->tmp_arr_size);
if (!cc->owned_by_scene) {
ufbxi_string_pool_temp_free(&cc->string_pool);
ufbxi_free_ator(cc->ator_tmp);
}
if (ok) {
return &cc->imp->cache;
} else {
ufbxi_fix_error_type(&cc->error, "Failed to load geometry cache");
if (!cc->owned_by_scene) {
ufbxi_buf_free(&cc->string_pool.buf);
ufbxi_free_ator(&cc->ator_result);
}
return NULL;
}
}
ufbxi_noinline static ufbx_geometry_cache *ufbxi_load_geometry_cache(ufbx_string filename, const ufbx_geometry_cache_opts *user_opts, ufbx_error *p_error)
{
ufbx_geometry_cache_opts opts;
if (user_opts) {
opts = *user_opts;
} else {
memset(&opts, 0, sizeof(opts));
}
ufbxi_cache_context cc = { UFBX_ERROR_NONE };
ufbxi_allocator ator_tmp = { 0 };
ufbxi_init_ator(&cc.error, &ator_tmp, &opts.temp_allocator, "temp");
ufbxi_init_ator(&cc.error, &cc.ator_result, &opts.result_allocator, "result");
cc.ator_tmp = &ator_tmp;
cc.opts = opts;
cc.open_file_cb = opts.open_file_cb;
cc.string_pool.error = &cc.error;
ufbxi_map_init(&cc.string_pool.map, cc.ator_tmp, &ufbxi_map_cmp_string, NULL);
cc.string_pool.buf.ator = &cc.ator_result;
cc.string_pool.buf.unordered = true;
cc.string_pool.initial_size = 64;
cc.result.ator = &cc.ator_result;
cc.frames_per_second = opts.frames_per_second > 0.0 ? opts.frames_per_second : 30.0;
ufbx_geometry_cache *cache = ufbxi_cache_load(&cc, filename);
if (p_error) {
if (cache) {
ufbxi_clear_error(p_error);
} else {
*p_error = cc.error;
}
}
return cache;
}
static ufbxi_noinline void ufbxi_free_geometry_cache_imp(ufbxi_geometry_cache_imp *imp)
{
ufbx_assert(imp->magic == UFBXI_CACHE_IMP_MAGIC);
ufbxi_buf_free(&imp->string_buf);
}
#else
ufbxi_geometry_cache_imp;
static ufbxi_noinline ufbx_geometry_cache *ufbxi_load_geometry_cache(ufbx_string filename, const ufbx_geometry_cache_opts *user_opts, ufbx_error *p_error)
{ … }
static ufbxi_forceinline void ufbxi_free_geometry_cache_imp(ufbxi_geometry_cache_imp *imp)
{ … }
#endif
ufbxi_external_file_type;
ufbxi_external_file;
static bool ufbxi_less_external_file(void *user, const void *va, const void *vb)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_load_external_cache(ufbxi_context *uc, ufbxi_external_file *file)
{ … }
static ufbxi_noinline ufbxi_external_file *ufbxi_find_external_file(ufbxi_external_file *files, size_t num_files, ufbxi_external_file_type type, const char *name)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_load_external_files(ufbxi_context *uc)
{ … }
static ufbxi_noinline void ufbxi_transform_to_axes(ufbxi_context *uc, ufbx_coordinate_axes dst_axes)
{ … }
static ufbxi_noinline int ufbxi_scale_units(ufbxi_context *uc, ufbx_real target_meters)
{ … }
static ufbxi_forceinline double ufbxi_find_cubic_bezier_t(double p1, double p2, double x0)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_evaluate_skinning(ufbx_scene *scene, ufbx_error *error, ufbxi_buf *buf_result, ufbxi_buf *buf_tmp,
double time, bool load_caches, ufbx_geometry_cache_data_opts *cache_opts)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_fixup_opts_string(ufbxi_context *uc, ufbx_string *str, bool push)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_resolve_warning_elements(ufbxi_context *uc)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_load_imp(ufbxi_context *uc)
{ … }
static ufbxi_noinline void ufbxi_free_temp(ufbxi_context *uc)
{ … }
static ufbxi_noinline void ufbxi_free_result(ufbxi_context *uc)
{ … }
static ufbxi_noinline ufbx_scene *ufbxi_load(ufbxi_context *uc, const ufbx_load_opts *user_opts, ufbx_error *p_error)
{ … }
static ufbxi_noinline ufbx_scene *ufbxi_load_not_found(const char *filename, size_t filename_len, ufbx_error *p_error)
{ … }
static ufbxi_forceinline bool ufbxi_override_less_than_prop(const ufbx_prop_override *over, uint32_t element_id, const ufbx_prop *prop)
{ … }
static ufbxi_forceinline bool ufbxi_override_equals_to_prop(const ufbx_prop_override *over, uint32_t element_id, const ufbx_prop *prop)
{ … }
static ufbxi_noinline bool ufbxi_find_prop_override(const ufbx_prop_override_list *overrides, uint32_t element_id, ufbx_prop *prop)
{ … }
static ufbxi_noinline ufbx_prop_override_list ufbxi_find_element_prop_overrides(const ufbx_prop_override_list *overrides, uint32_t element_id)
{ … }
ufbxi_anim_layer_combine_ctx;
static ufbxi_noinline double ufbxi_pow_abs(double v, double e)
{ … }
static ufbxi_noinline void ufbxi_combine_anim_layer(ufbxi_anim_layer_combine_ctx *ctx, ufbx_anim_layer *layer, ufbx_real weight, const char *prop_name, ufbx_vec3 *result, const ufbx_vec3 *value)
ufbxi_recursive_function_void(ufbxi_combine_anim_layer, (ctx, layer, weight, prop_name, result, value), 2,
(ufbxi_anim_layer_combine_ctx *ctx, ufbx_anim_layer *layer, ufbx_real weight, const char *prop_name, ufbx_vec3 *result, const ufbx_vec3 *value))
{ … }
static ufbxi_forceinline bool ufbxi_anim_layer_might_contain_id(const ufbx_anim_layer *layer, uint32_t id)
{ … }
static ufbxi_noinline void ufbxi_evaluate_props(const ufbx_anim *anim, const ufbx_element *element, double time, ufbx_prop *props, size_t num_props)
{ … }
static ufbxi_noinline void ufbxi_evaluate_connected_prop(ufbx_prop *prop, const ufbx_anim *anim, const ufbx_element *element, const char *name, double time)
ufbxi_recursive_function_void(ufbxi_evaluate_connected_prop, (prop, anim, element, name, time), 3,
(ufbx_prop *prop, const ufbx_anim *anim, const ufbx_element *element, const char *name, double time))
{ … }
ufbxi_prop_iter;
static ufbxi_noinline void ufbxi_init_prop_iter_slow(ufbxi_prop_iter *iter, const ufbx_anim *anim, const ufbx_element *element)
{ … }
static ufbxi_forceinline void ufbxi_init_prop_iter(ufbxi_prop_iter *iter, const ufbx_anim *anim, const ufbx_element *element)
{ … }
static ufbxi_noinline const ufbx_prop *ufbxi_next_prop_slow(ufbxi_prop_iter *iter)
{ … }
static ufbxi_forceinline const ufbx_prop *ufbxi_next_prop(ufbxi_prop_iter *iter)
{ … }
static ufbxi_noinline ufbx_props ufbxi_evaluate_selected_props(const ufbx_anim *anim, const ufbx_element *element, double time, ufbx_prop *props, const char *const *prop_names, size_t max_props)
{ … }
#if UFBXI_FEATURE_SCENE_EVALUATION
typedef struct {
char *src_element;
char *dst_element;
ufbxi_scene_imp *src_imp;
ufbx_scene src_scene;
ufbx_evaluate_opts opts;
ufbx_anim *anim;
double time;
ufbx_error error;
ufbxi_allocator ator_result;
ufbxi_allocator ator_tmp;
ufbxi_buf result;
ufbxi_buf tmp;
ufbx_scene scene;
ufbxi_scene_imp *scene_imp;
} ufbxi_eval_context;
static ufbxi_forceinline ufbx_element *ufbxi_translate_element(ufbxi_eval_context *ec, void *elem)
{
return elem ? (ufbx_element*)(ec->dst_element + ((char*)elem - ec->src_element)) : NULL;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_translate_element_list(ufbxi_eval_context *ec, void *p_list)
{
ufbx_element_list *list = (ufbx_element_list*)p_list;
size_t count = list->count;
ufbx_element **src = list->data;
ufbx_element **dst = ufbxi_push(&ec->result, ufbx_element*, count);
ufbxi_check_err(&ec->error, dst);
list->data = dst;
for (size_t i = 0; i < count; i++) {
dst[i] = ufbxi_translate_element(ec, src[i]);
}
return 1;
}
static ufbxi_noinline void ufbxi_translate_maps(ufbxi_eval_context *ec, ufbx_material_map *maps, size_t count)
{
ufbxi_nounroll ufbxi_for(ufbx_material_map, map, maps, count) {
map->texture = (ufbx_texture*)ufbxi_translate_element(ec, map->texture);
}
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_translate_anim(ufbxi_eval_context *ec, ufbx_anim **p_anim)
{
ufbx_anim *anim = ufbxi_push_copy(&ec->result, ufbx_anim, 1, *p_anim);
ufbxi_check_err(&ec->error, anim);
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &anim->layers));
*p_anim = anim;
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_evaluate_imp(ufbxi_eval_context *ec)
{
ec->scene = ec->src_scene;
size_t num_elements = ec->scene.elements.count;
char *element_data = (char*)ufbxi_push(&ec->result, uint64_t, ec->scene.metadata.element_buffer_size/8);
ufbxi_check_err(&ec->error, element_data);
ec->scene.elements.data = ufbxi_push(&ec->result, ufbx_element*, num_elements);
ufbxi_check_err(&ec->error, ec->scene.elements.data);
ec->src_element = (char*)ec->src_scene.elements.data[0];
ec->dst_element = element_data;
for (size_t i = 0; i < UFBX_ELEMENT_TYPE_COUNT; i++) {
ec->scene.elements_by_type[i].data = ufbxi_push(&ec->result, ufbx_element*, ec->scene.elements_by_type[i].count);
ufbxi_check_err(&ec->error, ec->scene.elements_by_type[i].data);
}
size_t num_connections = ec->scene.connections_dst.count;
ec->scene.connections_src.data = ufbxi_push(&ec->result, ufbx_connection, num_connections);
ec->scene.connections_dst.data = ufbxi_push(&ec->result, ufbx_connection, num_connections);
ufbxi_check_err(&ec->error, ec->scene.connections_src.data);
ufbxi_check_err(&ec->error, ec->scene.connections_dst.data);
for (size_t i = 0; i < num_connections; i++) {
ufbx_connection *src = &ec->scene.connections_src.data[i];
ufbx_connection *dst = &ec->scene.connections_dst.data[i];
*src = ec->src_scene.connections_src.data[i];
*dst = ec->src_scene.connections_dst.data[i];
src->src = ufbxi_translate_element(ec, src->src);
src->dst = ufbxi_translate_element(ec, src->dst);
dst->src = ufbxi_translate_element(ec, dst->src);
dst->dst = ufbxi_translate_element(ec, dst->dst);
}
ec->scene.elements_by_name.data = ufbxi_push(&ec->result, ufbx_name_element, num_elements);
ufbxi_check_err(&ec->error, ec->scene.elements_by_name.data);
ec->scene.root_node = (ufbx_node*)ufbxi_translate_element(ec, ec->scene.root_node);
ufbxi_check_err(&ec->error, ufbxi_translate_anim(ec, &ec->scene.anim));
for (size_t i = 0; i < num_elements; i++) {
ufbx_element *src = ec->src_scene.elements.data[i];
ufbx_element *dst = ufbxi_translate_element(ec, src);
size_t size = ufbx_element_type_size[src->type];
ufbx_assert(size > 0);
memcpy(dst, src, size);
ec->scene.elements.data[i] = dst;
ec->scene.elements_by_type[src->type].data[src->typed_id] = dst;
dst->connections_src.data = ec->scene.connections_src.data + (dst->connections_src.data - ec->src_scene.connections_src.data);
dst->connections_dst.data = ec->scene.connections_dst.data + (dst->connections_dst.data - ec->src_scene.connections_dst.data);
if (dst->instances.count > 0) {
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &dst->instances));
}
ufbx_name_element named = ec->src_scene.elements_by_name.data[i];
named.element = ufbxi_translate_element(ec, named.element);
ec->scene.elements_by_name.data[i] = named;
}
ufbxi_for_ptr_list(ufbx_node, p_node, ec->scene.nodes) {
ufbx_node *node = *p_node;
node->parent = (ufbx_node*)ufbxi_translate_element(ec, node->parent);
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &node->children));
node->attrib = ufbxi_translate_element(ec, node->attrib);
node->mesh = (ufbx_mesh*)ufbxi_translate_element(ec, node->mesh);
node->light = (ufbx_light*)ufbxi_translate_element(ec, node->light);
node->camera = (ufbx_camera*)ufbxi_translate_element(ec, node->camera);
node->bone = (ufbx_bone*)ufbxi_translate_element(ec, node->bone);
node->inherit_scale_node = (ufbx_node*)ufbxi_translate_element(ec, node->inherit_scale_node);
node->scale_helper = (ufbx_node*)ufbxi_translate_element(ec, node->scale_helper);
node->bind_pose = (ufbx_pose*)ufbxi_translate_element(ec, node->bind_pose);
if (node->all_attribs.count > 1) {
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &node->all_attribs));
} else if (node->all_attribs.count == 1) {
node->all_attribs.data = &node->attrib;
}
node->geometry_transform_helper = (ufbx_node*)ufbxi_translate_element(ec, node->geometry_transform_helper);
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &node->materials));
}
ufbxi_for_ptr_list(ufbx_mesh, p_mesh, ec->scene.meshes) {
ufbx_mesh *mesh = *p_mesh;
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &mesh->materials));
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &mesh->skin_deformers));
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &mesh->blend_deformers));
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &mesh->cache_deformers));
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &mesh->all_deformers));
}
ufbxi_for_ptr_list(ufbx_stereo_camera, p_stereo, ec->scene.stereo_cameras) {
ufbx_stereo_camera *stereo = *p_stereo;
stereo->left = (ufbx_camera*)ufbxi_translate_element(ec, stereo->left);
stereo->right = (ufbx_camera*)ufbxi_translate_element(ec, stereo->right);
}
ufbxi_for_ptr_list(ufbx_skin_deformer, p_skin, ec->scene.skin_deformers) {
ufbx_skin_deformer *skin = *p_skin;
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &skin->clusters));
}
ufbxi_for_ptr_list(ufbx_skin_cluster, p_cluster, ec->scene.skin_clusters) {
ufbx_skin_cluster *cluster = *p_cluster;
cluster->bone_node = (ufbx_node*)ufbxi_translate_element(ec, cluster->bone_node);
}
ufbxi_for_ptr_list(ufbx_blend_deformer, p_blend, ec->scene.blend_deformers) {
ufbx_blend_deformer *blend = *p_blend;
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &blend->channels));
}
ufbxi_for_ptr_list(ufbx_blend_channel, p_chan, ec->scene.blend_channels) {
ufbx_blend_channel *chan = *p_chan;
ufbx_blend_keyframe *keys = ufbxi_push(&ec->result, ufbx_blend_keyframe, chan->keyframes.count);
ufbxi_check_err(&ec->error, keys);
for (size_t i = 0; i < chan->keyframes.count; i++) {
keys[i] = chan->keyframes.data[i];
keys[i].shape = (ufbx_blend_shape*)ufbxi_translate_element(ec, keys[i].shape);
}
chan->keyframes.data = keys;
chan->target_shape = (ufbx_blend_shape*)ufbxi_translate_element(ec, chan->target_shape);
}
ufbxi_for_ptr_list(ufbx_cache_deformer, p_deformer, ec->scene.cache_deformers) {
ufbx_cache_deformer *deformer = *p_deformer;
deformer->file = (ufbx_cache_file*)ufbxi_translate_element(ec, deformer->file);
}
ufbxi_for_ptr_list(ufbx_material, p_material, ec->scene.materials) {
ufbx_material *material = *p_material;
material->shader = (ufbx_shader*)ufbxi_translate_element(ec, material->shader);
ufbxi_translate_maps(ec, material->fbx.maps, UFBX_MATERIAL_FBX_MAP_COUNT);
ufbxi_translate_maps(ec, material->pbr.maps, UFBX_MATERIAL_PBR_MAP_COUNT);
ufbx_material_texture *textures = ufbxi_push(&ec->result, ufbx_material_texture, material->textures.count);
ufbxi_check_err(&ec->error, textures);
for (size_t i = 0; i < material->textures.count; i++) {
textures[i] = material->textures.data[i];
textures[i].texture = (ufbx_texture*)ufbxi_translate_element(ec, textures[i].texture);
}
material->textures.data = textures;
}
ufbxi_for_ptr_list(ufbx_texture, p_texture, ec->scene.textures) {
ufbx_texture *texture = *p_texture;
texture->video = (ufbx_video*)ufbxi_translate_element(ec, texture->video);
ufbx_texture_layer *layers = ufbxi_push(&ec->result, ufbx_texture_layer, texture->layers.count);
ufbxi_check_err(&ec->error, layers);
for (size_t i = 0; i < texture->layers.count; i++) {
layers[i] = texture->layers.data[i];
layers[i].texture = (ufbx_texture*)ufbxi_translate_element(ec, layers[i].texture);
}
texture->layers.data = layers;
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &texture->file_textures));
if (texture->shader) {
ufbx_shader_texture *shader = texture->shader;
shader = ufbxi_push_copy(&ec->result, ufbx_shader_texture, 1, shader);
ufbxi_check_err(&ec->error, shader);
texture->shader = shader;
ufbx_shader_texture_input *inputs = ufbxi_push_copy(&ec->result, ufbx_shader_texture_input, shader->inputs.count, shader->inputs.data);
ufbxi_check_err(&ec->error, inputs);
shader->inputs.data = inputs;
}
}
ufbxi_for_ptr_list(ufbx_shader, p_shader, ec->scene.shaders) {
ufbx_shader *shader = *p_shader;
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &shader->bindings));
}
ufbxi_for_ptr_list(ufbx_display_layer, p_layer, ec->scene.display_layers) {
ufbx_display_layer *layer = *p_layer;
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &layer->nodes));
}
ufbxi_for_ptr_list(ufbx_selection_set, p_set, ec->scene.selection_sets) {
ufbx_selection_set *set = *p_set;
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &set->nodes));
}
ufbxi_for_ptr_list(ufbx_selection_node, p_node, ec->scene.selection_nodes) {
ufbx_selection_node *node = *p_node;
node->target_node = (ufbx_node*)ufbxi_translate_element(ec, node->target_node);
node->target_mesh = (ufbx_mesh*)ufbxi_translate_element(ec, node->target_mesh);
}
ufbxi_for_ptr_list(ufbx_constraint, p_constraint, ec->scene.constraints) {
ufbx_constraint *constraint = *p_constraint;
constraint->node = (ufbx_node*)ufbxi_translate_element(ec, constraint->node);
constraint->aim_up_node = (ufbx_node*)ufbxi_translate_element(ec, constraint->aim_up_node);
constraint->ik_effector = (ufbx_node*)ufbxi_translate_element(ec, constraint->ik_effector);
constraint->ik_end_node = (ufbx_node*)ufbxi_translate_element(ec, constraint->ik_end_node);
ufbx_constraint_target *targets = ufbxi_push(&ec->result, ufbx_constraint_target, constraint->targets.count);
ufbxi_check_err(&ec->error, targets);
for (size_t i = 0; i < constraint->targets.count; i++) {
targets[i] = constraint->targets.data[i];
targets[i].node = (ufbx_node*)ufbxi_translate_element(ec, targets[i].node);
}
constraint->targets.data = targets;
}
ufbxi_for_ptr_list(ufbx_audio_layer, p_layer, ec->scene.audio_layers) {
ufbx_audio_layer *layer = *p_layer;
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &layer->clips));
}
ufbxi_for_ptr_list(ufbx_anim_stack, p_stack, ec->scene.anim_stacks) {
ufbx_anim_stack *stack = *p_stack;
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &stack->layers));
ufbxi_check_err(&ec->error, ufbxi_translate_anim(ec, &stack->anim));
}
ufbxi_for_ptr_list(ufbx_anim_layer, p_layer, ec->scene.anim_layers) {
ufbx_anim_layer *layer = *p_layer;
ufbxi_check_err(&ec->error, ufbxi_translate_element_list(ec, &layer->anim_values));
ufbx_anim_prop *props = ufbxi_push(&ec->result, ufbx_anim_prop, layer->anim_props.count + 1);
ufbxi_check_err(&ec->error, props);
for (size_t i = 0; i < layer->anim_props.count; i++) {
props[i] = layer->anim_props.data[i];
props[i].element = ufbxi_translate_element(ec, props[i].element);
props[i].anim_value = (ufbx_anim_value*)ufbxi_translate_element(ec, props[i].anim_value);
}
memset(props + layer->anim_props.count, 0, sizeof(ufbx_anim_prop));
layer->anim_props.data = props;
}
ufbxi_for_ptr_list(ufbx_pose, p_pose, ec->scene.poses) {
ufbx_pose *pose = *p_pose;
ufbx_bone_pose *bones = ufbxi_push(&ec->result, ufbx_bone_pose, pose->bone_poses.count);
ufbxi_check_err(&ec->error, bones);
for (size_t i = 0; i < pose->bone_poses.count; i++) {
bones[i] = pose->bone_poses.data[i];
bones[i].bone_node = (ufbx_node*)ufbxi_translate_element(ec, bones[i].bone_node);
}
pose->bone_poses.data = bones;
}
ufbxi_check_err(&ec->error, ufbxi_translate_anim(ec, &ec->anim));
ufbxi_for_ptr_list(ufbx_anim_value, p_value, ec->scene.anim_values) {
ufbx_anim_value *value = *p_value;
value->curves[0] = (ufbx_anim_curve*)ufbxi_translate_element(ec, value->curves[0]);
value->curves[1] = (ufbx_anim_curve*)ufbxi_translate_element(ec, value->curves[1]);
value->curves[2] = (ufbx_anim_curve*)ufbxi_translate_element(ec, value->curves[2]);
}
ufbx_anim anim = *ec->anim;
ufbx_prop_override *over = anim.prop_overrides.data, *over_end = ufbxi_add_ptr(over, anim.prop_overrides.count);
ufbxi_for_ptr_list(ufbx_element, p_elem, ec->scene.elements) {
ufbx_element *elem = *p_elem;
size_t num_animated = elem->props.num_animated;
size_t num_override = 0;
while (over != over_end && over->element_id == elem->element_id) {
num_override++;
over++;
}
num_animated += num_override;
if (num_animated == 0) continue;
anim.prop_overrides.data = ufbxi_sub_ptr(over, num_override);
anim.prop_overrides.count = num_override;
ufbx_prop *props = ufbxi_push(&ec->result, ufbx_prop, num_animated);
ufbxi_check_err(&ec->error, props);
elem->props = ufbx_evaluate_props(&anim, elem, ec->time, props, num_animated);
elem->props.defaults = &ec->src_scene.elements.data[elem->element_id]->props;
}
ufbxi_update_scene(&ec->scene, false, anim.transform_overrides.data, anim.transform_overrides.count);
if (ec->opts.evaluate_skinning) {
ufbx_geometry_cache_data_opts cache_opts = { 0 };
cache_opts.open_file_cb = ec->opts.open_file_cb;
ufbxi_check_err(&ec->error, ufbxi_evaluate_skinning(&ec->scene, &ec->error, &ec->result, &ec->tmp,
ec->time, ec->opts.load_external_files && ec->opts.evaluate_caches, &cache_opts));
}
ufbxi_scene_imp *imp = ufbxi_push_zero(&ec->result, ufbxi_scene_imp, 1);
ufbxi_check_err(&ec->error, imp);
ufbx_assert(ec->src_imp->magic == UFBXI_SCENE_IMP_MAGIC);
ufbxi_init_ref(&imp->refcount, UFBXI_SCENE_IMP_MAGIC, &ec->src_imp->refcount);
imp->magic = UFBXI_SCENE_IMP_MAGIC;
imp->scene = ec->scene;
imp->refcount.ator = ec->ator_result;
imp->refcount.ator.error = NULL;
imp->refcount.buf = ec->result;
imp->refcount.buf.ator = &imp->refcount.ator;
imp->scene.metadata.result_memory_used = imp->refcount.ator.current_size;
imp->scene.metadata.temp_memory_used = ec->ator_tmp.current_size;
imp->scene.metadata.result_allocs = imp->refcount.ator.num_allocs;
imp->scene.metadata.temp_allocs = ec->ator_tmp.num_allocs;
ufbxi_for_ptr_list(ufbx_element, p_elem, imp->scene.elements) {
(*p_elem)->scene = &imp->scene;
}
ec->scene_imp = imp;
ec->result.ator = &ec->ator_result;
return 1;
}
ufbxi_nodiscard static ufbxi_noinline ufbx_scene *ufbxi_evaluate_scene(ufbxi_eval_context *ec, ufbx_scene *scene, const ufbx_anim *anim, double time, const ufbx_evaluate_opts *user_opts, ufbx_error *p_error)
{
if (user_opts) {
ec->opts = *user_opts;
} else {
memset(&ec->opts, 0, sizeof(ec->opts));
}
ec->src_imp = ufbxi_get_imp(ufbxi_scene_imp, scene);
ec->src_scene = *scene;
ec->anim = anim ? (ufbx_anim*)anim : scene->anim;
ec->time = time;
ufbxi_init_ator(&ec->error, &ec->ator_tmp, &ec->opts.temp_allocator, "temp");
ufbxi_init_ator(&ec->error, &ec->ator_result, &ec->opts.result_allocator, "result");
ec->result.ator = &ec->ator_result;
ec->tmp.ator = &ec->ator_tmp;
ec->result.unordered = true;
ec->tmp.unordered = true;
if (ufbxi_evaluate_imp(ec)) {
ufbxi_buf_free(&ec->tmp);
ufbxi_free_ator(&ec->ator_tmp);
if (p_error) {
ufbxi_clear_error(p_error);
}
return &ec->scene_imp->scene;
} else {
ufbxi_fix_error_type(&ec->error, "Failed to evaluate");
if (p_error) *p_error = ec->error;
ufbxi_buf_free(&ec->tmp);
ufbxi_buf_free(&ec->result);
ufbxi_free_ator(&ec->ator_tmp);
ufbxi_free_ator(&ec->ator_result);
return NULL;
}
}
#endif
ufbxi_create_anim_context;
ufbxi_nodiscard static ufbxi_noinline int ufbxi_check_string(ufbx_error *error, ufbx_string *dst, const ufbx_string *src)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_push_anim_string(ufbxi_create_anim_context *ac, ufbx_string *str)
{ … }
static bool ufbxi_prop_override_prop_name_less(void *user, const void *va, const void *vb)
{ … }
static bool ufbxi_prop_override_less(void *user, const void *va, const void *vb)
{ … }
static int ufbxi_cmp_transform_override(const void *va, const void *vb)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_create_anim_imp(ufbxi_create_anim_context *ac)
{ … }
ufbxi_baked_anim_imp;
#if UFBXI_FEATURE_ANIMATION_BAKING
ufbxi_bake_time;
UFBX_LIST_TYPE(…) …;
ufbxi_bake_context;
ufbxi_bake_prop;
static int ufbxi_cmp_bake_prop(const void *va, const void *vb)
{ … }
ufbx_static_assert(…);
ufbx_static_assert(…);
ufbx_static_assert(…);
static ufbxi_forceinline int ufbxi_cmp_bake_time(ufbxi_bake_time a, ufbxi_bake_time b)
{ … }
static int ufbxi_cmp_bake_time_fn(const void *va, const void *vb)
{ … }
ufbxi_nodiscard static ufbxi_forceinline int ufbxi_bake_push_time(ufbxi_bake_context *bc, double time, uint32_t flags)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_times(ufbxi_bake_context *bc, const ufbx_anim_value *anim_value, bool resample_linear, uint32_t key_flag)
{ … }
static const char *const ufbxi_transform_props[] = …;
static const char *const ufbxi_complex_translation_props[] = …;
static const char *const ufbxi_complex_rotation_props[] = …;
static const char *const ufbxi_complex_rotation_sources[] = …;
ufbxi_nodiscard static ufbxi_noinline bool ufbxi_in_list(const char *const *items, size_t count, const char *item)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_finalize_bake_times(ufbxi_bake_context *bc, ufbxi_bake_time_list *p_dst)
{ … }
#define ufbxi_add_epsilon(a, epsilon) …
#define ufbxi_sub_epsilon(a, epsilon) …
static ufbxi_noinline bool ufbxi_postprocess_step(ufbxi_bake_context *bc, double prev_time, double next_time, double *p_time, uint32_t flags)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_postprocess_vec3(ufbxi_bake_context *bc, ufbx_baked_vec3_list *p_dst, bool *p_constant, ufbx_baked_vec3_list src)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_postprocess_quat(ufbxi_bake_context *bc, ufbx_baked_quat_list *p_dst, bool *p_constant, ufbx_baked_quat_list src)
{ … }
static ufbxi_forceinline double ufbxi_bake_time_sample_time(ufbxi_bake_time time)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_push_resampled_times(ufbxi_bake_context *bc, const ufbx_baked_vec3_list *p_keys)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_node_imp(ufbxi_bake_context *bc, uint32_t element_id, ufbxi_bake_prop *props, size_t count)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_node(ufbxi_bake_context *bc, uint32_t element_id, ufbxi_bake_prop *props, size_t count)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_anim_prop(ufbxi_bake_context *bc, ufbx_element *element, const char *prop_name, ufbxi_bake_prop *props, size_t count)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_element(ufbxi_bake_context *bc, uint32_t element_id, ufbxi_bake_prop *props, size_t count)
{ … }
static ufbxi_noinline bool ufbxi_baked_node_less(void *user, const void *va, const void *vb)
{ … }
static ufbxi_noinline bool ufbxi_baked_element_less(void *user, const void *va, const void *vb)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_anim(ufbxi_bake_context *bc)
{ … }
ufbxi_nodiscard static ufbxi_noinline int ufbxi_bake_anim_imp(ufbxi_bake_context *bc, const ufbx_anim *anim)
{ … }
#endif
static ufbxi_forceinline ufbx_real ufbxi_nurbs_weight(const ufbx_real_list *knots, size_t knot, size_t degree, ufbx_real u)
{ … }
static ufbxi_forceinline ufbx_real ufbxi_nurbs_deriv(const ufbx_real_list *knots, size_t knot, size_t degree)
{ … }
ufbxi_line_curve_imp;
ufbx_static_assert(…);
#if UFBXI_FEATURE_TESSELLATION
typedef struct {
ufbx_error error;
ufbx_tessellate_curve_opts opts;
const ufbx_nurbs_curve *curve;
ufbxi_allocator ator_tmp;
ufbxi_allocator ator_result;
ufbxi_buf result;
ufbx_line_curve line;
ufbxi_line_curve_imp *imp;
} ufbxi_tessellate_curve_context;
typedef struct {
ufbx_error error;
ufbx_tessellate_surface_opts opts;
const ufbx_nurbs_surface *surface;
ufbxi_allocator ator_tmp;
ufbxi_allocator ator_result;
ufbxi_buf tmp;
ufbxi_buf result;
ufbxi_map position_map;
ufbx_mesh mesh;
ufbxi_mesh_imp *imp;
} ufbxi_tessellate_surface_context;
ufbxi_nodiscard static ufbxi_noinline int ufbxi_tessellate_nurbs_curve_imp(ufbxi_tessellate_curve_context *tc)
{
if (tc->opts.span_subdivision <= 0) {
tc->opts.span_subdivision = 4;
}
size_t num_sub = tc->opts.span_subdivision;
const ufbx_nurbs_curve *curve = tc->curve;
ufbx_line_curve *line = &tc->line;
ufbxi_check_err_msg(&tc->error, curve->basis.valid && curve->control_points.count > 0, "Bad NURBS geometry");
ufbxi_init_ator(&tc->error, &tc->ator_tmp, &tc->opts.temp_allocator, "temp");
ufbxi_init_ator(&tc->error, &tc->ator_result, &tc->opts.result_allocator, "result");
tc->result.unordered = true;
tc->result.ator = &tc->ator_result;
size_t num_spans = curve->basis.spans.count;
{
size_t over_spans = num_spans * 2 * sizeof(ufbx_real);
size_t over = over_spans * num_sub;
ufbxi_check_err(&tc->error, !ufbxi_does_overflow(over, over_spans, num_sub));
}
bool is_open = curve->basis.topology == UFBX_NURBS_TOPOLOGY_OPEN;
size_t num_indices = num_spans + (num_spans - 1) * (num_sub - 1);
size_t num_vertices = num_indices - (is_open ? 0u : 1u);
ufbxi_check_err(&tc->error, num_indices <= INT32_MAX);
uint32_t *indices = ufbxi_push(&tc->result, uint32_t, num_indices);
ufbx_vec3 *vertices = ufbxi_push(&tc->result, ufbx_vec3, num_vertices);
ufbx_line_segment *segments = ufbxi_push(&tc->result, ufbx_line_segment, 1);
ufbxi_check_err(&tc->error, indices && vertices && segments);
for (size_t span_ix = 0; span_ix < num_spans; span_ix++) {
size_t num_splits = span_ix + 1 == num_spans ? 1 : num_sub;
for (size_t sub_ix = 0; sub_ix < num_splits; sub_ix++) {
size_t ix = span_ix * num_sub + sub_ix;
if (ix < num_vertices) {
ufbx_real u = curve->basis.spans.data[span_ix];
if (sub_ix > 0) {
ufbx_real t = (ufbx_real)sub_ix / (ufbx_real)num_sub;
u = u * (1.0f - t) + t * curve->basis.spans.data[span_ix + 1];
}
ufbx_curve_point point = ufbx_evaluate_nurbs_curve(curve, u);
vertices[ix] = point.position;
indices[ix] = (uint32_t)ix;
} else {
indices[ix] = 0;
}
}
}
segments[0].index_begin = 0;
segments[0].num_indices = (uint32_t)num_indices;
line->element.name.data = ufbxi_empty_char;
line->element.type = UFBX_ELEMENT_LINE_CURVE;
line->element.typed_id = UINT32_MAX;
line->element.element_id = UINT32_MAX;
line->color.x = 1.0f;
line->color.y = 1.0f;
line->color.z = 1.0f;
line->control_points.data = vertices;
line->control_points.count = num_vertices;
line->point_indices.data = indices;
line->point_indices.count = num_indices;
line->segments.data = segments;
line->segments.count = 1;
line->from_tessellated_nurbs = true;
tc->imp = ufbxi_push(&tc->result, ufbxi_line_curve_imp, 1);
ufbxi_check_err(&tc->error, tc->imp);
ufbxi_init_ref(&tc->imp->refcount, UFBXI_LINE_CURVE_IMP_MAGIC, &(ufbxi_get_imp(ufbxi_scene_imp, curve->element.scene))->refcount);
tc->imp->magic = UFBXI_LINE_CURVE_IMP_MAGIC;
tc->imp->curve = tc->line;
tc->imp->refcount.ator = tc->ator_result;
tc->imp->refcount.buf = tc->result;
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_tessellate_nurbs_surface_imp(ufbxi_tessellate_surface_context *tc)
{
if (tc->opts.span_subdivision_u <= 0) {
tc->opts.span_subdivision_u = 4;
}
if (tc->opts.span_subdivision_v <= 0) {
tc->opts.span_subdivision_v = 4;
}
size_t sub_u = tc->opts.span_subdivision_u;
size_t sub_v = tc->opts.span_subdivision_v;
const ufbx_nurbs_surface *surface = tc->surface;
ufbx_mesh *mesh = &tc->mesh;
ufbxi_check_err_msg(&tc->error, surface->basis_u.valid && surface->basis_v.valid
&& surface->num_control_points_u > 0 && surface->num_control_points_v > 0, "Bad NURBS geometry");
ufbxi_init_ator(&tc->error, &tc->ator_tmp, &tc->opts.temp_allocator, "temp");
ufbxi_init_ator(&tc->error, &tc->ator_result, &tc->opts.result_allocator, "result");
tc->result.unordered = true;
tc->tmp.unordered = true;
tc->result.ator = &tc->ator_result;
tc->tmp.ator = &tc->ator_tmp;
bool open_u = surface->basis_u.topology == UFBX_NURBS_TOPOLOGY_OPEN;
bool open_v = surface->basis_v.topology == UFBX_NURBS_TOPOLOGY_OPEN;
size_t spans_u = surface->basis_u.spans.count;
size_t spans_v = surface->basis_v.spans.count;
{
size_t over_spans_u = spans_u * 2 * sizeof(ufbx_real);
size_t over_spans_v = spans_v * 2 * sizeof(ufbx_real);
size_t over_u = over_spans_u * sub_u;
size_t over_v = over_spans_v * sub_v;
size_t over_uv = over_u * over_v;
ufbxi_check_err(&tc->error, !ufbxi_does_overflow(over_u, over_spans_u, sub_u));
ufbxi_check_err(&tc->error, !ufbxi_does_overflow(over_v, over_spans_v, sub_v));
ufbxi_check_err(&tc->error, !ufbxi_does_overflow(over_uv, over_u, over_v));
}
size_t faces_u = (spans_u - 1) * sub_u;
size_t faces_v = (spans_v - 1) * sub_v;
size_t indices_u = spans_u + (spans_u - 1) * (sub_u - 1);
size_t indices_v = spans_v + (spans_v - 1) * (sub_v - 1);
size_t num_faces = faces_u * faces_v;
size_t num_indices = indices_u * indices_v;
ufbxi_check_err(&tc->error, num_indices <= INT32_MAX);
uint32_t *position_ix = ufbxi_push(&tc->tmp, uint32_t, num_indices);
ufbx_vec3 *positions = ufbxi_push(&tc->result, ufbx_vec3, num_indices + 1);
ufbx_vec3 *normals = ufbxi_push(&tc->result, ufbx_vec3, num_indices + 1);
ufbx_vec2 *uvs = ufbxi_push(&tc->result, ufbx_vec2, num_indices + 1);
ufbx_vec3 *tangents = ufbxi_push(&tc->result, ufbx_vec3, num_indices + 1);
ufbx_vec3 *bitangents = ufbxi_push(&tc->result, ufbx_vec3, num_indices + 1);
ufbxi_check_err(&tc->error, position_ix && uvs && tangents && bitangents);
*positions++ = ufbx_zero_vec3;
*normals++ = ufbx_zero_vec3;
*uvs++ = ufbx_zero_vec2;
*tangents++ = ufbx_zero_vec3;
*bitangents++ = ufbx_zero_vec3;
uint32_t num_positions = 0;
for (size_t span_v = 0; span_v < spans_v; span_v++) {
size_t splits_v = span_v + 1 == spans_v ? 1 : sub_v;
for (size_t split_v = 0; split_v < splits_v; split_v++) {
size_t ix_v = span_v * sub_v + split_v;
ufbx_assert(ix_v < indices_v);
ufbx_real v = surface->basis_v.spans.data[span_v];
if (split_v > 0) {
ufbx_real t = (ufbx_real)split_v / (ufbx_real)splits_v;
v = v * (1.0f - t) + t * surface->basis_v.spans.data[span_v + 1];
}
ufbx_real original_v = v;
if (span_v + 1 == spans_v && !open_v) {
v = surface->basis_v.spans.data[0];
}
for (size_t span_u = 0; span_u < spans_u; span_u++) {
size_t splits_u = span_u + 1 == spans_u ? 1 : sub_u;
for (size_t split_u = 0; split_u < splits_u; split_u++) {
size_t ix_u = span_u * sub_u + split_u;
ufbx_assert(ix_u < indices_u);
ufbx_real u = surface->basis_u.spans.data[span_u];
if (split_u > 0) {
ufbx_real t = (ufbx_real)split_u / (ufbx_real)splits_u;
u = u * (1.0f - t) + t * surface->basis_u.spans.data[span_u + 1];
}
ufbx_real original_u = u;
if (span_u + 1 == spans_u && !open_u) {
u = surface->basis_u.spans.data[0];
}
ufbx_surface_point point = ufbx_evaluate_nurbs_surface(surface, u, v);
ufbx_vec3 pos = point.position;
ufbx_vec3 tangent_u = ufbxi_slow_normalize3(&point.derivative_u);
ufbx_vec3 tangent_v = ufbxi_slow_normalize3(&point.derivative_v);
size_t neighbors[5];
size_t num_neighbors = 0;
if ((span_v == 0 && (span_u > 0 || split_u > 0)) || (span_u == 0 && (span_v > 0 || split_v > 0))) {
neighbors[num_neighbors++] = 0;
}
if (span_v + 1 == spans_v) {
neighbors[num_neighbors++] = ix_u;
if (span_u > 0 || split_u > 0) {
neighbors[num_neighbors++] = ix_v * indices_u;
}
}
if (span_u + 1 == spans_u) {
neighbors[num_neighbors++] = ix_v * indices_u;
if (span_v > 0 || split_v > 0) {
neighbors[num_neighbors++] = indices_u - 1;
}
}
size_t ix = ix_v * indices_u + ix_u;
uint32_t pos_ix = num_positions;
for (size_t i = 0; i < num_neighbors; i++) {
size_t nb_ix = neighbors[i];
ufbx_assert(nb_ix < ix);
uint32_t nb_pos_ix = position_ix[nb_ix];
ufbx_vec3 nb_pos = positions[nb_pos_ix];
ufbx_real dx = nb_pos.x - pos.x;
ufbx_real dy = nb_pos.y - pos.y;
ufbx_real dz = nb_pos.z - pos.z;
ufbx_real delta = dx*dx + dy*dy + dz*dz;
if (delta < 0.0000001f) {
pos_ix = nb_pos_ix;
break;
}
}
position_ix[ix] = pos_ix;
if (pos_ix == num_positions) {
positions[pos_ix] = pos;
num_positions = pos_ix + 1;
}
uvs[ix].x = original_u;
uvs[ix].y = original_v;
tangents[ix] = tangent_u;
bitangents[ix] = tangent_v;
}
}
}
}
ufbx_face *faces = ufbxi_push(&tc->result, ufbx_face, num_faces);
uint32_t *vertex_ix = ufbxi_push(&tc->result, uint32_t, num_faces * 4);
uint32_t *attrib_ix = ufbxi_push(&tc->result, uint32_t, num_faces * 4);
ufbxi_check_err(&tc->error, faces && vertex_ix && attrib_ix);
size_t face_ix = 0;
size_t dst_index = 0;
size_t num_triangles = 0;
for (size_t face_v = 0; face_v < faces_v; face_v++) {
for (size_t face_u = 0; face_u < faces_u; face_u++) {
attrib_ix[dst_index + 0] = (uint32_t)((face_v + 0) * indices_u + (face_u + 0));
attrib_ix[dst_index + 1] = (uint32_t)((face_v + 0) * indices_u + (face_u + 1));
attrib_ix[dst_index + 2] = (uint32_t)((face_v + 1) * indices_u + (face_u + 1));
attrib_ix[dst_index + 3] = (uint32_t)((face_v + 1) * indices_u + (face_u + 0));
vertex_ix[dst_index + 0] = position_ix[attrib_ix[dst_index + 0]];
vertex_ix[dst_index + 1] = position_ix[attrib_ix[dst_index + 1]];
vertex_ix[dst_index + 2] = position_ix[attrib_ix[dst_index + 2]];
vertex_ix[dst_index + 3] = position_ix[attrib_ix[dst_index + 3]];
bool is_triangle = false;
for (size_t prev_ix = 0; prev_ix < 4; prev_ix++) {
size_t next_ix = (prev_ix + 1) % 4;
if (vertex_ix[dst_index + prev_ix] == vertex_ix[dst_index + next_ix]) {
for (size_t i = next_ix; i < 3; i++) {
attrib_ix[dst_index + i] = attrib_ix[dst_index + i + 1];
vertex_ix[dst_index + i] = vertex_ix[dst_index + i + 1];
}
is_triangle = true;
break;
}
}
faces[face_ix].index_begin = (uint32_t)dst_index;
faces[face_ix].num_indices = is_triangle ? 3 : 4;
dst_index += is_triangle ? 3 : 4;
num_triangles += is_triangle ? 1 : 2;
face_ix++;
}
}
ufbxi_check_err(&tc->error, positions && normals);
mesh->element.name.data = ufbxi_empty_char;
mesh->element.type = UFBX_ELEMENT_MESH;
mesh->element.typed_id = UINT32_MAX;
mesh->element.element_id = UINT32_MAX;
mesh->vertices.data = positions;
mesh->vertices.count = num_positions;
mesh->num_vertices = num_positions;
mesh->vertex_indices.data = vertex_ix;
mesh->vertex_indices.count = dst_index;
mesh->faces.data = faces;
mesh->faces.count = num_faces;
mesh->vertex_position.exists = true;
mesh->vertex_position.values.data = positions;
mesh->vertex_position.values.count = num_positions;
mesh->vertex_position.indices.data = vertex_ix;
mesh->vertex_position.indices.count = dst_index;
mesh->vertex_position.unique_per_vertex = true;
mesh->vertex_uv.exists = true;
mesh->vertex_uv.values.data = uvs;
mesh->vertex_uv.values.count = dst_index;
mesh->vertex_uv.indices.data = attrib_ix;
mesh->vertex_uv.indices.count = dst_index;
mesh->vertex_normal.exists = true;
mesh->vertex_normal.values.data = normals;
mesh->vertex_normal.values.count = num_positions;
mesh->vertex_normal.indices.data = vertex_ix;
mesh->vertex_normal.indices.count = dst_index;
mesh->vertex_tangent.exists = true;
mesh->vertex_tangent.values.data = tangents;
mesh->vertex_tangent.values.count = dst_index;
mesh->vertex_tangent.indices.data = attrib_ix;
mesh->vertex_tangent.indices.count = dst_index;
mesh->vertex_bitangent.exists = true;
mesh->vertex_bitangent.values.data = bitangents;
mesh->vertex_bitangent.values.count = dst_index;
mesh->vertex_bitangent.indices.data = attrib_ix;
mesh->vertex_bitangent.indices.count = dst_index;
mesh->num_faces = num_faces;
mesh->num_triangles = num_triangles;
mesh->num_indices = dst_index;
mesh->max_face_triangles = 2;
if (surface->material) {
mesh->face_material.data = ufbxi_push_zero(&tc->result, uint32_t, num_faces);
ufbxi_check_err(&tc->error, mesh->face_material.data);
ufbx_material **mat = ufbxi_push_zero(&tc->result, ufbx_material*, 1);
ufbxi_check_err(&tc->error, mat);
*mat = surface->material;
mesh->materials.data = mat;
mesh->materials.count = 1;
}
if (!tc->opts.skip_mesh_parts) {
mesh->material_parts.count = 1;
mesh->material_parts.data = ufbxi_push_zero(&tc->result, ufbx_mesh_part, 1);
ufbxi_check_err(&tc->error, mesh->material_parts.data);
}
ufbxi_check_err(&tc->error, ufbxi_finalize_mesh_material(&tc->result, &tc->error, mesh));
ufbxi_check_err(&tc->error, ufbxi_finalize_mesh(&tc->result, &tc->error, mesh));
mesh->generated_normals = true;
ufbx_compute_normals(mesh, &mesh->vertex_position,
mesh->vertex_normal.indices.data, mesh->vertex_normal.indices.count,
mesh->vertex_normal.values.data, mesh->vertex_normal.values.count);
if (surface->flip_normals) {
ufbxi_nounroll ufbxi_for_list(ufbx_vec3, normal, mesh->vertex_normal.values) {
normal->x *= -1.0f;
normal->y *= -1.0f;
normal->z *= -1.0f;
}
}
tc->imp = ufbxi_push(&tc->result, ufbxi_mesh_imp, 1);
ufbxi_check_err(&tc->error, tc->imp);
ufbxi_init_ref(&tc->imp->refcount, UFBXI_MESH_IMP_MAGIC, &(ufbxi_get_imp(ufbxi_scene_imp, surface->element.scene))->refcount);
tc->imp->magic = UFBXI_MESH_IMP_MAGIC;
tc->imp->mesh = tc->mesh;
tc->imp->refcount.ator = tc->ator_result;
tc->imp->refcount.buf = tc->result;
tc->imp->mesh.subdivision_evaluated = true;
return 1;
}
#endif
#if UFBXI_FEATURE_KD
ufbxi_kd_node;
ufbxi_ngon_context;
ufbxi_kd_triangle;
ufbxi_noinline static ufbx_vec2 ufbxi_ngon_project(ufbxi_ngon_context *nc, uint32_t index)
{ … }
ufbxi_forceinline static ufbx_real ufbxi_orient2d(ufbx_vec2 a, ufbx_vec2 b, ufbx_vec2 c)
{ … }
ufbxi_noinline static bool ufbxi_kd_check_point(ufbxi_ngon_context *nc, const ufbxi_kd_triangle *tri, uint32_t index)
{ … }
ufbxi_noinline static bool ufbxi_kd_check_slow(ufbxi_ngon_context *nc, const ufbxi_kd_triangle *tri, uint32_t begin, uint32_t count, uint32_t axis)
ufbxi_recursive_function(bool, ufbxi_kd_check_slow, (nc, tri, begin, count, axis), 32 - UFBXI_KD_FAST_DEPTH,
(ufbxi_ngon_context *nc, const ufbxi_kd_triangle *tri, uint32_t begin, uint32_t count, uint32_t axis))
{ … }
ufbxi_noinline static bool ufbxi_kd_check_fast(ufbxi_ngon_context *nc, const ufbxi_kd_triangle *tri, uint32_t kd_index, uint32_t axis, uint32_t depth)
ufbxi_recursive_function(bool, ufbxi_kd_check_fast, (nc, tri, kd_index, axis, depth), UFBXI_KD_FAST_DEPTH,
(ufbxi_ngon_context *nc, const ufbxi_kd_triangle *tri, uint32_t kd_index, uint32_t axis, uint32_t depth))
{ … }
ufbxi_noinline static bool ufbxi_kd_check(ufbxi_ngon_context *nc, const ufbx_vec2 *points, const uint32_t *indices)
{ … }
ufbxi_noinline static bool ufbxi_kd_index_less(void *user, const void *va, const void *vb)
{ … }
ufbxi_noinline static void ufbxi_kd_build(ufbxi_ngon_context *nc, uint32_t *indices, uint32_t *tmp, uint32_t num, uint32_t axis, uint32_t fast_index, uint32_t depth)
ufbxi_recursive_function_void(ufbxi_kd_build, (nc, indices, tmp, num, axis, fast_index, depth), 32,
(ufbxi_ngon_context *nc, uint32_t *indices, uint32_t *tmp, uint32_t num, uint32_t axis, uint32_t fast_index, uint32_t depth))
{ … }
#endif
#if UFBXI_FEATURE_TRIANGULATION
ufbxi_noinline static ufbx_real ufbxi_ngon_tri_weight(const ufbx_vec2 *points)
{ … }
ufbxi_noinline static uint32_t ufbxi_triangulate_ngon(ufbxi_ngon_context *nc, uint32_t *indices, uint32_t num_indices)
{ … }
#endif
static int ufbxi_cmp_topo_index_prev_next(const void *va, const void *vb)
{ … }
static int ufbxi_cmp_topo_index_index(const void *va, const void *vb)
{ … }
ufbxi_noinline static void ufbxi_compute_topology(const ufbx_mesh *mesh, ufbx_topo_edge *topo)
{ … }
static bool ufbxi_is_edge_smooth(const ufbx_mesh *mesh, const ufbx_topo_edge *topo, size_t num_topo, uint32_t index, bool assume_smooth)
{ … }
#if UFBXI_FEATURE_SUBDIVISION
typedef struct {
const void *data;
ufbx_real weight;
} ufbxi_subdivide_input;
typedef int ufbxi_subdivide_sum_fn(void *user, void *output, const ufbxi_subdivide_input *inputs, size_t num_inputs);
typedef struct {
ufbxi_subdivide_sum_fn *sum_fn;
void *sum_user;
const void *values;
size_t stride;
const uint32_t *indices;
bool check_split_data;
bool ignore_indices;
ufbx_subdivision_boundary boundary;
} ufbxi_subdivide_layer_input;
typedef struct {
void *values;
size_t num_values;
uint32_t *indices;
size_t num_indices;
bool unique_per_vertex;
} ufbxi_subdivide_layer_output;
typedef struct {
ufbx_subdivision_weight *weights;
size_t num_weights;
} ufbxi_subdivision_vertex_weights;
typedef struct {
ufbxi_mesh_imp *imp;
ufbx_error error;
ufbx_mesh *src_mesh_ptr;
ufbx_mesh src_mesh;
ufbx_mesh dst_mesh;
ufbx_topo_edge *topo;
size_t num_topo;
ufbx_subdivide_opts opts;
ufbxi_allocator ator_result;
ufbxi_allocator ator_tmp;
ufbxi_buf result;
ufbxi_buf tmp;
ufbxi_buf source;
ufbxi_subdivide_input *inputs;
size_t inputs_cap;
ufbx_real *tmp_vertex_weights;
ufbx_subdivision_weight *tmp_weights;
size_t total_weights;
size_t max_vertex_weights;
} ufbxi_subdivide_context;
static int ufbxi_subdivide_sum_vec2(void *user, void *output, const ufbxi_subdivide_input *inputs, size_t num_inputs)
{
(void)user;
ufbx_vec2 dst = { 0 };
ufbxi_nounroll for (size_t i = 0; i != num_inputs; i++) {
const ufbx_vec2 *src = (const ufbx_vec2*)inputs[i].data;
ufbx_real weight = inputs[i].weight;
dst.x += src->x * weight;
dst.y += src->y * weight;
}
*(ufbx_vec2*)output = dst;
return 1;
}
static int ufbxi_subdivide_sum_vec3(void *user, void *output, const ufbxi_subdivide_input *inputs, size_t num_inputs)
{
(void)user;
ufbx_vec3 dst = { 0 };
ufbxi_nounroll for (size_t i = 0; i != num_inputs; i++) {
const ufbx_vec3 *src = (const ufbx_vec3*)inputs[i].data;
ufbx_real weight = inputs[i].weight;
dst.x += src->x * weight;
dst.y += src->y * weight;
dst.z += src->z * weight;
}
*(ufbx_vec3*)output = dst;
return 1;
}
static int ufbxi_subdivide_sum_vec4(void *user, void *output, const ufbxi_subdivide_input *inputs, size_t num_inputs)
{
(void)user;
ufbx_vec4 dst = { 0 };
ufbxi_nounroll for (size_t i = 0; i != num_inputs; i++) {
const ufbx_vec4 *src = (const ufbx_vec4*)inputs[i].data;
ufbx_real weight = inputs[i].weight;
dst.x += src->x * weight;
dst.y += src->y * weight;
dst.z += src->z * weight;
dst.w += src->w * weight;
}
*(ufbx_vec4*)output = dst;
return 1;
}
static ufbxi_noinline int ufbxi_cmp_subdivision_weight(const void *va, const void *vb)
{
ufbx_subdivision_weight a = *(const ufbx_subdivision_weight*)va, b = *(const ufbx_subdivision_weight*)vb;
ufbxi_dev_assert(a.index != b.index);
if (a.weight != b.weight) return a.weight > b.weight ? -1 : +1;
return a.index < b.index ? -1 : +1;
}
static int ufbxi_subdivide_sum_vertex_weights(void *user, void *output, const ufbxi_subdivide_input *inputs, size_t num_inputs)
{
ufbxi_subdivide_context *sc = (ufbxi_subdivide_context*)user;
ufbx_real *vertex_weights = sc->tmp_vertex_weights;
ufbx_subdivision_weight *tmp_weights = sc->tmp_weights;
size_t num_weights = 0;
ufbxi_nounroll for (size_t input_ix = 0; input_ix != num_inputs; input_ix++) {
ufbxi_subdivision_vertex_weights src = *(const ufbxi_subdivision_vertex_weights*)inputs[input_ix].data;
ufbx_real input_weight = inputs[input_ix].weight;
for (size_t weight_ix = 0; weight_ix < src.num_weights; weight_ix++) {
ufbx_real weight = input_weight * src.weights[weight_ix].weight;
if (weight < 1.175494351e-38f) continue;
uint32_t vx = src.weights[weight_ix].index;
ufbxi_dev_assert(vx < sc->src_mesh.num_vertices);
ufbx_real prev = vertex_weights[vx];
vertex_weights[vx] = prev + weight;
if (prev == 0.0f) {
tmp_weights[num_weights++].index = vx;
}
}
}
ufbxi_nounroll for (size_t i = 0; i != num_weights; i++) {
uint32_t vx = tmp_weights[i].index;
tmp_weights[i].weight = vertex_weights[vx];
vertex_weights[vx] = 0.0f;
}
qsort(tmp_weights, num_weights, sizeof(ufbx_subdivision_weight), ufbxi_cmp_subdivision_weight);
if (sc->max_vertex_weights != SIZE_MAX) {
num_weights = ufbxi_min_sz(sc->max_vertex_weights, num_weights);
ufbx_real prefix_weight = 0.0f;
ufbxi_nounroll for (size_t i = 0; i != num_weights; i++) {
prefix_weight += tmp_weights[i].weight;
}
ufbxi_nounroll for (size_t i = 0; i != num_weights; i++) {
tmp_weights[i].weight /= prefix_weight;
}
}
sc->total_weights += num_weights;
ufbx_subdivision_weight *weights = ufbxi_push_copy(&sc->tmp, ufbx_subdivision_weight, num_weights, tmp_weights);
ufbxi_check_err(&sc->error, weights);
ufbxi_subdivision_vertex_weights *dst = (ufbxi_subdivision_vertex_weights*)output;
dst->weights = weights;
dst->num_weights = num_weights;
return 1;
}
static ufbxi_subdivide_sum_fn *const ufbxi_real_sum_fns[] = {
NULL,
&ufbxi_subdivide_sum_vec2,
&ufbxi_subdivide_sum_vec3,
&ufbxi_subdivide_sum_vec4,
};
ufbxi_noinline static bool ufbxi_is_edge_split(const ufbxi_subdivide_layer_input *input, const ufbx_topo_edge *topo, uint32_t index)
{
uint32_t twin = topo[index].twin;
if (twin != UFBX_NO_INDEX) {
uint32_t a0 = input->indices[index];
uint32_t a1 = input->indices[topo[index].next];
uint32_t b0 = input->indices[topo[twin].next];
uint32_t b1 = input->indices[twin];
if (a0 == b0 && a1 == b1) return false;
if (!input->check_split_data) return true;
size_t stride = input->stride;
char *da0 = (char*)input->values + a0 * stride;
char *da1 = (char*)input->values + a1 * stride;
char *db0 = (char*)input->values + b0 * stride;
char *db1 = (char*)input->values + b1 * stride;
if (!memcmp(da0, db0, stride) && !memcmp(da1, db1, stride)) return false;
return true;
}
return false;
}
static ufbx_real ufbxi_edge_crease(const ufbx_mesh *mesh, bool split, const ufbx_topo_edge *topo, uint32_t index)
{
if (topo[index].twin == UFBX_NO_INDEX) return 1.0f;
if (split) return 1.0f;
if (mesh->edge_crease.data && topo[index].edge != UFBX_NO_INDEX) return mesh->edge_crease.data[topo[index].edge] * (ufbx_real)10.0;
return 0.0f;
}
static ufbxi_noinline int ufbxi_subdivide_layer(ufbxi_subdivide_context *sc, ufbxi_subdivide_layer_output *output, const ufbxi_subdivide_layer_input *input)
{
ufbx_subdivision_boundary boundary = input->boundary;
const ufbx_mesh *mesh = &sc->src_mesh;
const ufbx_topo_edge *topo = sc->topo;
size_t num_topo = sc->num_topo;
uint32_t *edge_indices = ufbxi_push(&sc->result, uint32_t, mesh->num_indices);
ufbxi_check_err(&sc->error, edge_indices);
size_t num_edge_values = 0;
for (uint32_t ix = 0; ix < (uint32_t)mesh->num_indices; ix++) {
uint32_t twin = topo[ix].twin;
if (twin < ix && !ufbxi_is_edge_split(input, topo, ix)) {
edge_indices[ix] = edge_indices[twin];
} else {
edge_indices[ix] = (uint32_t)num_edge_values++;
}
}
size_t stride = input->stride;
size_t num_initial_values = (num_edge_values + mesh->num_faces + mesh->num_indices);
char *values = (char*)ufbxi_push_size(&sc->tmp, stride, num_initial_values);
ufbxi_check_err(&sc->error, values);
char *face_values = values;
char *edge_values = face_values + mesh->num_faces * stride;
char *vertex_values = edge_values + num_edge_values * stride;
size_t num_vertex_values = 0;
uint32_t *vertex_indices = ufbxi_push(&sc->result, uint32_t, mesh->num_indices);
ufbxi_check_err(&sc->error, vertex_indices);
size_t min_inputs = ufbxi_max_sz(32, mesh->max_face_triangles + 2);
ufbxi_check_err(&sc->error, ufbxi_grow_array(&sc->ator_tmp, &sc->inputs, &sc->inputs_cap, min_inputs));
ufbxi_subdivide_input *inputs = sc->inputs;
output->unique_per_vertex = true;
bool sharp_corners = false;
bool sharp_splits = false;
bool sharp_all = false;
switch (boundary) {
case UFBX_SUBDIVISION_BOUNDARY_DEFAULT:
case UFBX_SUBDIVISION_BOUNDARY_SHARP_NONE:
case UFBX_SUBDIVISION_BOUNDARY_LEGACY:
break;
case UFBX_SUBDIVISION_BOUNDARY_SHARP_CORNERS:
sharp_corners = true;
break;
case UFBX_SUBDIVISION_BOUNDARY_SHARP_BOUNDARY:
sharp_corners = true;
sharp_splits = true;
break;
case UFBX_SUBDIVISION_BOUNDARY_SHARP_INTERIOR:
sharp_all = true;
break;
default:
ufbxi_unreachable("Bad boundary mode");
}
ufbxi_subdivide_sum_fn *sum_fn = input->sum_fn;
void *sum_user = input->sum_user;
ufbxi_nounroll for (size_t i = 0; i < mesh->num_indices; i++) {
vertex_indices[i] = UFBX_NO_INDEX;
}
for (size_t fi = 0; fi < mesh->num_faces; fi++) {
ufbx_face face = mesh->faces.data[fi];
char *dst = face_values + fi * stride;
ufbx_real weight = 1.0f / (ufbx_real)face.num_indices;
for (uint32_t ci = 0; ci < face.num_indices; ci++) {
uint32_t ix = face.index_begin + ci;
inputs[ci].data = (const char*)input->values + input->indices[ix] * stride;
inputs[ci].weight = weight;
}
ufbxi_check_err(&sc->error, sum_fn(sum_user, dst, inputs, face.num_indices));
}
for (uint32_t ix = 0; ix < mesh->num_indices; ix++) {
char *dst = edge_values + edge_indices[ix] * stride;
uint32_t twin = topo[ix].twin;
bool split = ufbxi_is_edge_split(input, topo, ix);
if (split || (topo[ix].flags & UFBX_TOPO_NON_MANIFOLD) != 0) {
output->unique_per_vertex = false;
}
ufbx_real crease = 0.0f;
if (split || twin == UFBX_NO_INDEX) {
crease = 1.0f;
} else if (topo[ix].edge != UFBX_NO_INDEX && mesh->edge_crease.data) {
crease = mesh->edge_crease.data[topo[ix].edge] * (ufbx_real)10.0;
}
if (sharp_all) crease = 1.0f;
const char *v0 = (const char*)input->values + input->indices[ix] * stride;
const char *v1 = (const char*)input->values + input->indices[topo[ix].next] * stride;
if (twin < ix && !split) {
} else if (crease <= 0.0f) {
const char *f0 = face_values + topo[ix].face * stride;
const char *f1 = face_values + topo[twin].face * stride;
inputs[0].data = v0;
inputs[0].weight = 0.25f;
inputs[1].data = v1;
inputs[1].weight = 0.25f;
inputs[2].data = f0;
inputs[2].weight = 0.25f;
inputs[3].data = f1;
inputs[3].weight = 0.25f;
ufbxi_check_err(&sc->error, sum_fn(sum_user, dst, inputs, 4));
} else if (crease >= 1.0f) {
inputs[0].data = v0;
inputs[0].weight = 0.5f;
inputs[1].data = v1;
inputs[1].weight = 0.5f;
ufbxi_check_err(&sc->error, sum_fn(sum_user, dst, inputs, 2));
} else if (crease < 1.0f) {
const char *f0 = face_values + topo[ix].face * stride;
const char *f1 = face_values + topo[twin].face * stride;
ufbx_real w0 = 0.25f + 0.25f * crease;
ufbx_real w1 = 0.25f - 0.25f * crease;
inputs[0].data = v0;
inputs[0].weight = w0;
inputs[1].data = v1;
inputs[1].weight = w0;
inputs[2].data = f0;
inputs[2].weight = w1;
inputs[3].data = f1;
inputs[3].weight = w1;
ufbxi_check_err(&sc->error, sum_fn(sum_user, dst, inputs, 4));
}
}
for (size_t vi = 0; vi < mesh->num_vertices; vi++) {
uint32_t original_start = mesh->vertex_first_index.data[vi];
if (original_start == UFBX_NO_INDEX) continue;
uint32_t start = original_start;
for (uint32_t cur = start;;) {
uint32_t prev = ufbx_topo_prev_vertex_edge(topo, num_topo, cur);
if (prev == UFBX_NO_INDEX) { start = cur; break; }
if (ufbxi_is_edge_split(input, topo, prev)) start = cur;
if (prev == original_start) break;
cur = prev;
}
original_start = start;
while (start != UFBX_NO_INDEX) {
if (start != original_start) {
output->unique_per_vertex = false;
}
uint32_t value_index = (uint32_t)num_vertex_values++;
char *dst = vertex_values + value_index * stride;
ufbx_real total_crease = 0.0f;
size_t num_crease = 0;
size_t num_split = 0;
bool on_boundary = false;
bool non_manifold = false;
size_t crease_input_indices[2];
uint32_t start_prev = topo[start].prev;
uint32_t end_edge = topo[start_prev].twin;
size_t valence = 2;
non_manifold |= (topo[start].flags & UFBX_TOPO_NON_MANIFOLD) != 0;
non_manifold |= (topo[start_prev].flags & UFBX_TOPO_NON_MANIFOLD) != 0;
const char *v0 = (const char*)input->values + input->indices[start] * stride;
size_t num_inputs = 4;
{
const char *e0 = (const char*)input->values + input->indices[topo[start].next] * stride;
const char *e1 = (const char*)input->values + input->indices[start_prev] * stride;
const char *f0 = face_values + topo[start].face * stride;
inputs[0].data = v0;
inputs[1].data = e0;
inputs[2].data = e1;
inputs[3].data = f0;
}
bool start_split = ufbxi_is_edge_split(input, topo, start);
bool prev_split = end_edge != UFBX_NO_INDEX && ufbxi_is_edge_split(input, topo, end_edge);
ufbx_real start_crease = ufbxi_edge_crease(mesh, start_split, topo, start);
if (start_crease > 0.0f) {
total_crease += start_crease;
crease_input_indices[num_crease++] = 1;
}
ufbx_real prev_crease = ufbxi_edge_crease(mesh, prev_split, topo, start_prev);
if (prev_crease > 0.0f) {
total_crease += prev_crease;
crease_input_indices[num_crease++] = 2;
}
if (end_edge != UFBX_NO_INDEX) {
if (prev_split) {
num_split++;
}
} else {
on_boundary = true;
}
ufbxi_check_err(&sc->error, vertex_indices[start] == UFBX_NO_INDEX);
vertex_indices[start] = value_index;
if (start_split) {
start = ufbx_topo_next_vertex_edge(topo, num_topo, start);
num_split++;
} else {
uint32_t cur = start;
for (;;) {
cur = ufbx_topo_next_vertex_edge(topo, num_topo, cur);
if (cur == UFBX_NO_INDEX) {
on_boundary = true;
start = UFBX_NO_INDEX;
break;
}
non_manifold |= (topo[cur].flags & UFBX_TOPO_NON_MANIFOLD) != 0;
ufbxi_check_err(&sc->error, vertex_indices[cur] == UFBX_NO_INDEX);
vertex_indices[cur] = value_index;
bool split = ufbxi_is_edge_split(input, topo, cur);
if (cur == end_edge && !split) {
ufbxi_check_err(&sc->error, ufbxi_grow_array(&sc->ator_tmp, &sc->inputs, &sc->inputs_cap, num_inputs + 1));
const char *f0 = face_values + topo[cur].face * stride;
inputs[num_inputs].data = f0;
start = UFBX_NO_INDEX;
num_inputs += 1;
break;
}
ufbx_real cur_crease = ufbxi_edge_crease(mesh, split, topo, cur);
if (cur_crease > 0.0f) {
total_crease += cur_crease;
if (num_crease < 2) crease_input_indices[num_crease] = num_inputs;
num_crease++;
}
{
ufbxi_check_err(&sc->error, ufbxi_grow_array(&sc->ator_tmp, &sc->inputs, &sc->inputs_cap, num_inputs + 2));
inputs = sc->inputs;
const char *e0 = (char*)input->values + input->indices[topo[cur].next] * stride;
const char *f0 = face_values + topo[cur].face * stride;
inputs[num_inputs + 0].data = e0;
inputs[num_inputs + 1].data = f0;
num_inputs += 2;
}
valence++;
if (split) {
start = ufbx_topo_next_vertex_edge(topo, num_topo, cur);
num_split++;
break;
}
}
}
if (start == original_start) start = UFBX_NO_INDEX;
ufbx_real fe_weight = 1.0f / (ufbx_real)(valence*valence);
ufbx_real v_weight = (ufbx_real)(valence - 2) / (ufbx_real)valence;
if (num_crease > 2
|| (sharp_corners && valence == 2 && (num_split > 0 || on_boundary))
|| (sharp_splits && (num_split > 0 || on_boundary))
|| sharp_all
|| non_manifold) {
inputs[0].data = v0;
inputs[0].weight = 1.0f;
num_inputs = 1;
} else if (num_crease == 2) {
total_crease *= 0.5f;
if (total_crease < 0.0f) total_crease = 0.0f;
if (total_crease > 1.0f) total_crease = 1.0f;
inputs[0].weight = v_weight * (1.0f - total_crease) + 0.75f * total_crease;
ufbx_real few = fe_weight * (1.0f - total_crease);
for (size_t i = 1; i < num_inputs; i++) {
inputs[i].weight = few;
}
inputs[crease_input_indices[0]].weight += 0.125f * total_crease;
inputs[crease_input_indices[1]].weight += 0.125f * total_crease;
} else {
inputs[0].weight = v_weight;
for (size_t i = 1; i < num_inputs; i++) {
inputs[i].weight = fe_weight;
}
}
if (mesh->vertex_crease.exists) {
ufbx_real v = ufbx_get_vertex_real(&mesh->vertex_crease, original_start);
v *= (ufbx_real)10.0;
if (v > 0.0f) {
if (v > 1.0) v = 1.0f;
ufbx_real iv = 1.0f - v;
inputs[0].weight = 1.0f * v + (inputs[0].weight) * iv;
for (size_t i = 1; i < num_inputs; i++) {
inputs[i].weight *= iv;
}
}
}
#if defined(UFBX_REGRESSION)
{
ufbx_real total_weight = 0.0f;
for (size_t i = 0; i < num_inputs; i++) {
total_weight += inputs[i].weight;
}
ufbx_assert(ufbx_fabs(total_weight - 1.0f) < 0.001f);
}
#endif
ufbxi_check_err(&sc->error, sum_fn(sum_user, dst, inputs, num_inputs));
}
}
for (size_t old_ix = 0; old_ix < mesh->num_indices; old_ix++) {
uint32_t ix = vertex_indices[old_ix];
if (ix == UFBX_NO_INDEX) {
ix = (uint32_t)num_vertex_values++;
vertex_indices[old_ix] = ix;
const char *src = (const char*)input->values + input->indices[old_ix] * stride;
char *dst = vertex_values + ix * stride;
inputs[0].data = src;
inputs[0].weight = 1.0f;
ufbxi_check_err(&sc->error, sum_fn(sum_user, dst, inputs, 1));
}
}
ufbx_assert(num_vertex_values <= mesh->num_indices);
size_t num_values = num_edge_values + mesh->num_faces + num_vertex_values;
char *new_values = (char*)ufbxi_push_size(&sc->result, stride, (num_values+1));
ufbxi_check_err(&sc->error, new_values);
memset(new_values, 0, stride);
new_values += stride;
memcpy(new_values, values, num_values * stride);
output->values = new_values;
output->num_values = num_values;
if (!input->ignore_indices) {
uint32_t *new_indices = ufbxi_push(&sc->result, uint32_t, mesh->num_indices * 4);
ufbxi_check_err(&sc->error, new_indices);
uint32_t face_start = 0;
uint32_t edge_start = (uint32_t)(face_start + mesh->num_faces);
uint32_t vert_start = (uint32_t)(edge_start + num_edge_values);
uint32_t *p_ix = new_indices;
for (size_t ix = 0; ix < mesh->num_indices; ix++) {
p_ix[0] = vert_start + vertex_indices[ix];
p_ix[1] = edge_start + edge_indices[ix];
p_ix[2] = face_start + topo[ix].face;
p_ix[3] = edge_start + edge_indices[topo[ix].prev];
p_ix += 4;
}
output->indices = new_indices;
output->num_indices = mesh->num_indices * 4;
} else {
output->indices = NULL;
output->num_indices = 0;
}
return 1;
}
static ufbxi_noinline int ufbxi_subdivide_attrib(ufbxi_subdivide_context *sc, ufbx_vertex_attrib *attrib, ufbx_subdivision_boundary boundary, bool check_split_data)
{
if (!attrib->exists) return 1;
ufbx_assert(attrib->value_reals >= 2 && attrib->value_reals <= 4);
ufbxi_subdivide_layer_input input;
input.sum_fn = ufbxi_real_sum_fns[attrib->value_reals - 1];
input.sum_user = NULL;
input.values = attrib->values.data;
input.indices = attrib->indices.data;
input.stride = attrib->value_reals * sizeof(ufbx_real);
input.boundary = boundary;
input.check_split_data = check_split_data;
input.ignore_indices = false;
ufbxi_subdivide_layer_output output;
ufbxi_check_err(&sc->error, ufbxi_subdivide_layer(sc, &output, &input));
attrib->values.data = output.values;
attrib->indices.data = output.indices;
attrib->values.count = output.num_values;
attrib->indices.count = output.num_indices;
return 1;
}
static ufbxi_noinline ufbxi_subdivision_vertex_weights *ufbxi_subdivision_copy_weights(ufbxi_subdivide_context *sc, ufbx_subdivision_weight_range_list ranges, ufbx_subdivision_weight_list weights)
{
ufbxi_subdivision_vertex_weights *dst = ufbxi_push(&sc->tmp, ufbxi_subdivision_vertex_weights, ranges.count);
ufbxi_check_return_err(&sc->error, dst, NULL);
ufbxi_nounroll for (size_t i = 0; i != ranges.count; i++) {
ufbx_subdivision_weight_range range = ranges.data[i];
dst[i].weights = weights.data + range.weight_begin;
dst[i].num_weights = range.num_weights;
}
return dst;
}
static ufbxi_noinline ufbxi_subdivision_vertex_weights *ufbxi_init_source_vertex_weights(ufbxi_subdivide_context *sc, size_t num_vertices)
{
ufbxi_subdivision_vertex_weights *dst = ufbxi_push(&sc->tmp, ufbxi_subdivision_vertex_weights, num_vertices);
ufbx_subdivision_weight *weights = ufbxi_push(&sc->tmp, ufbx_subdivision_weight, num_vertices);
ufbxi_check_return_err(&sc->error, dst && weights, NULL);
ufbxi_nounroll for (size_t i = 0; i != num_vertices; i++) {
dst[i].weights = weights + i;
dst[i].num_weights = 1;
weights[i].index = (uint32_t)i;
weights[i].weight = 1.0f;
}
return dst;
}
static ufbxi_noinline ufbxi_subdivision_vertex_weights *ufbxi_init_skin_weights(ufbxi_subdivide_context *sc, size_t num_vertices, const ufbx_skin_deformer *skin)
{
ufbxi_subdivision_vertex_weights *dst = ufbxi_push(&sc->tmp, ufbxi_subdivision_vertex_weights, num_vertices);
ufbxi_check_return_err(&sc->error, dst, NULL);
for (size_t i = 0; i < num_vertices; i++) {
ufbxi_dev_assert(i < skin->vertices.count);
ufbx_skin_vertex vertex = skin->vertices.data[i];
size_t num_weights = ufbxi_min_sz(sc->max_vertex_weights, vertex.num_weights);
ufbx_subdivision_weight *weights = ufbxi_push(&sc->tmp, ufbx_subdivision_weight, num_weights);
ufbxi_check_err(&sc->error, weights);
const ufbx_skin_weight *skin_weights = skin->weights.data + vertex.weight_begin;
dst[i].weights = weights;
dst[i].num_weights = num_weights;
ufbxi_nounroll for (size_t wi = 0; wi != num_weights; wi++) {
ufbxi_check_err(&sc->error, skin_weights[wi].cluster_index <= INT32_MAX);
weights[wi].index = skin_weights[wi].cluster_index;
weights[wi].weight = skin_weights[wi].weight;
}
}
return dst;
}
static ufbxi_noinline int ufbxi_subdivide_weights(ufbxi_subdivide_context *sc, ufbx_subdivision_weight_range_list *ranges,
ufbx_subdivision_weight_list *weights, const ufbxi_subdivision_vertex_weights *src)
{
ufbxi_check_err(&sc->error, src);
ufbxi_subdivide_layer_input input;
input.sum_fn = ufbxi_subdivide_sum_vertex_weights;
input.sum_user = sc;
input.values = src;
input.indices = sc->src_mesh.vertex_indices.data;
input.stride = sizeof(ufbxi_subdivision_vertex_weights);
input.boundary = sc->opts.boundary;
input.check_split_data = false;
input.ignore_indices = true;
sc->total_weights = 0;
ufbxi_subdivide_layer_output output;
ufbxi_check_err(&sc->error, ufbxi_subdivide_layer(sc, &output, &input));
size_t num_vertices = output.num_values;
ufbx_assert(num_vertices == sc->dst_mesh.vertex_position.values.count);
ufbx_subdivision_weight_range *dst_ranges = ufbxi_push(&sc->result, ufbx_subdivision_weight_range, num_vertices);
ufbx_subdivision_weight *dst_weights = ufbxi_push(&sc->result, ufbx_subdivision_weight, sc->total_weights);
ufbxi_check_err(&sc->error, ranges && weights);
ufbxi_subdivision_vertex_weights *src_weights = (ufbxi_subdivision_vertex_weights*)output.values;
size_t weight_offset = 0;
for (size_t vi = 0; vi < num_vertices; vi++) {
ufbxi_subdivision_vertex_weights ws = src_weights[vi];
ufbxi_check_err(&sc->error, (size_t)UINT32_MAX - weight_offset >= ws.num_weights);
dst_ranges[vi].weight_begin = (uint32_t)weight_offset;
dst_ranges[vi].num_weights = (uint32_t)ws.num_weights;
memcpy(dst_weights + weight_offset, ws.weights, ws.num_weights * sizeof(ufbx_subdivision_weight));
weight_offset += ws.num_weights;
}
ranges->data = dst_ranges;
ranges->count = num_vertices;
weights->data = dst_weights;
weights->count = sc->total_weights;
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_subdivide_vertex_crease(ufbxi_subdivide_context *sc, ufbx_vertex_real *ufbxi_restrict dst, const ufbx_vertex_real *ufbxi_restrict src)
{
size_t src_indices = src->indices.count;
size_t src_values = src->values.count;
dst->values.count = src_values + 1;
dst->values.data = ufbxi_push(&sc->result, ufbx_real, dst->values.count);
ufbxi_check_err(&sc->error, dst->values.data);
dst->values.data[src_values] = 0.0f;
dst->indices.count = src_indices * 4;
dst->indices.data = ufbxi_push(&sc->result, uint32_t, dst->indices.count);
ufbxi_check_err(&sc->error, dst->indices.data);
ufbxi_nounroll for (size_t i = 0; i < src_values; i++) {
ufbx_real crease = src->values.data[i];
if (crease < 0.999f) crease -= 0.1f;
if (crease < 0.0f) crease = 0.0f;
dst->values.data[i] = crease;
}
uint32_t zero_index = (uint32_t)src_values;
ufbxi_nounroll for (size_t i = 0; i < src_indices; i++) {
uint32_t *quad = dst->indices.data + i * 4;
quad[0] = src->indices.data[i];
quad[1] = zero_index;
quad[2] = zero_index;
quad[3] = zero_index;
}
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_subdivide_mesh_level(ufbxi_subdivide_context *sc)
{
const ufbx_mesh *mesh = &sc->src_mesh;
ufbx_mesh *result = &sc->dst_mesh;
*result = *mesh;
ufbx_topo_edge *topo = ufbxi_push(&sc->tmp, ufbx_topo_edge, mesh->num_indices);
ufbxi_check_err(&sc->error, topo);
ufbx_compute_topology(mesh, topo, mesh->num_indices);
sc->topo = topo;
sc->num_topo = mesh->num_indices;
ufbxi_check_err(&sc->error, ufbxi_subdivide_attrib(sc, (ufbx_vertex_attrib*)&result->vertex_position, sc->opts.boundary, false));
memset(&result->vertex_uv, 0, sizeof(result->vertex_uv));
memset(&result->vertex_tangent, 0, sizeof(result->vertex_tangent));
memset(&result->vertex_bitangent, 0, sizeof(result->vertex_bitangent));
memset(&result->vertex_color, 0, sizeof(result->vertex_color));
result->uv_sets.data = ufbxi_push_copy(&sc->result, ufbx_uv_set, result->uv_sets.count, result->uv_sets.data);
ufbxi_check_err(&sc->error, result->uv_sets.data);
result->color_sets.data = ufbxi_push_copy(&sc->result, ufbx_color_set, result->color_sets.count, result->color_sets.data);
ufbxi_check_err(&sc->error, result->color_sets.data);
ufbxi_for_list(ufbx_uv_set, set, result->uv_sets) {
ufbxi_check_err(&sc->error, ufbxi_subdivide_attrib(sc, (ufbx_vertex_attrib*)&set->vertex_uv, sc->opts.uv_boundary, true));
if (sc->opts.interpolate_tangents) {
ufbxi_check_err(&sc->error, ufbxi_subdivide_attrib(sc, (ufbx_vertex_attrib*)&set->vertex_tangent, sc->opts.uv_boundary, true));
ufbxi_check_err(&sc->error, ufbxi_subdivide_attrib(sc, (ufbx_vertex_attrib*)&set->vertex_bitangent, sc->opts.uv_boundary, true));
} else {
memset(&set->vertex_tangent, 0, sizeof(set->vertex_tangent));
memset(&set->vertex_bitangent, 0, sizeof(set->vertex_bitangent));
}
}
ufbxi_for_list(ufbx_color_set, set, result->color_sets) {
ufbxi_check_err(&sc->error, ufbxi_subdivide_attrib(sc, (ufbx_vertex_attrib*)&set->vertex_color, sc->opts.uv_boundary, true));
}
if (result->uv_sets.count > 0) {
result->vertex_uv = result->uv_sets.data[0].vertex_uv;
result->vertex_bitangent = result->uv_sets.data[0].vertex_bitangent;
result->vertex_tangent = result->uv_sets.data[0].vertex_tangent;
}
if (result->color_sets.count > 0) {
result->vertex_color = result->color_sets.data[0].vertex_color;
}
if (sc->opts.interpolate_normals && !sc->opts.ignore_normals) {
ufbxi_check_err(&sc->error, ufbxi_subdivide_attrib(sc, (ufbx_vertex_attrib*)&result->vertex_normal, sc->opts.boundary, true));
ufbxi_for_list(ufbx_vec3, normal, result->vertex_normal.values) {
*normal = ufbxi_slow_normalize3(normal);
}
if (mesh->skinned_normal.values.data == mesh->vertex_normal.values.data) {
result->skinned_normal = result->vertex_normal;
} else {
ufbxi_check_err(&sc->error, ufbxi_subdivide_attrib(sc, (ufbx_vertex_attrib*)&result->skinned_normal, sc->opts.boundary, true));
ufbxi_for_list(ufbx_vec3, normal, result->skinned_normal.values) {
*normal = ufbxi_slow_normalize3(normal);
}
}
}
if (result->vertex_crease.exists) {
ufbxi_check_err(&sc->error, ufbxi_subdivide_vertex_crease(sc, &result->vertex_crease, &mesh->vertex_crease));
}
if (mesh->skinned_position.values.data == mesh->vertex_position.values.data) {
result->skinned_position = result->vertex_position;
} else {
ufbxi_check_err(&sc->error, ufbxi_subdivide_attrib(sc, (ufbx_vertex_attrib*)&result->skinned_position, sc->opts.boundary, false));
}
ufbx_subdivision_result *result_sub = ufbxi_push_zero(&sc->result, ufbx_subdivision_result, 1);
ufbxi_check_err(&sc->error, result_sub);
result->subdivision_result = result_sub;
if (sc->opts.evaluate_source_vertices || sc->opts.evaluate_skin_weights) {
ufbx_subdivision_result *mesh_sub = mesh->subdivision_result;
ufbx_skin_deformer *skin = NULL;
if (sc->opts.evaluate_skin_weights) {
if (mesh->skin_deformers.count > 0) {
ufbxi_check_err(&sc->error, sc->opts.skin_deformer_index < mesh->skin_deformers.count);
skin = mesh->skin_deformers.data[sc->opts.skin_deformer_index];
}
}
size_t max_weights = 0;
if (sc->opts.evaluate_source_vertices) {
max_weights = ufbxi_max_sz(max_weights, mesh->num_vertices);
}
if (skin) {
max_weights = ufbxi_max_sz(max_weights, skin->clusters.count);
}
sc->tmp_vertex_weights = ufbxi_push_zero(&sc->tmp, ufbx_real, mesh->num_vertices);
sc->tmp_weights = ufbxi_push(&sc->tmp, ufbx_subdivision_weight, max_weights);
ufbxi_check_err(&sc->error, sc->tmp_vertex_weights && sc->tmp_weights);
if (sc->opts.evaluate_source_vertices) {
sc->max_vertex_weights = sc->opts.max_source_vertices ? sc->opts.max_source_vertices : SIZE_MAX;
ufbxi_subdivision_vertex_weights *weights;
if (mesh_sub && mesh_sub->source_vertex_ranges.count > 0) {
weights = ufbxi_subdivision_copy_weights(sc, mesh_sub->source_vertex_ranges, mesh_sub->source_vertex_weights);
} else {
weights = ufbxi_init_source_vertex_weights(sc, mesh->num_vertices);
}
ufbxi_check_err(&sc->error, ufbxi_subdivide_weights(sc, &result_sub->source_vertex_ranges, &result_sub->source_vertex_weights, weights));
}
if (skin) {
sc->max_vertex_weights = sc->opts.max_skin_weights ? sc->opts.max_skin_weights : SIZE_MAX;
ufbxi_subdivision_vertex_weights *weights;
if (mesh_sub && mesh_sub->source_vertex_ranges.count > 0) {
weights = ufbxi_subdivision_copy_weights(sc, mesh_sub->skin_cluster_ranges, mesh_sub->skin_cluster_weights);
} else {
weights = ufbxi_init_skin_weights(sc, mesh->num_vertices, skin);
}
ufbxi_check_err(&sc->error, ufbxi_subdivide_weights(sc, &result_sub->skin_cluster_ranges, &result_sub->skin_cluster_weights, weights));
}
}
result->num_vertices = result->vertex_position.values.count;
result->num_indices = mesh->num_indices * 4;
result->num_faces = mesh->num_indices;
result->num_triangles = mesh->num_indices * 2;
result->vertex_indices.data = result->vertex_position.indices.data;
result->vertex_indices.count = result->num_indices;
result->vertices.data = result->vertex_position.values.data;
result->vertices.count = result->num_vertices;
result->faces.count = result->num_faces;
result->faces.data = ufbxi_push(&sc->result, ufbx_face, result->num_faces);
ufbxi_check_err(&sc->error, result->faces.data);
for (size_t i = 0; i < result->num_faces; i++) {
result->faces.data[i].index_begin = (uint32_t)(i * 4);
result->faces.data[i].num_indices = 4;
}
if (mesh->edges.data) {
result->num_edges = mesh->num_edges*2 + result->num_faces;
result->edges.count = result->num_edges;
result->edges.data = ufbxi_push(&sc->result, ufbx_edge, result->num_edges);
ufbxi_check_err(&sc->error, result->edges.data);
if (mesh->edge_crease.data) {
result->edge_crease.count = result->num_edges;
result->edge_crease.data = ufbxi_push(&sc->result, ufbx_real, result->num_edges);
ufbxi_check_err(&sc->error, result->edge_crease.data);
}
if (mesh->edge_smoothing.data) {
result->edge_smoothing.count = result->num_edges;
result->edge_smoothing.data = ufbxi_push(&sc->result, bool, result->num_edges);
ufbxi_check_err(&sc->error, result->edge_smoothing.data);
}
if (mesh->edge_visibility.data) {
result->edge_visibility.count = result->num_edges;
result->edge_visibility.data = ufbxi_push(&sc->result, bool, result->num_edges);
ufbxi_check_err(&sc->error, result->edge_visibility.data);
}
size_t di = 0;
for (size_t i = 0; i < mesh->num_edges; i++) {
ufbx_edge edge = mesh->edges.data[i];
uint32_t face_ix = topo[edge.a].face;
ufbx_face face = mesh->faces.data[face_ix];
uint32_t offset = edge.a - face.index_begin;
uint32_t next = (offset + 1) % (uint32_t)face.num_indices;
uint32_t a = (face.index_begin + offset) * 4;
uint32_t b = (face.index_begin + next) * 4;
result->edges.data[di + 0].a = a;
result->edges.data[di + 0].b = a + 1;
result->edges.data[di + 1].a = b + 3;
result->edges.data[di + 1].b = b;
if (mesh->edge_crease.data) {
ufbx_real crease = mesh->edge_crease.data[i];
if (crease < 0.999f) crease -= (ufbx_real)0.1;
if (crease < 0.0f) crease = 0.0f;
result->edge_crease.data[di + 0] = crease;
result->edge_crease.data[di + 1] = crease;
}
if (mesh->edge_smoothing.data) {
result->edge_smoothing.data[di + 0] = mesh->edge_smoothing.data[i];
result->edge_smoothing.data[di + 1] = mesh->edge_smoothing.data[i];
}
if (mesh->edge_visibility.data) {
result->edge_visibility.data[di + 0] = mesh->edge_visibility.data[i];
result->edge_visibility.data[di + 1] = mesh->edge_visibility.data[i];
}
di += 2;
}
for (size_t fi = 0; fi < result->num_faces; fi++) {
result->edges.data[di].a = (uint32_t)(fi * 4 + 1);
result->edges.data[di].b = (uint32_t)(fi * 4 + 2);
if (result->edge_crease.data) {
result->edge_crease.data[di] = 0.0f;
}
if (result->edge_smoothing.data) {
result->edge_smoothing.data[di + 0] = true;
}
if (result->edge_visibility.data) {
result->edge_visibility.data[di + 0] = false;
}
di++;
}
}
if (mesh->face_material.data) {
result->face_material.count = result->num_faces;
result->face_material.data = ufbxi_push(&sc->result, uint32_t, result->num_faces);
ufbxi_check_err(&sc->error, result->face_material.data);
}
if (mesh->face_smoothing.data) {
result->face_smoothing.count = result->num_faces;
result->face_smoothing.data = ufbxi_push(&sc->result, bool, result->num_faces);
ufbxi_check_err(&sc->error, result->face_smoothing.data);
}
if (mesh->face_group.data) {
result->face_group.count = result->num_faces;
result->face_group.data = ufbxi_push(&sc->result, uint32_t, result->num_faces);
ufbxi_check_err(&sc->error, result->face_group.data);
}
if (mesh->face_hole.data) {
result->face_hole.count = result->num_faces;
result->face_hole.data = ufbxi_push(&sc->result, bool, result->num_faces);
ufbxi_check_err(&sc->error, result->face_hole.data);
}
if (result->material_parts.count > 0) {
result->material_parts.data = ufbxi_push_zero(&sc->result, ufbx_mesh_part, result->material_parts.count);
ufbxi_check_err(&sc->error, result->materials.data);
}
size_t index_offset = 0;
for (size_t i = 0; i < mesh->num_faces; i++) {
ufbx_face face = mesh->faces.data[i];
uint32_t mat = 0;
if (mesh->face_material.data) {
mat = mesh->face_material.data[i];
for (size_t ci = 0; ci < face.num_indices; ci++) {
result->face_material.data[index_offset + ci] = mat;
}
}
if (mesh->face_smoothing.data) {
bool flag = mesh->face_smoothing.data[i];
for (size_t ci = 0; ci < face.num_indices; ci++) {
result->face_smoothing.data[index_offset + ci] = flag;
}
}
if (mesh->face_group.data) {
uint32_t group = mesh->face_group.data[i];
for (size_t ci = 0; ci < face.num_indices; ci++) {
result->face_group.data[index_offset + ci] = group;
}
}
if (mesh->face_hole.data) {
bool flag = mesh->face_hole.data[i];
for (size_t ci = 0; ci < face.num_indices; ci++) {
result->face_hole.data[index_offset + ci] = flag;
}
}
index_offset += face.num_indices;
}
result->vertex_first_index.count = 0;
ufbxi_check_err(&sc->error, ufbxi_finalize_mesh_material(&sc->result, &sc->error, result));
ufbxi_check_err(&sc->error, ufbxi_finalize_mesh(&sc->result, &sc->error, result));
ufbxi_check_err(&sc->error, ufbxi_update_face_groups(&sc->result, &sc->error, result, true));
return 1;
}
ufbxi_nodiscard static ufbxi_noinline int ufbxi_subdivide_mesh_imp(ufbxi_subdivide_context *sc, size_t level)
{
if (sc->opts.boundary == UFBX_SUBDIVISION_BOUNDARY_DEFAULT) {
sc->opts.boundary = sc->src_mesh.subdivision_boundary;
}
if (sc->opts.uv_boundary == UFBX_SUBDIVISION_BOUNDARY_DEFAULT) {
sc->opts.uv_boundary = sc->src_mesh.subdivision_uv_boundary;
}
ufbxi_init_ator(&sc->error, &sc->ator_tmp, &sc->opts.temp_allocator, "temp");
ufbxi_init_ator(&sc->error, &sc->ator_result, &sc->opts.result_allocator, "result");
sc->result.unordered = true;
sc->source.unordered = true;
sc->tmp.unordered = true;
sc->source.ator = &sc->ator_tmp;
sc->tmp.ator = &sc->ator_tmp;
for (size_t i = 1; i < level; i++) {
sc->result.ator = &sc->ator_tmp;
ufbxi_check_err(&sc->error, ufbxi_subdivide_mesh_level(sc));
sc->src_mesh = sc->dst_mesh;
ufbxi_buf_free(&sc->source);
ufbxi_buf_free(&sc->tmp);
sc->source = sc->result;
memset(&sc->result, 0, sizeof(sc->result));
}
sc->result.ator = &sc->ator_result;
ufbxi_check_err(&sc->error, ufbxi_subdivide_mesh_level(sc));
ufbxi_buf_free(&sc->tmp);
ufbx_mesh *mesh = &sc->dst_mesh;
mesh->max_face_triangles = 2;
mesh->num_empty_faces = 0;
mesh->num_point_faces = 0;
mesh->num_line_faces = 0;
if (!sc->opts.interpolate_normals) {
memset(&mesh->vertex_normal, 0, sizeof(mesh->vertex_normal));
memset(&mesh->skinned_normal, 0, sizeof(mesh->skinned_normal));
}
if (!sc->opts.interpolate_normals && !sc->opts.ignore_normals) {
ufbx_topo_edge *topo = ufbxi_push(&sc->tmp, ufbx_topo_edge, mesh->num_indices);
ufbxi_check_err(&sc->error, topo);
ufbx_compute_topology(mesh, topo, mesh->num_indices);
uint32_t *normal_indices = ufbxi_push(&sc->result, uint32_t, mesh->num_indices);
ufbxi_check_err(&sc->error, normal_indices);
size_t num_normals = ufbx_generate_normal_mapping(mesh, topo, mesh->num_indices, normal_indices, mesh->num_indices, true);
if (num_normals == mesh->num_vertices) {
mesh->skinned_normal.unique_per_vertex = true;
}
ufbx_vec3 *normal_data = ufbxi_push(&sc->result, ufbx_vec3, num_normals + 1);
ufbxi_check_err(&sc->error, normal_data);
normal_data[0] = ufbx_zero_vec3;
normal_data++;
ufbx_compute_normals(mesh, &mesh->skinned_position, normal_indices, mesh->num_indices, normal_data, num_normals);
mesh->generated_normals = true;
mesh->vertex_normal.exists = true;
mesh->vertex_normal.values.data = normal_data;
mesh->vertex_normal.values.count = num_normals;
mesh->vertex_normal.indices.data = normal_indices;
mesh->vertex_normal.indices.count = mesh->num_indices;
mesh->skinned_normal = mesh->vertex_normal;
}
ufbxi_refcount *parent = NULL;
if (sc->src_mesh_ptr->subdivision_evaluated && sc->src_mesh_ptr->from_tessellated_nurbs) {
parent = &(ufbxi_get_imp(ufbxi_mesh_imp, sc->src_mesh_ptr))->refcount;
} else {
parent = &(ufbxi_get_imp(ufbxi_scene_imp, sc->src_mesh_ptr->element.scene))->refcount;
}
ufbxi_patch_mesh_reals(mesh);
sc->imp = ufbxi_push(&sc->result, ufbxi_mesh_imp, 1);
ufbxi_check_err(&sc->error, sc->imp);
sc->dst_mesh.subdivision_result->result_memory_used = sc->ator_result.current_size;
sc->dst_mesh.subdivision_result->temp_memory_used = sc->ator_tmp.current_size;
sc->dst_mesh.subdivision_result->result_allocs = sc->ator_result.num_allocs;
sc->dst_mesh.subdivision_result->temp_allocs = sc->ator_tmp.num_allocs;
ufbxi_init_ref(&sc->imp->refcount, UFBXI_MESH_IMP_MAGIC, parent);
sc->imp->magic = UFBXI_MESH_IMP_MAGIC;
sc->imp->mesh = sc->dst_mesh;
sc->imp->refcount.ator = sc->ator_result;
sc->imp->refcount.buf = sc->result;
sc->imp->mesh.subdivision_evaluated = true;
return 1;
}
ufbxi_noinline static ufbx_mesh *ufbxi_subdivide_mesh(const ufbx_mesh *mesh, size_t level, const ufbx_subdivide_opts *user_opts, ufbx_error *p_error)
{
ufbxi_subdivide_context sc = { 0 };
if (user_opts) {
sc.opts = *user_opts;
}
sc.src_mesh_ptr = (ufbx_mesh*)mesh;
sc.src_mesh = *mesh;
int ok = ufbxi_subdivide_mesh_imp(&sc, level);
ufbxi_free(&sc.ator_tmp, ufbxi_subdivide_input, sc.inputs, sc.inputs_cap);
ufbxi_buf_free(&sc.tmp);
ufbxi_buf_free(&sc.source);
if (ok) {
ufbxi_free_ator(&sc.ator_tmp);
if (p_error) {
ufbxi_clear_error(p_error);
}
ufbxi_mesh_imp *imp = sc.imp;
return &imp->mesh;
} else {
ufbxi_fix_error_type(&sc.error, "Failed to subdivide");
if (p_error) *p_error = sc.error;
ufbxi_buf_free(&sc.result);
ufbxi_free_ator(&sc.ator_tmp);
ufbxi_free_ator(&sc.ator_result);
return NULL;
}
}
#else
ufbxi_noinline static ufbx_mesh *ufbxi_subdivide_mesh(const ufbx_mesh *mesh, size_t level, const ufbx_subdivide_opts *user_opts, ufbx_error *p_error)
{ … }
#endif
#if UFBXI_FEATURE_INDEX_GENERATION
static int ufbxi_map_cmp_vertex(void *user, const void *va, const void *vb)
{
size_t size = *(size_t*)user;
#if defined(UFBX_REGRESSION)
ufbx_assert(size % 8 == 0);
#endif
for (size_t i = 0; i < size; i += 8) {
uint64_t a = *(const uint64_t*)((const char*)va + i);
uint64_t b = *(const uint64_t*)((const char*)vb + i);
if (a != b) return a < b ? -1 : +1;
}
return 0;
}
typedef struct {
char *begin, *ptr;
size_t vertex_size;
size_t packed_offset;
} ufbxi_vertex_stream;
static ufbxi_noinline size_t ufbxi_generate_indices(const ufbx_vertex_stream *user_streams, size_t num_streams, uint32_t *indices, size_t num_indices, const ufbx_allocator_opts *allocator, ufbx_error *error)
{
bool fail = false;
ufbxi_allocator ator = { 0 };
ufbxi_init_ator(error, &ator, allocator, "allocator");
ufbxi_vertex_stream local_streams[16];
uint64_t local_packed_vertex[64];
ufbxi_vertex_stream *streams = NULL;
if (num_streams > ufbxi_arraycount(local_streams)) {
streams = ufbxi_alloc(&ator, ufbxi_vertex_stream, num_streams);
if (!streams) fail = true;
} else {
streams = local_streams;
}
size_t packed_size = 0;
if (!fail) {
for (size_t i = 0; i < num_streams; i++) {
if (user_streams[i].vertex_count < num_indices) {
ufbxi_fmt_err_info(error, "%zu", i);
ufbxi_report_err_msg(error, "user_streams[i].vertex_count < num_indices", "Truncated vertex stream");
fail = true;
break;
}
size_t vertex_size = user_streams[i].vertex_size;
size_t align = ufbxi_size_align_mask(vertex_size);
packed_size = ufbxi_align_to_mask(packed_size, align);
streams[i].ptr = streams[i].begin = (char*)user_streams[i].data;
streams[i].vertex_size = vertex_size;
streams[i].packed_offset = packed_size;
packed_size += vertex_size;
}
packed_size = ufbxi_align_to_mask(packed_size, 7);
}
if (!fail && packed_size == 0) {
ufbxi_report_err_msg(error, "packed_size != 0", "Zero vertex size");
fail = true;
}
char *packed_vertex = NULL;
if (!fail) {
if (packed_size > sizeof(local_packed_vertex)) {
ufbx_assert(packed_size % 8 == 0);
packed_vertex = (char*)ufbxi_alloc(&ator, uint64_t, packed_size / 8);
if (!packed_vertex) fail = true;
} else {
packed_vertex = (char*)local_packed_vertex;
}
}
ufbxi_map map = { 0 };
ufbxi_map_init(&map, &ator, &ufbxi_map_cmp_vertex, &packed_size);
if (num_indices > 0 && !ufbxi_map_grow_size(&map, packed_size, num_indices)) {
fail = true;
}
if (!fail) {
ufbx_assert(packed_vertex != NULL);
memset(packed_vertex, 0, packed_size);
for (size_t i = 0; i < num_indices; i++) {
for (size_t si = 0; si < num_streams; si++) {
size_t size = streams[si].vertex_size, offset = streams[si].packed_offset;
char *ptr = streams[si].ptr;
memcpy(packed_vertex + offset, ptr, size);
streams[si].ptr = ptr + size;
}
uint32_t hash = ufbxi_hash_string(packed_vertex, packed_size);
void *entry = ufbxi_map_find_size(&map, packed_size, hash, packed_vertex);
if (!entry) {
entry = ufbxi_map_insert_size(&map, packed_size, hash, packed_vertex);
if (!entry) {
fail = true;
break;
}
memcpy(entry, packed_vertex, packed_size);
}
uint32_t index = (uint32_t)(ufbxi_to_size((char*)entry - (char*)map.items) / packed_size);
indices[i] = index;
}
}
size_t result_vertices = 0;
if (!fail) {
result_vertices = map.size;
for (size_t si = 0; si < num_streams; si++) {
size_t vertex_size = streams[si].vertex_size;
char *dst = streams[si].begin;
char *src = ufbxi_add_ptr((char*)map.items, streams[si].packed_offset);
for (size_t i = 0; i < result_vertices; i++) {
memcpy(dst, src, vertex_size);
dst += vertex_size;
src += packed_size;
}
}
ufbxi_clear_error(error);
} else {
ufbxi_fix_error_type(error, "Failed to generate indices");
}
if (streams && streams != local_streams) {
ufbxi_free(&ator, ufbxi_vertex_stream, streams, num_streams);
}
if (packed_vertex && packed_vertex != (char*)local_packed_vertex) {
ufbxi_free(&ator, uint64_t, packed_vertex, packed_size / 8);
}
ufbxi_map_free(&map);
ufbxi_free_ator(&ator);
return result_vertices;
}
#else
static ufbxi_noinline size_t ufbxi_generate_indices(const ufbx_vertex_stream *user_streams, size_t num_streams, uint32_t *indices, size_t num_indices, const ufbx_allocator_opts *allocator, ufbx_error *error)
{ … }
#endif
static ufbxi_noinline void ufbxi_free_scene_imp(ufbxi_scene_imp *imp)
{ … }
static ufbxi_noinline void ufbxi_init_ref(ufbxi_refcount *refcount, uint32_t magic, ufbxi_refcount *parent)
{ … }
static ufbxi_noinline void ufbxi_retain_ref(ufbxi_refcount *refcount)
{ … }
static ufbxi_noinline void ufbxi_release_ref(ufbxi_refcount *refcount)
{ … }
static ufbxi_noinline void *ufbxi_uninitialized_options(ufbx_error *p_error)
{ … }
#define ufbxi_check_opts_ptr(m_type, m_opts, m_error) …
#define ufbxi_check_opts_return(m_value, m_opts, m_error) …
#define ufbxi_check_opts_return_no_error(m_value, m_opts) …
#ifdef __cplusplus
extern "C" {
#endif
ufbx_abi_data_def const ufbx_string ufbx_empty_string = …;
ufbx_abi_data_def const ufbx_blob ufbx_empty_blob = …;
ufbx_abi_data_def const ufbx_matrix ufbx_identity_matrix = …;
ufbx_abi_data_def const ufbx_transform ufbx_identity_transform = …;
ufbx_abi_data_def const ufbx_vec2 ufbx_zero_vec2 = …;
ufbx_abi_data_def const ufbx_vec3 ufbx_zero_vec3 = …;
ufbx_abi_data_def const ufbx_vec4 ufbx_zero_vec4 = …;
ufbx_abi_data_def const ufbx_quat ufbx_identity_quat = …;
ufbx_abi_data_def const ufbx_coordinate_axes ufbx_axes_right_handed_y_up = …;
ufbx_abi_data_def const ufbx_coordinate_axes ufbx_axes_right_handed_z_up = …;
ufbx_abi_data_def const ufbx_coordinate_axes ufbx_axes_left_handed_y_up = …;
ufbx_abi_data_def const ufbx_coordinate_axes ufbx_axes_left_handed_z_up = …;
ufbx_abi_data_def const size_t ufbx_element_type_size[UFBX_ELEMENT_TYPE_COUNT] = …;
ufbx_abi bool ufbx_open_file(ufbx_stream *stream, const char *path, size_t path_len)
{ … }
ufbx_abi bool ufbx_default_open_file(void *user, ufbx_stream *stream, const char *path, size_t path_len, const ufbx_open_file_info *info)
{ … }
ufbx_abi bool ufbx_open_memory(ufbx_stream *stream, const void *data, size_t data_size, const ufbx_open_memory_opts *opts, ufbx_error *error)
{ … }
ufbx_abi bool ufbx_is_thread_safe(void)
{ … }
ufbx_abi ufbx_scene *ufbx_load_memory(const void *data, size_t size, const ufbx_load_opts *opts, ufbx_error *error)
{ … }
ufbx_abi ufbx_scene *ufbx_load_file(const char *filename, const ufbx_load_opts *opts, ufbx_error *error)
{ … }
ufbx_abi ufbx_scene *ufbx_load_file_len(const char *filename, size_t filename_len, const ufbx_load_opts *opts, ufbx_error *error)
{ … }
ufbx_abi ufbx_scene *ufbx_load_stdio(void *file_void, const ufbx_load_opts *opts, ufbx_error *error)
{ … }
ufbx_abi ufbx_scene *ufbx_load_stdio_prefix(void *file_void, const void *prefix, size_t prefix_size, const ufbx_load_opts *opts, ufbx_error *error)
{ … }
ufbx_abi ufbx_scene *ufbx_load_stream(const ufbx_stream *stream, const ufbx_load_opts *opts, ufbx_error *error)
{ … }
ufbx_abi ufbx_scene *ufbx_load_stream_prefix(const ufbx_stream *stream, const void *prefix, size_t prefix_size, const ufbx_load_opts *opts, ufbx_error *error)
{ … }
ufbx_abi void ufbx_free_scene(ufbx_scene *scene)
{ … }
ufbx_abi void ufbx_retain_scene(ufbx_scene *scene)
{ … }
ufbx_abi ufbxi_noinline size_t ufbx_format_error(char *dst, size_t dst_size, const ufbx_error *error)
{ … }
ufbx_abi ufbx_prop *ufbx_find_prop_len(const ufbx_props *props, const char *name, size_t name_len)
{ … }
ufbx_abi ufbx_real ufbx_find_real_len(const ufbx_props *props, const char *name, size_t name_len, ufbx_real def)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_find_vec3_len(const ufbx_props *props, const char *name, size_t name_len, ufbx_vec3 def)
{ … }
ufbx_abi ufbxi_noinline int64_t ufbx_find_int_len(const ufbx_props *props, const char *name, size_t name_len, int64_t def)
{ … }
ufbx_abi bool ufbx_find_bool_len(const ufbx_props *props, const char *name, size_t name_len, bool def)
{ … }
ufbx_abi ufbxi_noinline ufbx_string ufbx_find_string_len(const ufbx_props *props, const char *name, size_t name_len, ufbx_string def)
{ … }
ufbx_abi ufbx_blob ufbx_find_blob_len(const ufbx_props *props, const char *name, size_t name_len, ufbx_blob def)
{ … }
ufbx_abi ufbx_prop *ufbx_find_prop_concat(const ufbx_props *props, const ufbx_string *parts, size_t num_parts)
{ … }
ufbx_abi ufbx_element *ufbx_find_element_len(const ufbx_scene *scene, ufbx_element_type type, const char *name, size_t name_len)
{ … }
ufbx_abi ufbx_element *ufbx_get_prop_element(const ufbx_element *element, const ufbx_prop *prop, ufbx_element_type type)
{ … }
ufbx_abi ufbx_element *ufbx_find_prop_element_len(const ufbx_element *element, const char *name, size_t name_len, ufbx_element_type type)
{ … }
ufbx_abi ufbx_node *ufbx_find_node_len(const ufbx_scene *scene, const char *name, size_t name_len)
{ … }
ufbx_abi ufbx_anim_stack *ufbx_find_anim_stack_len(const ufbx_scene *scene, const char *name, size_t name_len)
{ … }
ufbx_abi ufbx_material *ufbx_find_material_len(const ufbx_scene *scene, const char *name, size_t name_len)
{ … }
ufbx_abi ufbx_anim_prop *ufbx_find_anim_prop_len(const ufbx_anim_layer *layer, const ufbx_element *element, const char *prop, size_t prop_len)
{ … }
ufbx_abi ufbxi_noinline ufbx_anim_prop_list ufbx_find_anim_props(const ufbx_anim_layer *layer, const ufbx_element *element)
{ … }
ufbx_abi ufbxi_noinline ufbx_matrix ufbx_get_compatible_matrix_for_normals(const ufbx_node *node)
{ … }
ufbx_abi ufbx_real ufbx_evaluate_curve(const ufbx_anim_curve *curve, double time, ufbx_real default_value)
{ … }
ufbx_abi ufbxi_noinline ufbx_real ufbx_evaluate_anim_value_real(const ufbx_anim_value *anim_value, double time)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_evaluate_anim_value_vec3(const ufbx_anim_value *anim_value, double time)
{ … }
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)
{ … }
ufbx_abi ufbxi_noinline ufbx_props ufbx_evaluate_props(const ufbx_anim *anim, const ufbx_element *element, double time, ufbx_prop *buffer, size_t buffer_size)
{ … }
ufbx_abi ufbxi_noinline ufbx_transform ufbx_evaluate_transform(const ufbx_anim *anim, const ufbx_node *node, double time)
{ … }
static const char *const ufbxi_transform_props_all[] = …;
static const char *const ufbxi_transform_props_rotation[] = …;
static const char *const ufbxi_transform_props_scale[] = …;
static const char *const ufbxi_transform_props_rotation_scale[] = …;
ufbx_abi ufbxi_noinline ufbx_transform ufbx_evaluate_transform_flags(const ufbx_anim *anim, const ufbx_node *node, double time, uint32_t flags)
{ … }
ufbx_abi ufbx_real ufbx_evaluate_blend_weight(const ufbx_anim *anim, const ufbx_blend_channel *channel, double time)
{ … }
ufbx_abi ufbx_scene *ufbx_evaluate_scene(const ufbx_scene *scene, const ufbx_anim *anim, double time, const ufbx_evaluate_opts *opts, ufbx_error *error)
{ … }
ufbx_abi ufbx_anim *ufbx_create_anim(const ufbx_scene *scene, const ufbx_anim_opts *opts, ufbx_error *error)
{ … }
ufbx_abi void ufbx_free_anim(ufbx_anim *anim)
{ … }
ufbx_abi void ufbx_retain_anim(ufbx_anim *anim)
{ … }
ufbx_abi ufbx_baked_anim *ufbx_bake_anim(const ufbx_scene *scene, const ufbx_anim *anim, const ufbx_bake_opts *opts, ufbx_error *error)
{ … }
ufbx_abi void ufbx_retain_baked_anim(ufbx_baked_anim *bake)
{ … }
ufbx_abi void ufbx_free_baked_anim(ufbx_baked_anim *bake)
{ … }
ufbx_abi ufbx_baked_node *ufbx_find_baked_node_by_typed_id(ufbx_baked_anim *bake, uint32_t typed_id)
{ … }
ufbx_abi ufbx_baked_node *ufbx_find_baked_node(ufbx_baked_anim *bake, ufbx_node *node)
{ … }
ufbx_abi ufbx_baked_element *ufbx_find_baked_element_by_element_id(ufbx_baked_anim *bake, uint32_t element_id)
{ … }
ufbx_abi ufbx_baked_element *ufbx_find_baked_element(ufbx_baked_anim *bake, ufbx_element *element)
{ … }
ufbx_abi ufbx_vec3 ufbx_evaluate_baked_vec3(ufbx_baked_vec3_list keyframes, double time)
{ … }
ufbx_abi ufbx_quat ufbx_evaluate_baked_quat(ufbx_baked_quat_list keyframes, double time)
{ … }
ufbx_abi ufbx_bone_pose *ufbx_get_bone_pose(const ufbx_pose *pose, const ufbx_node *node)
{ … }
ufbx_abi ufbx_texture *ufbx_find_prop_texture_len(const ufbx_material *material, const char *name, size_t name_len)
{ … }
ufbx_abi ufbx_string ufbx_find_shader_prop_len(const ufbx_shader *shader, const char *name, size_t name_len)
{ … }
ufbx_abi ufbx_shader_prop_binding_list ufbx_find_shader_prop_bindings_len(const ufbx_shader *shader, const char *name, size_t name_len)
{ … }
ufbx_abi ufbx_shader_texture_input *ufbx_find_shader_texture_input_len(const ufbx_shader_texture *shader, const char *name, size_t name_len)
{ … }
ufbx_abi bool ufbx_coordinate_axes_valid(ufbx_coordinate_axes axes)
{ … }
ufbx_abi ufbx_quat ufbx_quat_mul(ufbx_quat a, ufbx_quat b)
{ … }
ufbx_abi ufbx_vec3 ufbx_vec3_normalize(ufbx_vec3 v)
{ … }
ufbx_abi ufbxi_noinline ufbx_real ufbx_quat_dot(ufbx_quat a, ufbx_quat b)
{ … }
ufbx_abi ufbxi_noinline ufbx_quat ufbx_quat_normalize(ufbx_quat q)
{ … }
ufbx_abi ufbxi_noinline ufbx_quat ufbx_quat_fix_antipodal(ufbx_quat q, ufbx_quat reference)
{ … }
ufbx_abi ufbxi_noinline ufbx_quat ufbx_quat_slerp(ufbx_quat a, ufbx_quat b, ufbx_real t)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_quat_rotate_vec3(ufbx_quat q, ufbx_vec3 v)
{ … }
ufbx_abi ufbxi_noinline ufbx_quat ufbx_euler_to_quat(ufbx_vec3 v, ufbx_rotation_order order)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_quat_to_euler(ufbx_quat q, ufbx_rotation_order order)
{ … }
ufbx_abi ufbxi_noinline ufbx_matrix ufbx_matrix_mul(const ufbx_matrix *a, const ufbx_matrix *b)
{ … }
ufbx_abi ufbx_real ufbx_matrix_determinant(const ufbx_matrix *m)
{ … }
ufbx_abi ufbx_matrix ufbx_matrix_invert(const ufbx_matrix *m)
{ … }
ufbx_abi ufbxi_noinline ufbx_matrix ufbx_matrix_for_normals(const ufbx_matrix *m)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_transform_position(const ufbx_matrix *m, ufbx_vec3 v)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_transform_direction(const ufbx_matrix *m, ufbx_vec3 v)
{ … }
ufbx_abi ufbxi_noinline ufbx_matrix ufbx_transform_to_matrix(const ufbx_transform *t)
{ … }
ufbx_abi ufbxi_noinline ufbx_transform ufbx_matrix_to_transform(const ufbx_matrix *m)
{ … }
ufbx_abi ufbxi_noinline ufbx_matrix ufbx_catch_get_skin_vertex_matrix(ufbx_panic *panic, const ufbx_skin_deformer *skin, size_t vertex, const ufbx_matrix *fallback)
{ … }
ufbx_abi ufbxi_noinline uint32_t ufbx_get_blend_shape_offset_index(const ufbx_blend_shape *shape, size_t vertex)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_get_blend_shape_vertex_offset(const ufbx_blend_shape *shape, size_t vertex)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_get_blend_vertex_offset(const ufbx_blend_deformer *blend, size_t vertex)
{ … }
ufbx_abi void ufbx_add_blend_shape_vertex_offsets(const ufbx_blend_shape *shape, ufbx_vec3 *vertices, size_t num_vertices, ufbx_real weight)
{ … }
ufbx_abi void ufbx_add_blend_vertex_offsets(const ufbx_blend_deformer *blend, ufbx_vec3 *vertices, size_t num_vertices, ufbx_real weight)
{ … }
ufbx_abi size_t ufbx_evaluate_nurbs_basis(const ufbx_nurbs_basis *basis, ufbx_real u, ufbx_real *weights, size_t num_weights, ufbx_real *derivatives, size_t num_derivatives)
{ … }
ufbx_abi ufbxi_noinline ufbx_curve_point ufbx_evaluate_nurbs_curve(const ufbx_nurbs_curve *curve, ufbx_real u)
{ … }
ufbx_abi ufbxi_noinline ufbx_surface_point ufbx_evaluate_nurbs_surface(const ufbx_nurbs_surface *surface, ufbx_real u, ufbx_real v)
{ … }
ufbx_abi ufbx_line_curve *ufbx_tessellate_nurbs_curve(const ufbx_nurbs_curve *curve, const ufbx_tessellate_curve_opts *opts, ufbx_error *error)
{ … }
ufbx_abi ufbx_mesh *ufbx_tessellate_nurbs_surface(const ufbx_nurbs_surface *surface, const ufbx_tessellate_surface_opts *opts, ufbx_error *error)
{ … }
ufbx_abi void ufbx_free_line_curve(ufbx_line_curve *line_curve)
{ … }
ufbx_abi void ufbx_retain_line_curve(ufbx_line_curve *line_curve)
{ … }
ufbx_abi uint32_t ufbx_find_face_index(ufbx_mesh *mesh, size_t index)
{ … }
ufbx_abi ufbxi_noinline uint32_t ufbx_catch_triangulate_face(ufbx_panic *panic, uint32_t *indices, size_t num_indices, const ufbx_mesh *mesh, ufbx_face face)
{ … }
ufbx_abi void ufbx_catch_compute_topology(ufbx_panic *panic, const ufbx_mesh *mesh, ufbx_topo_edge *indices, size_t num_indices)
{ … }
ufbx_abi uint32_t ufbx_catch_topo_next_vertex_edge(ufbx_panic *panic, const ufbx_topo_edge *topo, size_t num_topo, uint32_t index)
{ … }
ufbx_abi uint32_t ufbx_catch_topo_prev_vertex_edge(ufbx_panic *panic, const ufbx_topo_edge *topo, size_t num_topo, uint32_t index)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_catch_get_weighted_face_normal(ufbx_panic *panic, const ufbx_vertex_vec3 *positions, ufbx_face face)
{ … }
size_t ufbx_catch_generate_normal_mapping(ufbx_panic *panic, const ufbx_mesh *mesh, const ufbx_topo_edge *topo, size_t num_topo, uint32_t *normal_indices, size_t num_normal_indices, bool assume_smooth)
{ … }
ufbx_abi size_t ufbx_generate_normal_mapping(const ufbx_mesh *mesh, const ufbx_topo_edge *topo, size_t num_topo, uint32_t *normal_indices, size_t num_normal_indices, bool assume_smooth)
{ … }
ufbx_abi void ufbx_catch_compute_normals(ufbx_panic *panic, const ufbx_mesh *mesh, const ufbx_vertex_vec3 *positions, const uint32_t *normal_indices, size_t num_normal_indices, ufbx_vec3 *normals, size_t num_normals)
{ … }
ufbx_abi void ufbx_compute_normals(const ufbx_mesh *mesh, const ufbx_vertex_vec3 *positions, const uint32_t *normal_indices, size_t num_normal_indices, ufbx_vec3 *normals, size_t num_normals)
{ … }
ufbx_abi ufbx_mesh *ufbx_subdivide_mesh(const ufbx_mesh *mesh, size_t level, const ufbx_subdivide_opts *opts, ufbx_error *error)
{ … }
ufbx_abi void ufbx_free_mesh(ufbx_mesh *mesh)
{ … }
ufbx_abi void ufbx_retain_mesh(ufbx_mesh *mesh)
{ … }
ufbx_abi ufbx_geometry_cache *ufbx_load_geometry_cache(
const char *filename,
const ufbx_geometry_cache_opts *opts, ufbx_error *error)
{ … }
ufbx_abi ufbx_geometry_cache *ufbx_load_geometry_cache_len(
const char *filename, size_t filename_len,
const ufbx_geometry_cache_opts *opts, ufbx_error *error)
{ … }
ufbx_abi void ufbx_free_geometry_cache(ufbx_geometry_cache *cache)
{ … }
ufbx_abi void ufbx_retain_geometry_cache(ufbx_geometry_cache *cache)
{ … }
ufbxi_geometry_cache_buffer;
ufbx_abi ufbxi_noinline size_t ufbx_read_geometry_cache_real(const ufbx_cache_frame *frame, ufbx_real *data, size_t count, const ufbx_geometry_cache_data_opts *user_opts)
{ … }
ufbx_abi ufbxi_noinline size_t ufbx_sample_geometry_cache_real(const ufbx_cache_channel *channel, double time, ufbx_real *data, size_t count, const ufbx_geometry_cache_data_opts *user_opts)
{ … }
ufbx_abi ufbxi_noinline size_t ufbx_read_geometry_cache_vec3(const ufbx_cache_frame *frame, ufbx_vec3 *data, size_t count, const ufbx_geometry_cache_data_opts *opts)
{ … }
ufbx_abi ufbxi_noinline size_t ufbx_sample_geometry_cache_vec3(const ufbx_cache_channel *channel, double time, ufbx_vec3 *data, size_t count, const ufbx_geometry_cache_data_opts *opts)
{ … }
ufbx_abi ufbx_dom_node *ufbx_dom_find_len(const ufbx_dom_node *parent, const char *name, size_t name_len)
{ … }
ufbx_abi size_t ufbx_generate_indices(const ufbx_vertex_stream *streams, size_t num_streams, uint32_t *indices, size_t num_indices, const ufbx_allocator_opts *allocator, ufbx_error *error)
{ … }
ufbx_abi void ufbx_thread_pool_run_task(ufbx_thread_pool_context ctx, uint32_t index)
{ … }
ufbx_abi void ufbx_thread_pool_set_user_ptr(ufbx_thread_pool_context ctx, void *user)
{ … }
ufbx_abi void *ufbx_thread_pool_get_user_ptr(ufbx_thread_pool_context ctx)
{ … }
ufbx_abi ufbxi_noinline ufbx_real ufbx_catch_get_vertex_real(ufbx_panic *panic, const ufbx_vertex_real *v, size_t index)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec2 ufbx_catch_get_vertex_vec2(ufbx_panic *panic, const ufbx_vertex_vec2 *v, size_t index)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec3 ufbx_catch_get_vertex_vec3(ufbx_panic *panic, const ufbx_vertex_vec3 *v, size_t index)
{ … }
ufbx_abi ufbxi_noinline ufbx_vec4 ufbx_catch_get_vertex_vec4(ufbx_panic *panic, const ufbx_vertex_vec4 *v, size_t index)
{ … }
ufbx_abi ufbx_real ufbx_catch_get_vertex_w_vec3(ufbx_panic *panic, const ufbx_vertex_vec3 *v, size_t index)
{ … }
ufbx_abi ufbx_unknown *ufbx_as_unknown(const ufbx_element *element) { … }
ufbx_abi ufbx_node *ufbx_as_node(const ufbx_element *element) { … }
ufbx_abi ufbx_mesh *ufbx_as_mesh(const ufbx_element *element) { … }
ufbx_abi ufbx_light *ufbx_as_light(const ufbx_element *element) { … }
ufbx_abi ufbx_camera *ufbx_as_camera(const ufbx_element *element) { … }
ufbx_abi ufbx_bone *ufbx_as_bone(const ufbx_element *element) { … }
ufbx_abi ufbx_empty *ufbx_as_empty(const ufbx_element *element) { … }
ufbx_abi ufbx_line_curve *ufbx_as_line_curve(const ufbx_element *element) { … }
ufbx_abi ufbx_nurbs_curve *ufbx_as_nurbs_curve(const ufbx_element *element) { … }
ufbx_abi ufbx_nurbs_surface *ufbx_as_nurbs_surface(const ufbx_element *element) { … }
ufbx_abi ufbx_nurbs_trim_surface *ufbx_as_nurbs_trim_surface(const ufbx_element *element) { … }
ufbx_abi ufbx_nurbs_trim_boundary *ufbx_as_nurbs_trim_boundary(const ufbx_element *element) { … }
ufbx_abi ufbx_procedural_geometry *ufbx_as_procedural_geometry(const ufbx_element *element) { … }
ufbx_abi ufbx_stereo_camera *ufbx_as_stereo_camera(const ufbx_element *element) { … }
ufbx_abi ufbx_camera_switcher *ufbx_as_camera_switcher(const ufbx_element *element) { … }
ufbx_abi ufbx_marker *ufbx_as_marker(const ufbx_element *element) { … }
ufbx_abi ufbx_lod_group *ufbx_as_lod_group(const ufbx_element *element) { … }
ufbx_abi ufbx_skin_deformer *ufbx_as_skin_deformer(const ufbx_element *element) { … }
ufbx_abi ufbx_skin_cluster *ufbx_as_skin_cluster(const ufbx_element *element) { … }
ufbx_abi ufbx_blend_deformer *ufbx_as_blend_deformer(const ufbx_element *element) { … }
ufbx_abi ufbx_blend_channel *ufbx_as_blend_channel(const ufbx_element *element) { … }
ufbx_abi ufbx_blend_shape *ufbx_as_blend_shape(const ufbx_element *element) { … }
ufbx_abi ufbx_cache_deformer *ufbx_as_cache_deformer(const ufbx_element *element) { … }
ufbx_abi ufbx_cache_file *ufbx_as_cache_file(const ufbx_element *element) { … }
ufbx_abi ufbx_material *ufbx_as_material(const ufbx_element *element) { … }
ufbx_abi ufbx_texture *ufbx_as_texture(const ufbx_element *element) { … }
ufbx_abi ufbx_video *ufbx_as_video(const ufbx_element *element) { … }
ufbx_abi ufbx_shader *ufbx_as_shader(const ufbx_element *element) { … }
ufbx_abi ufbx_shader_binding *ufbx_as_shader_binding(const ufbx_element *element) { … }
ufbx_abi ufbx_anim_stack *ufbx_as_anim_stack(const ufbx_element *element) { … }
ufbx_abi ufbx_anim_layer *ufbx_as_anim_layer(const ufbx_element *element) { … }
ufbx_abi ufbx_anim_value *ufbx_as_anim_value(const ufbx_element *element) { … }
ufbx_abi ufbx_anim_curve *ufbx_as_anim_curve(const ufbx_element *element) { … }
ufbx_abi ufbx_display_layer *ufbx_as_display_layer(const ufbx_element *element) { … }
ufbx_abi ufbx_selection_set *ufbx_as_selection_set(const ufbx_element *element) { … }
ufbx_abi ufbx_selection_node *ufbx_as_selection_node(const ufbx_element *element) { … }
ufbx_abi ufbx_character *ufbx_as_character(const ufbx_element *element) { … }
ufbx_abi ufbx_constraint *ufbx_as_constraint(const ufbx_element *element) { … }
ufbx_abi ufbx_audio_layer *ufbx_as_audio_layer(const ufbx_element *element) { … }
ufbx_abi ufbx_audio_clip *ufbx_as_audio_clip(const ufbx_element *element) { … }
ufbx_abi ufbx_pose *ufbx_as_pose(const ufbx_element *element) { … }
ufbx_abi ufbx_metadata_object *ufbx_as_metadata_object(const ufbx_element *element) { … }
#ifdef __cplusplus
}
#endif
#endif
#if defined(_MSC_VER)
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif