Skip to content

Commit 4d9dda4

Browse files
authored
Merge branch 'master' into uni-35744-check-in-fbxsdk
2 parents 0f8f221 + 97f100b commit 4d9dda4

18 files changed

+9747
-145
lines changed

Assets/FbxExporters/Editor/FbxExporter.cs

Lines changed: 176 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,86 @@ private static bool ExportUVs(FbxMesh fbxMesh, MeshInfo mesh, int[] unmergedTria
426426
return k > 0;
427427
}
428428

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+
429509
/// <summary>
430510
/// Takes in a left-handed UnityEngine.Vector3 denoting a normal,
431511
/// returns a right-handed FbxVector4.
@@ -754,6 +834,9 @@ bool ExportMesh (MeshInfo meshInfo, FbxNode fbxNode)
754834
// Set up normals, etc.
755835
ExportComponentAttributes (meshInfo, fbxMesh, unmergedPolygons.ToArray());
756836

837+
// Set up blend shapes.
838+
ExportBlendShapes(meshInfo, fbxMesh, fbxScene, unmergedPolygons.ToArray());
839+
757840
// set the fbxNode containing the mesh
758841
fbxNode.SetNodeAttribute (fbxMesh);
759842
fbxNode.SetShadingMode (FbxNode.EShadingMode.eWireFrame);
@@ -781,31 +864,28 @@ SkinnedMeshRenderer unitySkin
781864
Debug.Log (string.Format ("exporting {0} {1}", "Skin", fbxNode.GetName ()));
782865

783866

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);
792868

793-
// export skin mesh
794869
FbxMesh fbxMesh = null;
795-
if (ExportMesh (meshInfo, fbxNode)) {
796-
fbxMesh = fbxNode.GetMesh ();
870+
if (ExportMesh(meshInfo, fbxNode))
871+
{
872+
fbxMesh = fbxNode.GetMesh();
797873
}
798-
799-
if (fbxMesh == null) {
800-
Debug.LogError ("Could not find mesh");
874+
if (fbxMesh == null)
875+
{
876+
Debug.LogError("Could not find mesh");
801877
return false;
802878
}
803879

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);
806885

807-
// add bind pose
808-
ExportBindPose (unitySkin, fbxNode, fbxScene, skinnedMeshToBonesMap);
886+
// add bind pose
887+
ExportBindPose (unitySkin, fbxNode, fbxScene, skinnedMeshToBonesMap);
888+
}
809889

810890
return true;
811891
}
@@ -1025,8 +1105,17 @@ private bool ExportSkin (SkinnedMeshRenderer skinnedMesh,
10251105
/// </summary>
10261106
private void SetVertexWeights (MeshInfo meshInfo, Dictionary<int, FbxCluster> boneCluster)
10271107
{
1108+
HashSet<int> visitedVertices = new HashSet<int> ();
1109+
10281110
// set the vertex weights for each bone
10291111
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+
10301119
var boneWeights = meshInfo.BoneWeights;
10311120
int[] indices = {
10321121
boneWeights [i].boneIndex0,
@@ -1048,7 +1137,8 @@ boneWeights [i].weight3
10481137
if (!boneCluster.ContainsKey (indices [j])) {
10491138
continue;
10501139
}
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]);
10521142
}
10531143
}
10541144
}
@@ -1127,6 +1217,30 @@ public static FbxVector4 ConvertQuaternionToXYZEuler (FbxQuaternion quat)
11271217
return new FbxVector4 (vector4.X, -vector4.Y, -vector4.Z, vector4.W);
11281218
}
11291219

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+
11301244
// get a fbxNode's global default position.
11311245
protected bool ExportTransform (UnityEngine.Transform unityTransform, FbxNode fbxNode, Vector3 newCenter, TransformExportType exportType)
11321246
{
@@ -1527,6 +1641,12 @@ public static bool TryGetValue(string uniPropertyName, out FbxPropertyChannelPai
15271641
return true;
15281642
}
15291643

1644+
if (uniPropertyName.StartsWith("field of view", ct))
1645+
{
1646+
prop = new FbxPropertyChannelPair("FieldOfView", null);
1647+
return true;
1648+
}
1649+
15301650
prop = new FbxPropertyChannelPair ();
15311651
return false;
15321652
}
@@ -1586,8 +1706,9 @@ Key [] ComputeKeys(UnityEngine.Quaternion restRotation, FbxNode node) {
15861706
var fbxPreRotationEuler = node.GetRotationActive()
15871707
? node.GetPreRotation(FbxNode.EPivotSet.eSourcePivot)
15881708
: 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);
15911712
fbxPreRotationInverse.Inverse();
15921713

15931714
// If we're only animating along certain coords for some
@@ -1616,17 +1737,23 @@ Key [] ComputeKeys(UnityEngine.Quaternion restRotation, FbxNode node) {
16161737
(z == null) ? lclQuaternion[2] : z.Evaluate(seconds),
16171738
(w == null) ? lclQuaternion[3] : w.Evaluate(seconds));
16181739

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+
16191746
// Cancel out the pre-rotation. Order matters. FBX reads left-to-right.
16201747
// When we run animation we will apply:
16211748
// pre-rotation
16221749
// then pre-rotation inverse
16231750
// then animation.
1624-
var fbxQuat = fbxPreRotationInverse * fbxFinalAnimation;
1751+
var fbxFinalQuat = fbxPreRotationInverse * fbxFinalAnimation;
16251752

16261753
// Store the key so we can sort them later.
16271754
Key key;
16281755
key.time = FbxTime.FromSecondDouble(seconds);
1629-
key.euler = ModelExporter.ConvertQuaternionToXYZEuler(fbxQuat);
1756+
key.euler = ModelExporter.QuaternionToEuler (fbxFinalQuat);;
16301757
keys[i++] = key;
16311758
}
16321759

@@ -1899,6 +2026,8 @@ protected int ExportTransformHierarchy(
18992026
/// </summary>
19002027
protected bool ExportComponents(FbxScene fbxScene)
19012028
{
2029+
var animationNodes = new HashSet<GameObject> ();
2030+
19022031
int numObjectsExported = 0;
19032032
int objectCount = MapUnityObjectToFbxNode.Count;
19042033
foreach (KeyValuePair<GameObject, FbxNode> entry in MapUnityObjectToFbxNode) {
@@ -1932,13 +2061,35 @@ protected bool ExportComponents(FbxScene fbxScene)
19322061
ExportLight (unityGo, fbxScene, fbxNode);
19332062
}
19342063

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+
}
19372070

2071+
// export all GameObjects that have animation
2072+
if (animationNodes.Count > 0) {
2073+
foreach (var go in animationNodes) {
2074+
ExportAnimation (go, fbxScene);
2075+
}
19382076
}
2077+
19392078
return true;
19402079
}
19412080

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+
19422093
/// <summary>
19432094
/// A count of how many GameObjects we are exporting, to have a rough
19442095
/// idea of how long creating the scene will take.

0 commit comments

Comments
 (0)