Fix: New FBX importer was not handling blender specific FullWeights
- Update to latest ufbx version that adds support for FullWeights - Handle that in the same way as the Python importer did - Add test files from ufbx test suite Pull Request: https://projects.blender.org/blender/blender/pulls/138811
This commit is contained in:
committed by
Aras Pranckevicius
parent
fba5e2ed58
commit
6a0e6f5cff
2
extern/ufbx/README.blender
vendored
2
extern/ufbx/README.blender
vendored
@@ -2,6 +2,6 @@ Project: ufbx - Single source file FBX loader
|
|||||||
URL: https://github.com/ufbx/ufbx
|
URL: https://github.com/ufbx/ufbx
|
||||||
License: SPDX:MIT
|
License: SPDX:MIT
|
||||||
Copyright: "Copyright (c) 2020 Samuli Raivio"
|
Copyright: "Copyright (c) 2020 Samuli Raivio"
|
||||||
Upstream version: v0.18.1-dev (42860b7, 2025-04-11)
|
Upstream version: v0.19.0-dev (6c0179d, 2025-04-27)
|
||||||
Local modifications:
|
Local modifications:
|
||||||
* None
|
* None
|
||||||
|
|||||||
171
extern/ufbx/ufbx.c
vendored
171
extern/ufbx/ufbx.c
vendored
@@ -868,7 +868,7 @@ enum { UFBX_MAXIMUM_ALIGNMENT = sizeof(void*) > 8 ? sizeof(void*) : 8 };
|
|||||||
|
|
||||||
// -- Version
|
// -- Version
|
||||||
|
|
||||||
#define UFBX_SOURCE_VERSION ufbx_pack_version(0, 18, 1)
|
#define UFBX_SOURCE_VERSION ufbx_pack_version(0, 19, 0)
|
||||||
ufbx_abi_data_def const uint32_t ufbx_source_version = UFBX_SOURCE_VERSION;
|
ufbx_abi_data_def const uint32_t ufbx_source_version = UFBX_SOURCE_VERSION;
|
||||||
|
|
||||||
ufbx_static_assert(source_header_version, UFBX_SOURCE_VERSION/1000u == UFBX_HEADER_VERSION/1000u);
|
ufbx_static_assert(source_header_version, UFBX_SOURCE_VERSION/1000u == UFBX_HEADER_VERSION/1000u);
|
||||||
@@ -3401,7 +3401,7 @@ static ufbxi_noinline void ufbxi_panicf_imp(ufbx_panic *panic, const char *fmt,
|
|||||||
|
|
||||||
static ufbxi_noinline int ufbxi_fail_imp_err(ufbx_error *err, const char *cond, const char *func, uint32_t line)
|
static ufbxi_noinline int ufbxi_fail_imp_err(ufbx_error *err, const char *cond, const char *func, uint32_t line)
|
||||||
{
|
{
|
||||||
if (cond[0] == '$') {
|
if (cond && cond[0] == '$') {
|
||||||
if (!err->description.data) {
|
if (!err->description.data) {
|
||||||
err->description.data = cond + 1;
|
err->description.data = cond + 1;
|
||||||
err->description.length = strlen(err->description.data);
|
err->description.length = strlen(err->description.data);
|
||||||
@@ -3416,6 +3416,8 @@ static ufbxi_noinline int ufbxi_fail_imp_err(ufbx_error *err, const char *cond,
|
|||||||
// NOTE: This is the base function all fails boil down to, place a breakpoint here to
|
// NOTE: This is the base function all fails boil down to, place a breakpoint here to
|
||||||
// break at the first error
|
// break at the first error
|
||||||
#if UFBXI_FEATURE_ERROR_STACK
|
#if UFBXI_FEATURE_ERROR_STACK
|
||||||
|
ufbx_assert(cond);
|
||||||
|
ufbx_assert(func);
|
||||||
if (err->stack_size < UFBX_ERROR_STACK_MAX_DEPTH) {
|
if (err->stack_size < UFBX_ERROR_STACK_MAX_DEPTH) {
|
||||||
ufbx_error_frame *frame = &err->stack[err->stack_size++];
|
ufbx_error_frame *frame = &err->stack[err->stack_size++];
|
||||||
frame->description.data = cond;
|
frame->description.data = cond;
|
||||||
@@ -3524,14 +3526,21 @@ static ufbxi_noinline void ufbxi_clear_error(ufbx_error *err)
|
|||||||
#define ufbxi_line __LINE__
|
#define ufbxi_line __LINE__
|
||||||
#define ufbxi_cond_str(cond) #cond
|
#define ufbxi_cond_str(cond) #cond
|
||||||
#else
|
#else
|
||||||
#define ufbxi_function ""
|
#define ufbxi_function NULL
|
||||||
#define ufbxi_line 0
|
#define ufbxi_line 0
|
||||||
#define ufbxi_cond_str(cond) ""
|
#define ufbxi_cond_str(cond) ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ufbxi_check_err(err, cond) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_imp_err((err), ufbxi_cond_str(cond), ufbxi_function, ufbxi_line); return 0; } } while (0)
|
#if UFBXI_FEATURE_ERROR_STACK
|
||||||
#define ufbxi_check_return_err(err, cond, ret) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_imp_err((err), ufbxi_cond_str(cond), ufbxi_function, ufbxi_line); return ret; } } while (0)
|
#define ufbxi_fail_err_no_msg(err, cond, func, line) ufbxi_fail_imp_err((err), (cond), (func), (line))
|
||||||
#define ufbxi_fail_err(err, desc) return ufbxi_fail_imp_err(err, desc, ufbxi_function, ufbxi_line)
|
#else
|
||||||
|
static ufbxi_noinline int ufbxi_fail_imp_err_no_stack(ufbx_error *err) { return ufbxi_fail_imp_err(err, NULL, NULL, 0); }
|
||||||
|
#define ufbxi_fail_err_no_msg(err, cond, func, line) ufbxi_fail_imp_err_no_stack((err))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ufbxi_check_err(err, cond) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_err_no_msg((err), ufbxi_cond_str(cond), ufbxi_function, ufbxi_line); return 0; } } while (0)
|
||||||
|
#define ufbxi_check_return_err(err, cond, ret) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_err_no_msg((err), ufbxi_cond_str(cond), ufbxi_function, ufbxi_line); return ret; } } while (0)
|
||||||
|
#define ufbxi_fail_err(err, desc) return ufbxi_fail_err_no_msg(err, desc, ufbxi_function, ufbxi_line)
|
||||||
|
|
||||||
#define ufbxi_check_err_msg(err, cond, msg) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_imp_err((err), ufbxi_error_msg(ufbxi_cond_str(cond), msg), ufbxi_function, ufbxi_line); return 0; } } while (0)
|
#define ufbxi_check_err_msg(err, cond, msg) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_imp_err((err), ufbxi_error_msg(ufbxi_cond_str(cond), msg), ufbxi_function, ufbxi_line); return 0; } } while (0)
|
||||||
#define ufbxi_check_return_err_msg(err, cond, ret, msg) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_imp_err((err), ufbxi_error_msg(ufbxi_cond_str(cond), msg), ufbxi_function, ufbxi_line); return ret; } } while (0)
|
#define ufbxi_check_return_err_msg(err, cond, ret, msg) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_imp_err((err), ufbxi_error_msg(ufbxi_cond_str(cond), msg), ufbxi_function, ufbxi_line); return ret; } } while (0)
|
||||||
@@ -6414,7 +6423,6 @@ typedef struct {
|
|||||||
|
|
||||||
uint64_t usemtl_fbx_id;
|
uint64_t usemtl_fbx_id;
|
||||||
uint32_t usemtl_index;
|
uint32_t usemtl_index;
|
||||||
ufbx_string usemtl_name;
|
|
||||||
|
|
||||||
uint32_t face_material;
|
uint32_t face_material;
|
||||||
|
|
||||||
@@ -6575,6 +6583,7 @@ typedef struct {
|
|||||||
bool has_geometry_transform_nodes;
|
bool has_geometry_transform_nodes;
|
||||||
bool has_scale_helper_nodes;
|
bool has_scale_helper_nodes;
|
||||||
bool retain_vertex_w;
|
bool retain_vertex_w;
|
||||||
|
bool blender_full_weights;
|
||||||
|
|
||||||
ufbx_mirror_axis mirror_axis;
|
ufbx_mirror_axis mirror_axis;
|
||||||
|
|
||||||
@@ -6626,10 +6635,17 @@ static ufbxi_noinline int ufbxi_fail_imp(ufbxi_context *uc, const char *cond, co
|
|||||||
return ufbxi_fail_imp_err(&uc->error, cond, func, line);
|
return ufbxi_fail_imp_err(&uc->error, cond, func, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ufbxi_check(cond) if (ufbxi_unlikely(!ufbxi_trace(cond))) return ufbxi_fail_imp(uc, ufbxi_cond_str(cond), ufbxi_function, ufbxi_line)
|
#if UFBXI_FEATURE_ERROR_STACK
|
||||||
#define ufbxi_check_return(cond, ret) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_imp(uc, ufbxi_cond_str(cond), ufbxi_function, ufbxi_line); return ret; } } while (0)
|
#define ufbxi_fail_no_msg(uc, cond, func, line) ufbxi_fail_imp((uc), (cond), (func), (line))
|
||||||
#define ufbxi_fail(desc) return ufbxi_fail_imp(uc, desc, ufbxi_function, ufbxi_line)
|
#else
|
||||||
#define ufbxi_fail_return(desc, ret) do { ufbxi_fail_imp(uc, desc, ufbxi_function, ufbxi_line); return ret; } while (0)
|
static ufbxi_noinline int ufbxi_fail_imp_no_stack(ufbxi_context *uc) { return ufbxi_fail_imp_err(&uc->error, NULL, NULL, 0); }
|
||||||
|
#define ufbxi_fail_no_msg(uc, cond, func, line) ufbxi_fail_imp_no_stack((uc))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ufbxi_check(cond) if (ufbxi_unlikely(!ufbxi_trace(cond))) return ufbxi_fail_no_msg(uc, ufbxi_cond_str(cond), ufbxi_function, ufbxi_line)
|
||||||
|
#define ufbxi_check_return(cond, ret) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_no_msg(uc, ufbxi_cond_str(cond), ufbxi_function, ufbxi_line); return ret; } } while (0)
|
||||||
|
#define ufbxi_fail(desc) return ufbxi_fail_no_msg(uc, desc, ufbxi_function, ufbxi_line)
|
||||||
|
#define ufbxi_fail_return(desc, ret) do { ufbxi_fail_no_msg(uc, desc, ufbxi_function, ufbxi_line); return ret; } while (0)
|
||||||
|
|
||||||
#define ufbxi_check_msg(cond, msg) if (ufbxi_unlikely(!ufbxi_trace(cond))) return ufbxi_fail_imp(uc, ufbxi_error_msg(ufbxi_cond_str(cond), msg), ufbxi_function, ufbxi_line)
|
#define ufbxi_check_msg(cond, msg) if (ufbxi_unlikely(!ufbxi_trace(cond))) return ufbxi_fail_imp(uc, ufbxi_error_msg(ufbxi_cond_str(cond), msg), ufbxi_function, ufbxi_line)
|
||||||
#define ufbxi_check_return_msg(cond, ret, msg) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_imp(uc, ufbxi_error_msg(ufbxi_cond_str(cond), msg), ufbxi_function, ufbxi_line); return ret; } } while (0)
|
#define ufbxi_check_return_msg(cond, ret, msg) do { if (ufbxi_unlikely(!ufbxi_trace(cond))) { ufbxi_fail_imp(uc, ufbxi_error_msg(ufbxi_cond_str(cond), msg), ufbxi_function, ufbxi_line); return ret; } } while (0)
|
||||||
@@ -7934,7 +7950,7 @@ typedef enum {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UFBXI_ARRAY_FLAG_RESULT = 0x1, // < Allocate the array from the result buffer
|
UFBXI_ARRAY_FLAG_RESULT = 0x1, // < Allocate the array from the result buffer
|
||||||
UFBXI_ARRAY_FLAG_TMP_BUF = 0x2, // < Allocate the array from the result buffer
|
UFBXI_ARRAY_FLAG_TMP_BUF = 0x2, // < Allocate the array from the long-term temporary buffer
|
||||||
UFBXI_ARRAY_FLAG_PAD_BEGIN = 0x4, // < Pad the begin of the array with 4 zero elements to guard from invalid -1 index accesses
|
UFBXI_ARRAY_FLAG_PAD_BEGIN = 0x4, // < Pad the begin of the array with 4 zero elements to guard from invalid -1 index accesses
|
||||||
UFBXI_ARRAY_FLAG_ACCURATE_F32 = 0x8, // < Must be parsed as bit-accurate 32-bit floats
|
UFBXI_ARRAY_FLAG_ACCURATE_F32 = 0x8, // < Must be parsed as bit-accurate 32-bit floats
|
||||||
} ufbxi_array_flags;
|
} ufbxi_array_flags;
|
||||||
@@ -8155,7 +8171,7 @@ static bool ufbxi_is_array_node(ufbxi_context *uc, ufbxi_parse_state parent, con
|
|||||||
// in versions >= 7200 as some of the elements aren't actually floats (!)
|
// in versions >= 7200 as some of the elements aren't actually floats (!)
|
||||||
info->type = uc->from_ascii && uc->version >= 7200 ? 'i' : 'f';
|
info->type = uc->from_ascii && uc->version >= 7200 ? 'i' : 'f';
|
||||||
if (uc->opts.ignore_animation) info->type = '-';
|
if (uc->opts.ignore_animation) info->type = '-';
|
||||||
if (uc->from_ascii && uc->version >= 7200) {
|
if (uc->from_ascii && uc->version < 7200) {
|
||||||
info->flags |= UFBXI_ARRAY_FLAG_ACCURATE_F32;
|
info->flags |= UFBXI_ARRAY_FLAG_ACCURATE_F32;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -8403,14 +8419,8 @@ static bool ufbxi_is_array_node(ufbxi_context *uc, ufbxi_parse_state parent, con
|
|||||||
info->flags = UFBXI_ARRAY_FLAG_RESULT;
|
info->flags = UFBXI_ARRAY_FLAG_RESULT;
|
||||||
return true;
|
return true;
|
||||||
} else if (name == ufbxi_FullWeights) {
|
} else if (name == ufbxi_FullWeights) {
|
||||||
// Ignore blend shape FullWeights as it's used in Blender for vertex groups
|
|
||||||
// which we don't currently handle. https://developer.blender.org/T90382
|
|
||||||
// TODO: Should we present this to users anyway somehow?
|
|
||||||
info->type = 'r';
|
info->type = 'r';
|
||||||
if (!uc->opts.disable_quirks && uc->exporter == UFBX_EXPORTER_BLENDER_BINARY) {
|
info->flags = (uint8_t)(info->flags | (uc->blender_full_weights ? UFBXI_ARRAY_FLAG_RESULT : UFBXI_ARRAY_FLAG_TMP_BUF));
|
||||||
info->type = '-';
|
|
||||||
}
|
|
||||||
info->flags |= UFBXI_ARRAY_FLAG_TMP_BUF;
|
|
||||||
return true;
|
return true;
|
||||||
} else if (!strcmp(name, "TransformAssociateModel")) {
|
} else if (!strcmp(name, "TransformAssociateModel")) {
|
||||||
info->type = uc->opts.retain_dom ? 'r' : '-';
|
info->type = uc->opts.retain_dom ? 'r' : '-';
|
||||||
@@ -10722,14 +10732,14 @@ ufbxi_nodiscard static ufbxi_noinline int ufbxi_retain_dom_node(ufbxi_context *u
|
|||||||
val->value_float = (double)(val->value_int = (int64_t)arr->size);
|
val->value_float = (double)(val->value_int = (int64_t)arr->size);
|
||||||
|
|
||||||
switch (arr->type) {
|
switch (arr->type) {
|
||||||
case 'c': val->type = UFBX_DOM_VALUE_ARRAY_I8; break;
|
case 'c': val->type = UFBX_DOM_VALUE_BLOB; break;
|
||||||
case 'b': val->type = UFBX_DOM_VALUE_ARRAY_I8; break;
|
case 'b': val->type = UFBX_DOM_VALUE_BLOB; break;
|
||||||
case 'i': val->type = UFBX_DOM_VALUE_ARRAY_I32; break;
|
case 'i': val->type = UFBX_DOM_VALUE_ARRAY_I32; break;
|
||||||
case 'l': val->type = UFBX_DOM_VALUE_ARRAY_I64; break;
|
case 'l': val->type = UFBX_DOM_VALUE_ARRAY_I64; break;
|
||||||
case 'f': val->type = UFBX_DOM_VALUE_ARRAY_F32; break;
|
case 'f': val->type = UFBX_DOM_VALUE_ARRAY_F32; break;
|
||||||
case 'd': val->type = UFBX_DOM_VALUE_ARRAY_F64; break;
|
case 'd': val->type = UFBX_DOM_VALUE_ARRAY_F64; break;
|
||||||
case 's': val->type = UFBX_DOM_VALUE_ARRAY_RAW_STRING; break;
|
case 's': val->type = UFBX_DOM_VALUE_ARRAY_BLOB; break;
|
||||||
case 'C': val->type = UFBX_DOM_VALUE_ARRAY_RAW_STRING; break;
|
case 'C': val->type = UFBX_DOM_VALUE_ARRAY_BLOB; break;
|
||||||
case '-': val->type = UFBX_DOM_VALUE_ARRAY_IGNORED; break;
|
case '-': val->type = UFBX_DOM_VALUE_ARRAY_IGNORED; break;
|
||||||
default: ufbxi_fail("Bad array type");
|
default: ufbxi_fail("Bad array type");
|
||||||
}
|
}
|
||||||
@@ -12033,7 +12043,7 @@ static bool ufbxi_match_version_string(const char *fmt, ufbx_string str, uint32_
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ufbxi_nodiscard static int ufbxi_match_exporter(ufbxi_context *uc)
|
ufbxi_nodiscard ufbxi_noinline static int ufbxi_match_exporter(ufbxi_context *uc)
|
||||||
{
|
{
|
||||||
ufbx_string creator = uc->scene.metadata.creator;
|
ufbx_string creator = uc->scene.metadata.creator;
|
||||||
uint32_t version[3] = { 0 };
|
uint32_t version[3] = { 0 };
|
||||||
@@ -12069,6 +12079,10 @@ ufbxi_nodiscard static int ufbxi_match_exporter(ufbxi_context *uc)
|
|||||||
uc->exporter_version = 0;
|
uc->exporter_version = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (uc->exporter == UFBX_EXPORTER_BLENDER_BINARY) {
|
||||||
|
uc->blender_full_weights = true;
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17088,6 +17102,8 @@ ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_indices(ufbxi_context
|
|||||||
uc->obj.face_material = index - mesh->usemtl_base;
|
uc->obj.face_material = index - mesh->usemtl_base;
|
||||||
}
|
}
|
||||||
uc->obj.face_material = entry->user_id - mesh->usemtl_base;
|
uc->obj.face_material = entry->user_id - mesh->usemtl_base;
|
||||||
|
} else {
|
||||||
|
uc->obj.face_material = UFBX_NO_INDEX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17246,7 +17262,14 @@ ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_comment(ufbxi_context
|
|||||||
|
|
||||||
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_material(ufbxi_context *uc)
|
ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_material(ufbxi_context *uc)
|
||||||
{
|
{
|
||||||
ufbxi_check(uc->obj.num_tokens >= 2);
|
uc->obj.material_dirty = true;
|
||||||
|
|
||||||
|
// Allow empty `usemtl` lines to specify "no material".
|
||||||
|
if (uc->obj.num_tokens < 2) {
|
||||||
|
uc->obj.usemtl_fbx_id = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
ufbx_string name = ufbxi_obj_span_token(uc, 1, SIZE_MAX);
|
ufbx_string name = ufbxi_obj_span_token(uc, 1, SIZE_MAX);
|
||||||
|
|
||||||
ufbxi_check(ufbxi_push_string_place_str(&uc->string_pool, &name, false));
|
ufbxi_check(ufbxi_push_string_place_str(&uc->string_pool, &name, false));
|
||||||
@@ -17257,7 +17280,6 @@ ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_material(ufbxi_context
|
|||||||
ufbxi_fbx_id_entry *entry = ufbxi_find_fbx_id(uc, fbx_id);
|
ufbxi_fbx_id_entry *entry = ufbxi_find_fbx_id(uc, fbx_id);
|
||||||
|
|
||||||
uc->obj.usemtl_fbx_id = fbx_id;
|
uc->obj.usemtl_fbx_id = fbx_id;
|
||||||
uc->obj.usemtl_name = name;
|
|
||||||
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
ufbxi_element_info info = { 0 };
|
ufbxi_element_info info = { 0 };
|
||||||
@@ -17276,8 +17298,6 @@ ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_material(ufbxi_context
|
|||||||
uc->obj.tmp_materials[id] = material;
|
uc->obj.tmp_materials[id] = material;
|
||||||
}
|
}
|
||||||
|
|
||||||
uc->obj.material_dirty = true;
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17789,7 +17809,8 @@ ufbxi_nodiscard static ufbxi_noinline int ufbxi_obj_parse_mtl(ufbxi_context *uc)
|
|||||||
|
|
||||||
ufbx_string cmd = uc->obj.tokens[0];
|
ufbx_string cmd = uc->obj.tokens[0];
|
||||||
if (ufbxi_str_equal(cmd, ufbxi_str_c("newmtl"))) {
|
if (ufbxi_str_equal(cmd, ufbxi_str_c("newmtl"))) {
|
||||||
// HACK: Reuse mesh material parsing
|
// HACK: Reuse mesh material parsing, but don't allow for empty material name
|
||||||
|
ufbxi_check(uc->obj.num_tokens >= 2);
|
||||||
ufbxi_check(ufbxi_obj_flush_material(uc));
|
ufbxi_check(ufbxi_obj_flush_material(uc));
|
||||||
ufbxi_check(ufbxi_obj_parse_material(uc));
|
ufbxi_check(ufbxi_obj_parse_material(uc));
|
||||||
} else if (cmd.length > 4 && !memcmp(cmd.data, "map_", 4)) {
|
} else if (cmd.length > 4 && !memcmp(cmd.data, "map_", 4)) {
|
||||||
@@ -21776,10 +21797,23 @@ ufbxi_nodiscard ufbxi_noinline static int ufbxi_finalize_scene(ufbxi_context *uc
|
|||||||
|
|
||||||
for (size_t i = 0; i < channel->keyframes.count; i++) {
|
for (size_t i = 0; i < channel->keyframes.count; i++) {
|
||||||
ufbx_blend_keyframe *key = &channel->keyframes.data[i];
|
ufbx_blend_keyframe *key = &channel->keyframes.data[i];
|
||||||
|
key->target_weight = 1.0f;
|
||||||
if (i < full_weights->count) {
|
if (i < full_weights->count) {
|
||||||
key->target_weight = full_weights->data[i] / (ufbx_real)100.0;
|
if (!uc->blender_full_weights) {
|
||||||
} else {
|
key->target_weight = full_weights->data[i] / (ufbx_real)100.0;
|
||||||
key->target_weight = 1.0f;
|
} else if (full_weights->count == key->shape->num_offsets) {
|
||||||
|
if (i == 0) {
|
||||||
|
// Duplicate `index_data` for modification if we retain DOM
|
||||||
|
if (uc->opts.retain_dom) {
|
||||||
|
full_weights->data = ufbxi_push_copy(&uc->result, ufbx_real, full_weights->count, full_weights->data);
|
||||||
|
ufbxi_check(full_weights->data);
|
||||||
|
}
|
||||||
|
ufbxi_for_list(ufbx_real, p_weight, *full_weights) {
|
||||||
|
*p_weight /= (ufbx_real)100.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key->shape->offset_weights = *full_weights;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21790,7 +21824,6 @@ ufbxi_nodiscard ufbxi_noinline static int ufbxi_finalize_scene(ufbxi_context *uc
|
|||||||
channel->target_shape = channel->keyframes.data[channel->keyframes.count - 1].shape;
|
channel->target_shape = channel->keyframes.data[channel->keyframes.count - 1].shape;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ufbxi_buf_free(&uc->tmp_full_weights);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// Generate and patch procedural index buffers
|
// Generate and patch procedural index buffers
|
||||||
@@ -31817,10 +31850,15 @@ ufbx_abi void ufbx_add_blend_shape_vertex_offsets(const ufbx_blend_shape *shape,
|
|||||||
size_t num_offsets = shape->num_offsets;
|
size_t num_offsets = shape->num_offsets;
|
||||||
uint32_t *vertex_indices = shape->offset_vertices.data;
|
uint32_t *vertex_indices = shape->offset_vertices.data;
|
||||||
ufbx_vec3 *offsets = shape->position_offsets.data;
|
ufbx_vec3 *offsets = shape->position_offsets.data;
|
||||||
|
ufbx_real_list weights = shape->offset_weights;
|
||||||
for (size_t i = 0; i < num_offsets; i++) {
|
for (size_t i = 0; i < num_offsets; i++) {
|
||||||
uint32_t index = vertex_indices[i];
|
uint32_t index = vertex_indices[i];
|
||||||
if (index < num_vertices) {
|
if (index < num_vertices) {
|
||||||
ufbxi_add_weighted_vec3(&vertices[index], offsets[i], weight);
|
ufbx_real vertex_weight = weight;
|
||||||
|
if (i < weights.count) {
|
||||||
|
vertex_weight *= weights.data[i];
|
||||||
|
}
|
||||||
|
ufbxi_add_weighted_vec3(&vertices[index], offsets[i], vertex_weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32819,6 +32857,69 @@ ufbx_abi ufbx_audio_clip *ufbx_as_audio_clip(const ufbx_element *element) { retu
|
|||||||
ufbx_abi ufbx_pose *ufbx_as_pose(const ufbx_element *element) { return element && element->type == UFBX_ELEMENT_POSE ? (ufbx_pose*)element : NULL; }
|
ufbx_abi ufbx_pose *ufbx_as_pose(const ufbx_element *element) { return element && element->type == UFBX_ELEMENT_POSE ? (ufbx_pose*)element : NULL; }
|
||||||
ufbx_abi ufbx_metadata_object *ufbx_as_metadata_object(const ufbx_element *element) { return element && element->type == UFBX_ELEMENT_METADATA_OBJECT ? (ufbx_metadata_object*)element : NULL; }
|
ufbx_abi ufbx_metadata_object *ufbx_as_metadata_object(const ufbx_element *element) { return element && element->type == UFBX_ELEMENT_METADATA_OBJECT ? (ufbx_metadata_object*)element : NULL; }
|
||||||
|
|
||||||
|
ufbx_abi bool ufbx_dom_is_array(const ufbx_dom_node *node) {
|
||||||
|
if (!node || node->values.count != 1) return false;
|
||||||
|
ufbx_dom_value v = node->values.data[0];
|
||||||
|
return v.type >= UFBX_DOM_VALUE_ARRAY_I32 && v.type <= UFBX_DOM_VALUE_ARRAY_BLOB;
|
||||||
|
}
|
||||||
|
ufbx_abi size_t ufbx_dom_array_size(const ufbx_dom_node *node) {
|
||||||
|
return ufbx_dom_is_array(node) ? (size_t)node->values.data[0].value_int : (size_t)0;
|
||||||
|
}
|
||||||
|
ufbx_abi ufbx_int32_list ufbx_dom_as_int32_list(const ufbx_dom_node *node) {
|
||||||
|
ufbx_int32_list list = { NULL, 0 };
|
||||||
|
if (node && node->values.count == 1 && node->values.data[0].type == UFBX_DOM_VALUE_ARRAY_I32) {
|
||||||
|
ufbx_dom_value value = node->values.data[0];
|
||||||
|
list.data = (int32_t*)value.value_blob.data;
|
||||||
|
list.count = value.value_blob.size / sizeof(int32_t);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
ufbx_abi ufbx_int64_list ufbx_dom_as_int64_list(const ufbx_dom_node *node) {
|
||||||
|
ufbx_int64_list list = { NULL, 0 };
|
||||||
|
if (node && node->values.count == 1 && node->values.data[0].type == UFBX_DOM_VALUE_ARRAY_I64) {
|
||||||
|
ufbx_dom_value value = node->values.data[0];
|
||||||
|
list.data = (int64_t*)value.value_blob.data;
|
||||||
|
list.count = value.value_blob.size / sizeof(int64_t);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
ufbx_abi ufbx_float_list ufbx_dom_as_float_list(const ufbx_dom_node *node) {
|
||||||
|
ufbx_float_list list = { NULL, 0 };
|
||||||
|
if (node && node->values.count == 1 && node->values.data[0].type == UFBX_DOM_VALUE_ARRAY_F32) {
|
||||||
|
ufbx_dom_value value = node->values.data[0];
|
||||||
|
list.data = (float*)value.value_blob.data;
|
||||||
|
list.count = value.value_blob.size / sizeof(float);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
ufbx_abi ufbx_double_list ufbx_dom_as_double_list(const ufbx_dom_node *node) {
|
||||||
|
ufbx_double_list list = { NULL, 0 };
|
||||||
|
if (node && node->values.count == 1 && node->values.data[0].type == UFBX_DOM_VALUE_ARRAY_F64) {
|
||||||
|
ufbx_dom_value value = node->values.data[0];
|
||||||
|
list.data = (double*)value.value_blob.data;
|
||||||
|
list.count = value.value_blob.size / sizeof(double);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
ufbx_abi ufbx_real_list ufbx_dom_as_real_list(const ufbx_dom_node *node) {
|
||||||
|
ufbx_real_list list = { NULL, 0 };
|
||||||
|
if (node && node->values.count == 1 && node->values.data[0].type == (sizeof(ufbx_real) == sizeof(double) ? UFBX_DOM_VALUE_ARRAY_F64 : UFBX_DOM_VALUE_ARRAY_F32)) {
|
||||||
|
ufbx_dom_value value = node->values.data[0];
|
||||||
|
list.data = (ufbx_real*)value.value_blob.data;
|
||||||
|
list.count = value.value_blob.size / sizeof(ufbx_real);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
ufbx_abi ufbx_blob_list ufbx_dom_as_blob_list(const ufbx_dom_node *node) {
|
||||||
|
ufbx_blob_list list = { NULL, 0 };
|
||||||
|
if (node && node->values.count == 1 && node->values.data[0].type == UFBX_DOM_VALUE_ARRAY_BLOB) {
|
||||||
|
ufbx_dom_value value = node->values.data[0];
|
||||||
|
list.data = (ufbx_blob*)value.value_blob.data;
|
||||||
|
list.count = value.value_blob.size / sizeof(ufbx_blob);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
// -- String API
|
// -- String API
|
||||||
|
|
||||||
ufbx_abi ufbx_prop *ufbx_find_prop(const ufbx_props *props, const char *name) { return ufbx_find_prop_len(props, name, strlen(name)); }
|
ufbx_abi ufbx_prop *ufbx_find_prop(const ufbx_props *props, const char *name) { return ufbx_find_prop_len(props, name, strlen(name)); }
|
||||||
|
|||||||
26
extern/ufbx/ufbx.h
vendored
26
extern/ufbx/ufbx.h
vendored
@@ -267,7 +267,7 @@ struct ufbx_converter { };
|
|||||||
// `ufbx_source_version` contains the version of the corresponding source file.
|
// `ufbx_source_version` contains the version of the corresponding source file.
|
||||||
// HINT: The version can be compared numerically to the result of `ufbx_pack_version()`,
|
// HINT: The version can be compared numerically to the result of `ufbx_pack_version()`,
|
||||||
// for example `#if UFBX_VERSION >= ufbx_pack_version(0, 12, 0)`.
|
// for example `#if UFBX_VERSION >= ufbx_pack_version(0, 12, 0)`.
|
||||||
#define UFBX_HEADER_VERSION ufbx_pack_version(0, 18, 1)
|
#define UFBX_HEADER_VERSION ufbx_pack_version(0, 19, 0)
|
||||||
#define UFBX_VERSION UFBX_HEADER_VERSION
|
#define UFBX_VERSION UFBX_HEADER_VERSION
|
||||||
|
|
||||||
// -- Basic types
|
// -- Basic types
|
||||||
@@ -400,12 +400,12 @@ UFBX_LIST_TYPE(ufbx_string_list, ufbx_string);
|
|||||||
typedef enum ufbx_dom_value_type UFBX_ENUM_REPR {
|
typedef enum ufbx_dom_value_type UFBX_ENUM_REPR {
|
||||||
UFBX_DOM_VALUE_NUMBER,
|
UFBX_DOM_VALUE_NUMBER,
|
||||||
UFBX_DOM_VALUE_STRING,
|
UFBX_DOM_VALUE_STRING,
|
||||||
UFBX_DOM_VALUE_ARRAY_I8,
|
UFBX_DOM_VALUE_BLOB,
|
||||||
UFBX_DOM_VALUE_ARRAY_I32,
|
UFBX_DOM_VALUE_ARRAY_I32,
|
||||||
UFBX_DOM_VALUE_ARRAY_I64,
|
UFBX_DOM_VALUE_ARRAY_I64,
|
||||||
UFBX_DOM_VALUE_ARRAY_F32,
|
UFBX_DOM_VALUE_ARRAY_F32,
|
||||||
UFBX_DOM_VALUE_ARRAY_F64,
|
UFBX_DOM_VALUE_ARRAY_F64,
|
||||||
UFBX_DOM_VALUE_ARRAY_RAW_STRING,
|
UFBX_DOM_VALUE_ARRAY_BLOB,
|
||||||
UFBX_DOM_VALUE_ARRAY_IGNORED,
|
UFBX_DOM_VALUE_ARRAY_IGNORED,
|
||||||
|
|
||||||
UFBX_ENUM_FORCE_WIDTH(UFBX_DOM_VALUE_TYPE)
|
UFBX_ENUM_FORCE_WIDTH(UFBX_DOM_VALUE_TYPE)
|
||||||
@@ -415,6 +415,12 @@ UFBX_ENUM_TYPE(ufbx_dom_value_type, UFBX_DOM_VALUE_TYPE, UFBX_DOM_VALUE_ARRAY_IG
|
|||||||
|
|
||||||
typedef struct ufbx_dom_node ufbx_dom_node;
|
typedef struct ufbx_dom_node ufbx_dom_node;
|
||||||
|
|
||||||
|
UFBX_LIST_TYPE(ufbx_int32_list, int32_t);
|
||||||
|
UFBX_LIST_TYPE(ufbx_int64_list, int64_t);
|
||||||
|
UFBX_LIST_TYPE(ufbx_float_list, float);
|
||||||
|
UFBX_LIST_TYPE(ufbx_double_list, double);
|
||||||
|
UFBX_LIST_TYPE(ufbx_blob_list, ufbx_blob);
|
||||||
|
|
||||||
typedef struct ufbx_dom_value {
|
typedef struct ufbx_dom_value {
|
||||||
ufbx_dom_value_type type;
|
ufbx_dom_value_type type;
|
||||||
ufbx_string value_str;
|
ufbx_string value_str;
|
||||||
@@ -2097,6 +2103,10 @@ struct ufbx_blend_shape {
|
|||||||
ufbx_uint32_list offset_vertices; // < Indices to `ufbx_mesh.vertices[]`
|
ufbx_uint32_list offset_vertices; // < Indices to `ufbx_mesh.vertices[]`
|
||||||
ufbx_vec3_list position_offsets; // < Always specified per-vertex offsets
|
ufbx_vec3_list position_offsets; // < Always specified per-vertex offsets
|
||||||
ufbx_vec3_list normal_offsets; // < Empty if not specified
|
ufbx_vec3_list normal_offsets; // < Empty if not specified
|
||||||
|
|
||||||
|
// Optional weights for the offsets.
|
||||||
|
// NOTE: These are technically not supported in FBX and are only written by Blender.
|
||||||
|
ufbx_real_list offset_weights;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum ufbx_cache_file_format UFBX_ENUM_REPR {
|
typedef enum ufbx_cache_file_format UFBX_ENUM_REPR {
|
||||||
@@ -5778,6 +5788,16 @@ 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_pose *ufbx_as_pose(const ufbx_element *element);
|
||||||
ufbx_abi ufbx_metadata_object *ufbx_as_metadata_object(const ufbx_element *element);
|
ufbx_abi ufbx_metadata_object *ufbx_as_metadata_object(const ufbx_element *element);
|
||||||
|
|
||||||
|
// Functions for interfacing with DOM lists
|
||||||
|
ufbx_abi bool ufbx_dom_is_array(const ufbx_dom_node *node);
|
||||||
|
ufbx_abi size_t ufbx_dom_array_size(const ufbx_dom_node *node);
|
||||||
|
ufbx_abi ufbx_int32_list ufbx_dom_as_int32_list(const ufbx_dom_node *node);
|
||||||
|
ufbx_abi ufbx_int64_list ufbx_dom_as_int64_list(const ufbx_dom_node *node);
|
||||||
|
ufbx_abi ufbx_float_list ufbx_dom_as_float_list(const ufbx_dom_node *node);
|
||||||
|
ufbx_abi ufbx_double_list ufbx_dom_as_double_list(const ufbx_dom_node *node);
|
||||||
|
ufbx_abi ufbx_real_list ufbx_dom_as_real_list(const ufbx_dom_node *node);
|
||||||
|
ufbx_abi ufbx_blob_list ufbx_dom_as_blob_list(const ufbx_dom_node *node);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "BKE_attribute.hh"
|
#include "BKE_attribute.hh"
|
||||||
|
#include "BKE_deform.hh"
|
||||||
#include "BKE_key.hh"
|
#include "BKE_key.hh"
|
||||||
#include "BKE_lib_id.hh"
|
#include "BKE_lib_id.hh"
|
||||||
#include "BKE_material.hh"
|
#include "BKE_material.hh"
|
||||||
@@ -338,13 +339,75 @@ static bool import_blend_shapes(Main &bmain,
|
|||||||
const ufbx_vec3 &delta = fchan->target_shape->position_offsets[i];
|
const ufbx_vec3 &delta = fchan->target_shape->position_offsets[i];
|
||||||
kb_data[idx] += float3(delta.x, delta.y, delta.z);
|
kb_data[idx] += float3(delta.x, delta.y, delta.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
mapping.el_to_shape_key.add(&fchan->element, mesh_key);
|
mapping.el_to_shape_key.add(&fchan->element, mesh_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mesh_key != nullptr;
|
return mesh_key != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle Blender-specific "FullWeights" that for each blend shape also create
|
||||||
|
* a weighted vertex group for itself. */
|
||||||
|
static void import_blend_shape_full_weights(const FbxElementMapping &mapping,
|
||||||
|
const ufbx_mesh *fmesh,
|
||||||
|
Mesh *mesh,
|
||||||
|
Object *obj)
|
||||||
|
{
|
||||||
|
for (const ufbx_blend_deformer *fdeformer : fmesh->blend_deformers) {
|
||||||
|
for (const ufbx_blend_channel *fchan : fdeformer->channels) {
|
||||||
|
Key *key = mapping.el_to_shape_key.lookup_default(&fchan->element, nullptr);
|
||||||
|
if (fchan->target_shape == nullptr || key == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fchan->target_shape->offset_weights.count != fchan->target_shape->num_offsets) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyBlock *kb = BKE_keyblock_find_name(key, fchan->target_shape->name.data);
|
||||||
|
if (kb == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore cases where all weights are 1.0 (group has no effect),
|
||||||
|
* and cases where any weights are outside of 0..1 range (apparently some files have
|
||||||
|
* invalid negative weights and should be ignored). */
|
||||||
|
bool all_one = true;
|
||||||
|
bool all_unorm = true;
|
||||||
|
for (ufbx_real w : fchan->target_shape->offset_weights) {
|
||||||
|
if (w != 1.0) {
|
||||||
|
all_one = false;
|
||||||
|
}
|
||||||
|
if (w < 0.0 || w > 1.0) {
|
||||||
|
all_unorm = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (all_one || !all_unorm) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int group_index = BKE_defgroup_name_index(&mesh->vertex_group_names, kb->name);
|
||||||
|
if (group_index < 0) {
|
||||||
|
BKE_object_defgroup_add_name(obj, kb->name);
|
||||||
|
group_index = BKE_defgroup_name_index(&mesh->vertex_group_names, kb->name);
|
||||||
|
if (group_index < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
|
||||||
|
for (int i = 0; i < fchan->target_shape->num_offsets; i++) {
|
||||||
|
const int idx = fchan->target_shape->offset_vertices[i];
|
||||||
|
if (idx >= 0 && idx < dverts.size()) {
|
||||||
|
const float w = fchan->target_shape->offset_weights[i];
|
||||||
|
MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[idx], group_index);
|
||||||
|
dw->weight = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STRNCPY(kb->vgroup, kb->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void import_meshes(Main &bmain,
|
void import_meshes(Main &bmain,
|
||||||
const ufbx_scene &fbx,
|
const ufbx_scene &fbx,
|
||||||
FbxElementMapping &mapping,
|
FbxElementMapping &mapping,
|
||||||
@@ -491,6 +554,10 @@ void import_meshes(Main &bmain,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (any_shapes) {
|
||||||
|
import_blend_shape_full_weights(mapping, fmesh, mesh, obj);
|
||||||
|
}
|
||||||
|
|
||||||
/* Assign materials. */
|
/* Assign materials. */
|
||||||
if (fmesh->materials.count > 0 && node->materials.count == fmesh->materials.count) {
|
if (fmesh->materials.count > 0 && node->materials.count == fmesh->materials.count) {
|
||||||
int mat_index = 0;
|
int mat_index = 0;
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
==== Meshes: 1
|
||||||
|
- Mesh 'Cube.001' vtx:8 face:6 loop:24 edge:12
|
||||||
|
- 0 1 3 2 2 ... 0 7 3 1 5
|
||||||
|
- 0/1 1/3 2/3 0/2 3/7 ... 5/7 4/5 4/6 1/5 0/4
|
||||||
|
- attr 'position' FLOAT_VECTOR POINT
|
||||||
|
- (-1.000, -1.000, -1.000)
|
||||||
|
- (-1.000, -1.000, 1.000)
|
||||||
|
- (-1.000, 1.000, -1.000)
|
||||||
|
...
|
||||||
|
- (1.000, -1.000, 1.000)
|
||||||
|
- (1.000, 1.000, -1.000)
|
||||||
|
- (1.000, 1.000, 1.000)
|
||||||
|
- attr 'sharp_edge' BOOLEAN EDGE
|
||||||
|
- 1 1 1 1 1 ... 1 1 1 1 1
|
||||||
|
- attr 'custom_normal' INT16_2D CORNER
|
||||||
|
- (0, 0)
|
||||||
|
- (0, 0)
|
||||||
|
- (0, 0)
|
||||||
|
...
|
||||||
|
- (0, 0)
|
||||||
|
- (0, 0)
|
||||||
|
- (0, 0)
|
||||||
|
- attr 'UVMap' FLOAT2 CORNER
|
||||||
|
- (0.375, 0.000)
|
||||||
|
- (0.625, 0.000)
|
||||||
|
- (0.625, 0.250)
|
||||||
|
...
|
||||||
|
- (0.875, 0.500)
|
||||||
|
- (0.875, 0.750)
|
||||||
|
- (0.625, 0.750)
|
||||||
|
- vertex groups:
|
||||||
|
-
|
||||||
|
- 0=0.000
|
||||||
|
-
|
||||||
|
- 0=1.000
|
||||||
|
- 1=0.500
|
||||||
|
- shape key 'Basis' w:0.000 vgrp:'' 0:(-1.000, -1.000, -1.000) 1:(-1.000, -1.000, 1.000) 2:(-1.000, 1.000, -1.000)
|
||||||
|
- shape key 'Key' w:0.000 vgrp:'Key' 0:(-1.000, -1.000, -1.000) 1:(-1.000, -1.000, 3.000) 2:(-1.000, 1.000, -1.000)
|
||||||
|
- shape key 'Side' w:0.000 vgrp:'Side' 0:(-1.000, -1.000, -1.000) 1:(-1.000, -1.000, 1.000) 2:(-1.000, 1.000, -1.000)
|
||||||
|
|
||||||
|
==== Objects: 1
|
||||||
|
- Obj 'Cube' MESH data:'Cube.001'
|
||||||
|
- pos 0.000, 0.000, 0.000
|
||||||
|
- rot 0.000, 0.000, 0.000 (XYZ)
|
||||||
|
- scl 1.000, 1.000, 1.000
|
||||||
|
- 2 vertex groups
|
||||||
|
- 'Key' 'Side'
|
||||||
|
- anim act:Cube|Action slot:OBCube blend:REPLACE drivers:0
|
||||||
|
|
||||||
|
==== Actions: 1
|
||||||
|
- Action 'Cube|Action' curverange:(1.0 .. 30.0) layers:1
|
||||||
|
- ActionLayer Layer strips:1
|
||||||
|
- Keyframe strip channelbags:2
|
||||||
|
- Channelbag slot 'KEKey' curves:2
|
||||||
|
- fcu 'key_blocks["Key"].value[0]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (3.000, 0.000) lh:(2.667, 0.000 AUTO_CLAMPED) rh:(3.333, 0.000 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 1.000) lh:(27.667, 1.000 AUTO_CLAMPED) rh:(28.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (29.000, 1.000) lh:(28.667, 1.000 AUTO_CLAMPED) rh:(29.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (30.000, 1.000) lh:(29.667, 1.000 AUTO_CLAMPED) rh:(30.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'key_blocks["Side"].value[0]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.056) lh:(1.667, 0.032 AUTO_CLAMPED) rh:(2.333, 0.079 AUTO_CLAMPED)
|
||||||
|
- (3.000, 0.111) lh:(2.667, 0.094 AUTO_CLAMPED) rh:(3.333, 0.128 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 0.950) lh:(27.667, 0.942 AUTO_CLAMPED) rh:(28.333, 0.958 AUTO_CLAMPED)
|
||||||
|
- (29.000, 0.975) lh:(28.667, 0.964 AUTO_CLAMPED) rh:(29.333, 0.986 AUTO_CLAMPED)
|
||||||
|
- (30.000, 1.000) lh:(29.667, 1.000 AUTO_CLAMPED) rh:(30.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- Channelbag slot 'OBCube' curves:9
|
||||||
|
- fcu 'location[0]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (3.000, 0.000) lh:(2.667, 0.000 AUTO_CLAMPED) rh:(3.333, 0.000 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 0.000) lh:(27.667, 0.000 AUTO_CLAMPED) rh:(28.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (29.000, 0.000) lh:(28.667, 0.000 AUTO_CLAMPED) rh:(29.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (30.000, 0.000) lh:(29.667, 0.000 AUTO_CLAMPED) rh:(30.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'location[1]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (3.000, 0.000) lh:(2.667, 0.000 AUTO_CLAMPED) rh:(3.333, 0.000 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 0.000) lh:(27.667, 0.000 AUTO_CLAMPED) rh:(28.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (29.000, 0.000) lh:(28.667, 0.000 AUTO_CLAMPED) rh:(29.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (30.000, 0.000) lh:(29.667, 0.000 AUTO_CLAMPED) rh:(30.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'location[2]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (3.000, 0.000) lh:(2.667, 0.000 AUTO_CLAMPED) rh:(3.333, 0.000 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 0.000) lh:(27.667, 0.000 AUTO_CLAMPED) rh:(28.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (29.000, 0.000) lh:(28.667, 0.000 AUTO_CLAMPED) rh:(29.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (30.000, 0.000) lh:(29.667, 0.000 AUTO_CLAMPED) rh:(30.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'rotation_euler[0]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (3.000, 0.000) lh:(2.667, 0.000 AUTO_CLAMPED) rh:(3.333, 0.000 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 0.000) lh:(27.667, 0.000 AUTO_CLAMPED) rh:(28.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (29.000, 0.000) lh:(28.667, 0.000 AUTO_CLAMPED) rh:(29.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (30.000, 0.000) lh:(29.667, 0.000 AUTO_CLAMPED) rh:(30.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'rotation_euler[1]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (3.000, 0.000) lh:(2.667, 0.000 AUTO_CLAMPED) rh:(3.333, 0.000 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 0.000) lh:(27.667, 0.000 AUTO_CLAMPED) rh:(28.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (29.000, 0.000) lh:(28.667, 0.000 AUTO_CLAMPED) rh:(29.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (30.000, 0.000) lh:(29.667, 0.000 AUTO_CLAMPED) rh:(30.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'rotation_euler[2]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (3.000, 0.000) lh:(2.667, 0.000 AUTO_CLAMPED) rh:(3.333, 0.000 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 0.000) lh:(27.667, 0.000 AUTO_CLAMPED) rh:(28.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (29.000, 0.000) lh:(28.667, 0.000 AUTO_CLAMPED) rh:(29.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (30.000, 0.000) lh:(29.667, 0.000 AUTO_CLAMPED) rh:(30.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'scale[0]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30 grp:'Cube'
|
||||||
|
- (1.000, 1.000) lh:(0.667, 1.000 AUTO_CLAMPED) rh:(1.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 1.000) lh:(1.667, 1.000 AUTO_CLAMPED) rh:(2.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (3.000, 1.000) lh:(2.667, 1.000 AUTO_CLAMPED) rh:(3.333, 1.000 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 1.000) lh:(27.667, 1.000 AUTO_CLAMPED) rh:(28.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (29.000, 1.000) lh:(28.667, 1.000 AUTO_CLAMPED) rh:(29.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (30.000, 1.000) lh:(29.667, 1.000 AUTO_CLAMPED) rh:(30.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'scale[1]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30 grp:'Cube'
|
||||||
|
- (1.000, 1.000) lh:(0.667, 1.000 AUTO_CLAMPED) rh:(1.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 1.000) lh:(1.667, 1.000 AUTO_CLAMPED) rh:(2.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (3.000, 1.000) lh:(2.667, 1.000 AUTO_CLAMPED) rh:(3.333, 1.000 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 1.000) lh:(27.667, 1.000 AUTO_CLAMPED) rh:(28.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (29.000, 1.000) lh:(28.667, 1.000 AUTO_CLAMPED) rh:(29.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (30.000, 1.000) lh:(29.667, 1.000 AUTO_CLAMPED) rh:(30.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'scale[2]' smooth:CONT_ACCEL extra:CONSTANT keyframes:30 grp:'Cube'
|
||||||
|
- (1.000, 1.000) lh:(0.667, 1.000 AUTO_CLAMPED) rh:(1.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 1.000) lh:(1.667, 1.000 AUTO_CLAMPED) rh:(2.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (3.000, 1.000) lh:(2.667, 1.000 AUTO_CLAMPED) rh:(3.333, 1.000 AUTO_CLAMPED)
|
||||||
|
...
|
||||||
|
- (28.000, 1.000) lh:(27.667, 1.000 AUTO_CLAMPED) rh:(28.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (29.000, 1.000) lh:(28.667, 1.000 AUTO_CLAMPED) rh:(29.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (30.000, 1.000) lh:(29.667, 1.000 AUTO_CLAMPED) rh:(30.333, 1.000 AUTO_CLAMPED)
|
||||||
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
==== Meshes: 1
|
||||||
|
- Mesh 'Cube.001' vtx:8 face:6 loop:24 edge:12
|
||||||
|
- 0 1 3 2 2 ... 0 7 3 1 5
|
||||||
|
- 0/1 1/3 2/3 0/2 3/7 ... 5/7 4/5 4/6 1/5 0/4
|
||||||
|
- attr 'position' FLOAT_VECTOR POINT
|
||||||
|
- (-1.000, -1.000, -1.000)
|
||||||
|
- (-1.000, -1.000, 1.000)
|
||||||
|
- (-1.000, 1.000, -1.000)
|
||||||
|
...
|
||||||
|
- (1.000, -1.000, 1.000)
|
||||||
|
- (1.000, 1.000, -1.000)
|
||||||
|
- (1.000, 1.000, 1.000)
|
||||||
|
- attr 'sharp_edge' BOOLEAN EDGE
|
||||||
|
- 1 1 1 1 1 ... 1 1 1 1 1
|
||||||
|
- attr 'custom_normal' INT16_2D CORNER
|
||||||
|
- (0, 0)
|
||||||
|
- (0, 0)
|
||||||
|
- (0, 0)
|
||||||
|
...
|
||||||
|
- (0, 0)
|
||||||
|
- (0, 0)
|
||||||
|
- (0, 0)
|
||||||
|
- vertex groups:
|
||||||
|
-
|
||||||
|
- 0=0.000
|
||||||
|
-
|
||||||
|
- 0=1.000
|
||||||
|
-
|
||||||
|
- shape key 'Basis' w:0.000 vgrp:'' 0:(-1.000, -1.000, -1.000) 1:(-1.000, -1.000, 1.000) 2:(-1.000, 1.000, -1.000)
|
||||||
|
- shape key 'Key' w:1.000 vgrp:'Key' 0:(-1.000, -1.000, -1.000) 1:(-1.000, -1.000, 3.000) 2:(-1.000, 1.000, -1.000)
|
||||||
|
|
||||||
|
==== Objects: 1
|
||||||
|
- Obj 'Cube' MESH data:'Cube.001'
|
||||||
|
- pos 0.000, 0.000, 0.000
|
||||||
|
- rot 0.000, 0.000, 0.000 (XYZ)
|
||||||
|
- scl 1.000, 1.000, 1.000
|
||||||
|
- 1 vertex groups
|
||||||
|
- 'Key'
|
||||||
|
- anim act:Cube|Action slot:OBCube blend:REPLACE drivers:0
|
||||||
|
|
||||||
|
==== Actions: 1
|
||||||
|
- Action 'Cube|Action' curverange:(1.0 .. 2.0) layers:1
|
||||||
|
- ActionLayer Layer strips:1
|
||||||
|
- Keyframe strip channelbags:2
|
||||||
|
- Channelbag slot 'KEKey' curves:1
|
||||||
|
- fcu 'key_blocks["Key"].value[0]' smooth:CONT_ACCEL extra:CONSTANT keyframes:2
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- Channelbag slot 'OBCube' curves:9
|
||||||
|
- fcu 'location[0]' smooth:CONT_ACCEL extra:CONSTANT keyframes:2 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'location[1]' smooth:CONT_ACCEL extra:CONSTANT keyframes:2 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'location[2]' smooth:CONT_ACCEL extra:CONSTANT keyframes:2 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'rotation_euler[0]' smooth:CONT_ACCEL extra:CONSTANT keyframes:2 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'rotation_euler[1]' smooth:CONT_ACCEL extra:CONSTANT keyframes:2 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'rotation_euler[2]' smooth:CONT_ACCEL extra:CONSTANT keyframes:2 grp:'Cube'
|
||||||
|
- (1.000, 0.000) lh:(0.667, 0.000 AUTO_CLAMPED) rh:(1.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 0.000) lh:(1.667, 0.000 AUTO_CLAMPED) rh:(2.333, 0.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'scale[0]' smooth:CONT_ACCEL extra:CONSTANT keyframes:2 grp:'Cube'
|
||||||
|
- (1.000, 1.000) lh:(0.667, 1.000 AUTO_CLAMPED) rh:(1.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 1.000) lh:(1.667, 1.000 AUTO_CLAMPED) rh:(2.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'scale[1]' smooth:CONT_ACCEL extra:CONSTANT keyframes:2 grp:'Cube'
|
||||||
|
- (1.000, 1.000) lh:(0.667, 1.000 AUTO_CLAMPED) rh:(1.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 1.000) lh:(1.667, 1.000 AUTO_CLAMPED) rh:(2.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- fcu 'scale[2]' smooth:CONT_ACCEL extra:CONSTANT keyframes:2 grp:'Cube'
|
||||||
|
- (1.000, 1.000) lh:(0.667, 1.000 AUTO_CLAMPED) rh:(1.333, 1.000 AUTO_CLAMPED)
|
||||||
|
- (2.000, 1.000) lh:(1.667, 1.000 AUTO_CLAMPED) rh:(2.333, 1.000 AUTO_CLAMPED)
|
||||||
|
|
||||||
BIN
tests/files/io_tests/fbx/ufbx_blender440_shape_weight_anim_74b.fbx
(Stored with Git LFS)
Normal file
BIN
tests/files/io_tests/fbx/ufbx_blender440_shape_weight_anim_74b.fbx
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
tests/files/io_tests/fbx/ufbx_blender_279_shape_weights_74b.fbx
(Stored with Git LFS)
Normal file
BIN
tests/files/io_tests/fbx/ufbx_blender_279_shape_weights_74b.fbx
(Stored with Git LFS)
Normal file
Binary file not shown.
Reference in New Issue
Block a user