private Key [] ComputeKeys(UnityEngine.Quaternion restRotation, FbxNode node) { // Get the source pivot pre-rotation if any, so we can // remove it from the animation we get from Unity. var fbxPreRotationEuler = node.GetRotationActive() ? node.GetPreRotation(FbxNode.EPivotSet.eSourcePivot) : new FbxVector4(); // Get the inverse of the prerotation var fbxPreRotationInverse = ModelExporter.EulerToQuaternion(fbxPreRotationEuler); fbxPreRotationInverse.Inverse(); // Find when we have keys set. var keyTimes = (UnityEditor.Formats.Fbx.Exporter.ModelExporter.ExportSettings.BakeAnimationProperty) ? ModelExporter.GetSampleTimes(GetCurves(), SampleRate) : ModelExporter.GetKeyTimes(GetCurves()); // Convert to the Key type. var keys = new Key[keyTimes.Count]; int i = 0; foreach (var seconds in keyTimes) { var fbxFinalAnimation = GetConvertedQuaternionRotation(seconds, restRotation); // Cancel out the pre-rotation. Order matters. FBX reads left-to-right. // When we run animation we will apply: // pre-rotation // then pre-rotation inverse // then animation. var fbxFinalQuat = fbxPreRotationInverse * fbxFinalAnimation; // Store the key so we can sort them later. Key key = new Key(); key.time = FbxTime.FromSecondDouble(seconds); key.euler = ModelExporter.QuaternionToEuler(fbxFinalQuat); keys[i++] = key; } // Sort the keys by time System.Array.Sort(keys, (Key a, Key b) => a.time.CompareTo(b.time)); return(keys); }
// compare the hierarchy and transform of two nodes private void CheckSceneHelper(FbxNode node1, FbxNode node2) { if (node1 == null && node2 == null) { return; } Assert.IsNotNull(node1); Assert.IsNotNull(node2); Assert.AreEqual(node1.GetChildCount(), node2.GetChildCount()); // compare the transforms Assert.AreEqual(node1.LclTranslation.Get(), node2.LclTranslation.Get()); Assert.AreEqual(node1.LclRotation.Get(), node2.LclRotation.Get()); Assert.AreEqual(node1.LclScaling.Get(), node2.LclScaling.Get()); Assert.AreEqual(node1.GetPreRotation(FbxNode.EPivotSet.eSourcePivot), node2.GetPreRotation(FbxNode.EPivotSet.eSourcePivot)); Assert.AreEqual(node1.GetPostRotation(FbxNode.EPivotSet.eSourcePivot), node2.GetPostRotation(FbxNode.EPivotSet.eSourcePivot)); Assert.AreEqual(node1.GetRotationPivot(FbxNode.EPivotSet.eSourcePivot), node2.GetRotationPivot(FbxNode.EPivotSet.eSourcePivot)); Assert.AreEqual(node1.GetScalingPivot(FbxNode.EPivotSet.eSourcePivot), node2.GetScalingPivot(FbxNode.EPivotSet.eSourcePivot)); Assert.AreEqual(node1.GetRotationOffset(FbxNode.EPivotSet.eSourcePivot), node2.GetRotationOffset(FbxNode.EPivotSet.eSourcePivot)); Assert.AreEqual(node1.GetScalingOffset(FbxNode.EPivotSet.eSourcePivot), node2.GetScalingOffset(FbxNode.EPivotSet.eSourcePivot)); Assert.AreEqual(node1.GetName(), node2.GetName()); for (int i = 0; i < node1.GetChildCount(); i++) { // recurse through the hierarchy CheckSceneHelper(node1.GetChild(i), node2.GetChild(i)); } }
/// <summary> /// Process transformation data and setup Transform component /// </summary> private void ProcessTransform(FbxNode fbxNode, GameObject unityGo) { // Construct rotation matrices FbxVector4 fbxRotation = new FbxVector4(fbxNode.LclRotation.Get()); FbxAMatrix fbxRotationM = new FbxAMatrix(); fbxRotationM.SetR(fbxRotation); FbxVector4 fbxPreRotation = new FbxVector4(fbxNode.GetPreRotation(FbxNode.EPivotSet.eSourcePivot)); FbxAMatrix fbxPreRotationM = new FbxAMatrix(); fbxPreRotationM.SetR(fbxPreRotation); FbxVector4 fbxPostRotation = new FbxVector4(fbxNode.GetPostRotation(FbxNode.EPivotSet.eSourcePivot)); FbxAMatrix fbxPostRotationM = new FbxAMatrix(); fbxPostRotationM.SetR(fbxPostRotation); // Construct translation matrix FbxAMatrix fbxTranslationM = new FbxAMatrix(); FbxVector4 fbxTranslation = new FbxVector4(fbxNode.LclTranslation.Get()); fbxTranslationM.SetT(fbxTranslation); // Construct scaling matrix FbxAMatrix fbxScalingM = new FbxAMatrix(); FbxVector4 fbxScaling = new FbxVector4(fbxNode.LclScaling.Get()); fbxScalingM.SetS(fbxScaling); // Construct offset and pivot matrices FbxAMatrix fbxRotationOffsetM = new FbxAMatrix(); FbxVector4 fbxRotationOffset = fbxNode.GetRotationOffset(FbxNode.EPivotSet.eSourcePivot); fbxRotationOffsetM.SetT(fbxRotationOffset); FbxAMatrix fbxRotationPivotM = new FbxAMatrix(); FbxVector4 fbxRotationPivot = fbxNode.GetRotationPivot(FbxNode.EPivotSet.eSourcePivot); fbxRotationPivotM.SetT(fbxRotationPivot); FbxAMatrix fbxScalingOffsetM = new FbxAMatrix(); FbxVector4 fbxScalingOffset = fbxNode.GetScalingOffset(FbxNode.EPivotSet.eSourcePivot); fbxScalingOffsetM.SetT(fbxScalingOffset); FbxAMatrix fbxScalingPivotM = new FbxAMatrix(); FbxVector4 fbxScalingPivot = fbxNode.GetScalingPivot(FbxNode.EPivotSet.eSourcePivot); fbxScalingPivotM.SetT(fbxScalingPivot); FbxAMatrix fbxTransform = fbxTranslationM * fbxRotationOffsetM * fbxRotationPivotM * fbxPreRotationM * fbxRotationM * fbxPostRotationM * fbxRotationPivotM.Inverse() * fbxScalingOffsetM * fbxScalingPivotM * fbxScalingM * fbxScalingPivotM.Inverse(); FbxVector4 lclTrs = fbxTransform.GetT(); FbxQuaternion lclRot = fbxTransform.GetQ(); FbxVector4 lclScl = fbxTransform.GetS(); Debug.Log(string.Format("processing {3} Lcl : T({0}) R({1}) S({2})", lclTrs.ToString(), lclRot.ToString(), lclScl.ToString(), fbxNode.GetName())); unityGo.transform.localPosition = new Vector3((float)lclTrs[0], (float)lclTrs[1], (float)lclTrs[2]); unityGo.transform.localRotation = new Quaternion((float)lclRot[0], (float)lclRot[1], (float)lclRot[2], (float)lclRot[3]); unityGo.transform.localScale = new Vector3((float)lclScl[0], (float)lclScl[1], (float)lclScl[2]); }
Key [] ComputeKeys(UnityEngine.Quaternion restRotation, FbxNode node) { // Get the source pivot pre-rotation if any, so we can // remove it from the animation we get from Unity. var fbxPreRotationEuler = node.GetRotationActive() ? node.GetPreRotation(FbxNode.EPivotSet.eSourcePivot) : new FbxVector4(); var fbxPreRotationInverse = new FbxQuaternion(); fbxPreRotationInverse.ComposeSphericalXYZ(fbxPreRotationEuler); fbxPreRotationInverse.Inverse(); // If we're only animating along certain coords for some // reason, we'll need to fill in the other coords with the // rest-pose value. var lclQuaternion = new FbxQuaternion(restRotation.x, restRotation.y, restRotation.z, restRotation.w); // Find when we have keys set. var keyTimes = new HashSet <float>(); if (x != null) { foreach (var key in x.keys) { keyTimes.Add(key.time); } } if (y != null) { foreach (var key in y.keys) { keyTimes.Add(key.time); } } if (z != null) { foreach (var key in z.keys) { keyTimes.Add(key.time); } } if (w != null) { foreach (var key in w.keys) { keyTimes.Add(key.time); } } // Convert to the Key type. var keys = new Key[keyTimes.Count]; int i = 0; foreach (var seconds in keyTimes) { // The final animation, including the effect of pre-rotation. // If we have no curve, assume the node has the correct rotation right now. // We need to evaluate since we might only have keys in one of the axes. var fbxFinalAnimation = new FbxQuaternion( (x == null) ? lclQuaternion[0] : x.Evaluate(seconds), (y == null) ? lclQuaternion[1] : y.Evaluate(seconds), (z == null) ? lclQuaternion[2] : z.Evaluate(seconds), (w == null) ? lclQuaternion[3] : w.Evaluate(seconds)); // Cancel out the pre-rotation. Order matters. FBX reads left-to-right. // When we run animation we will apply: // pre-rotation // then pre-rotation inverse // then animation. var fbxAnimation = fbxPreRotationInverse * fbxFinalAnimation; // Store the key so we can sort them later. Key key; key.time = FbxTime.FromSecondDouble(seconds); key.euler = fbxAnimation.DecomposeSphericalXYZ(); keys[i++] = key; } // Sort the keys by time System.Array.Sort(keys, (Key a, Key b) => a.time.CompareTo(b.time)); return(keys); }