🧭 Node Transforms(ノード変換)
FBX のノード変換は、一連の変換のチェーンとして構成されています。
もし特別な理由がなければ、FBX 固有の変換モデルを直接扱うよりも、
ufbx が提供する変換表現を使用することを強く推奨します。
詳細は → Elements / Nodes / Transforms を参照してください。
🔩 基本構造(Transform Properties)
FBX 内でのノード変換は、いくつかのプロパティで構成されています。
プロパティ名 | 意味 |
|---|---|
| 親ノードに対する平行移動 |
| 親ノードに対する非一様スケール |
| 親ノードに対する回転(オイラー角) |
|
のオイラー回転順序(
) |
🎯 ピボット・オフセット関連
以下のプロパティによって、回転やスケールの中心・ずれが定義されます。
プロパティ名 | 内容 |
|---|---|
| スケーリングの中心点 |
| スケール後のオフセット移動 |
| 回転の中心点 |
| 回転後のオフセット移動 |
さらに "Lcl Rotation" 以外に、2種類の補助回転が存在します:
プロパティ名 | 内容 |
|---|---|
|
の前に適用される回転(常に XYZ 順) |
|
の後に適用される 逆回転 (常に XYZ 順) |
⚙️ 変換チェーンの計算例
以下は、ufbx の内部補正(adjust transform)を考慮しない
基本的なノード変換の計算例です。
// 手動で `ufbx_node.node_to_parent` を計算
// ※ ufbx固有の調整変換(adjust transform)は考慮しない
Matrix4 get_transform(ufbx_node *node)
{
ufbx_props *props = &node->props;
// 主要プロパティを取得(必要に応じてカーブから評価)
int64_t rotation_order = ufbx_find_int(props, "RotationOrder", 0);
Vector3 lcl_translation = ufbx_find_vec3(props, "Lcl Translation", ufbx_zero_vec3);
Vector3 lcl_scaling = ufbx_find_vec3(props, "Lcl Scaling", ufbx_zero_vec3);
Vector3 lcl_rotation = ufbx_find_vec3(props, "Lcl Rotation", ufbx_zero_vec3);
Vector3 rotation_pivot = ufbx_find_vec3(props, "RotationPivot", ufbx_zero_vec3);
Vector3 scaling_pivot = ufbx_find_vec3(props, "ScalingPivot", ufbx_zero_vec3);
Vector3 rotation_offset = ufbx_find_vec3(props, "RotationOffset", ufbx_zero_vec3);
Vector3 scaling_offset = ufbx_find_vec3(props, "ScalingOffset", ufbx_zero_vec3);
Vector3 pre_rotation = ufbx_find_vec3(props, "PreRotation", ufbx_zero_vec3);
Vector3 post_rotation = ufbx_find_vec3(props, "PostRotation", ufbx_zero_vec3);
// オイラー角 → クォータニオン変換
Quaternion lcl_quat = Quaternion_euler(lcl_rotation, (EulerOrder)rotation_order);
Quaternion pre_quat = Quaternion_euler(pre_rotation, EulerOrder_XYZ);
Quaternion post_quat = Quaternion_euler(post_rotation, EulerOrder_XYZ);
Matrix4 m = Matrix4_identity;
// スケール適用(ピボット・オフセット含む)
m = Matrix4_translate(-scaling_pivot) * m;
m = Matrix4_scale_nonuniform(lcl_scaling) * m;
m = Matrix4_translate(scaling_pivot) * m;
m = Matrix4_translate(scaling_offset) * m;
// 回転適用(PostRotation は反転)
m = Matrix4_translate(-rotation_pivot) * m;
m = Matrix4_rotate(Quaternion_inverse(post_quat)) * m;
m = Matrix4_rotate(lcl_quat) * m;
m = Matrix4_rotate(pre_quat) * m;
m = Matrix4_translate(rotation_pivot) * m;
m = Matrix4_translate(rotation_offset) * m;
// 最後に平行移動
m = Matrix4_translate(lcl_translation) * m;
return m;
}🧮 Adjust Transforms(調整変換)
FBX の座標系やピボットの扱いに応じて、
ufbx は内部でいくつかの「調整変換(adjust transform)」を自動的に付与します。
これらを考慮しないと、見た目上の座標がずれることがあります。
主な adjust transform の対応表
機能 | 対応するフィールド |
|---|---|
|
,
|
|
,
|
|
|
|
|
|
|
|
|
ufbx_node.has_adjust_transform が true の場合は、
ノードが非単位(≠ identity)の調整変換を持っています。
ただし、常に適用しても安全です。
💻 Adjust 変換対応版の完全計算例
// ufbx固有の adjust transform を考慮した `ufbx_node.node_to_parent` の計算
Matrix4 get_transform(ufbx_node *node)
{
ufbx_props *props = &node->props;
// プロパティの取得
int64_t rotation_order = ufbx_find_int(props, "RotationOrder", 0);
Vector3 lcl_translation = ufbx_find_vec3(props, "Lcl Translation", ufbx_zero_vec3);
Vector3 lcl_scaling = ufbx_find_vec3(props, "Lcl Scaling", ufbx_zero_vec3);
Vector3 lcl_rotation = ufbx_find_vec3(props, "Lcl Rotation", ufbx_zero_vec3);
Vector3 rotation_pivot = ufbx_find_vec3(props, "RotationPivot", ufbx_zero_vec3);
Vector3 scaling_pivot = ufbx_find_vec3(props, "ScalingPivot", ufbx_zero_vec3);
Vector3 rotation_offset = ufbx_find_vec3(props, "RotationOffset", ufbx_zero_vec3);
Vector3 scaling_offset = ufbx_find_vec3(props, "ScalingOffset", ufbx_zero_vec3);
Vector3 pre_rotation = ufbx_find_vec3(props, "PreRotation", ufbx_zero_vec3);
Vector3 post_rotation = ufbx_find_vec3(props, "PostRotation", ufbx_zero_vec3);
// ufbx 特有の調整パラメータ
Vector3 adjust_pre_translation = node->adjust_pre_translation;
Quaternion adjust_pre_rotation = node->adjust_pre_rotation;
Quaternion adjust_post_rotation = node->adjust_post_rotation;
float adjust_pre_scale = (float)node->adjust_pre_scale;
float adjust_post_scale = (float)node->adjust_post_scale;
float adjust_translation_scale = (float)node->adjust_translation_scale;
// クォータニオン化
Quaternion lcl_quat = Quaternion_euler(lcl_rotation, (EulerOrder)rotation_order);
Quaternion pre_quat = Quaternion_euler(pre_rotation, EulerOrder_XYZ);
Quaternion post_quat = Quaternion_euler(post_rotation, EulerOrder_XYZ);
Matrix4 m = Matrix4_identity;
// 後処理(post-adjust)
m = Matrix4_rotate(adjust_post_rotation) * m;
m = Matrix4_scale(adjust_post_scale) * m;
// スケール
m = Matrix4_translate(-scaling_pivot) * m;
m = Matrix4_scale_nonuniform(lcl_scaling) * m;
m = Matrix4_translate(scaling_pivot) * m;
m = Matrix4_translate(scaling_offset) * m;
// 回転
m = Matrix4_translate(-rotation_pivot) * m;
m = Matrix4_rotate(Quaternion_inverse(post_quat)) * m;
m = Matrix4_rotate(lcl_quat) * m;
m = Matrix4_rotate(pre_quat) * m;
m = Matrix4_translate(rotation_pivot) * m;
m = Matrix4_translate(rotation_offset) * m;
// 平行移動
m = Matrix4_translate(lcl_translation) * m;
// 前処理(pre-adjust)
m = Matrix4_translate(adjust_pre_translation) * m;
m = Matrix4_rotate(adjust_pre_rotation) * m;
m = Matrix4_scale(adjust_pre_scale) * m;
// translation のみスケーリング
m.m03 *= adjust_translation_scale;
m.m13 *= adjust_translation_scale;
m.m23 *= adjust_translation_scale;
return m;
}📘 実際の実装例:ufbxi_get_transform() on GitHub
🧱 Geometric Transforms(ジオメトリ変換)
ジオメトリ変換(geometry transforms)は、
ノードの子ノードには影響せず、ノード自身の内容(例:メッシュ)だけを変換します。
詳細は → Elements / Nodes / Geometry Transforms
プロパティ名 | 内容 |
|---|---|
| ノード内コンテンツの平行移動 |
| ノード内コンテンツのスケール |
| ノード内コンテンツの回転(常に XYZ 順) |
💻 Geometry Transform の計算例
// 手動で `ufbx_node.geometry_to_node` を計算
Matrix4 get_geometry_transform(ufbx_node *node)
{
ufbx_props *props = &node->props;
Vector3 geo_translation = ufbx_find_vec3(props, "GeometricTranslation", ufbx_zero_vec3);
Vector3 geo_scaling = ufbx_find_vec3(props, "GeometricScaling", ufbx_zero_vec3);
Vector3 geo_rotation = ufbx_find_vec3(props, "GeometricRotation", ufbx_zero_vec3);
Quaternion geo_quat = Quaternion_euler(geo_rotation, EulerOrder_XYZ);
Matrix4 m = Matrix4_identity;
m = Matrix4_scale_nonuniform(geo_scaling) * m;
m = Matrix4_rotate(geo_quat) * m;
m = Matrix4_translate(geo_translation) * m;
return m;
}📘 備考
このドキュメントは MIT / Public Domain ライセンスのもとで公開された
ufbx (c) 2020 Samuli Raivio の内容を翻訳・整形しています。
No Comments