Skip to content

Commit 4904af7

Browse files
committed
Uni-35935-export-keytangents-with-unbaked-curves
1 parent 21becc4 commit 4904af7

File tree

5 files changed

+352
-420
lines changed

5 files changed

+352
-420
lines changed

Assets/FbxExporters/Editor/FbxExportSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ public static string[] DCCVendorLocations
427427
public bool launchAfterInstallation = true;
428428
public bool HideSendToUnityMenu = true;
429429
public int ExportFormatSelection;
430+
public bool BakeAnimation = true;
430431

431432
public string IntegrationSavePath;
432433

@@ -463,6 +464,7 @@ protected override void LoadDefaults()
463464
IntegrationSavePath = DefaultIntegrationSavePath;
464465
dccOptionPaths = null;
465466
dccOptionNames = null;
467+
BakeAnimation = true;
466468
}
467469

468470
/// <summary>

Assets/FbxExporters/Editor/FbxExporter.cs

Lines changed: 154 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,13 +1444,111 @@ protected bool ExportLight (GameObject unityGo, FbxScene fbxScene, FbxNode fbxNo
14441444
return true;
14451445
}
14461446

1447+
/// <summary>
1448+
/// Export animation curve key frames with key tangents
1449+
/// </summary>
1450+
protected void ExportAnimationKeyFrames (AnimationCurve uniAnimCurve, FbxAnimCurve fbxAnimCurve,
1451+
UnityToMayaConvertSceneHelper convertSceneHelper)
1452+
{
1453+
// TODO: map key tangents mode between Unity and FBX
1454+
Dictionary<AnimationUtility.TangentMode, List<FbxAnimCurveDef.ETangentMode>> MapUnityKeyTangentModeToFBX =
1455+
new Dictionary<AnimationUtility.TangentMode, List<FbxAnimCurveDef.ETangentMode>>
1456+
{
1457+
//TangeantAuto|GenericTimeIndependent|GenericClampProgressive
1458+
{AnimationUtility.TangentMode.Free, new List<FbxAnimCurveDef.ETangentMode>{FbxAnimCurveDef.ETangentMode.eTangentAuto,FbxAnimCurveDef.ETangentMode.eTangentGenericClampProgressive}},
1459+
1460+
//TangeantAuto|GenericTimeIndependent
1461+
{AnimationUtility.TangentMode.Auto, new List<FbxAnimCurveDef.ETangentMode>{FbxAnimCurveDef.ETangentMode.eTangentAuto,FbxAnimCurveDef.ETangentMode.eTangentGenericTimeIndependent}},
1462+
1463+
//TangeantAuto|GenericTimeIndependent|GenericClampProgressive
1464+
{AnimationUtility.TangentMode.ClampedAuto, new List<FbxAnimCurveDef.ETangentMode>{FbxAnimCurveDef.ETangentMode.eTangentAuto,FbxAnimCurveDef.ETangentMode.eTangentGenericClampProgressive}},
1465+
};
1466+
1467+
// Copy Unity AnimCurve to FBX AnimCurve.
1468+
// NOTE: only cubic keys are supported by the FbxImporter
1469+
using (new FbxAnimCurveModifyHelper(new List<FbxAnimCurve>{fbxAnimCurve}))
1470+
{
1471+
for (int keyIndex = 0; keyIndex < uniAnimCurve.length; ++keyIndex)
1472+
{
1473+
var uniKeyFrame = uniAnimCurve [keyIndex];
1474+
var fbxTime = FbxTime.FromSecondDouble (uniKeyFrame.time);
1475+
1476+
int fbxKeyIndex = fbxAnimCurve.KeyAdd (fbxTime);
1477+
1478+
fbxAnimCurve.KeySet (fbxKeyIndex,
1479+
fbxTime,
1480+
convertSceneHelper.Convert(uniKeyFrame.value)
1481+
);
1482+
1483+
// configure tangents
1484+
var lTangent = AnimationUtility.GetKeyLeftTangentMode(uniAnimCurve, keyIndex);
1485+
var rTangent = AnimationUtility.GetKeyRightTangentMode(uniAnimCurve, keyIndex);
1486+
1487+
if (!(MapUnityKeyTangentModeToFBX.ContainsKey(lTangent) && MapUnityKeyTangentModeToFBX.ContainsKey(rTangent)))
1488+
{
1489+
Debug.LogWarning(string.Format("key[{0}] missing tangent mapping ({1},{2})", keyIndex, lTangent.ToString(), rTangent.ToString()));
1490+
continue;
1491+
}
1492+
1493+
// TODO : handle broken tangents
1494+
1495+
// TODO : set key tangents
1496+
}
1497+
}
1498+
}
1499+
1500+
/// <summary>
1501+
/// Export animation curve key samples
1502+
/// </summary>
1503+
protected void ExportAnimationSamples (AnimationCurve uniAnimCurve, FbxAnimCurve fbxAnimCurve,
1504+
double sampleRate,
1505+
UnityToMayaConvertSceneHelper convertSceneHelper)
1506+
{
1507+
using (new FbxAnimCurveModifyHelper(new List<FbxAnimCurve>{fbxAnimCurve}))
1508+
{
1509+
double fs = 1.0/sampleRate;
1510+
int numKeys = uniAnimCurve.length;
1511+
1512+
int numSamples = 0;
1513+
int sampleIndex = 0;
1514+
double currSample = double.MaxValue, firstTime = double.MaxValue, lastTime = double.MaxValue;
1515+
1516+
if (numKeys>0)
1517+
{
1518+
firstTime = uniAnimCurve[0].time;
1519+
lastTime = uniAnimCurve[uniAnimCurve.length-1].time;
1520+
1521+
numSamples = (int)((float)((lastTime-firstTime) * sampleRate));
1522+
Debug.Log(string.Format("Exporting Animation Samples : firstTime={0}, lastTime={1} frameRate={2} numSamples={3}",
1523+
firstTime, lastTime, sampleRate, numSamples));
1524+
1525+
currSample = firstTime;
1526+
}
1527+
1528+
for (sampleIndex = 0, currSample = firstTime; currSample < lastTime; ++sampleIndex, currSample += fs)
1529+
{
1530+
float currSampleValue = uniAnimCurve.Evaluate((float)currSample);
1531+
1532+
var fbxTime = FbxTime.FromSecondDouble (currSample);
1533+
1534+
int fbxKeyIndex = fbxAnimCurve.KeyAdd (fbxTime);
1535+
1536+
fbxAnimCurve.KeySet (fbxKeyIndex,
1537+
fbxTime,
1538+
convertSceneHelper.Convert(currSampleValue)
1539+
);
1540+
}
1541+
}
1542+
}
1543+
14471544
/// <summary>
14481545
/// Export an AnimationCurve.
14491546
/// NOTE: This is not used for rotations, because we need to convert from
14501547
/// quaternion to euler and various other stuff.
14511548
/// </summary>
14521549
protected void ExportAnimationCurve (UnityEngine.Object uniObj,
14531550
AnimationCurve uniAnimCurve,
1551+
float frameRate,
14541552
string uniPropertyName,
14551553
FbxScene fbxScene,
14561554
FbxAnimLayer fbxAnimLayer)
@@ -1477,6 +1575,7 @@ protected void ExportAnimationCurve (UnityEngine.Object uniObj,
14771575
Debug.LogError(string.Format("no fbx node for {0}", unityGo.ToString()));
14781576
return;
14791577
}
1578+
14801579
// map unity property name to fbx property
14811580
var fbxProperty = fbxNode.FindProperty(fbxPropertyChannelPair.Property, false);
14821581
if (!fbxProperty.IsValid())
@@ -1496,70 +1595,24 @@ protected void ExportAnimationCurve (UnityEngine.Object uniObj,
14961595
// Create the AnimCurve on the channel
14971596
FbxAnimCurve fbxAnimCurve = fbxProperty.GetCurve (fbxAnimLayer, fbxPropertyChannelPair.Channel, true);
14981597

1499-
var transformBindings = new UnityToMayaConvertSceneHelper (uniPropertyName);
1500-
1501-
// TODO: read the curve interpolation
1502-
FbxAnimCurveDef.EInterpolationType fbxInterpolation =
1503-
FbxAnimCurveDef.EInterpolationType.eInterpolationCubic;
1598+
// create a convert scene helper so that we can convert from Unity to Maya
1599+
// AxisSystem (LeftHanded to RightHanded) and FBX's default units
1600+
// (Meters to Centimetres)
1601+
var convertSceneHelper = new UnityToMayaConvertSceneHelper (uniPropertyName);
15041602

1505-
Dictionary<AnimationUtility.TangentMode, List<FbxAnimCurveDef.ETangentMode>> MapUnityKeyTangentModeToFBX =
1506-
new Dictionary<AnimationUtility.TangentMode, List<FbxAnimCurveDef.ETangentMode>>
1603+
// TODO: we'll resample the curve so we don't have to
1604+
// configure tangents
1605+
if (ModelExporter.ExportSettings.BakeAnimation)
15071606
{
1508-
//TangeantAuto|GenericTimeIndependent|GenericClampProgressive
1509-
{AnimationUtility.TangentMode.Free, new List<FbxAnimCurveDef.ETangentMode>{FbxAnimCurveDef.ETangentMode.eTangentAuto,FbxAnimCurveDef.ETangentMode.eTangentGenericClampProgressive}},
1510-
1511-
//TangeantAuto|GenericTimeIndependent
1512-
{AnimationUtility.TangentMode.Auto, new List<FbxAnimCurveDef.ETangentMode>{FbxAnimCurveDef.ETangentMode.eTangentAuto,FbxAnimCurveDef.ETangentMode.eTangentGenericTimeIndependent}},
1513-
1514-
//TangeantAuto|GenericTimeIndependent|GenericClampProgressive
1515-
{AnimationUtility.TangentMode.ClampedAuto, new List<FbxAnimCurveDef.ETangentMode>{FbxAnimCurveDef.ETangentMode.eTangentAuto,FbxAnimCurveDef.ETangentMode.eTangentGenericClampProgressive}},
1516-
};
1517-
1518-
1519-
// copy Unity AnimCurve to FBX AnimCurve.
1520-
fbxAnimCurve.KeyModifyBegin ();
1521-
1522-
for (int keyIndex = 0, n = uniAnimCurve.length; keyIndex < n; ++keyIndex) {
1523-
var uniKeyFrame = uniAnimCurve [keyIndex];
1524-
var fbxTime = FbxTime.FromSecondDouble (uniKeyFrame.time);
1525-
1526-
keyIndex = fbxAnimCurve.KeyAdd (fbxTime);
1527-
1528-
fbxAnimCurve.KeySet (keyIndex,
1529-
fbxTime,
1530-
transformBindings.Convert(uniKeyFrame.value),
1531-
fbxInterpolation
1532-
);
1533-
1534-
if (fbxInterpolation==FbxAnimCurveDef.EInterpolationType.eInterpolationCubic)
1535-
{
1536-
var lTangent = AnimationUtility.GetKeyLeftTangentMode(uniAnimCurve, keyIndex);
1537-
var rTangent = AnimationUtility.GetKeyRightTangentMode(uniAnimCurve, keyIndex);
1538-
1539-
if (!MapUnityKeyTangentModeToFBX.ContainsKey(lTangent))
1540-
{
1541-
Debug.LogWarning(string.Format("key[{0}] missing tangent mapping ({1})", keyIndex, lTangent.ToString()));
1542-
continue;
1543-
}
1544-
1545-
// TODO : handle broken tangents
1546-
if (lTangent!=rTangent)
1547-
{
1548-
Debug.LogWarning(string.Format("key[{0}] broken tangents ({1},{2}) not supported", keyIndex, lTangent.ToString(), rTangent.ToString()));
1549-
}
1550-
1551-
foreach (FbxAnimCurveDef.ETangentMode fbxKeyTangentMode in MapUnityKeyTangentModeToFBX[lTangent])
1552-
{
1553-
fbxAnimCurve.KeySetTangentMode(keyIndex, fbxKeyTangentMode);
1554-
}
1555-
1556-
}
1607+
ExportAnimationSamples(uniAnimCurve, fbxAnimCurve, frameRate, convertSceneHelper);
1608+
}
1609+
else
1610+
{
1611+
ExportAnimationKeyFrames(uniAnimCurve, fbxAnimCurve, convertSceneHelper);
15571612
}
1558-
1559-
fbxAnimCurve.KeyModifyEnd();
15601613
}
15611614

1562-
class UnityToMayaConvertSceneHelper
1615+
public class UnityToMayaConvertSceneHelper
15631616
{
15641617
bool convertDistance = false;
15651618
bool convertLtoR = false;
@@ -1699,6 +1752,33 @@ public static bool TryGetValue(string uniPropertyName, out FbxPropertyChannelPai
16991752
}
17001753
}
17011754

1755+
/// <summary>
1756+
/// Exporting rotations is more complicated. We need to convert
1757+
/// from quaternion to euler. We use this class to help.
1758+
/// </summary>
1759+
class FbxAnimCurveModifyHelper : System.IDisposable
1760+
{
1761+
public List<FbxAnimCurve> Curves { get ; private set; }
1762+
1763+
public FbxAnimCurveModifyHelper(List<FbxAnimCurve> list)
1764+
{
1765+
Curves = list;
1766+
1767+
foreach (var curve in Curves)
1768+
curve.KeyModifyBegin();
1769+
}
1770+
1771+
~FbxAnimCurveModifyHelper() {
1772+
Dispose();
1773+
}
1774+
1775+
public void Dispose()
1776+
{
1777+
foreach (var curve in Curves)
1778+
curve.KeyModifyEnd();
1779+
}
1780+
}
1781+
17021782
/// <summary>
17031783
/// Exporting rotations is more complicated. We need to convert
17041784
/// from quaternion to euler. We use this class to help.
@@ -1818,27 +1898,21 @@ public void Animate(Transform unityTransform, FbxNode fbxNode, FbxAnimLayer fbxA
18181898
var fbxAnimCurveZ = fbxNode.LclRotation.GetCurve(fbxAnimLayer, Globals.FBXSDK_CURVENODE_COMPONENT_Z, true);
18191899

18201900
/* set the keys */
1821-
fbxAnimCurveX.KeyModifyBegin();
1822-
fbxAnimCurveY.KeyModifyBegin();
1823-
fbxAnimCurveZ.KeyModifyBegin();
1901+
using (new FbxAnimCurveModifyHelper(new List<FbxAnimCurve>{fbxAnimCurveX,fbxAnimCurveY,fbxAnimCurveZ}))
1902+
{
1903+
foreach (var key in ComputeKeys(unityTransform.localRotation, fbxNode)) {
18241904

1825-
var keys = ComputeKeys(unityTransform.localRotation, fbxNode);
1826-
for(int i = 0, n = keys.Length; i < n; ++i) {
1827-
var key = keys[i];
1828-
fbxAnimCurveX.KeyAdd(key.time);
1829-
fbxAnimCurveX.KeySet(i, key.time, (float)key.euler.X);
1905+
int i = fbxAnimCurveX.KeyAdd(key.time);
1906+
fbxAnimCurveX.KeySet(i, key.time, (float)key.euler.X);
18301907

1831-
fbxAnimCurveY.KeyAdd(key.time);
1832-
fbxAnimCurveY.KeySet(i, key.time, (float)key.euler.Y);
1908+
i = fbxAnimCurveY.KeyAdd(key.time);
1909+
fbxAnimCurveY.KeySet(i, key.time, (float)key.euler.Y);
18331910

1834-
fbxAnimCurveZ.KeyAdd(key.time);
1835-
fbxAnimCurveZ.KeySet(i, key.time, (float)key.euler.Z);
1911+
i = fbxAnimCurveZ.KeyAdd(key.time);
1912+
fbxAnimCurveZ.KeySet(i, key.time, (float)key.euler.Z);
1913+
}
18361914
}
18371915

1838-
fbxAnimCurveZ.KeyModifyEnd();
1839-
fbxAnimCurveY.KeyModifyEnd();
1840-
fbxAnimCurveX.KeyModifyEnd();
1841-
18421916
// Uni-35616 unroll curves to preserve continuous rotations
18431917
var fbxCurveNode = fbxNode.LclRotation.GetCurveNode(fbxAnimLayer, false /*should already exist*/);
18441918

@@ -1911,8 +1985,10 @@ protected void ExportAnimationClip (AnimationClip uniAnimClip, GameObject uniRoo
19111985
if (index == -1)
19121986
{
19131987
/* simple property (e.g. intensity), export right away */
1914-
ExportAnimationCurve (uniObj, uniAnimCurve, uniCurveBinding.propertyName,
1915-
fbxScene, fbxAnimLayer);
1988+
ExportAnimationCurve (uniObj, uniAnimCurve, uniAnimClip.frameRate,
1989+
uniCurveBinding.propertyName,
1990+
fbxScene,
1991+
fbxAnimLayer);
19161992
} else {
19171993
/* Rotation property; save it to convert quaternion -> euler later. */
19181994

@@ -2951,7 +3027,7 @@ public void Dispose ()
29513027
{
29523028
}
29533029

2954-
public bool Verbose { private set {;} get { return false; } }
3030+
public bool Verbose { private set {;} get { return true; } }
29553031

29563032
/// <summary>
29573033
/// manage the selection of a filename

0 commit comments

Comments
 (0)