@@ -426,6 +426,86 @@ private static bool ExportUVs(FbxMesh fbxMesh, MeshInfo mesh, int[] unmergedTria
426
426
return k > 0 ;
427
427
}
428
428
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
+
429
509
/// <summary>
430
510
/// Takes in a left-handed UnityEngine.Vector3 denoting a normal,
431
511
/// returns a right-handed FbxVector4.
@@ -754,6 +834,9 @@ bool ExportMesh (MeshInfo meshInfo, FbxNode fbxNode)
754
834
// Set up normals, etc.
755
835
ExportComponentAttributes ( meshInfo , fbxMesh , unmergedPolygons . ToArray ( ) ) ;
756
836
837
+ // Set up blend shapes.
838
+ ExportBlendShapes ( meshInfo , fbxMesh , fbxScene , unmergedPolygons . ToArray ( ) ) ;
839
+
757
840
// set the fbxNode containing the mesh
758
841
fbxNode . SetNodeAttribute ( fbxMesh ) ;
759
842
fbxNode . SetShadingMode ( FbxNode . EShadingMode . eWireFrame ) ;
@@ -781,31 +864,28 @@ SkinnedMeshRenderer unitySkin
781
864
Debug . Log ( string . Format ( "exporting {0} {1}" , "Skin" , fbxNode . GetName ( ) ) ) ;
782
865
783
866
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
- }
790
-
791
- var meshInfo = new MeshInfo ( unitySkin . sharedMesh , unitySkin . sharedMaterials ) ;
867
+ var meshInfo = new MeshInfo ( unitySkin . sharedMesh , unitySkin . sharedMaterials ) ;
792
868
793
- // export skin mesh
794
869
FbxMesh fbxMesh = null ;
795
- if ( ExportMesh ( meshInfo , fbxNode ) ) {
796
- fbxMesh = fbxNode . GetMesh ( ) ;
870
+ if ( ExportMesh ( meshInfo , fbxNode ) )
871
+ {
872
+ fbxMesh = fbxNode . GetMesh ( ) ;
797
873
}
798
-
799
- if ( fbxMesh == null ) {
800
- Debug . LogError ( "Could not find mesh" ) ;
874
+ if ( fbxMesh == null )
875
+ {
876
+ Debug . LogError ( "Could not find mesh" ) ;
801
877
return false ;
802
878
}
803
879
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 ) ;
806
885
807
- // add bind pose
808
- ExportBindPose ( unitySkin , fbxNode , fbxScene , skinnedMeshToBonesMap ) ;
886
+ // add bind pose
887
+ ExportBindPose ( unitySkin , fbxNode , fbxScene , skinnedMeshToBonesMap ) ;
888
+ }
809
889
810
890
return true ;
811
891
}
@@ -1025,8 +1105,17 @@ private bool ExportSkin (SkinnedMeshRenderer skinnedMesh,
1025
1105
/// </summary>
1026
1106
private void SetVertexWeights ( MeshInfo meshInfo , Dictionary < int , FbxCluster > boneCluster )
1027
1107
{
1108
+ HashSet < int > visitedVertices = new HashSet < int > ( ) ;
1109
+
1028
1110
// set the vertex weights for each bone
1029
1111
for ( int i = 0 ; i < meshInfo . BoneWeights . Length ; i ++ ) {
1112
+ var actualIndex = ControlPointToIndex [ meshInfo . Vertices [ i ] ] ;
1113
+
1114
+ if ( visitedVertices . Contains ( actualIndex ) ) {
1115
+ continue ;
1116
+ }
1117
+ visitedVertices . Add ( actualIndex ) ;
1118
+
1030
1119
var boneWeights = meshInfo . BoneWeights ;
1031
1120
int [ ] indices = {
1032
1121
boneWeights [ i ] . boneIndex0 ,
@@ -1048,7 +1137,8 @@ boneWeights [i].weight3
1048
1137
if ( ! boneCluster . ContainsKey ( indices [ j ] ) ) {
1049
1138
continue ;
1050
1139
}
1051
- boneCluster [ indices [ j ] ] . AddControlPointIndex ( ControlPointToIndex [ meshInfo . Vertices [ i ] ] , weights [ j ] ) ;
1140
+ // add vertex and weighting on vertex to this bone's cluster
1141
+ boneCluster [ indices [ j ] ] . AddControlPointIndex ( actualIndex , weights [ j ] ) ;
1052
1142
}
1053
1143
}
1054
1144
}
@@ -1127,6 +1217,30 @@ public static FbxVector4 ConvertQuaternionToXYZEuler (FbxQuaternion quat)
1127
1217
return new FbxVector4 ( vector4 . X , - vector4 . Y , - vector4 . Z , vector4 . W ) ;
1128
1218
}
1129
1219
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
+
1130
1244
// get a fbxNode's global default position.
1131
1245
protected bool ExportTransform ( UnityEngine . Transform unityTransform , FbxNode fbxNode , Vector3 newCenter , TransformExportType exportType )
1132
1246
{
@@ -1527,6 +1641,12 @@ public static bool TryGetValue(string uniPropertyName, out FbxPropertyChannelPai
1527
1641
return true ;
1528
1642
}
1529
1643
1644
+ if ( uniPropertyName . StartsWith ( "field of view" , ct ) )
1645
+ {
1646
+ prop = new FbxPropertyChannelPair ( "FieldOfView" , null ) ;
1647
+ return true ;
1648
+ }
1649
+
1530
1650
prop = new FbxPropertyChannelPair ( ) ;
1531
1651
return false ;
1532
1652
}
@@ -1586,8 +1706,9 @@ Key [] ComputeKeys(UnityEngine.Quaternion restRotation, FbxNode node) {
1586
1706
var fbxPreRotationEuler = node . GetRotationActive ( )
1587
1707
? node . GetPreRotation ( FbxNode . EPivotSet . eSourcePivot )
1588
1708
: new FbxVector4 ( ) ;
1589
- var fbxPreRotationInverse = new FbxQuaternion ( ) ;
1590
- fbxPreRotationInverse . ComposeSphericalXYZ ( fbxPreRotationEuler ) ;
1709
+
1710
+ // Get the inverse of the prerotation
1711
+ var fbxPreRotationInverse = ModelExporter . EulerToQuaternion ( fbxPreRotationEuler ) ;
1591
1712
fbxPreRotationInverse . Inverse ( ) ;
1592
1713
1593
1714
// If we're only animating along certain coords for some
@@ -1616,17 +1737,23 @@ Key [] ComputeKeys(UnityEngine.Quaternion restRotation, FbxNode node) {
1616
1737
( z == null ) ? lclQuaternion [ 2 ] : z . Evaluate ( seconds ) ,
1617
1738
( w == null ) ? lclQuaternion [ 3 ] : w . Evaluate ( seconds ) ) ;
1618
1739
1740
+ // convert the final animation to righthanded coords
1741
+ var finalEuler = ModelExporter . ConvertQuaternionToXYZEuler ( fbxFinalAnimation ) ;
1742
+
1743
+ // convert it back to a quaternion for multiplication
1744
+ fbxFinalAnimation = ModelExporter . EulerToQuaternion ( finalEuler ) ;
1745
+
1619
1746
// Cancel out the pre-rotation. Order matters. FBX reads left-to-right.
1620
1747
// When we run animation we will apply:
1621
1748
// pre-rotation
1622
1749
// then pre-rotation inverse
1623
1750
// then animation.
1624
- var fbxQuat = fbxPreRotationInverse * fbxFinalAnimation ;
1751
+ var fbxFinalQuat = fbxPreRotationInverse * fbxFinalAnimation ;
1625
1752
1626
1753
// Store the key so we can sort them later.
1627
1754
Key key ;
1628
1755
key . time = FbxTime . FromSecondDouble ( seconds ) ;
1629
- key . euler = ModelExporter . ConvertQuaternionToXYZEuler ( fbxQuat ) ;
1756
+ key . euler = ModelExporter . QuaternionToEuler ( fbxFinalQuat ) ; ;
1630
1757
keys [ i ++ ] = key ;
1631
1758
}
1632
1759
@@ -1899,6 +2026,8 @@ protected int ExportTransformHierarchy(
1899
2026
/// </summary>
1900
2027
protected bool ExportComponents ( FbxScene fbxScene )
1901
2028
{
2029
+ var animationNodes = new HashSet < GameObject > ( ) ;
2030
+
1902
2031
int numObjectsExported = 0 ;
1903
2032
int objectCount = MapUnityObjectToFbxNode . Count ;
1904
2033
foreach ( KeyValuePair < GameObject , FbxNode > entry in MapUnityObjectToFbxNode ) {
@@ -1932,13 +2061,35 @@ protected bool ExportComponents(FbxScene fbxScene)
1932
2061
ExportLight ( unityGo , fbxScene , fbxNode ) ;
1933
2062
}
1934
2063
1935
- // now (try) export animation
1936
- ExportAnimation ( unityGo , fbxScene ) ;
2064
+ // check if this object contains animation, keep track of it
2065
+ // if it does
2066
+ if ( GameObjectHasAnimation ( unityGo ) ) {
2067
+ animationNodes . Add ( unityGo ) ;
2068
+ }
2069
+ }
1937
2070
2071
+ // export all GameObjects that have animation
2072
+ if ( animationNodes . Count > 0 ) {
2073
+ foreach ( var go in animationNodes ) {
2074
+ ExportAnimation ( go , fbxScene ) ;
2075
+ }
1938
2076
}
2077
+
1939
2078
return true ;
1940
2079
}
1941
2080
2081
+ /// <summary>
2082
+ /// Checks if the GameObject has animation.
2083
+ /// </summary>
2084
+ /// <returns><c>true</c>, if object has animation, <c>false</c> otherwise.</returns>
2085
+ /// <param name="go">Go.</param>
2086
+ protected bool GameObjectHasAnimation ( GameObject go ) {
2087
+ return go != null &&
2088
+ go . GetComponent < Animator > ( ) ||
2089
+ go . GetComponent < Animation > ( ) ||
2090
+ go . GetComponent < UnityEngine . Playables . PlayableDirector > ( ) ;
2091
+ }
2092
+
1942
2093
/// <summary>
1943
2094
/// A count of how many GameObjects we are exporting, to have a rough
1944
2095
/// idea of how long creating the scene will take.
0 commit comments