Skip to content

Commit 1a1c5cc

Browse files
authored
Merge pull request #443 from Unity-Technologies/UNI-53811-orphan-bone-error-fix
UNI-53811 (packman) fix error when exporting bones that are not descandants of root
2 parents 0b7cf89 + b6e521d commit 1a1c5cc

File tree

1 file changed

+129
-150
lines changed

1 file changed

+129
-150
lines changed

Packages/com.unity.formats.fbx/Editor/Scripts/FbxExporter.cs

Lines changed: 129 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -984,10 +984,9 @@ private bool ExportSkeleton (SkinnedMeshRenderer skinnedMesh, FbxScene fbxScene,
984984
return false;
985985
}
986986

987-
// Three steps:
987+
// Two steps:
988988
// 0. Set up the map from bone to index.
989-
// 1. Gather complete list of bones
990-
// 2. Set the transforms.
989+
// 1. Set the transforms.
991990

992991
// Step 0: map transform to index so we can look up index by bone.
993992
Dictionary<Transform, int> index = new Dictionary<Transform, int>();
@@ -996,44 +995,11 @@ private bool ExportSkeleton (SkinnedMeshRenderer skinnedMesh, FbxScene fbxScene,
996995
index[unityBoneTransform] = boneIndex;
997996
}
998997

999-
// Step 1: gather all the bones
1000-
HashSet<Transform> boneSet = new HashSet<Transform> ();
1001-
var s = new Stack<Transform> (bones);
1002-
var root = skinnedMesh.rootBone;
1003-
while (s.Count > 0) {
1004-
var t = s.Pop ();
998+
skinnedMeshToBonesMap.Add (skinnedMesh, bones);
1005999

1006-
if (!boneSet.Add (t)) {
1007-
continue;
1008-
}
1009-
1010-
if (t.parent == null) {
1011-
Debug.LogWarningFormat (
1012-
"FbxExporter: {0} is a bone but not a descendant of {1}'s mesh's root bone.",
1013-
t.name, skinnedMesh.name
1014-
);
1015-
continue;
1016-
}
1017-
1018-
// Each skinned mesh in Unity has one root bone, but may have objects
1019-
// between the root bone and leaf bones that are not in the bone list.
1020-
// However all objects between two bones in a hierarchy should be bones
1021-
// as well.
1022-
// e.g. in rootBone -> bone1 -> obj1 -> bone2, obj1 should be a bone
1023-
//
1024-
// Traverse from all leaf bones to the root bone adding everything in between
1025-
// to the boneSet regardless of whether it is in the skinned mesh's bone list.
1026-
if (t != root && !boneSet.Contains(t.parent)) {
1027-
s.Push (t.parent);
1028-
}
1029-
}
1030-
1031-
var boneList = boneSet.ToArray();
1032-
skinnedMeshToBonesMap.Add (skinnedMesh, boneList);
1033-
1034-
// Step 2: Set transforms
1000+
// Step 1: Set transforms
10351001
var boneInfo = new SkinnedMeshBoneInfo (skinnedMesh, index);
1036-
foreach (var bone in boneList) {
1002+
foreach (var bone in bones) {
10371003
var fbxBone = MapUnityObjectToFbxNode [bone.gameObject];
10381004
ExportBoneTransform (fbxBone, fbxScene, bone, boneInfo);
10391005
}
@@ -2564,6 +2530,39 @@ private string GetUniqueName(string name)
25642530
return uniqueName;
25652531
}
25662532

2533+
/// <summary>
2534+
/// Create a fbxNode from unityGo.
2535+
/// </summary>
2536+
/// <param name="unityGo"></param>
2537+
/// <param name="fbxScene"></param>
2538+
/// <returns>the created FbxNode</returns>
2539+
private FbxNode CreateFbxNode(GameObject unityGo, FbxScene fbxScene)
2540+
{
2541+
2542+
string fbxName = unityGo.name;
2543+
if (ExportOptions.UseMayaCompatibleNames)
2544+
{
2545+
fbxName = ConvertToMayaCompatibleName(unityGo.name);
2546+
if (ExportOptions.AllowSceneModification)
2547+
{
2548+
unityGo.name = fbxName;
2549+
}
2550+
}
2551+
2552+
FbxNode fbxNode = FbxNode.Create(fbxScene, GetUniqueName(fbxName));
2553+
2554+
// Default inheritance type in FBX is RrSs, which causes scaling issues in Maya as
2555+
// both Maya and Unity use RSrs inheritance by default.
2556+
// Note: MotionBuilder uses RrSs inheritance by default as well, though it is possible
2557+
// to select a different inheritance type in the UI.
2558+
// Use RSrs as the scaling inheritance instead.
2559+
fbxNode.SetTransformationInheritType(FbxTransform.EInheritType.eInheritRSrs);
2560+
2561+
MapUnityObjectToFbxNode[unityGo] = fbxNode;
2562+
2563+
return fbxNode;
2564+
}
2565+
25672566
/// <summary>
25682567
/// Creates an FbxNode for each GameObject.
25692568
/// </summary>
@@ -2577,18 +2576,7 @@ internal int ExportTransformHierarchy(
25772576
{
25782577
int numObjectsExported = exportProgress;
25792578

2580-
string fbxName = unityGo.name;
2581-
if (ExportOptions.UseMayaCompatibleNames) {
2582-
fbxName = ConvertToMayaCompatibleName (unityGo.name);
2583-
if (ExportOptions.AllowSceneModification)
2584-
{
2585-
unityGo.name = fbxName;
2586-
}
2587-
}
2588-
2589-
// create an FbxNode and add it as a child of parent
2590-
FbxNode fbxNode = FbxNode.Create (fbxScene, GetUniqueName (fbxName));
2591-
MapUnityObjectToFbxNode [unityGo] = fbxNode;
2579+
FbxNode fbxNode = CreateFbxNode(unityGo, fbxScene);
25922580

25932581
if (Verbose)
25942582
Debug.Log (string.Format ("exporting {0}", fbxNode.GetName ()));
@@ -2602,13 +2590,6 @@ internal int ExportTransformHierarchy(
26022590
return -1;
26032591
}
26042592

2605-
// Default inheritance type in FBX is RrSs, which causes scaling issues in Maya as
2606-
// both Maya and Unity use RSrs inheritance by default.
2607-
// Note: MotionBuilder uses RrSs inheritance by default as well, though it is possible
2608-
// to select a different inheritance type in the UI.
2609-
// Use RSrs as the scaling inheritance instead.
2610-
fbxNode.SetTransformationInheritType (FbxTransform.EInheritType.eInheritRSrs);
2611-
26122593
ExportTransform (unityGo.transform, fbxNode, newCenter, exportType);
26132594

26142595
fbxNodeParent.AddChild (fbxNode);
@@ -2675,48 +2656,24 @@ internal int ExportAnimationOnly(
26752656
TransformExportType exportType = TransformExportType.Local
26762657
){
26772658
AnimationOnlyExportData exportData = (AnimationOnlyExportData)data;
2678-
2679-
// export any bones
2680-
var skinnedMeshRenderers = unityGO.GetComponentsInChildren<SkinnedMeshRenderer> ();
26812659
int numObjectsExported = exportProgress;
26822660

2683-
foreach (var skinnedMesh in skinnedMeshRenderers) {
2684-
var boneArray = skinnedMesh.bones;
2685-
var bones = new HashSet<GameObject>();
2686-
var boneDict = new Dictionary<Transform, int> ();
2687-
2688-
for (int i = 0; i < boneArray.Length; i++) {
2689-
bones.Add (boneArray [i].gameObject);
2690-
boneDict.Add (boneArray [i], i);
2691-
}
2692-
2693-
// get the bones that are also in the export set
2694-
bones.IntersectWith (exportData.Objects);
2695-
2696-
// remove the exported bones from the export set
2697-
exportData.Objects.ExceptWith (bones);
2698-
2699-
var boneInfo = new SkinnedMeshBoneInfo (skinnedMesh, boneDict);
2700-
foreach (var bone in bones) {
2701-
FbxNode node;
2702-
if (!ExportGameObjectAndParents (
2703-
bone.gameObject, unityGO, fbxScene, out node, newCenter,
2704-
exportType, ref numObjectsExported, objectCount,
2705-
boneInfo
2706-
)) {
2707-
// export cancelled
2708-
return -1;
2709-
}
2710-
}
2711-
}
2712-
27132661
// make sure anim destination node is exported as well
27142662
var exportSet = exportData.Objects;
2715-
if (ExportOptions.AnimationDest && ExportOptions.AnimationSource) {
2716-
exportSet.Add (ExportOptions.AnimationDest.gameObject);
2663+
if (ExportOptions.AnimationDest && ExportOptions.AnimationSource)
2664+
{
2665+
exportSet.Add(ExportOptions.AnimationDest.gameObject);
2666+
}
2667+
2668+
// first export all the animated bones that are in the export set
2669+
// as only a subset of bones are exported, but we still need to make sure the bone transforms are correct
2670+
if(!ExportAnimatedBones(unityGO, fbxScene, ref numObjectsExported, objectCount, exportData))
2671+
{
2672+
// export cancelled
2673+
return -1;
27172674
}
27182675

2719-
// export everything else
2676+
// export everything else and make sure all nodes are connected
27202677
foreach (var go in exportSet) {
27212678
FbxNode node;
27222679
if (!ExportGameObjectAndParents (
@@ -2765,6 +2722,57 @@ public SkinnedMeshBoneInfo(SkinnedMeshRenderer skinnedMesh, Dictionary<Transform
27652722
}
27662723
}
27672724

2725+
private bool ExportAnimatedBones (
2726+
GameObject unityGo,
2727+
FbxScene fbxScene,
2728+
ref int exportProgress,
2729+
int objectCount,
2730+
AnimationOnlyExportData exportData
2731+
)
2732+
{
2733+
var skinnedMeshRenderers = unityGo.GetComponentsInChildren<SkinnedMeshRenderer>();
2734+
foreach (var skinnedMesh in skinnedMeshRenderers)
2735+
{
2736+
var boneArray = skinnedMesh.bones;
2737+
var bones = new HashSet<GameObject>();
2738+
var boneDict = new Dictionary<Transform, int>();
2739+
2740+
for (int i = 0; i < boneArray.Length; i++)
2741+
{
2742+
bones.Add(boneArray[i].gameObject);
2743+
boneDict.Add(boneArray[i], i);
2744+
}
2745+
2746+
// get the bones that are also in the export set
2747+
bones.IntersectWith(exportData.Objects);
2748+
2749+
var boneInfo = new SkinnedMeshBoneInfo(skinnedMesh, boneDict);
2750+
foreach (var bone in bones)
2751+
{
2752+
FbxNode fbxNode;
2753+
// bone already exported
2754+
if (MapUnityObjectToFbxNode.TryGetValue(bone, out fbxNode))
2755+
{
2756+
continue;
2757+
}
2758+
fbxNode = CreateFbxNode(bone, fbxScene);
2759+
2760+
exportProgress++;
2761+
if (EditorUtility.DisplayCancelableProgressBar(
2762+
ProgressBarTitle,
2763+
string.Format("Creating FbxNode {0}/{1}", exportProgress, objectCount),
2764+
(exportProgress / (float)objectCount) * 0.5f))
2765+
{
2766+
// cancel silently
2767+
return false;
2768+
}
2769+
2770+
ExportBoneTransform(fbxNode, fbxScene, bone.transform, boneInfo);
2771+
}
2772+
}
2773+
return true;
2774+
}
2775+
27682776
/// <summary>
27692777
/// Exports the Gameobject and its ancestors.
27702778
/// </summary>
@@ -2778,58 +2786,34 @@ private bool ExportGameObjectAndParents(
27782786
Vector3 newCenter,
27792787
TransformExportType exportType,
27802788
ref int exportProgress,
2781-
int objectCount,
2782-
SkinnedMeshBoneInfo boneInfo = null)
2789+
int objectCount
2790+
)
27832791
{
2784-
// node already exists
2785-
if (MapUnityObjectToFbxNode.TryGetValue (unityGo, out fbxNode)) {
2786-
return true;
2787-
}
2788-
2789-
string fbxName = unityGo.name;
2790-
if (ExportOptions.UseMayaCompatibleNames) {
2791-
fbxName = ConvertToMayaCompatibleName (unityGo.name);
2792-
}
2793-
2794-
// create an FbxNode and add it as a child of parent
2795-
fbxNode = FbxNode.Create (fbxScene, GetUniqueName (fbxName));
2796-
MapUnityObjectToFbxNode [unityGo] = fbxNode;
2797-
2798-
exportProgress++;
2799-
if (EditorUtility.DisplayCancelableProgressBar (
2800-
ProgressBarTitle,
2801-
string.Format ("Creating FbxNode {0}/{1}", exportProgress, objectCount),
2802-
(exportProgress / (float)objectCount) * 0.5f)) {
2803-
// cancel silently
2804-
return false;
2805-
}
2806-
2807-
// Default inheritance type in FBX is RrSs, which causes scaling issues in Maya as
2808-
// both Maya and Unity use RSrs inheritance by default.
2809-
// Note: MotionBuilder uses RrSs inheritance by default as well, though it is possible
2810-
// to select a different inheritance type in the UI.
2811-
// Use RSrs as the scaling inhertiance instead.
2812-
fbxNode.SetTransformationInheritType (FbxTransform.EInheritType.eInheritRSrs);
2792+
// node doesn't exist so create it
2793+
if (!MapUnityObjectToFbxNode.TryGetValue(unityGo, out fbxNode))
2794+
{
2795+
fbxNode = CreateFbxNode(unityGo, fbxScene);
28132796

2814-
// TODO: check if GO is a bone and export accordingly
2815-
var exportedBoneTransform = boneInfo != null?
2816-
ExportBoneTransform (fbxNode, fbxScene, unityGo.transform, boneInfo) : false;
2797+
exportProgress++;
2798+
if (EditorUtility.DisplayCancelableProgressBar(
2799+
ProgressBarTitle,
2800+
string.Format("Creating FbxNode {0}/{1}", exportProgress, objectCount),
2801+
(exportProgress / (float)objectCount) * 0.5f))
2802+
{
2803+
// cancel silently
2804+
return false;
2805+
}
28172806

2818-
// export regular transform if we are not a bone or failed to export as a bone
2819-
if(!exportedBoneTransform){
2820-
ExportTransform (unityGo.transform, fbxNode, newCenter, exportType);
2807+
ExportTransform(unityGo.transform, fbxNode, newCenter, exportType);
28212808
}
28222809

2823-
if (unityGo == rootObject || unityGo.transform.parent == null) {
2824-
fbxScene.GetRootNode ().AddChild (fbxNode);
2810+
if (unityGo == rootObject || unityGo.transform.parent == null)
2811+
{
2812+
fbxScene.GetRootNode().AddChild(fbxNode);
28252813
return true;
28262814
}
28272815

2828-
SkinnedMeshBoneInfo parentBoneInfo = null;
2829-
if (boneInfo != null && boneInfo.skinnedMesh.rootBone != null && unityGo.transform != boneInfo.skinnedMesh.rootBone) {
2830-
parentBoneInfo = boneInfo;
2831-
}
2832-
2816+
// make sure all the nodes are connected and exported
28332817
FbxNode fbxNodeParent;
28342818
if (!ExportGameObjectAndParents (
28352819
unityGo.transform.parent.gameObject,
@@ -2839,8 +2823,7 @@ private bool ExportGameObjectAndParents(
28392823
newCenter,
28402824
TransformExportType.Local,
28412825
ref exportProgress,
2842-
objectCount,
2843-
parentBoneInfo
2826+
objectCount
28442827
)) {
28452828
// export cancelled
28462829
return false;
@@ -2891,17 +2874,13 @@ private bool ExportBoneTransform(
28912874
}
28922875

28932876
Matrix4x4 pose;
2894-
if (unityBone == rootBone) {
2895-
pose = (unityBone.parent.worldToLocalMatrix * skinnedMesh.transform.localToWorldMatrix * bindPose.inverse);
2896-
} else {
2897-
// get parent's bind pose
2898-
Matrix4x4 parentBindPose;
2899-
if (!boneInfo.boneToBindPose.TryGetValue (unityBone.parent, out parentBindPose)) {
2900-
parentBindPose = GetBindPose (unityBone.parent, bindPoses, boneDict, skinnedMesh);
2901-
boneInfo.boneToBindPose.Add (unityBone.parent, parentBindPose);
2902-
}
2903-
pose = parentBindPose * bindPose.inverse;
2877+
// get parent's bind pose
2878+
Matrix4x4 parentBindPose;
2879+
if (!boneInfo.boneToBindPose.TryGetValue (unityBone.parent, out parentBindPose)) {
2880+
parentBindPose = GetBindPose (unityBone.parent, bindPoses, boneDict, skinnedMesh);
2881+
boneInfo.boneToBindPose.Add (unityBone.parent, parentBindPose);
29042882
}
2883+
pose = parentBindPose * bindPose.inverse;
29052884

29062885
FbxVector4 translation, rotation, scale;
29072886
GetTRSFromMatrix (pose, out translation, out rotation, out scale);

0 commit comments

Comments
 (0)