Fix #138834: FBX importer sets pivots incorrectly in some cases
Fix the rest of #138834, pivot adjustment was not done when rotation pivot and scaling pivot inside FBX were not matching. Pull Request: https://projects.blender.org/blender/blender/pulls/140411
This commit is contained in:
committed by
Aras Pranckevicius
parent
6251f7f0b0
commit
074fcfe1a5
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.19.0-dev (6c0179d, 2025-04-27)
|
Upstream version: v0.19.0-dev (bad1b24, 2025-06-14)
|
||||||
Local modifications:
|
Local modifications:
|
||||||
* None
|
* None
|
||||||
|
|||||||
147
extern/ufbx/ufbx.c
vendored
147
extern/ufbx/ufbx.c
vendored
@@ -5944,6 +5944,11 @@ ufbx_inline ufbx_vec3 ufbxi_normalize3(ufbx_vec3 a) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ufbx_inline ufbx_vec3 ufbxi_neg3(ufbx_vec3 a) {
|
||||||
|
ufbx_vec3 v = { -a.x, -a.y, -a.z };
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
ufbx_inline ufbx_real ufbxi_distsq2(ufbx_vec2 a, ufbx_vec2 b) {
|
ufbx_inline ufbx_real ufbxi_distsq2(ufbx_vec2 a, ufbx_vec2 b) {
|
||||||
ufbx_real dx = a.x - b.x, dy = a.y - b.y;
|
ufbx_real dx = a.x - b.x, dy = a.y - b.y;
|
||||||
return dx*dx + dy*dy;
|
return dx*dx + dy*dy;
|
||||||
@@ -5960,7 +5965,6 @@ static ufbxi_noinline ufbx_vec3 ufbxi_slow_normalized_cross3(const ufbx_vec3 *a,
|
|||||||
// -- Threading
|
// -- Threading
|
||||||
|
|
||||||
typedef struct ufbxi_task ufbxi_task;
|
typedef struct ufbxi_task ufbxi_task;
|
||||||
typedef struct ufbxi_thread ufbxi_thread;
|
|
||||||
typedef struct ufbxi_thread_pool ufbxi_thread_pool;
|
typedef struct ufbxi_thread_pool ufbxi_thread_pool;
|
||||||
|
|
||||||
typedef bool ufbxi_task_fn(ufbxi_task *task);
|
typedef bool ufbxi_task_fn(ufbxi_task *task);
|
||||||
@@ -17985,6 +17989,23 @@ typedef struct {
|
|||||||
ufbx_vec3 constant_value;
|
ufbx_vec3 constant_value;
|
||||||
} ufbxi_pre_anim_value;
|
} ufbxi_pre_anim_value;
|
||||||
|
|
||||||
|
static bool ufbxi_pivot_nonzero(ufbx_vec3 offset)
|
||||||
|
{
|
||||||
|
// TODO: Expose this as a setting?
|
||||||
|
const double epsilon = 0.0009765625;
|
||||||
|
return ufbx_fabs(offset.x) >= epsilon || ufbx_fabs(offset.y) >= epsilon || ufbx_fabs(offset.z) >= epsilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ufbx_real ufbxi_pivot_div(ufbx_real offset, ufbx_real initial_scale)
|
||||||
|
{
|
||||||
|
const double epsilon = 0.0078125;
|
||||||
|
if (ufbx_fabs(initial_scale) >= epsilon) {
|
||||||
|
return offset / initial_scale;
|
||||||
|
} else {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Called between parsing and `ufbxi_finalize_scene()`.
|
// Called between parsing and `ufbxi_finalize_scene()`.
|
||||||
// This is a very messy function reminiscent of the _old_ ufbx, where we do
|
// This is a very messy function reminiscent of the _old_ ufbx, where we do
|
||||||
// multiple passes over connections without having a proper scene graph.
|
// multiple passes over connections without having a proper scene graph.
|
||||||
@@ -17997,7 +18018,7 @@ ufbxi_nodiscard ufbxi_noinline static int ufbxi_pre_finalize_scene(ufbxi_context
|
|||||||
bool required = false;
|
bool required = false;
|
||||||
if (uc->opts.geometry_transform_handling == UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES || uc->opts.geometry_transform_handling == UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY) required = true;
|
if (uc->opts.geometry_transform_handling == UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES || uc->opts.geometry_transform_handling == UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY) required = true;
|
||||||
if (uc->opts.inherit_mode_handling == UFBX_INHERIT_MODE_HANDLING_HELPER_NODES || uc->opts.inherit_mode_handling == UFBX_INHERIT_MODE_HANDLING_COMPENSATE || uc->opts.inherit_mode_handling == UFBX_INHERIT_MODE_HANDLING_COMPENSATE_NO_FALLBACK) required = true;
|
if (uc->opts.inherit_mode_handling == UFBX_INHERIT_MODE_HANDLING_HELPER_NODES || uc->opts.inherit_mode_handling == UFBX_INHERIT_MODE_HANDLING_COMPENSATE || uc->opts.inherit_mode_handling == UFBX_INHERIT_MODE_HANDLING_COMPENSATE_NO_FALLBACK) required = true;
|
||||||
if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT) required = true;
|
if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT || uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_ROTATION_PIVOT) required = true;
|
||||||
#if defined(UFBX_REGRESSION)
|
#if defined(UFBX_REGRESSION)
|
||||||
required = true;
|
required = true;
|
||||||
#endif
|
#endif
|
||||||
@@ -18022,6 +18043,9 @@ ufbxi_nodiscard ufbxi_noinline static int ufbxi_pre_finalize_scene(ufbxi_context
|
|||||||
bool *modify_not_supported = ufbxi_push_zero(&uc->tmp_parse, bool, num_elements);
|
bool *modify_not_supported = ufbxi_push_zero(&uc->tmp_parse, bool, num_elements);
|
||||||
ufbxi_check(modify_not_supported);
|
ufbxi_check(modify_not_supported);
|
||||||
|
|
||||||
|
ufbx_element_type *node_attrib_type = ufbxi_push_zero(&uc->tmp_parse, ufbx_element_type, num_nodes);
|
||||||
|
ufbxi_check(node_attrib_type);
|
||||||
|
|
||||||
bool *has_unscaled_children = ufbxi_push_zero(&uc->tmp_parse, bool, num_nodes);
|
bool *has_unscaled_children = ufbxi_push_zero(&uc->tmp_parse, bool, num_nodes);
|
||||||
ufbxi_check(has_unscaled_children);
|
ufbxi_check(has_unscaled_children);
|
||||||
|
|
||||||
@@ -18090,7 +18114,8 @@ ufbxi_nodiscard ufbxi_noinline static int ufbxi_pre_finalize_scene(ufbxi_context
|
|||||||
ufbx_node *dst_node = (ufbx_node*)dst;
|
ufbx_node *dst_node = (ufbx_node*)dst;
|
||||||
|
|
||||||
if (src->type >= UFBX_ELEMENT_TYPE_FIRST_ATTRIB && src->type <= UFBX_ELEMENT_TYPE_LAST_ATTRIB) {
|
if (src->type >= UFBX_ELEMENT_TYPE_FIRST_ATTRIB && src->type <= UFBX_ELEMENT_TYPE_LAST_ATTRIB) {
|
||||||
++instance_counts[src->element_id];
|
uint32_t count = ++instance_counts[src->element_id];
|
||||||
|
node_attrib_type[dst->typed_id] = count == 1 ? src->type : UFBX_ELEMENT_UNKNOWN;
|
||||||
|
|
||||||
// These must match what can be trasnsformed in `ufbxi_modify_geometry()`
|
// These must match what can be trasnsformed in `ufbxi_modify_geometry()`
|
||||||
switch (src->type) {
|
switch (src->type) {
|
||||||
@@ -18202,19 +18227,35 @@ ufbxi_nodiscard ufbxi_noinline static int ufbxi_pre_finalize_scene(ufbxi_context
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT) {
|
if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT || uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_ROTATION_PIVOT) {
|
||||||
for (size_t i = 0; i < num_nodes; i++) {
|
for (size_t i = 0; i < num_nodes; i++) {
|
||||||
ufbxi_pre_node *pre_node = &pre_nodes[i];
|
ufbxi_pre_node *pre_node = &pre_nodes[i];
|
||||||
ufbx_node *node = (ufbx_node*)elements[pre_node->element_id];
|
ufbx_node *node = (ufbx_node*)elements[pre_node->element_id];
|
||||||
|
|
||||||
ufbx_vec3 rotation_pivot = ufbxi_find_vec3(&node->props, ufbxi_RotationPivot, 0.0f, 0.0f, 0.0f);
|
ufbx_vec3 rotation_pivot = ufbxi_find_vec3(&node->props, ufbxi_RotationPivot, 0.0f, 0.0f, 0.0f);
|
||||||
ufbx_vec3 scaling_pivot = ufbxi_find_vec3(&node->props, ufbxi_ScalingPivot, 0.0f, 0.0f, 0.0f);
|
ufbx_vec3 scaling_pivot = ufbxi_find_vec3(&node->props, ufbxi_ScalingPivot, 0.0f, 0.0f, 0.0f);
|
||||||
if (!ufbxi_is_vec3_zero(rotation_pivot)) {
|
ufbx_vec3 scaling_offset = ufbxi_find_vec3(&node->props, ufbxi_ScalingOffset, 0.0f, 0.0f, 0.0f);
|
||||||
ufbx_real err = 0.0f;
|
|
||||||
err += (ufbx_real)ufbx_fabs(rotation_pivot.x - scaling_pivot.x);
|
|
||||||
err += (ufbx_real)ufbx_fabs(rotation_pivot.y - scaling_pivot.y);
|
|
||||||
err += (ufbx_real)ufbx_fabs(rotation_pivot.z - scaling_pivot.z);
|
|
||||||
|
|
||||||
|
bool should_modify_pivot = false;
|
||||||
|
if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT) {
|
||||||
|
should_modify_pivot = !ufbxi_is_vec3_zero(rotation_pivot);
|
||||||
|
} else if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_ROTATION_PIVOT) {
|
||||||
|
should_modify_pivot = ufbxi_pivot_nonzero(rotation_pivot) || ufbxi_pivot_nonzero(scaling_pivot) || ufbxi_pivot_nonzero(scaling_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_modify_pivot) {
|
||||||
|
bool skip_geometry_transform = false;
|
||||||
bool can_modify_geometry_transform = true;
|
bool can_modify_geometry_transform = true;
|
||||||
|
if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_ROTATION_PIVOT) {
|
||||||
|
if (node_attrib_type[node->typed_id] == UFBX_ELEMENT_EMPTY) {
|
||||||
|
if (!uc->opts.pivot_handling_retain_empties) {
|
||||||
|
skip_geometry_transform = true;
|
||||||
|
} else {
|
||||||
|
can_modify_geometry_transform = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (uc->opts.geometry_transform_handling == UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK) {
|
if (uc->opts.geometry_transform_handling == UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK) {
|
||||||
if (instance_counts[node->element_id] > 1 || modify_not_supported[node->element_id]) {
|
if (instance_counts[node->element_id] > 1 || modify_not_supported[node->element_id]) {
|
||||||
can_modify_geometry_transform = false;
|
can_modify_geometry_transform = false;
|
||||||
@@ -18225,24 +18266,77 @@ ufbxi_nodiscard ufbxi_noinline static int ufbxi_pre_finalize_scene(ufbxi_context
|
|||||||
can_modify_geometry_transform = false;
|
can_modify_geometry_transform = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err <= pivot_epsilon && can_modify_geometry_transform) {
|
bool can_modify_pivot = true;
|
||||||
size_t num_props = node->props.props.count;
|
if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT) {
|
||||||
ufbx_prop *new_props = ufbxi_push_zero(&uc->result, ufbx_prop, num_props + 3);
|
ufbx_real err = 0.0f;
|
||||||
ufbxi_check(new_props);
|
err += (ufbx_real)ufbx_fabs(rotation_pivot.x - scaling_pivot.x);
|
||||||
memcpy(new_props, node->props.props.data, num_props * sizeof(ufbx_prop));
|
err += (ufbx_real)ufbx_fabs(rotation_pivot.y - scaling_pivot.y);
|
||||||
|
err += (ufbx_real)ufbx_fabs(rotation_pivot.z - scaling_pivot.z);
|
||||||
|
if (err > pivot_epsilon) {
|
||||||
|
can_modify_pivot = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (can_modify_pivot && (can_modify_geometry_transform || skip_geometry_transform)) {
|
||||||
ufbx_vec3 geometric_translation = ufbxi_find_vec3(&node->props, ufbxi_GeometricTranslation, 0.0f, 0.0f, 0.0f);
|
ufbx_vec3 geometric_translation = ufbxi_find_vec3(&node->props, ufbxi_GeometricTranslation, 0.0f, 0.0f, 0.0f);
|
||||||
geometric_translation.x -= rotation_pivot.x;
|
|
||||||
geometric_translation.y -= rotation_pivot.y;
|
|
||||||
geometric_translation.z -= rotation_pivot.z;
|
|
||||||
|
|
||||||
ufbx_prop *dst = new_props + num_props;
|
ufbx_vec3 child_offset = { 0.0f };
|
||||||
ufbxi_init_synthetic_vec3_prop(&dst[0], ufbxi_RotationPivot, &ufbx_zero_vec3, UFBX_PROP_VECTOR);
|
ufbx_prop *new_props = NULL;
|
||||||
ufbxi_init_synthetic_vec3_prop(&dst[1], ufbxi_ScalingPivot, &ufbx_zero_vec3, UFBX_PROP_VECTOR);
|
size_t num_props = node->props.props.count;
|
||||||
ufbxi_init_synthetic_vec3_prop(&dst[2], ufbxi_GeometricTranslation, &geometric_translation, UFBX_PROP_VECTOR);
|
size_t new_prop_count = num_props;
|
||||||
|
if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT) {
|
||||||
|
ufbx_assert(!skip_geometry_transform); // not supporeted in legacy mode
|
||||||
|
child_offset = ufbxi_neg3(rotation_pivot);
|
||||||
|
geometric_translation = ufbxi_add3(geometric_translation, child_offset);
|
||||||
|
|
||||||
|
new_props = ufbxi_push_zero(&uc->result, ufbx_prop, num_props + 3);
|
||||||
|
ufbxi_check(new_props);
|
||||||
|
memcpy(new_props, node->props.props.data, num_props * sizeof(ufbx_prop));
|
||||||
|
|
||||||
|
ufbxi_init_synthetic_vec3_prop(&new_props[new_prop_count++], ufbxi_RotationPivot, &ufbx_zero_vec3, UFBX_PROP_VECTOR);
|
||||||
|
ufbxi_init_synthetic_vec3_prop(&new_props[new_prop_count++], ufbxi_ScalingPivot, &ufbx_zero_vec3, UFBX_PROP_VECTOR);
|
||||||
|
ufbxi_init_synthetic_vec3_prop(&new_props[new_prop_count++], ufbxi_GeometricTranslation, &geometric_translation, UFBX_PROP_VECTOR);
|
||||||
|
} else if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_ROTATION_PIVOT) {
|
||||||
|
// We can eliminate the post-rotation translation and move it to the geometry/children as follows.
|
||||||
|
// Let Z be the initial value of S in the transform (aka `initial_scale`):
|
||||||
|
//
|
||||||
|
// (Rp-1+Soff+Sp) + S * (Sp-1)
|
||||||
|
// S * (Sp-1 + (Rp-1+Soff+Sp)/S)
|
||||||
|
// S * (Sp-1 + (Rp-1+Soff+Sp)/S - (Rp-1+Soff+Sp)/Z + (Rp-1+Soff+Sp)/Z)
|
||||||
|
//
|
||||||
|
// (Rp-1 + Soff + Sp) + S * (-(Rp-1 + Soff + Sp)/Z + (Sp-1 + (Rp-1 + Soff + Sp)/Z))
|
||||||
|
// ^-scaled_offset--^ ^-unscaled_offset--^ ^-unscaled_offset--^
|
||||||
|
// ^---------------- 0, when S=Z ----------------^ ^------- child_offset ------^
|
||||||
|
//
|
||||||
|
// We need to be careful when doing this in case any component of Z is 0. Fortunately,
|
||||||
|
// the above holds for all `Z != 0`, it will just result in non-zero translation in the parent.
|
||||||
|
ufbx_vec3 initial_scale = ufbxi_find_vec3(&node->props, ufbxi_Lcl_Scaling, 1.0f, 1.0f, 1.0f);
|
||||||
|
ufbx_vec3 scaled_offset = ufbxi_sub3(ufbxi_add3(scaling_offset, scaling_pivot), rotation_pivot);
|
||||||
|
ufbx_vec3 unscaled_offset;
|
||||||
|
unscaled_offset.x = ufbxi_pivot_div(scaled_offset.x, initial_scale.x);
|
||||||
|
unscaled_offset.y = ufbxi_pivot_div(scaled_offset.y, initial_scale.y);
|
||||||
|
unscaled_offset.z = ufbxi_pivot_div(scaled_offset.z, initial_scale.z);
|
||||||
|
|
||||||
|
// Convert `scaled_offset + S*unscaled_offset` to FBX scaling pivot and offset.
|
||||||
|
ufbx_vec3 new_scaling_pivot = unscaled_offset;
|
||||||
|
ufbx_vec3 new_scaling_offset = ufbxi_sub3(scaled_offset, new_scaling_pivot);
|
||||||
|
child_offset = ufbxi_sub3(unscaled_offset, scaling_pivot);
|
||||||
|
|
||||||
|
new_props = ufbxi_push_zero(&uc->result, ufbx_prop, num_props + 4);
|
||||||
|
ufbxi_check(new_props);
|
||||||
|
memcpy(new_props, node->props.props.data, num_props * sizeof(ufbx_prop));
|
||||||
|
|
||||||
|
ufbxi_init_synthetic_vec3_prop(&new_props[new_prop_count++], ufbxi_RotationPivot, &ufbx_zero_vec3, UFBX_PROP_VECTOR);
|
||||||
|
ufbxi_init_synthetic_vec3_prop(&new_props[new_prop_count++], ufbxi_ScalingPivot, &new_scaling_pivot, UFBX_PROP_VECTOR);
|
||||||
|
ufbxi_init_synthetic_vec3_prop(&new_props[new_prop_count++], ufbxi_ScalingOffset, &new_scaling_offset, UFBX_PROP_VECTOR);
|
||||||
|
if (!skip_geometry_transform) {
|
||||||
|
geometric_translation = ufbxi_add3(geometric_translation, child_offset);
|
||||||
|
ufbxi_init_synthetic_vec3_prop(&new_props[new_prop_count++], ufbxi_GeometricTranslation, &geometric_translation, UFBX_PROP_VECTOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
node->props.props.data = new_props;
|
node->props.props.data = new_props;
|
||||||
node->props.props.count = num_props + 3;
|
node->props.props.count = new_prop_count;
|
||||||
ufbxi_check(ufbxi_sort_properties(uc, node->props.props.data, node->props.props.count));
|
ufbxi_check(ufbxi_sort_properties(uc, node->props.props.data, node->props.props.count));
|
||||||
ufbxi_deduplicate_properties(&node->props.props);
|
ufbxi_deduplicate_properties(&node->props.props);
|
||||||
|
|
||||||
@@ -18253,7 +18347,7 @@ ufbxi_nodiscard ufbxi_noinline static int ufbxi_pre_finalize_scene(ufbxi_context
|
|||||||
ufbxi_pre_node *pre_child = &pre_nodes[ix];
|
ufbxi_pre_node *pre_child = &pre_nodes[ix];
|
||||||
ufbx_node *child = (ufbx_node*)elements[pre_child->element_id];
|
ufbx_node *child = (ufbx_node*)elements[pre_child->element_id];
|
||||||
|
|
||||||
child->adjust_pre_translation = ufbxi_sub3(child->adjust_pre_translation, rotation_pivot);
|
child->adjust_pre_translation = ufbxi_add3(child->adjust_pre_translation, child_offset);
|
||||||
child->has_adjust_transform = true;
|
child->has_adjust_transform = true;
|
||||||
|
|
||||||
ix = pre_child->next_child;
|
ix = pre_child->next_child;
|
||||||
@@ -23515,8 +23609,13 @@ ufbxi_noinline static void ufbxi_update_adjust_transforms(ufbxi_context *uc, ufb
|
|||||||
light->local_direction.z = 0.0f;
|
light->local_direction.z = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
ufbx_real root_scale = ufbxi_min3(root_transform.scale);
|
|
||||||
scene->metadata.space_conversion = conversion;
|
scene->metadata.space_conversion = conversion;
|
||||||
|
scene->metadata.geometry_transform_handling = uc->opts.geometry_transform_handling;
|
||||||
|
scene->metadata.inherit_mode_handling = uc->opts.inherit_mode_handling;
|
||||||
|
scene->metadata.pivot_handling = uc->opts.pivot_handling;
|
||||||
|
scene->metadata.handedness_conversion_axis = uc->opts.handedness_conversion_axis;
|
||||||
|
|
||||||
|
ufbx_real root_scale = ufbxi_min3(root_transform.scale);
|
||||||
if (conversion == UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY) {
|
if (conversion == UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY) {
|
||||||
scene->metadata.geometry_scale = root_scale;
|
scene->metadata.geometry_scale = root_scale;
|
||||||
scene->metadata.root_scale = 1.0f;
|
scene->metadata.root_scale = 1.0f;
|
||||||
|
|||||||
192
extern/ufbx/ufbx.h
vendored
192
extern/ufbx/ufbx.h
vendored
@@ -3635,6 +3635,98 @@ typedef enum ufbx_space_conversion UFBX_ENUM_REPR {
|
|||||||
|
|
||||||
UFBX_ENUM_TYPE(ufbx_space_conversion, UFBX_SPACE_CONVERSION, UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY);
|
UFBX_ENUM_TYPE(ufbx_space_conversion, UFBX_SPACE_CONVERSION, UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY);
|
||||||
|
|
||||||
|
// How to handle FBX node geometry transforms.
|
||||||
|
// FBX nodes can have "geometry transforms" that affect only the attached meshes,
|
||||||
|
// but not the children. This is not allowed in many scene representations so
|
||||||
|
// ufbx provides some ways to simplify them.
|
||||||
|
// Geometry transforms can also be used to transform any other attributes such
|
||||||
|
// as lights or cameras.
|
||||||
|
typedef enum ufbx_geometry_transform_handling UFBX_ENUM_REPR {
|
||||||
|
|
||||||
|
// Preserve the geometry transforms as-is.
|
||||||
|
// To be correct for all files you have to use `ufbx_node.geometry_transform`,
|
||||||
|
// `ufbx_node.geometry_to_node`, or `ufbx_node.geometry_to_world` to compensate
|
||||||
|
// for any potential geometry transforms.
|
||||||
|
UFBX_GEOMETRY_TRANSFORM_HANDLING_PRESERVE,
|
||||||
|
|
||||||
|
// Add helper nodes between the nodes and geometry where needed.
|
||||||
|
// The created nodes have `ufbx_node.is_geometry_transform_helper` set and are
|
||||||
|
// named `ufbx_load_opts.geometry_transform_helper_name`.
|
||||||
|
UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES,
|
||||||
|
|
||||||
|
// Modify the geometry of meshes attached to nodes with geometry transforms.
|
||||||
|
// Will add helper nodes like `UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES` if
|
||||||
|
// necessary, for example if there are multiple instances of the same mesh with
|
||||||
|
// geometry transforms.
|
||||||
|
UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY,
|
||||||
|
|
||||||
|
// Modify the geometry of meshes attached to nodes with geometry transforms.
|
||||||
|
// NOTE: This will not work correctly for instanced geometry.
|
||||||
|
UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK,
|
||||||
|
|
||||||
|
UFBX_ENUM_FORCE_WIDTH(UFBX_GEOMETRY_TRANSFORM_HANDLING)
|
||||||
|
} ufbx_geometry_transform_handling;
|
||||||
|
|
||||||
|
UFBX_ENUM_TYPE(ufbx_geometry_transform_handling, UFBX_GEOMETRY_TRANSFORM_HANDLING, UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK);
|
||||||
|
|
||||||
|
// How to handle FBX transform inherit modes.
|
||||||
|
typedef enum ufbx_inherit_mode_handling UFBX_ENUM_REPR {
|
||||||
|
|
||||||
|
// Preserve inherit mode in `ufbx_node.inherit_mode`.
|
||||||
|
// NOTE: To correctly handle all scenes you would need to handle the
|
||||||
|
// non-standard inherit modes.
|
||||||
|
UFBX_INHERIT_MODE_HANDLING_PRESERVE,
|
||||||
|
|
||||||
|
// Create scale helper nodes parented to nodes that need special inheritance.
|
||||||
|
// Scale helper nodes will have `ufbx_node.is_scale_helper` and parents of
|
||||||
|
// scale helpers will have `ufbx_node.scale_helper` pointing to it.
|
||||||
|
UFBX_INHERIT_MODE_HANDLING_HELPER_NODES,
|
||||||
|
|
||||||
|
// Attempt to compensate for bone scale by inversely scaling children.
|
||||||
|
// NOTE: This only works for uniform non-animated scaling, if scale is
|
||||||
|
// non-uniform or animated, ufbx will add scale helpers in the same way
|
||||||
|
// as `UFBX_INHERIT_MODE_HANDLING_HELPER_NODES`.
|
||||||
|
UFBX_INHERIT_MODE_HANDLING_COMPENSATE,
|
||||||
|
|
||||||
|
// Attempt to compensate for bone scale by inversely scaling children.
|
||||||
|
// Will never create helper nodes.
|
||||||
|
UFBX_INHERIT_MODE_HANDLING_COMPENSATE_NO_FALLBACK,
|
||||||
|
|
||||||
|
// Ignore non-standard inheritance modes.
|
||||||
|
// Forces all nodes to have `UFBX_INHERIT_MODE_NORMAL` regardless of the
|
||||||
|
// inherit mode specified in the file. This can be useful for emulating
|
||||||
|
// results from importers/programs that don't support inherit modes.
|
||||||
|
UFBX_INHERIT_MODE_HANDLING_IGNORE,
|
||||||
|
|
||||||
|
UFBX_ENUM_FORCE_WIDTH(UFBX_INHERIT_MODE_HANDLING)
|
||||||
|
} ufbx_inherit_mode_handling;
|
||||||
|
|
||||||
|
UFBX_ENUM_TYPE(ufbx_inherit_mode_handling, UFBX_INHERIT_MODE_HANDLING, UFBX_INHERIT_MODE_HANDLING_IGNORE);
|
||||||
|
|
||||||
|
// How to handle FBX transform pivots.
|
||||||
|
typedef enum ufbx_pivot_handling UFBX_ENUM_REPR {
|
||||||
|
|
||||||
|
// Take pivots into account when computing the transform.
|
||||||
|
UFBX_PIVOT_HANDLING_RETAIN,
|
||||||
|
|
||||||
|
// Translate objects to be located at their pivot.
|
||||||
|
// NOTE: Only applied if rotation and scaling pivots are equal.
|
||||||
|
// NOTE: Results in geometric translation. Use `ufbx_geometry_transform_handling`
|
||||||
|
// to interpret these in a standard scene graph.
|
||||||
|
UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT,
|
||||||
|
|
||||||
|
// Translate objects to be located at their rotation pivot.
|
||||||
|
// NOTE: Results in geometric translation. Use `ufbx_geometry_transform_handling`
|
||||||
|
// to interpret these in a standard scene graph.
|
||||||
|
// NOTE: By default the original transforms of empties are not retained when using this,
|
||||||
|
// use `ufbx_load_opts.pivot_handling_retain_empties` to prevent adjusting these pivots.
|
||||||
|
UFBX_PIVOT_HANDLING_ADJUST_TO_ROTATION_PIVOT,
|
||||||
|
|
||||||
|
UFBX_ENUM_FORCE_WIDTH(UFBX_PIVOT_HANDLING)
|
||||||
|
} ufbx_pivot_handling;
|
||||||
|
|
||||||
|
UFBX_ENUM_TYPE(ufbx_pivot_handling, UFBX_PIVOT_HANDLING, UFBX_PIVOT_HANDLING_ADJUST_TO_ROTATION_PIVOT);
|
||||||
|
|
||||||
// Embedded thumbnail in the file, valid if the dimensions are non-zero.
|
// Embedded thumbnail in the file, valid if the dimensions are non-zero.
|
||||||
typedef struct ufbx_thumbnail {
|
typedef struct ufbx_thumbnail {
|
||||||
ufbx_props props;
|
ufbx_props props;
|
||||||
@@ -3731,8 +3823,12 @@ typedef struct ufbx_metadata {
|
|||||||
ufbx_string original_file_path;
|
ufbx_string original_file_path;
|
||||||
ufbx_blob raw_original_file_path;
|
ufbx_blob raw_original_file_path;
|
||||||
|
|
||||||
// Space conversion method used on the scene.
|
// Conversion methods applied for the scene.
|
||||||
ufbx_space_conversion space_conversion;
|
ufbx_space_conversion space_conversion;
|
||||||
|
ufbx_geometry_transform_handling geometry_transform_handling;
|
||||||
|
ufbx_inherit_mode_handling inherit_mode_handling;
|
||||||
|
ufbx_pivot_handling pivot_handling;
|
||||||
|
ufbx_mirror_axis handedness_conversion_axis;
|
||||||
|
|
||||||
// Transform that has been applied to root for axis/unit conversion.
|
// Transform that has been applied to root for axis/unit conversion.
|
||||||
ufbx_quat root_rotation;
|
ufbx_quat root_rotation;
|
||||||
@@ -4383,91 +4479,6 @@ typedef enum ufbx_unicode_error_handling UFBX_ENUM_REPR {
|
|||||||
|
|
||||||
UFBX_ENUM_TYPE(ufbx_unicode_error_handling, UFBX_UNICODE_ERROR_HANDLING, UFBX_UNICODE_ERROR_HANDLING_UNSAFE_IGNORE);
|
UFBX_ENUM_TYPE(ufbx_unicode_error_handling, UFBX_UNICODE_ERROR_HANDLING, UFBX_UNICODE_ERROR_HANDLING_UNSAFE_IGNORE);
|
||||||
|
|
||||||
// How to handle FBX node geometry transforms.
|
|
||||||
// FBX nodes can have "geometry transforms" that affect only the attached meshes,
|
|
||||||
// but not the children. This is not allowed in many scene representations so
|
|
||||||
// ufbx provides some ways to simplify them.
|
|
||||||
// Geometry transforms can also be used to transform any other attributes such
|
|
||||||
// as lights or cameras.
|
|
||||||
typedef enum ufbx_geometry_transform_handling UFBX_ENUM_REPR {
|
|
||||||
|
|
||||||
// Preserve the geometry transforms as-is.
|
|
||||||
// To be correct for all files you have to use `ufbx_node.geometry_transform`,
|
|
||||||
// `ufbx_node.geometry_to_node`, or `ufbx_node.geometry_to_world` to compensate
|
|
||||||
// for any potential geometry transforms.
|
|
||||||
UFBX_GEOMETRY_TRANSFORM_HANDLING_PRESERVE,
|
|
||||||
|
|
||||||
// Add helper nodes between the nodes and geometry where needed.
|
|
||||||
// The created nodes have `ufbx_node.is_geometry_transform_helper` set and are
|
|
||||||
// named `ufbx_load_opts.geometry_transform_helper_name`.
|
|
||||||
UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES,
|
|
||||||
|
|
||||||
// Modify the geometry of meshes attached to nodes with geometry transforms.
|
|
||||||
// Will add helper nodes like `UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES` if
|
|
||||||
// necessary, for example if there are multiple instances of the same mesh with
|
|
||||||
// geometry transforms.
|
|
||||||
UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY,
|
|
||||||
|
|
||||||
// Modify the geometry of meshes attached to nodes with geometry transforms.
|
|
||||||
// NOTE: This will not work correctly for instanced geometry.
|
|
||||||
UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK,
|
|
||||||
|
|
||||||
UFBX_ENUM_FORCE_WIDTH(UFBX_GEOMETRY_TRANSFORM_HANDLING)
|
|
||||||
} ufbx_geometry_transform_handling;
|
|
||||||
|
|
||||||
UFBX_ENUM_TYPE(ufbx_geometry_transform_handling, UFBX_GEOMETRY_TRANSFORM_HANDLING, UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK);
|
|
||||||
|
|
||||||
// How to handle FBX transform inherit modes.
|
|
||||||
typedef enum ufbx_inherit_mode_handling UFBX_ENUM_REPR {
|
|
||||||
|
|
||||||
// Preserve inherit mode in `ufbx_node.inherit_mode`.
|
|
||||||
// NOTE: To correctly handle all scenes you would need to handle the
|
|
||||||
// non-standard inherit modes.
|
|
||||||
UFBX_INHERIT_MODE_HANDLING_PRESERVE,
|
|
||||||
|
|
||||||
// Create scale helper nodes parented to nodes that need special inheritance.
|
|
||||||
// Scale helper nodes will have `ufbx_node.is_scale_helper` and parents of
|
|
||||||
// scale helpers will have `ufbx_node.scale_helper` pointing to it.
|
|
||||||
UFBX_INHERIT_MODE_HANDLING_HELPER_NODES,
|
|
||||||
|
|
||||||
// Attempt to compensate for bone scale by inversely scaling children.
|
|
||||||
// NOTE: This only works for uniform non-animated scaling, if scale is
|
|
||||||
// non-uniform or animated, ufbx will add scale helpers in the same way
|
|
||||||
// as `UFBX_INHERIT_MODE_HANDLING_HELPER_NODES`.
|
|
||||||
UFBX_INHERIT_MODE_HANDLING_COMPENSATE,
|
|
||||||
|
|
||||||
// Attempt to compensate for bone scale by inversely scaling children.
|
|
||||||
// Will never create helper nodes.
|
|
||||||
UFBX_INHERIT_MODE_HANDLING_COMPENSATE_NO_FALLBACK,
|
|
||||||
|
|
||||||
// Ignore non-standard inheritance modes.
|
|
||||||
// Forces all nodes to have `UFBX_INHERIT_MODE_NORMAL` regardless of the
|
|
||||||
// inherit mode specified in the file. This can be useful for emulating
|
|
||||||
// results from importers/programs that don't support inherit modes.
|
|
||||||
UFBX_INHERIT_MODE_HANDLING_IGNORE,
|
|
||||||
|
|
||||||
UFBX_ENUM_FORCE_WIDTH(UFBX_INHERIT_MODE_HANDLING)
|
|
||||||
} ufbx_inherit_mode_handling;
|
|
||||||
|
|
||||||
UFBX_ENUM_TYPE(ufbx_inherit_mode_handling, UFBX_INHERIT_MODE_HANDLING, UFBX_INHERIT_MODE_HANDLING_IGNORE);
|
|
||||||
|
|
||||||
// How to handle FBX transform pivots.
|
|
||||||
typedef enum ufbx_pivot_handling UFBX_ENUM_REPR {
|
|
||||||
|
|
||||||
// Take pivots into account when computing the transform.
|
|
||||||
UFBX_PIVOT_HANDLING_RETAIN,
|
|
||||||
|
|
||||||
// Translate objects to be located at their pivot.
|
|
||||||
// NOTE: Only applied if rotation and scaling pivots are equal.
|
|
||||||
// NOTE: Results in geometric translation. Use `ufbx_geometry_transform_handling`
|
|
||||||
// to interpret these in a standard scene graph.
|
|
||||||
UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT,
|
|
||||||
|
|
||||||
UFBX_ENUM_FORCE_WIDTH(UFBX_PIVOT_HANDLING)
|
|
||||||
} ufbx_pivot_handling;
|
|
||||||
|
|
||||||
UFBX_ENUM_TYPE(ufbx_pivot_handling, UFBX_PIVOT_HANDLING, UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT);
|
|
||||||
|
|
||||||
typedef enum ufbx_baked_key_flags UFBX_FLAG_REPR {
|
typedef enum ufbx_baked_key_flags UFBX_FLAG_REPR {
|
||||||
// This keyframe represents a constant step from the left side
|
// This keyframe represents a constant step from the left side
|
||||||
UFBX_BAKED_KEY_STEP_LEFT = 0x1,
|
UFBX_BAKED_KEY_STEP_LEFT = 0x1,
|
||||||
@@ -4792,13 +4803,16 @@ typedef struct ufbx_load_opts {
|
|||||||
// See `ufbx_inherit_mode_handling` for an explanation.
|
// See `ufbx_inherit_mode_handling` for an explanation.
|
||||||
ufbx_inherit_mode_handling inherit_mode_handling;
|
ufbx_inherit_mode_handling inherit_mode_handling;
|
||||||
|
|
||||||
|
// How to perform space conversion by `target_axes` and `target_unit_meters`.
|
||||||
|
// See `ufbx_space_conversion` for an explanation.
|
||||||
|
ufbx_space_conversion space_conversion;
|
||||||
|
|
||||||
// How to handle pivots.
|
// How to handle pivots.
|
||||||
// See `ufbx_pivot_handling` for an explanation.
|
// See `ufbx_pivot_handling` for an explanation.
|
||||||
ufbx_pivot_handling pivot_handling;
|
ufbx_pivot_handling pivot_handling;
|
||||||
|
|
||||||
// How to perform space conversion by `target_axes` and `target_unit_meters`.
|
// Retain the original transforms of empties when converting pivots.
|
||||||
// See `ufbx_space_conversion` for an explanation.
|
bool pivot_handling_retain_empties;
|
||||||
ufbx_space_conversion space_conversion;
|
|
||||||
|
|
||||||
// Axis used to mirror for conversion between left-handed and right-handed coordinates.
|
// Axis used to mirror for conversion between left-handed and right-handed coordinates.
|
||||||
ufbx_mirror_axis handedness_conversion_axis;
|
ufbx_mirror_axis handedness_conversion_axis;
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ void importer_main(Main *bmain, Scene *scene, ViewLayer *view_layer, const FBXIm
|
|||||||
* cause armatures/skins to not import correctly, when inserted in the middle of bone chain. */
|
* cause armatures/skins to not import correctly, when inserted in the middle of bone chain. */
|
||||||
opts.geometry_transform_handling = UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK;
|
opts.geometry_transform_handling = UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK;
|
||||||
|
|
||||||
opts.pivot_handling = UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT;
|
opts.pivot_handling = UFBX_PIVOT_HANDLING_ADJUST_TO_ROTATION_PIVOT;
|
||||||
|
|
||||||
opts.space_conversion = UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS;
|
opts.space_conversion = UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS;
|
||||||
opts.target_axes.right = UFBX_COORDINATE_AXIS_POSITIVE_X;
|
opts.target_axes.right = UFBX_COORDINATE_AXIS_POSITIVE_X;
|
||||||
|
|||||||
@@ -27,13 +27,13 @@
|
|||||||
- 2 0 1 3 6 ... 36 16 17 41 40
|
- 2 0 1 3 6 ... 36 16 17 41 40
|
||||||
- 2/3 0/2 0/1 1/3 4/6 ... 81/85 13/37 12/36 17/41 16/40
|
- 2/3 0/2 0/1 1/3 4/6 ... 81/85 13/37 12/36 17/41 16/40
|
||||||
- attr 'position' FLOAT_VECTOR POINT
|
- attr 'position' FLOAT_VECTOR POINT
|
||||||
- (541.521, 28.042, -164.886)
|
- (94.377, 9.507, 3.486)
|
||||||
- (455.221, 28.042, -164.886)
|
- (8.077, 9.507, 3.486)
|
||||||
- (541.521, 232.219, -164.886)
|
- (94.377, 213.684, 3.486)
|
||||||
...
|
...
|
||||||
- (541.098, 176.871, -159.367)
|
- (93.954, 158.336, 9.005)
|
||||||
- (545.096, 177.634, -159.447)
|
- (97.952, 159.099, 8.925)
|
||||||
- (547.631, 175.533, -159.239)
|
- (100.487, 156.998, 9.133)
|
||||||
|
|
||||||
- Mesh 'Mesh.003' vtx:52 face:44 loop:176 edge:92
|
- Mesh 'Mesh.003' vtx:52 face:44 loop:176 edge:92
|
||||||
- 2 0 1 3 6 ... 50 28 30 48 51
|
- 2 0 1 3 6 ... 50 28 30 48 51
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
- scl 1.000, 1.000, 1.000
|
- scl 1.000, 1.000, 1.000
|
||||||
- props: str:currentUVSet='map1'
|
- props: str:currentUVSet='map1'
|
||||||
- Obj 'Door_03' MESH data:'Mesh.002' par:'01'
|
- Obj 'Door_03' MESH data:'Mesh.002' par:'01'
|
||||||
- pos 710.133, 300.000, 305.051
|
- pos 262.989, 318.535, 136.679
|
||||||
- rot 3.142, 0.000, 0.000 (XYZ)
|
- rot 3.142, 0.000, 0.000 (XYZ)
|
||||||
- scl -1.000, -1.000, -1.000
|
- scl -1.000, -1.000, -1.000
|
||||||
- props: str:currentUVSet='map1'
|
- props: str:currentUVSet='map1'
|
||||||
|
|||||||
Reference in New Issue
Block a user