Skip to content

Commit 02b63eb

Browse files
authored
UT-3611 Add null checks when accessing bones. (#535)
Add null checks when getting bones, so export no longer fails with null bones..
1 parent a9041e1 commit 02b63eb

File tree

1 file changed

+69
-44
lines changed

1 file changed

+69
-44
lines changed

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

Lines changed: 69 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -963,24 +963,32 @@ SkinnedMeshRenderer unitySkin
963963
var bones = unitySkin.bones;
964964
foreach (var bone in bones)
965965
{
966-
var fbxBone = MapUnityObjectToFbxNode[bone.gameObject];
967-
ExportTransform(bone, fbxBone, newCenter: Vector3.zero, TransformExportType.Local);
966+
// ignore null bones
967+
if (bone != null)
968+
{
969+
var fbxBone = MapUnityObjectToFbxNode[bone.gameObject];
970+
ExportTransform(bone, fbxBone, newCenter: Vector3.zero, TransformExportType.Local);
968971

969-
// Cancel out the pre-rotation from the exported rotation
972+
// Cancel out the pre-rotation from the exported rotation
970973

971-
// Get prerotation
972-
var fbxPreRotationEuler = fbxBone.GetPreRotation(FbxNode.EPivotSet.eSourcePivot);
973-
// Convert the prerotation to a Quaternion
974-
var fbxPreRotationQuaternion = EulerToQuaternion(fbxPreRotationEuler);
975-
// Inverse of the prerotation
976-
fbxPreRotationQuaternion.Inverse();
974+
// Get prerotation
975+
var fbxPreRotationEuler = fbxBone.GetPreRotation(FbxNode.EPivotSet.eSourcePivot);
976+
// Convert the prerotation to a Quaternion
977+
var fbxPreRotationQuaternion = EulerToQuaternion(fbxPreRotationEuler);
978+
// Inverse of the prerotation
979+
fbxPreRotationQuaternion.Inverse();
977980

978-
// Multiply LclRotation by pre-rotation inverse to get the LclRotation without pre-rotation applied
979-
var finalLclRotationQuat = fbxPreRotationQuaternion * EulerToQuaternion(new FbxVector4(fbxBone.LclRotation.Get()));
981+
// Multiply LclRotation by pre-rotation inverse to get the LclRotation without pre-rotation applied
982+
var finalLclRotationQuat = fbxPreRotationQuaternion * EulerToQuaternion(new FbxVector4(fbxBone.LclRotation.Get()));
980983

981-
// Convert to Euler without axis conversion (Pre-rotation and LclRotation were already in Maya axis)
982-
// and update LclRotation
983-
fbxBone.LclRotation.Set(ToFbxDouble3(QuaternionToEuler(finalLclRotationQuat)));
984+
// Convert to Euler without axis conversion (Pre-rotation and LclRotation were already in Maya axis)
985+
// and update LclRotation
986+
fbxBone.LclRotation.Set(ToFbxDouble3(QuaternionToEuler(finalLclRotationQuat)));
987+
}
988+
else
989+
{
990+
Debug.Log("Warning: One or more bones are null. Skeleton may not export correctly.");
991+
}
984992
}
985993
}
986994

@@ -1043,16 +1051,25 @@ private bool ExportSkeleton (SkinnedMeshRenderer skinnedMesh, FbxScene fbxScene,
10431051
Dictionary<Transform, int> index = new Dictionary<Transform, int>();
10441052
for (int boneIndex = 0; boneIndex < bones.Length; boneIndex++) {
10451053
Transform unityBoneTransform = bones [boneIndex];
1046-
index[unityBoneTransform] = boneIndex;
1054+
1055+
// ignore null bones
1056+
if (unityBoneTransform != null)
1057+
{
1058+
index[unityBoneTransform] = boneIndex;
1059+
}
10471060
}
10481061

10491062
skinnedMeshToBonesMap.Add (skinnedMesh, bones);
10501063

10511064
// Step 1: Set transforms
10521065
var boneInfo = new SkinnedMeshBoneInfo (skinnedMesh, index);
10531066
foreach (var bone in bones) {
1054-
var fbxBone = MapUnityObjectToFbxNode [bone.gameObject];
1055-
ExportBoneTransform (fbxBone, fbxScene, bone, boneInfo);
1067+
// ignore null bones
1068+
if (bone != null)
1069+
{
1070+
var fbxBone = MapUnityObjectToFbxNode[bone.gameObject];
1071+
ExportBoneTransform(fbxBone, fbxScene, bone, boneInfo);
1072+
}
10561073
}
10571074
return true;
10581075
}
@@ -1072,24 +1089,28 @@ private bool ExportSkin (SkinnedMeshRenderer skinnedMesh,
10721089
Dictionary<int, FbxCluster> boneCluster = new Dictionary<int, FbxCluster> ();
10731090

10741091
for(int i = 0; i < skinnedMesh.bones.Length; i++) {
1075-
FbxNode fbxBoneNode = MapUnityObjectToFbxNode [skinnedMesh.bones[i].gameObject];
1092+
// ignore null bones
1093+
if (skinnedMesh.bones[i] != null)
1094+
{
1095+
FbxNode fbxBoneNode = MapUnityObjectToFbxNode[skinnedMesh.bones[i].gameObject];
10761096

1077-
// Create the deforming cluster
1078-
FbxCluster fbxCluster = FbxCluster.Create (fbxScene, "BoneWeightCluster");
1097+
// Create the deforming cluster
1098+
FbxCluster fbxCluster = FbxCluster.Create(fbxScene, "BoneWeightCluster");
10791099

1080-
fbxCluster.SetLink (fbxBoneNode);
1081-
fbxCluster.SetLinkMode (FbxCluster.ELinkMode.eNormalize);
1100+
fbxCluster.SetLink(fbxBoneNode);
1101+
fbxCluster.SetLinkMode(FbxCluster.ELinkMode.eNormalize);
10821102

1083-
boneCluster.Add (i, fbxCluster);
1103+
boneCluster.Add(i, fbxCluster);
10841104

1085-
// set the Transform and TransformLink matrix
1086-
fbxCluster.SetTransformMatrix (fbxMeshMatrix);
1105+
// set the Transform and TransformLink matrix
1106+
fbxCluster.SetTransformMatrix(fbxMeshMatrix);
10871107

1088-
FbxAMatrix fbxLinkMatrix = fbxBoneNode.EvaluateGlobalTransform ();
1089-
fbxCluster.SetTransformLinkMatrix (fbxLinkMatrix);
1108+
FbxAMatrix fbxLinkMatrix = fbxBoneNode.EvaluateGlobalTransform();
1109+
fbxCluster.SetTransformLinkMatrix(fbxLinkMatrix);
10901110

1091-
// add the cluster to the skin
1092-
fbxSkin.AddCluster (fbxCluster);
1111+
// add the cluster to the skin
1112+
fbxSkin.AddCluster(fbxCluster);
1113+
}
10931114
}
10941115

10951116
// set the vertex weights for each bone
@@ -1166,21 +1187,25 @@ private bool ExportBindPose (SkinnedMeshRenderer skinnedMesh, FbxNode fbxMeshNod
11661187
return false;
11671188
}
11681189
for (int i = 0; i < bones.Length; i++) {
1169-
FbxNode fbxBoneNode = MapUnityObjectToFbxNode [bones[i].gameObject];
1170-
1171-
// EvaluateGlobalTransform returns an FbxAMatrix (affine matrix)
1172-
// which has to be converted to an FbxMatrix so that it can be passed to fbxPose.Add().
1173-
// The hierarchy for FbxMatrix and FbxAMatrix is as follows:
1174-
//
1175-
// FbxDouble4x4
1176-
// / \
1177-
// FbxMatrix FbxAMatrix
1178-
//
1179-
// Therefore we can't convert directly from FbxAMatrix to FbxMatrix,
1180-
// however FbxMatrix has a constructor that takes an FbxAMatrix.
1181-
FbxMatrix fbxBindMatrix = new FbxMatrix(fbxBoneNode.EvaluateGlobalTransform ());
1182-
1183-
fbxPose.Add (fbxBoneNode, fbxBindMatrix);
1190+
// ignore null bones
1191+
if (bones[i] != null)
1192+
{
1193+
FbxNode fbxBoneNode = MapUnityObjectToFbxNode[bones[i].gameObject];
1194+
1195+
// EvaluateGlobalTransform returns an FbxAMatrix (affine matrix)
1196+
// which has to be converted to an FbxMatrix so that it can be passed to fbxPose.Add().
1197+
// The hierarchy for FbxMatrix and FbxAMatrix is as follows:
1198+
//
1199+
// FbxDouble4x4
1200+
// / \
1201+
// FbxMatrix FbxAMatrix
1202+
//
1203+
// Therefore we can't convert directly from FbxAMatrix to FbxMatrix,
1204+
// however FbxMatrix has a constructor that takes an FbxAMatrix.
1205+
FbxMatrix fbxBindMatrix = new FbxMatrix(fbxBoneNode.EvaluateGlobalTransform());
1206+
1207+
fbxPose.Add(fbxBoneNode, fbxBindMatrix);
1208+
}
11841209
}
11851210

11861211
fbxPose.Add (fbxMeshNode, new FbxMatrix (fbxMeshNode.EvaluateGlobalTransform ()));

0 commit comments

Comments
 (0)