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
|
||||
License: SPDX:MIT
|
||||
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:
|
||||
* 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_real dx = a.x - b.x, dy = a.y - b.y;
|
||||
return dx*dx + dy*dy;
|
||||
@@ -5960,7 +5965,6 @@ static ufbxi_noinline ufbx_vec3 ufbxi_slow_normalized_cross3(const ufbx_vec3 *a,
|
||||
// -- Threading
|
||||
|
||||
typedef struct ufbxi_task ufbxi_task;
|
||||
typedef struct ufbxi_thread ufbxi_thread;
|
||||
typedef struct ufbxi_thread_pool ufbxi_thread_pool;
|
||||
|
||||
typedef bool ufbxi_task_fn(ufbxi_task *task);
|
||||
@@ -17985,6 +17989,23 @@ typedef struct {
|
||||
ufbx_vec3 constant_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()`.
|
||||
// This is a very messy function reminiscent of the _old_ ufbx, where we do
|
||||
// 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;
|
||||
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.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)
|
||||
required = true;
|
||||
#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);
|
||||
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);
|
||||
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;
|
||||
|
||||
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()`
|
||||
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++) {
|
||||
ufbxi_pre_node *pre_node = &pre_nodes[i];
|
||||
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 scaling_pivot = ufbxi_find_vec3(&node->props, ufbxi_ScalingPivot, 0.0f, 0.0f, 0.0f);
|
||||
if (!ufbxi_is_vec3_zero(rotation_pivot)) {
|
||||
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);
|
||||
ufbx_vec3 scaling_offset = ufbxi_find_vec3(&node->props, ufbxi_ScalingOffset, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
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;
|
||||
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 (instance_counts[node->element_id] > 1 || modify_not_supported[node->element_id]) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (err <= pivot_epsilon && can_modify_geometry_transform) {
|
||||
size_t num_props = node->props.props.count;
|
||||
ufbx_prop *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));
|
||||
bool can_modify_pivot = true;
|
||||
if (uc->opts.pivot_handling == UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT) {
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
ufbxi_init_synthetic_vec3_prop(&dst[0], ufbxi_RotationPivot, &ufbx_zero_vec3, UFBX_PROP_VECTOR);
|
||||
ufbxi_init_synthetic_vec3_prop(&dst[1], ufbxi_ScalingPivot, &ufbx_zero_vec3, UFBX_PROP_VECTOR);
|
||||
ufbxi_init_synthetic_vec3_prop(&dst[2], ufbxi_GeometricTranslation, &geometric_translation, UFBX_PROP_VECTOR);
|
||||
ufbx_vec3 child_offset = { 0.0f };
|
||||
ufbx_prop *new_props = NULL;
|
||||
size_t num_props = node->props.props.count;
|
||||
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.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_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];
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ufbx_real root_scale = ufbxi_min3(root_transform.scale);
|
||||
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) {
|
||||
scene->metadata.geometry_scale = root_scale;
|
||||
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);
|
||||
|
||||
// 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.
|
||||
typedef struct ufbx_thumbnail {
|
||||
ufbx_props props;
|
||||
@@ -3731,8 +3823,12 @@ typedef struct ufbx_metadata {
|
||||
ufbx_string 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_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.
|
||||
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);
|
||||
|
||||
// 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 {
|
||||
// This keyframe represents a constant step from the left side
|
||||
UFBX_BAKED_KEY_STEP_LEFT = 0x1,
|
||||
@@ -4792,13 +4803,16 @@ typedef struct ufbx_load_opts {
|
||||
// See `ufbx_inherit_mode_handling` for an explanation.
|
||||
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.
|
||||
// See `ufbx_pivot_handling` for an explanation.
|
||||
ufbx_pivot_handling pivot_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;
|
||||
// Retain the original transforms of empties when converting pivots.
|
||||
bool pivot_handling_retain_empties;
|
||||
|
||||
// Axis used to mirror for conversion between left-handed and right-handed coordinates.
|
||||
ufbx_mirror_axis handedness_conversion_axis;
|
||||
|
||||
Reference in New Issue
Block a user