@@ -1791,53 +1791,88 @@ protected void ExportAnimationClip (AnimationClip uniAnimClip, GameObject uniRoo
1791
1791
*/
1792
1792
var rotations = new Dictionary < GameObject , RotationCurve > ( ) ;
1793
1793
1794
+ var unityCurves = new Dictionary < GameObject , List < UnityCurve > > ( ) ;
1795
+
1794
1796
foreach ( EditorCurveBinding uniCurveBinding in AnimationUtility . GetCurveBindings ( uniAnimClip ) ) {
1795
1797
Object uniObj = AnimationUtility . GetAnimatedObject ( uniRoot , uniCurveBinding ) ;
1796
- if ( ! uniObj ) { continue ; }
1798
+ if ( ! uniObj ) {
1799
+ continue ;
1800
+ }
1797
1801
1798
1802
AnimationCurve uniAnimCurve = AnimationUtility . GetEditorCurve ( uniAnimClip , uniCurveBinding ) ;
1799
- if ( uniAnimCurve == null ) { continue ; }
1800
-
1801
- if ( Verbose )
1802
- {
1803
- Debug . Log ( string . Format ( "Exporting animation curve bound to {0} {1}" ,
1804
- uniCurveBinding . propertyName , uniCurveBinding . path ) ) ;
1803
+ if ( uniAnimCurve == null ) {
1804
+ continue ;
1805
1805
}
1806
1806
1807
1807
var uniGO = GetGameObject ( uniObj ) ;
1808
1808
if ( ! uniGO ) {
1809
1809
continue ;
1810
1810
}
1811
-
1812
- // Do not create the curves if the component is a SkinnedMeshRenderer and if the option in FBX Export settings is toggled on.
1813
- if ( removeAnimationsFromSkinnedMeshRenderer && ( uniGO . GetComponent < SkinnedMeshRenderer > ( ) != null || uniGO . GetComponentInChildren < SkinnedMeshRenderer > ( ) != null ) )
1814
- {
1815
- continue ;
1816
- }
1817
-
1818
- int index = QuaternionCurve . GetQuaternionIndex ( uniCurveBinding . propertyName ) ;
1819
- if ( index >= 0 ) {
1820
- /* Rotation property; save it to convert quaternion -> euler later. */
1821
- RotationCurve rotCurve = GetRotationCurve < QuaternionCurve > ( uniGO , uniAnimClip . frameRate , ref rotations ) ;
1822
- rotCurve . SetCurve ( index , uniAnimCurve ) ;
1823
- continue ;
1824
- }
1825
1811
1826
- index = EulerCurve . GetEulerIndex ( uniCurveBinding . propertyName ) ;
1827
- if ( index >= 0 ) {
1828
- RotationCurve rotCurve = GetRotationCurve < EulerCurve > ( uniGO , uniAnimClip . frameRate , ref rotations ) ;
1829
- rotCurve . SetCurve ( index , uniAnimCurve ) ;
1812
+ if ( unityCurves . ContainsKey ( uniGO ) ) {
1813
+ unityCurves [ uniGO ] . Add ( new UnityCurve ( uniCurveBinding . propertyName , uniAnimCurve ) ) ;
1830
1814
continue ;
1831
1815
}
1816
+ unityCurves . Add ( uniGO , new List < UnityCurve > ( ) { new UnityCurve ( uniCurveBinding . propertyName , uniAnimCurve ) } ) ;
1817
+ }
1818
+
1819
+ // transfer root motion
1820
+ var animSource = ExportOptions . AnimationSource ;
1821
+ var animDest = ExportOptions . AnimationDest ;
1832
1822
1833
- /* simple property (e.g. intensity), export right away */
1834
- ExportAnimationCurve ( uniGO , uniAnimCurve , uniAnimClip . frameRate ,
1835
- uniCurveBinding . propertyName ,
1836
- fbxScene ,
1837
- fbxAnimLayer ) ;
1823
+ // list of source to dest
1824
+ var transformsInHierarchy = new List < Transform > ( ) ;
1825
+ var curr = animDest ;
1826
+ while ( curr != animSource ) {
1827
+ transformsInHierarchy . Add ( curr ) ;
1828
+ curr = curr . parent ;
1838
1829
}
1830
+ transformsInHierarchy . Add ( animSource ) ;
1831
+ transformsInHierarchy . Reverse ( ) ;
1832
+
1833
+ while ( transformsInHierarchy . Count >= 2 ) {
1834
+ var source = transformsInHierarchy [ 0 ] ;
1835
+ transformsInHierarchy . RemoveAt ( 0 ) ;
1836
+ var dest = transformsInHierarchy [ 0 ] ;
1837
+
1838
+ TransferMotion ( source , dest , uniAnimClip . frameRate , ref unityCurves ) ;
1839
+ }
1840
+
1841
+ foreach ( var kvp in unityCurves ) {
1842
+ var uniGO = kvp . Key ;
1843
+ foreach ( var uniCurve in kvp . Value ) {
1844
+ var propertyName = uniCurve . propertyName ;
1845
+ var uniAnimCurve = uniCurve . uniAnimCurve ;
1846
+
1847
+ // Do not create the curves if the component is a SkinnedMeshRenderer and if the option in FBX Export settings is toggled on.
1848
+ if ( removeAnimationsFromSkinnedMeshRenderer && ( uniGO . GetComponent < SkinnedMeshRenderer > ( ) != null || uniGO . GetComponentInChildren < SkinnedMeshRenderer > ( ) != null ) ) {
1849
+ continue ;
1850
+ }
1839
1851
1840
- /* now export all the quaternion curves */
1852
+ int index = QuaternionCurve . GetQuaternionIndex ( propertyName ) ;
1853
+ if ( index >= 0 ) {
1854
+ // Rotation property; save it to convert quaternion -> euler later.
1855
+ RotationCurve rotCurve = GetRotationCurve < QuaternionCurve > ( uniGO , uniAnimClip . frameRate , ref rotations ) ;
1856
+ rotCurve . SetCurve ( index , uniAnimCurve ) ;
1857
+ continue ;
1858
+ }
1859
+
1860
+ index = EulerCurve . GetEulerIndex ( propertyName ) ;
1861
+ if ( index >= 0 ) {
1862
+ RotationCurve rotCurve = GetRotationCurve < EulerCurve > ( uniGO , uniAnimClip . frameRate , ref rotations ) ;
1863
+ rotCurve . SetCurve ( index , uniAnimCurve ) ;
1864
+ continue ;
1865
+ }
1866
+
1867
+ // simple property (e.g. intensity), export right away
1868
+ ExportAnimationCurve ( uniGO , uniAnimCurve , uniAnimClip . frameRate ,
1869
+ propertyName ,
1870
+ fbxScene ,
1871
+ fbxAnimLayer ) ;
1872
+ }
1873
+ }
1874
+
1875
+ // now export all the quaternion curves
1841
1876
foreach ( var kvp in rotations ) {
1842
1877
var unityGo = kvp . Key ;
1843
1878
var rot = kvp . Value ;
@@ -1851,6 +1886,178 @@ protected void ExportAnimationClip (AnimationClip uniAnimClip, GameObject uniRoo
1851
1886
}
1852
1887
}
1853
1888
1889
+
1890
+ private void TransferMotion ( Transform source , Transform dest , float sampleRate , ref Dictionary < GameObject , List < UnityCurve > > unityCurves ) {
1891
+ // get sample times for curves in dest + source
1892
+ // at each sample time, evaluate all 18 anim curves, creating 2 transform matrices
1893
+ // combine the matrices, get the new values, apply to the 9 new anim curves for dest
1894
+ if ( dest . parent != source ) {
1895
+ Debug . LogError ( "dest must be a child of source" ) ;
1896
+ return ;
1897
+ }
1898
+
1899
+ List < UnityCurve > sourceUnityCurves ;
1900
+ if ( ! unityCurves . TryGetValue ( source . gameObject , out sourceUnityCurves ) ) {
1901
+ return ; // nothing to do
1902
+ }
1903
+
1904
+ List < UnityCurve > destUnityCurves ;
1905
+ if ( ! unityCurves . TryGetValue ( dest . gameObject , out destUnityCurves ) ) {
1906
+ destUnityCurves = new List < UnityCurve > ( ) ;
1907
+ }
1908
+
1909
+ List < AnimationCurve > animCurves = new List < AnimationCurve > ( ) ;
1910
+ foreach ( var curve in sourceUnityCurves ) {
1911
+ // TODO: check if curve is anim related
1912
+ animCurves . Add ( curve . uniAnimCurve ) ;
1913
+ }
1914
+ foreach ( var curve in destUnityCurves ) {
1915
+ animCurves . Add ( curve . uniAnimCurve ) ;
1916
+ }
1917
+
1918
+ var sampleTimes = GetSampleTimes ( animCurves . ToArray ( ) , sampleRate ) ;
1919
+ // need to create 9 new UnityCurves, one for each property
1920
+ var posKeyFrames = new Keyframe [ 3 ] [ ] ;
1921
+ var rotKeyFrames = new Keyframe [ 3 ] [ ] ;
1922
+ var scaleKeyFrames = new Keyframe [ 3 ] [ ] ;
1923
+
1924
+ for ( int t = 0 ; t < posKeyFrames . Length ; t ++ ) {
1925
+ posKeyFrames [ t ] = new Keyframe [ sampleTimes . Count ] ;
1926
+ rotKeyFrames [ t ] = new Keyframe [ sampleTimes . Count ] ;
1927
+ scaleKeyFrames [ t ] = new Keyframe [ sampleTimes . Count ] ;
1928
+ }
1929
+
1930
+ int i = 0 ;
1931
+ foreach ( var currSampleTime in sampleTimes )
1932
+ {
1933
+ var sourceLocalMatrix = GetTransformMatrix ( currSampleTime , source , sourceUnityCurves ) ;
1934
+ var destLocalMatrix = GetTransformMatrix ( currSampleTime , dest , destUnityCurves ) ;
1935
+
1936
+ // child * parent
1937
+ var mewLocalMatrix = destLocalMatrix * sourceLocalMatrix ;
1938
+
1939
+ // FBX is transposed relative to Unity: transpose as we convert.
1940
+ FbxMatrix matrix = new FbxMatrix ( ) ;
1941
+ matrix . SetColumn ( 0 , new FbxVector4 ( mewLocalMatrix . GetRow ( 0 ) . x , mewLocalMatrix . GetRow ( 0 ) . y , mewLocalMatrix . GetRow ( 0 ) . z , mewLocalMatrix . GetRow ( 0 ) . w ) ) ;
1942
+ matrix . SetColumn ( 1 , new FbxVector4 ( mewLocalMatrix . GetRow ( 1 ) . x , mewLocalMatrix . GetRow ( 1 ) . y , mewLocalMatrix . GetRow ( 1 ) . z , mewLocalMatrix . GetRow ( 1 ) . w ) ) ;
1943
+ matrix . SetColumn ( 2 , new FbxVector4 ( mewLocalMatrix . GetRow ( 2 ) . x , mewLocalMatrix . GetRow ( 2 ) . y , mewLocalMatrix . GetRow ( 2 ) . z , mewLocalMatrix . GetRow ( 2 ) . w ) ) ;
1944
+ matrix . SetColumn ( 3 , new FbxVector4 ( mewLocalMatrix . GetRow ( 3 ) . x , mewLocalMatrix . GetRow ( 3 ) . y , mewLocalMatrix . GetRow ( 3 ) . z , mewLocalMatrix . GetRow ( 3 ) . w ) ) ;
1945
+
1946
+ // FBX wants translation, rotation (in euler angles) and scale.
1947
+ // We assume there's no real shear, just rounding error.
1948
+ FbxVector4 translation , rotation , shear , scale ;
1949
+ double sign ;
1950
+ matrix . GetElements ( out translation , out rotation , out shear , out scale , out sign ) ;
1951
+
1952
+ for ( int k = 0 ; k < 3 ; k ++ ) {
1953
+ posKeyFrames [ k ] [ i ] = new Keyframe ( currSampleTime , ( float ) translation [ k ] ) ;
1954
+ rotKeyFrames [ k ] [ i ] = new Keyframe ( currSampleTime , ( float ) rotation [ k ] ) ;
1955
+ scaleKeyFrames [ k ] [ i ] = new Keyframe ( currSampleTime , ( float ) scale [ k ] ) ;
1956
+ }
1957
+ i ++ ;
1958
+ }
1959
+
1960
+ // create the new list of unity curves, and add it to dest's curves
1961
+ var newUnityCurves = new List < UnityCurve > ( ) ;
1962
+ string posPropName = "m_LocalPosition." ;
1963
+ string rotPropName = "localEulerAnglesRaw." ;
1964
+ string scalePropName = "m_LocalScale." ;
1965
+ var xyz = new string [ ] { "x" , "y" , "z" } ;
1966
+ for ( int j = 0 ; j < 3 ; j ++ ) {
1967
+ var posUniCurve = new UnityCurve ( posPropName + xyz [ j ] , new AnimationCurve ( posKeyFrames [ j ] ) ) ;
1968
+ newUnityCurves . Add ( posUniCurve ) ;
1969
+
1970
+ var rotUniCurve = new UnityCurve ( rotPropName + xyz [ j ] , new AnimationCurve ( rotKeyFrames [ j ] ) ) ;
1971
+ newUnityCurves . Add ( rotUniCurve ) ;
1972
+
1973
+ var scaleUniCurve = new UnityCurve ( scalePropName + xyz [ j ] , new AnimationCurve ( scaleKeyFrames [ j ] ) ) ;
1974
+ newUnityCurves . Add ( scaleUniCurve ) ;
1975
+ }
1976
+
1977
+ unityCurves . Remove ( source . gameObject ) ;
1978
+ unityCurves [ dest . gameObject ] = newUnityCurves ;
1979
+ }
1980
+
1981
+ private Matrix4x4 GetTransformMatrix ( float currSampleTime , Transform orig , List < UnityCurve > unityCurves ) {
1982
+ var sourcePos = orig . localPosition ;
1983
+ var sourceRot = orig . localRotation ;
1984
+ var sourceScale = orig . localScale ;
1985
+ foreach ( var uniCurve in unityCurves ) {
1986
+ float currSampleValue = uniCurve . uniAnimCurve . Evaluate ( ( float ) currSampleTime ) ;
1987
+ string propName = uniCurve . propertyName ;
1988
+ // try position, scale, quat then euler
1989
+ int temp = QuaternionCurve . GetQuaternionIndex ( propName ) ;
1990
+ if ( temp >= 0 ) {
1991
+ sourceRot [ temp ] = currSampleValue ;
1992
+ continue ;
1993
+ }
1994
+ temp = EulerCurve . GetEulerIndex ( propName ) ;
1995
+ if ( temp >= 0 ) {
1996
+ var euler = sourceRot . eulerAngles ;
1997
+ euler [ temp ] = currSampleValue ;
1998
+ sourceRot . eulerAngles = euler ;
1999
+ continue ;
2000
+ }
2001
+ temp = GetPositionIndex ( propName ) ;
2002
+ if ( temp >= 0 ) {
2003
+ sourcePos [ temp ] = currSampleValue ;
2004
+ continue ;
2005
+ }
2006
+ temp = GetScaleIndex ( propName ) ;
2007
+ if ( temp >= 0 ) {
2008
+ sourceScale [ temp ] = currSampleValue ;
2009
+ }
2010
+ }
2011
+
2012
+ return Matrix4x4 . TRS ( sourcePos , sourceRot , sourceScale ) ;
2013
+ }
2014
+
2015
+ struct UnityCurve {
2016
+ public string propertyName ;
2017
+ public AnimationCurve uniAnimCurve ;
2018
+
2019
+ public UnityCurve ( string propertyName , AnimationCurve uniAnimCurve ) {
2020
+ this . propertyName = propertyName ;
2021
+ this . uniAnimCurve = uniAnimCurve ;
2022
+ }
2023
+ }
2024
+
2025
+ private int GetPositionIndex ( string uniPropertyName ) {
2026
+ System . StringComparison ct = System . StringComparison . CurrentCulture ;
2027
+ bool isEulerComponent = uniPropertyName . StartsWith ( "m_LocalPosition." , ct ) ;
2028
+
2029
+ if ( ! isEulerComponent ) { return - 1 ; }
2030
+
2031
+ switch ( uniPropertyName [ uniPropertyName . Length - 1 ] ) {
2032
+ case 'x' :
2033
+ return 0 ;
2034
+ case 'y' :
2035
+ return 1 ;
2036
+ case 'z' :
2037
+ return 2 ;
2038
+ default :
2039
+ return - 1 ;
2040
+ }
2041
+ }
2042
+
2043
+ private int GetScaleIndex ( string uniPropertyName ) {
2044
+ System . StringComparison ct = System . StringComparison . CurrentCulture ;
2045
+ bool isEulerComponent = uniPropertyName . StartsWith ( "m_LocalScale." , ct ) ;
2046
+
2047
+ if ( ! isEulerComponent ) { return - 1 ; }
2048
+
2049
+ switch ( uniPropertyName [ uniPropertyName . Length - 1 ] ) {
2050
+ case 'x' :
2051
+ return 0 ;
2052
+ case 'y' :
2053
+ return 1 ;
2054
+ case 'z' :
2055
+ return 2 ;
2056
+ default :
2057
+ return - 1 ;
2058
+ }
2059
+ }
2060
+
1854
2061
/// <summary>
1855
2062
/// Gets or creates the rotation curve for GameObject uniGO.
1856
2063
/// </summary>
0 commit comments