Skip to content

Commit dc77670

Browse files
committed
Added Unit Test for getUnityObjectName and getFBXObjectName, improved UI for Manual Update
1 parent c3f2709 commit dc77670

File tree

4 files changed

+279
-75
lines changed

4 files changed

+279
-75
lines changed

Assets/FbxExporters/Editor/FbxExporter.cs

Lines changed: 145 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,86 @@ private static bool ExportUVs(FbxMesh fbxMesh, MeshInfo mesh, int[] unmergedTria
426426
return k > 0;
427427
}
428428

429+
/// <summary>
430+
/// Export the mesh's blend shapes.
431+
/// </summary>
432+
private bool ExportBlendShapes(MeshInfo mesh, FbxMesh fbxMesh, FbxScene fbxScene, int[] unmergedTriangles)
433+
{
434+
var umesh = mesh.mesh;
435+
if (umesh.blendShapeCount == 0)
436+
return false;
437+
438+
var fbxBlendShape = FbxBlendShape.Create(fbxScene, umesh.name + "_BlendShape");
439+
fbxMesh.AddDeformer(fbxBlendShape);
440+
441+
var numVertices = umesh.vertexCount;
442+
var basePoints = umesh.vertices;
443+
var baseNormals = umesh.normals;
444+
var baseTangents = umesh.tangents;
445+
var deltaPoints = new Vector3[numVertices];
446+
var deltaNormals = new Vector3[numVertices];
447+
var deltaTangents = new Vector3[numVertices];
448+
449+
for (int bi = 0; bi < umesh.blendShapeCount; ++bi)
450+
{
451+
var bsName = umesh.GetBlendShapeName(bi);
452+
var numFrames = umesh.GetBlendShapeFrameCount(bi);
453+
var fbxChannel = FbxBlendShapeChannel.Create(fbxScene, bsName);
454+
fbxBlendShape.AddBlendShapeChannel(fbxChannel);
455+
456+
for (int fi = 0; fi < numFrames; ++fi)
457+
{
458+
var weight = umesh.GetBlendShapeFrameWeight(bi, fi);
459+
umesh.GetBlendShapeFrameVertices(bi, fi, deltaPoints, deltaNormals, deltaTangents);
460+
461+
var fbxShape = FbxShape.Create(fbxScene, "");
462+
fbxChannel.AddTargetShape(fbxShape, weight);
463+
464+
// control points
465+
fbxShape.InitControlPoints(ControlPointToIndex.Count());
466+
for (int vi = 0; vi < numVertices; ++vi)
467+
{
468+
int ni = ControlPointToIndex[basePoints[vi]];
469+
var v = basePoints[vi] + deltaPoints[vi];
470+
fbxShape.SetControlPointAt(ConvertToRightHanded(v, UnitScaleFactor), ni);
471+
}
472+
473+
// normals
474+
if (mesh.HasValidNormals())
475+
{
476+
var elemNormals = fbxShape.CreateElementNormal();
477+
elemNormals.SetMappingMode(FbxLayerElement.EMappingMode.eByPolygonVertex);
478+
elemNormals.SetReferenceMode(FbxLayerElement.EReferenceMode.eDirect);
479+
var dstNormals = elemNormals.GetDirectArray();
480+
dstNormals.SetCount(unmergedTriangles.Length);
481+
for (int ii = 0; ii < unmergedTriangles.Length; ++ii)
482+
{
483+
int vi = unmergedTriangles[ii];
484+
var n = baseNormals[vi] + deltaNormals[vi];
485+
dstNormals.SetAt(ii, ConvertToRightHanded(n));
486+
}
487+
}
488+
489+
// tangents
490+
if (mesh.HasValidTangents())
491+
{
492+
var elemTangents = fbxShape.CreateElementTangent();
493+
elemTangents.SetMappingMode(FbxLayerElement.EMappingMode.eByPolygonVertex);
494+
elemTangents.SetReferenceMode(FbxLayerElement.EReferenceMode.eDirect);
495+
var dstTangents = elemTangents.GetDirectArray();
496+
dstTangents.SetCount(unmergedTriangles.Length);
497+
for (int ii = 0; ii < unmergedTriangles.Length; ++ii)
498+
{
499+
int vi = unmergedTriangles[ii];
500+
var t = (Vector3)baseTangents[vi] + deltaTangents[vi];
501+
dstTangents.SetAt(ii, ConvertToRightHanded(t));
502+
}
503+
}
504+
}
505+
}
506+
return true;
507+
}
508+
429509
/// <summary>
430510
/// Takes in a left-handed UnityEngine.Vector3 denoting a normal,
431511
/// returns a right-handed FbxVector4.
@@ -754,6 +834,9 @@ bool ExportMesh (MeshInfo meshInfo, FbxNode fbxNode)
754834
// Set up normals, etc.
755835
ExportComponentAttributes (meshInfo, fbxMesh, unmergedPolygons.ToArray());
756836

837+
// Set up blend shapes.
838+
ExportBlendShapes(meshInfo, fbxMesh, fbxScene, unmergedPolygons.ToArray());
839+
757840
// set the fbxNode containing the mesh
758841
fbxNode.SetNodeAttribute (fbxMesh);
759842
fbxNode.SetShadingMode (FbxNode.EShadingMode.eWireFrame);
@@ -781,31 +864,28 @@ SkinnedMeshRenderer unitySkin
781864
Debug.Log (string.Format ("exporting {0} {1}", "Skin", fbxNode.GetName ()));
782865

783866

784-
Dictionary<SkinnedMeshRenderer, Transform[]> skinnedMeshToBonesMap;
785-
// export skeleton
786-
if (!ExportSkeleton (unitySkin, fbxScene, out skinnedMeshToBonesMap)) {
787-
Debug.LogWarning ("failed to export skeleton");
788-
return false;
789-
}
867+
var meshInfo = new MeshInfo(unitySkin.sharedMesh, unitySkin.sharedMaterials);
790868

791-
var meshInfo = new MeshInfo (unitySkin.sharedMesh, unitySkin.sharedMaterials);
792-
793-
// export skin mesh
794869
FbxMesh fbxMesh = null;
795-
if (ExportMesh (meshInfo, fbxNode)) {
796-
fbxMesh = fbxNode.GetMesh ();
870+
if (ExportMesh(meshInfo, fbxNode))
871+
{
872+
fbxMesh = fbxNode.GetMesh();
797873
}
798-
799-
if (fbxMesh == null) {
800-
Debug.LogError ("Could not find mesh");
874+
if (fbxMesh == null)
875+
{
876+
Debug.LogError("Could not find mesh");
801877
return false;
802878
}
803879

804-
// bind mesh to skeleton
805-
ExportSkin (unitySkin, meshInfo, fbxScene, fbxMesh, fbxNode);
880+
Dictionary<SkinnedMeshRenderer, Transform[]> skinnedMeshToBonesMap;
881+
// export skeleton
882+
if (ExportSkeleton (unitySkin, fbxScene, out skinnedMeshToBonesMap)) {
883+
// bind mesh to skeleton
884+
ExportSkin (unitySkin, meshInfo, fbxScene, fbxMesh, fbxNode);
806885

807-
// add bind pose
808-
ExportBindPose (unitySkin, fbxNode, fbxScene, skinnedMeshToBonesMap);
886+
// add bind pose
887+
ExportBindPose (unitySkin, fbxNode, fbxScene, skinnedMeshToBonesMap);
888+
}
809889

810890
return true;
811891
}
@@ -1137,6 +1217,30 @@ public static FbxVector4 ConvertQuaternionToXYZEuler (FbxQuaternion quat)
11371217
return new FbxVector4 (vector4.X, -vector4.Y, -vector4.Z, vector4.W);
11381218
}
11391219

1220+
/// <summary>
1221+
/// Euler to quaternion without axis conversion.
1222+
/// </summary>
1223+
/// <returns>a quaternion.</returns>
1224+
/// <param name="euler">Euler.</param>
1225+
public static FbxQuaternion EulerToQuaternion(FbxVector4 euler)
1226+
{
1227+
FbxAMatrix m = new FbxAMatrix ();
1228+
m.SetR (euler);
1229+
return m.GetQ ();
1230+
}
1231+
1232+
/// <summary>
1233+
/// Quaternion to euler without axis conversion.
1234+
/// </summary>
1235+
/// <returns>a euler.</returns>
1236+
/// <param name="quat">Quaternion.</param>
1237+
public static FbxVector4 QuaternionToEuler(FbxQuaternion quat)
1238+
{
1239+
FbxAMatrix m = new FbxAMatrix ();
1240+
m.SetQ (quat);
1241+
return m.GetR ();
1242+
}
1243+
11401244
// get a fbxNode's global default position.
11411245
protected bool ExportTransform (UnityEngine.Transform unityTransform, FbxNode fbxNode, Vector3 newCenter, TransformExportType exportType)
11421246
{
@@ -1447,11 +1551,12 @@ public float Convert(float value)
14471551

14481552
/// <summary>
14491553
/// Store FBX property name and channel name
1554+
/// Default constructor added because it needs to be called before autoimplemented properties can be assigned. Otherwise we get build errors
14501555
/// </summary>
14511556
struct FbxPropertyChannelPair {
14521557
public string Property { get ; private set; }
14531558
public string Channel { get ; private set; }
1454-
public FbxPropertyChannelPair(string p, string c) {
1559+
public FbxPropertyChannelPair(string p, string c):this() {
14551560
Property = p;
14561561
Channel = c;
14571562
}
@@ -1602,8 +1707,9 @@ Key [] ComputeKeys(UnityEngine.Quaternion restRotation, FbxNode node) {
16021707
var fbxPreRotationEuler = node.GetRotationActive()
16031708
? node.GetPreRotation(FbxNode.EPivotSet.eSourcePivot)
16041709
: new FbxVector4();
1605-
var fbxPreRotationInverse = new FbxQuaternion();
1606-
fbxPreRotationInverse.ComposeSphericalXYZ(fbxPreRotationEuler);
1710+
1711+
// Get the inverse of the prerotation
1712+
var fbxPreRotationInverse = ModelExporter.EulerToQuaternion (fbxPreRotationEuler);
16071713
fbxPreRotationInverse.Inverse();
16081714

16091715
// If we're only animating along certain coords for some
@@ -1632,17 +1738,23 @@ Key [] ComputeKeys(UnityEngine.Quaternion restRotation, FbxNode node) {
16321738
(z == null) ? lclQuaternion[2] : z.Evaluate(seconds),
16331739
(w == null) ? lclQuaternion[3] : w.Evaluate(seconds));
16341740

1741+
// convert the final animation to righthanded coords
1742+
var finalEuler = ModelExporter.ConvertQuaternionToXYZEuler(fbxFinalAnimation);
1743+
1744+
// convert it back to a quaternion for multiplication
1745+
fbxFinalAnimation = ModelExporter.EulerToQuaternion (finalEuler);
1746+
16351747
// Cancel out the pre-rotation. Order matters. FBX reads left-to-right.
16361748
// When we run animation we will apply:
16371749
// pre-rotation
16381750
// then pre-rotation inverse
16391751
// then animation.
1640-
var fbxQuat = fbxPreRotationInverse * fbxFinalAnimation;
1752+
var fbxFinalQuat = fbxPreRotationInverse * fbxFinalAnimation;
16411753

16421754
// Store the key so we can sort them later.
16431755
Key key;
16441756
key.time = FbxTime.FromSecondDouble(seconds);
1645-
key.euler = ModelExporter.ConvertQuaternionToXYZEuler(fbxQuat);
1757+
key.euler = ModelExporter.QuaternionToEuler (fbxFinalQuat);;
16461758
keys[i++] = key;
16471759
}
16481760

@@ -1681,6 +1793,12 @@ public void Animate(Transform unityTransform, FbxNode fbxNode, FbxAnimLayer fbxA
16811793
fbxAnimCurveY.KeyModifyEnd();
16821794
fbxAnimCurveX.KeyModifyEnd();
16831795

1796+
// Uni-35616 unroll curves to preserve continuous rotations
1797+
var fbxCurveNode = fbxNode.LclRotation.GetCurveNode(fbxAnimLayer, false /*should already exist*/);
1798+
1799+
FbxAnimCurveFilterUnroll fbxAnimUnrollFilter = new FbxAnimCurveFilterUnroll();
1800+
fbxAnimUnrollFilter.Apply(fbxCurveNode);
1801+
16841802
if (Verbose) {
16851803
Debug.Log("Exported rotation animation for " + fbxNode.GetName());
16861804
}
@@ -1717,7 +1835,8 @@ protected void ExportAnimationClip (AnimationClip uniAnimClip, GameObject uniRoo
17171835
if (timeMode == FbxTime.EMode.eCustom) {
17181836
timeMode = FbxTime.EMode.eFrames30;
17191837
}
1720-
FbxTime.SetGlobalTimeMode (timeMode);
1838+
1839+
fbxScene.GetGlobalSettings ().SetTimeMode (timeMode);
17211840

17221841
// set time correctly
17231842
var fbxStartTime = FbxTime.FromSecondDouble (0);
@@ -2352,7 +2471,7 @@ static void OnContextItem (MenuCommand command)
23522471
}
23532472

23542473
/// <summary>
2355-
// Validate the menu item defined by the function above.
2474+
/// Validate the menu item defined by the function above.
23562475
/// </summary>
23572476
[MenuItem (MenuItemName, true, 30)]
23582477
public static bool OnValidateMenuItem ()
@@ -2367,6 +2486,7 @@ public static void DisplayNoSelectionDialog()
23672486
"No GameObjects selected for export.",
23682487
"Ok");
23692488
}
2489+
23702490
//
23712491
// export mesh info from Unity
23722492
//

0 commit comments

Comments
 (0)