@@ -1444,13 +1444,136 @@ protected bool ExportLight (GameObject unityGo, FbxScene fbxScene, FbxNode fbxNo
1444
1444
return true ;
1445
1445
}
1446
1446
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
+
1447
1569
/// <summary>
1448
1570
/// Export an AnimationCurve.
1449
1571
/// NOTE: This is not used for rotations, because we need to convert from
1450
1572
/// quaternion to euler and various other stuff.
1451
1573
/// </summary>
1452
1574
protected void ExportAnimationCurve ( UnityEngine . Object uniObj ,
1453
1575
AnimationCurve uniAnimCurve ,
1576
+ float frameRate ,
1454
1577
string uniPropertyName ,
1455
1578
FbxScene fbxScene ,
1456
1579
FbxAnimLayer fbxAnimLayer )
@@ -1477,6 +1600,7 @@ protected void ExportAnimationCurve (UnityEngine.Object uniObj,
1477
1600
Debug . LogError ( string . Format ( "no fbx node for {0}" , unityGo . ToString ( ) ) ) ;
1478
1601
return ;
1479
1602
}
1603
+
1480
1604
// map unity property name to fbx property
1481
1605
var fbxProperty = fbxNode . FindProperty ( fbxPropertyChannelPair . Property , false ) ;
1482
1606
if ( ! fbxProperty . IsValid ( ) )
@@ -1496,23 +1620,24 @@ protected void ExportAnimationCurve (UnityEngine.Object uniObj,
1496
1620
// Create the AnimCurve on the channel
1497
1621
FbxAnimCurve fbxAnimCurve = fbxProperty . GetCurve ( fbxAnimLayer , fbxPropertyChannelPair . Channel , true ) ;
1498
1622
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 ) ;
1500
1627
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 ) ;
1510
1637
}
1511
-
1512
- fbxAnimCurve . KeyModifyEnd ( ) ;
1513
1638
}
1514
1639
1515
- class UnityToMayaConvertSceneHelper
1640
+ public class UnityToMayaConvertSceneHelper
1516
1641
{
1517
1642
bool convertDistance = false ;
1518
1643
bool convertLtoR = false ;
@@ -1653,11 +1778,39 @@ public static bool TryGetValue(string uniPropertyName, out FbxPropertyChannelPai
1653
1778
}
1654
1779
}
1655
1780
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
+
1656
1808
/// <summary>
1657
1809
/// Exporting rotations is more complicated. We need to convert
1658
1810
/// from quaternion to euler. We use this class to help.
1659
1811
/// </summary>
1660
1812
class QuaternionCurve {
1813
+ public double sampleRate ;
1661
1814
public AnimationCurve x ;
1662
1815
public AnimationCurve y ;
1663
1816
public AnimationCurve z ;
@@ -1718,11 +1871,11 @@ Key [] ComputeKeys(UnityEngine.Quaternion restRotation, FbxNode node) {
1718
1871
var lclQuaternion = new FbxQuaternion ( restRotation . x , restRotation . y , restRotation . z , restRotation . w ) ;
1719
1872
1720
1873
// 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 ) ;
1726
1879
1727
1880
// Convert to the Key type.
1728
1881
var keys = new Key [ keyTimes . Count ] ;
@@ -1772,27 +1925,21 @@ public void Animate(Transform unityTransform, FbxNode fbxNode, FbxAnimLayer fbxA
1772
1925
var fbxAnimCurveZ = fbxNode . LclRotation . GetCurve ( fbxAnimLayer , Globals . FBXSDK_CURVENODE_COMPONENT_Z , true ) ;
1773
1926
1774
1927
/* 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 ) ) {
1778
1931
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 ) ;
1784
1934
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 ) ;
1787
1937
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
+ }
1790
1941
}
1791
1942
1792
- fbxAnimCurveZ . KeyModifyEnd ( ) ;
1793
- fbxAnimCurveY . KeyModifyEnd ( ) ;
1794
- fbxAnimCurveX . KeyModifyEnd ( ) ;
1795
-
1796
1943
// Uni-35616 unroll curves to preserve continuous rotations
1797
1944
var fbxCurveNode = fbxNode . LclRotation . GetCurveNode ( fbxAnimLayer , false /*should already exist*/ ) ;
1798
1945
@@ -1835,7 +1982,8 @@ protected void ExportAnimationClip (AnimationClip uniAnimClip, GameObject uniRoo
1835
1982
if ( timeMode == FbxTime . EMode . eCustom ) {
1836
1983
timeMode = FbxTime . EMode . eFrames30 ;
1837
1984
}
1838
- FbxTime . SetGlobalTimeMode ( timeMode ) ;
1985
+
1986
+ fbxScene . GetGlobalSettings ( ) . SetTimeMode ( timeMode ) ;
1839
1987
1840
1988
// set time correctly
1841
1989
var fbxStartTime = FbxTime . FromSecondDouble ( 0 ) ;
@@ -1865,8 +2013,10 @@ protected void ExportAnimationClip (AnimationClip uniAnimClip, GameObject uniRoo
1865
2013
if ( index == - 1 )
1866
2014
{
1867
2015
/* 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 ) ;
1870
2020
} else {
1871
2021
/* Rotation property; save it to convert quaternion -> euler later. */
1872
2022
@@ -1875,7 +2025,7 @@ protected void ExportAnimationClip (AnimationClip uniAnimClip, GameObject uniRoo
1875
2025
1876
2026
QuaternionCurve quat ;
1877
2027
if ( ! quaternions . TryGetValue ( uniGO , out quat ) ) {
1878
- quat = new QuaternionCurve ( ) ;
2028
+ quat = new QuaternionCurve { sampleRate = uniAnimClip . frameRate } ;
1879
2029
quaternions . Add ( uniGO , quat ) ;
1880
2030
}
1881
2031
quat . SetCurve ( index , uniAnimCurve ) ;
0 commit comments