Skip to content

Commit b8fef0a

Browse files
author
DESKTOP-F8VO8FK\Austin
committed
Merge branch 'master' into Uni-35974-sprint43-release
2 parents b72a363 + c41ffe9 commit b8fef0a

File tree

5 files changed

+661
-522
lines changed

5 files changed

+661
-522
lines changed

Assets/FbxExporters/Editor/FbxExportSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ public static string[] DCCVendorLocations
434434
public bool launchAfterInstallation = true;
435435
public bool HideSendToUnityMenu = true;
436436
public int ExportFormatSelection;
437+
public bool BakeAnimation = true;
437438

438439
public string IntegrationSavePath;
439440

@@ -471,6 +472,7 @@ protected override void LoadDefaults()
471472
IntegrationSavePath = DefaultIntegrationSavePath;
472473
dccOptionPaths = null;
473474
dccOptionNames = null;
475+
BakeAnimation = true;
474476
}
475477

476478
/// <summary>

Assets/FbxExporters/Editor/FbxExporter.cs

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

1447+
/// <summary>
1448+
/// Return set of sample times to cover all keys on animation curves
1449+
/// </summary>
1450+
public static HashSet<float> GetSampleTimes(AnimationCurve[] animCurves, double sampleRate)
1451+
{
1452+
var keyTimes = new HashSet<float>();
1453+
double fs = 1.0/sampleRate;
1454+
1455+
double currSample = double.MaxValue, firstTime = double.MaxValue, lastTime = double.MinValue;
1456+
1457+
foreach (var ac in animCurves)
1458+
{
1459+
if (ac==null || ac.length<=0) continue;
1460+
1461+
firstTime = System.Math.Min(firstTime, ac[0].time);
1462+
lastTime = System.Math.Max(lastTime, ac[ac.length-1].time);
1463+
}
1464+
1465+
for (currSample = firstTime; currSample < lastTime; currSample += fs)
1466+
{
1467+
keyTimes.Add((float)currSample);
1468+
}
1469+
1470+
return keyTimes;
1471+
}
1472+
1473+
/// <summary>
1474+
/// Return set of all keys times on animation curves
1475+
/// </summary>
1476+
public static HashSet<float> GetKeyTimes(AnimationCurve[] animCurves)
1477+
{
1478+
var keyTimes = new HashSet<float>();
1479+
1480+
foreach (var ac in animCurves)
1481+
{
1482+
if (ac!=null) foreach(var key in ac.keys) { keyTimes.Add(key.time); }
1483+
}
1484+
1485+
return keyTimes;
1486+
}
1487+
1488+
/// <summary>
1489+
/// Export animation curve key frames with key tangents
1490+
/// NOTE : This is a work in progress (WIP). We only export the key time and value on
1491+
/// a Cubic curve using the default tangents.
1492+
/// </summary>
1493+
protected void ExportAnimationKeys (AnimationCurve uniAnimCurve, FbxAnimCurve fbxAnimCurve,
1494+
UnityToMayaConvertSceneHelper convertSceneHelper)
1495+
{
1496+
// TODO: complete the mapping between key tangents modes Unity and FBX
1497+
Dictionary<AnimationUtility.TangentMode, List<FbxAnimCurveDef.ETangentMode>> MapUnityKeyTangentModeToFBX =
1498+
new Dictionary<AnimationUtility.TangentMode, List<FbxAnimCurveDef.ETangentMode>>
1499+
{
1500+
//TangeantAuto|GenericTimeIndependent|GenericClampProgressive
1501+
{AnimationUtility.TangentMode.Free, new List<FbxAnimCurveDef.ETangentMode>{FbxAnimCurveDef.ETangentMode.eTangentAuto,FbxAnimCurveDef.ETangentMode.eTangentGenericClampProgressive}},
1502+
1503+
//TangeantAuto|GenericTimeIndependent
1504+
{AnimationUtility.TangentMode.Auto, new List<FbxAnimCurveDef.ETangentMode>{FbxAnimCurveDef.ETangentMode.eTangentAuto,FbxAnimCurveDef.ETangentMode.eTangentGenericTimeIndependent}},
1505+
1506+
//TangeantAuto|GenericTimeIndependent|GenericClampProgressive
1507+
{AnimationUtility.TangentMode.ClampedAuto, new List<FbxAnimCurveDef.ETangentMode>{FbxAnimCurveDef.ETangentMode.eTangentAuto,FbxAnimCurveDef.ETangentMode.eTangentGenericClampProgressive}},
1508+
};
1509+
1510+
// Copy Unity AnimCurve to FBX AnimCurve.
1511+
// NOTE: only cubic keys are supported by the FbxImporter
1512+
using (new FbxAnimCurveModifyHelper(new List<FbxAnimCurve>{fbxAnimCurve}))
1513+
{
1514+
for (int keyIndex = 0; keyIndex < uniAnimCurve.length; ++keyIndex)
1515+
{
1516+
var uniKeyFrame = uniAnimCurve [keyIndex];
1517+
var fbxTime = FbxTime.FromSecondDouble (uniKeyFrame.time);
1518+
1519+
int fbxKeyIndex = fbxAnimCurve.KeyAdd (fbxTime);
1520+
1521+
fbxAnimCurve.KeySet (fbxKeyIndex,
1522+
fbxTime,
1523+
convertSceneHelper.Convert(uniKeyFrame.value)
1524+
);
1525+
1526+
// configure tangents
1527+
var lTangent = AnimationUtility.GetKeyLeftTangentMode(uniAnimCurve, keyIndex);
1528+
var rTangent = AnimationUtility.GetKeyRightTangentMode(uniAnimCurve, keyIndex);
1529+
1530+
if (!(MapUnityKeyTangentModeToFBX.ContainsKey(lTangent) && MapUnityKeyTangentModeToFBX.ContainsKey(rTangent)))
1531+
{
1532+
Debug.LogWarning(string.Format("key[{0}] missing tangent mapping ({1},{2})", keyIndex, lTangent.ToString(), rTangent.ToString()));
1533+
continue;
1534+
}
1535+
1536+
// TODO : handle broken tangents
1537+
1538+
// TODO : set key tangents
1539+
}
1540+
}
1541+
}
1542+
1543+
/// <summary>
1544+
/// Export animation curve key samples
1545+
/// </summary>
1546+
protected void ExportAnimationSamples (AnimationCurve uniAnimCurve, FbxAnimCurve fbxAnimCurve,
1547+
double sampleRate,
1548+
UnityToMayaConvertSceneHelper convertSceneHelper)
1549+
{
1550+
1551+
using (new FbxAnimCurveModifyHelper(new List<FbxAnimCurve>{fbxAnimCurve}))
1552+
{
1553+
foreach (var currSampleTime in GetSampleTimes(new AnimationCurve[]{uniAnimCurve}, sampleRate))
1554+
{
1555+
float currSampleValue = uniAnimCurve.Evaluate((float)currSampleTime);
1556+
1557+
var fbxTime = FbxTime.FromSecondDouble (currSampleTime);
1558+
1559+
int fbxKeyIndex = fbxAnimCurve.KeyAdd (fbxTime);
1560+
1561+
fbxAnimCurve.KeySet (fbxKeyIndex,
1562+
fbxTime,
1563+
convertSceneHelper.Convert(currSampleValue)
1564+
);
1565+
}
1566+
}
1567+
}
1568+
14471569
/// <summary>
14481570
/// Export an AnimationCurve.
14491571
/// NOTE: This is not used for rotations, because we need to convert from
14501572
/// quaternion to euler and various other stuff.
14511573
/// </summary>
14521574
protected void ExportAnimationCurve (UnityEngine.Object uniObj,
14531575
AnimationCurve uniAnimCurve,
1576+
float frameRate,
14541577
string uniPropertyName,
14551578
FbxScene fbxScene,
14561579
FbxAnimLayer fbxAnimLayer)
@@ -1477,6 +1600,7 @@ protected void ExportAnimationCurve (UnityEngine.Object uniObj,
14771600
Debug.LogError(string.Format("no fbx node for {0}", unityGo.ToString()));
14781601
return;
14791602
}
1603+
14801604
// map unity property name to fbx property
14811605
var fbxProperty = fbxNode.FindProperty(fbxPropertyChannelPair.Property, false);
14821606
if (!fbxProperty.IsValid())
@@ -1496,23 +1620,24 @@ protected void ExportAnimationCurve (UnityEngine.Object uniObj,
14961620
// Create the AnimCurve on the channel
14971621
FbxAnimCurve fbxAnimCurve = fbxProperty.GetCurve (fbxAnimLayer, fbxPropertyChannelPair.Channel, true);
14981622

1499-
var transformBindings = new UnityToMayaConvertSceneHelper (uniPropertyName);
1623+
// create a convert scene helper so that we can convert from Unity to Maya
1624+
// AxisSystem (LeftHanded to RightHanded) and FBX's default units
1625+
// (Meters to Centimetres)
1626+
var convertSceneHelper = new UnityToMayaConvertSceneHelper (uniPropertyName);
15001627

1501-
// copy Unity AnimCurve to FBX AnimCurve.
1502-
fbxAnimCurve.KeyModifyBegin ();
1503-
1504-
for (int keyIndex = 0, n = uniAnimCurve.length; keyIndex < n; ++keyIndex) {
1505-
var uniKeyFrame = uniAnimCurve [keyIndex];
1506-
var fbxTime = FbxTime.FromSecondDouble (uniKeyFrame.time);
1507-
1508-
keyIndex = fbxAnimCurve.KeyAdd (fbxTime);
1509-
fbxAnimCurve.KeySet (keyIndex, fbxTime, transformBindings.Convert(uniKeyFrame.value));
1628+
// TODO: we'll resample the curve so we don't have to
1629+
// configure tangents
1630+
if (ModelExporter.ExportSettings.BakeAnimation)
1631+
{
1632+
ExportAnimationSamples(uniAnimCurve, fbxAnimCurve, frameRate, convertSceneHelper);
1633+
}
1634+
else
1635+
{
1636+
ExportAnimationKeys(uniAnimCurve, fbxAnimCurve, convertSceneHelper);
15101637
}
1511-
1512-
fbxAnimCurve.KeyModifyEnd();
15131638
}
15141639

1515-
class UnityToMayaConvertSceneHelper
1640+
public class UnityToMayaConvertSceneHelper
15161641
{
15171642
bool convertDistance = false;
15181643
bool convertLtoR = false;
@@ -1653,11 +1778,39 @@ public static bool TryGetValue(string uniPropertyName, out FbxPropertyChannelPai
16531778
}
16541779
}
16551780

1781+
/// <summary>
1782+
/// Exporting rotations is more complicated. We need to convert
1783+
/// from quaternion to euler. We use this class to help.
1784+
/// </summary>
1785+
class FbxAnimCurveModifyHelper : System.IDisposable
1786+
{
1787+
public List<FbxAnimCurve> Curves { get ; private set; }
1788+
1789+
public FbxAnimCurveModifyHelper(List<FbxAnimCurve> list)
1790+
{
1791+
Curves = list;
1792+
1793+
foreach (var curve in Curves)
1794+
curve.KeyModifyBegin();
1795+
}
1796+
1797+
~FbxAnimCurveModifyHelper() {
1798+
Dispose();
1799+
}
1800+
1801+
public void Dispose()
1802+
{
1803+
foreach (var curve in Curves)
1804+
curve.KeyModifyEnd();
1805+
}
1806+
}
1807+
16561808
/// <summary>
16571809
/// Exporting rotations is more complicated. We need to convert
16581810
/// from quaternion to euler. We use this class to help.
16591811
/// </summary>
16601812
class QuaternionCurve {
1813+
public double sampleRate;
16611814
public AnimationCurve x;
16621815
public AnimationCurve y;
16631816
public AnimationCurve z;
@@ -1718,11 +1871,11 @@ Key [] ComputeKeys(UnityEngine.Quaternion restRotation, FbxNode node) {
17181871
var lclQuaternion = new FbxQuaternion(restRotation.x, restRotation.y, restRotation.z, restRotation.w);
17191872

17201873
// Find when we have keys set.
1721-
var keyTimes = new HashSet<float>();
1722-
if (x != null) { foreach(var key in x.keys) { keyTimes.Add(key.time); } }
1723-
if (y != null) { foreach(var key in y.keys) { keyTimes.Add(key.time); } }
1724-
if (z != null) { foreach(var key in z.keys) { keyTimes.Add(key.time); } }
1725-
if (w != null) { foreach(var key in w.keys) { keyTimes.Add(key.time); } }
1874+
var animCurves = new AnimationCurve[]{x,y,z,w};
1875+
var keyTimes =
1876+
(FbxExporters.Editor.ModelExporter.ExportSettings.BakeAnimation)
1877+
? ModelExporter.GetSampleTimes(animCurves, sampleRate)
1878+
: ModelExporter.GetKeyTimes(animCurves);
17261879

17271880
// Convert to the Key type.
17281881
var keys = new Key[keyTimes.Count];
@@ -1772,27 +1925,21 @@ public void Animate(Transform unityTransform, FbxNode fbxNode, FbxAnimLayer fbxA
17721925
var fbxAnimCurveZ = fbxNode.LclRotation.GetCurve(fbxAnimLayer, Globals.FBXSDK_CURVENODE_COMPONENT_Z, true);
17731926

17741927
/* set the keys */
1775-
fbxAnimCurveX.KeyModifyBegin();
1776-
fbxAnimCurveY.KeyModifyBegin();
1777-
fbxAnimCurveZ.KeyModifyBegin();
1928+
using (new FbxAnimCurveModifyHelper(new List<FbxAnimCurve>{fbxAnimCurveX,fbxAnimCurveY,fbxAnimCurveZ}))
1929+
{
1930+
foreach (var key in ComputeKeys(unityTransform.localRotation, fbxNode)) {
17781931

1779-
var keys = ComputeKeys(unityTransform.localRotation, fbxNode);
1780-
for(int i = 0, n = keys.Length; i < n; ++i) {
1781-
var key = keys[i];
1782-
fbxAnimCurveX.KeyAdd(key.time);
1783-
fbxAnimCurveX.KeySet(i, key.time, (float)key.euler.X);
1932+
int i = fbxAnimCurveX.KeyAdd(key.time);
1933+
fbxAnimCurveX.KeySet(i, key.time, (float)key.euler.X);
17841934

1785-
fbxAnimCurveY.KeyAdd(key.time);
1786-
fbxAnimCurveY.KeySet(i, key.time, (float)key.euler.Y);
1935+
i = fbxAnimCurveY.KeyAdd(key.time);
1936+
fbxAnimCurveY.KeySet(i, key.time, (float)key.euler.Y);
17871937

1788-
fbxAnimCurveZ.KeyAdd(key.time);
1789-
fbxAnimCurveZ.KeySet(i, key.time, (float)key.euler.Z);
1938+
i = fbxAnimCurveZ.KeyAdd(key.time);
1939+
fbxAnimCurveZ.KeySet(i, key.time, (float)key.euler.Z);
1940+
}
17901941
}
17911942

1792-
fbxAnimCurveZ.KeyModifyEnd();
1793-
fbxAnimCurveY.KeyModifyEnd();
1794-
fbxAnimCurveX.KeyModifyEnd();
1795-
17961943
// Uni-35616 unroll curves to preserve continuous rotations
17971944
var fbxCurveNode = fbxNode.LclRotation.GetCurveNode(fbxAnimLayer, false /*should already exist*/);
17981945

@@ -1835,7 +1982,8 @@ protected void ExportAnimationClip (AnimationClip uniAnimClip, GameObject uniRoo
18351982
if (timeMode == FbxTime.EMode.eCustom) {
18361983
timeMode = FbxTime.EMode.eFrames30;
18371984
}
1838-
FbxTime.SetGlobalTimeMode (timeMode);
1985+
1986+
fbxScene.GetGlobalSettings ().SetTimeMode (timeMode);
18391987

18401988
// set time correctly
18411989
var fbxStartTime = FbxTime.FromSecondDouble (0);
@@ -1865,8 +2013,10 @@ protected void ExportAnimationClip (AnimationClip uniAnimClip, GameObject uniRoo
18652013
if (index == -1)
18662014
{
18672015
/* simple property (e.g. intensity), export right away */
1868-
ExportAnimationCurve (uniObj, uniAnimCurve, uniCurveBinding.propertyName,
1869-
fbxScene, fbxAnimLayer);
2016+
ExportAnimationCurve (uniObj, uniAnimCurve, uniAnimClip.frameRate,
2017+
uniCurveBinding.propertyName,
2018+
fbxScene,
2019+
fbxAnimLayer);
18702020
} else {
18712021
/* Rotation property; save it to convert quaternion -> euler later. */
18722022

@@ -1875,7 +2025,7 @@ protected void ExportAnimationClip (AnimationClip uniAnimClip, GameObject uniRoo
18752025

18762026
QuaternionCurve quat;
18772027
if (!quaternions.TryGetValue (uniGO, out quat)) {
1878-
quat = new QuaternionCurve ();
2028+
quat = new QuaternionCurve {sampleRate = uniAnimClip.frameRate};
18792029
quaternions.Add (uniGO, quat);
18802030
}
18812031
quat.SetCurve (index, uniAnimCurve);

0 commit comments

Comments
 (0)