/// <summary> /// Exports all animation /// </summary> private void ExportAnimationClip(AnimationClip unityAnimClip, GameObject unityRoot, FbxScene fbxScene) { if (unityAnimClip == null) { return; } // setup anim stack FbxAnimStack fbxAnimStack = FbxAnimStack.Create(fbxScene, unityAnimClip.name); fbxAnimStack.Description.Set("Animation Take: " + unityAnimClip.name); // add one mandatory animation layer FbxAnimLayer fbxAnimLayer = FbxAnimLayer.Create(fbxScene, "Animation Base Layer"); fbxAnimStack.AddMember(fbxAnimLayer); // Set up the FPS so our frame-relative math later works out // Custom frame rate isn't really supported in FBX SDK (there's // a bug), so try hard to find the nearest time mode. FbxTime.EMode timeMode = FbxTime.EMode.eCustom; double precision = 1e-6; while (timeMode == FbxTime.EMode.eCustom && precision < 1000) { timeMode = FbxTime.ConvertFrameRateToTimeMode(unityAnimClip.frameRate, precision); precision *= 10; } if (timeMode == FbxTime.EMode.eCustom) { timeMode = FbxTime.EMode.eFrames30; } FbxTime.SetGlobalTimeMode(timeMode); // set time correctly var fbxStartTime = FbxTime.FromSecondDouble(0); var fbxStopTime = FbxTime.FromSecondDouble(unityAnimClip.length); fbxAnimStack.SetLocalTimeSpan(new FbxTimeSpan(fbxStartTime, fbxStopTime)); foreach (EditorCurveBinding unityCurveBinding in AnimationUtility.GetCurveBindings(unityAnimClip)) { Object unityObj = AnimationUtility.GetAnimatedObject(unityRoot, unityCurveBinding); if (!unityObj) { continue; } AnimationCurve unityAnimCurve = AnimationUtility.GetEditorCurve(unityAnimClip, unityCurveBinding); if (unityAnimCurve == null) { continue; } ExportAnimCurve(unityObj, unityAnimCurve, unityCurveBinding.propertyName, fbxAnimLayer); } }
protected FbxAnimStack CreateAnimStack(FbxScene scene) { FbxAnimStack fbxAnimStack = FbxAnimStack.Create(scene, "animClip"); fbxAnimStack.Description.Set("Animation Take"); FbxTime.EMode timeMode = FbxTime.EMode.eFrames30; scene.GetGlobalSettings().SetTimeMode(timeMode); // set time correctly var fbxStartTime = FbxTime.FromSecondDouble(0); var fbxStopTime = FbxTime.FromSecondDouble(25); fbxAnimStack.SetLocalTimeSpan(new FbxTimeSpan(fbxStartTime, fbxStopTime)); return(fbxAnimStack); }
/// <summary> /// Export an AnimationClip as a single take /// </summary> protected void ExportAnimationClip(AnimationClip unityAnimClip, GameObject unityRoot, FbxScene fbxScene) { if (Verbose) { Debug.Log(string.Format("exporting clip {1} for {0}", unityRoot.name, unityAnimClip.name)); } // setup anim stack FbxAnimStack fbxAnimStack = FbxAnimStack.Create(fbxScene, unityAnimClip.name); fbxAnimStack.Description.Set("Animation Take: " + unityAnimClip.name); // add one mandatory animation layer FbxAnimLayer fbxAnimLayer = FbxAnimLayer.Create(fbxScene, "Animation Base Layer"); fbxAnimStack.AddMember(fbxAnimLayer); // Set up the FPS so our frame-relative math later works out // Custom frame rate isn't really supported in FBX SDK (there's // a bug), so try hard to find the nearest time mode. FbxTime.EMode timeMode = FbxTime.EMode.eCustom; double precision = 1e-6; while (timeMode == FbxTime.EMode.eCustom && precision < 1000) { timeMode = FbxTime.ConvertFrameRateToTimeMode(unityAnimClip.frameRate, precision); precision *= 10; } if (timeMode == FbxTime.EMode.eCustom) { timeMode = FbxTime.EMode.eFrames30; } FbxTime.SetGlobalTimeMode(timeMode); // set time correctly var fbxStartTime = FbxTime.FromSecondDouble(0); var fbxStopTime = FbxTime.FromSecondDouble(unityAnimClip.length); fbxAnimStack.SetLocalTimeSpan(new FbxTimeSpan(fbxStartTime, fbxStopTime)); /* The major difficulty: Unity uses quaternions for rotation * (which is how it should be) but FBX uses euler angles. So we * need to gather up the list of transform curves per object. */ var quaternions = new Dictionary <UnityEngine.GameObject, QuaternionCurve> (); foreach (EditorCurveBinding unityCurveBinding in AnimationUtility.GetCurveBindings(unityAnimClip)) { Object unityObj = AnimationUtility.GetAnimatedObject(unityRoot, unityCurveBinding); if (!unityObj) { continue; } AnimationCurve unityAnimCurve = AnimationUtility.GetEditorCurve(unityAnimClip, unityCurveBinding); if (unityAnimCurve == null) { continue; } int index = QuaternionCurve.GetQuaternionIndex(unityCurveBinding.propertyName); if (index == -1) { if (Verbose) { Debug.Log(string.Format("export binding {1} for {0}", unityCurveBinding.propertyName, unityObj.ToString())); } /* Some normal property (e.g. translation), export right away */ ExportAnimCurve(unityObj, unityAnimCurve, unityCurveBinding.propertyName, fbxScene, fbxAnimLayer); } else { /* Rotation property; save it to convert quaternion -> euler later. */ var unityGo = GetGameObject(unityObj); if (!unityGo) { continue; } QuaternionCurve quat; if (!quaternions.TryGetValue(unityGo, out quat)) { quat = new QuaternionCurve(); quaternions.Add(unityGo, quat); } quat.SetCurve(index, unityAnimCurve); } } /* now export all the quaternion curves */ foreach (var kvp in quaternions) { var unityGo = kvp.Key; var quat = kvp.Value; FbxNode fbxNode; if (!MapUnityObjectToFbxNode.TryGetValue(unityGo, out fbxNode)) { Debug.LogError(string.Format("no fbxnode found for '0'", unityGo.name)); continue; } quat.Animate(unityGo.transform, fbxNode, fbxAnimLayer, Verbose); } }
void ExportAnimSequenceToFbx(AnimSequence AnimSeq, ref List <FbxNode> BoneNodes, FbxAnimLayer InAnimLayer, float AnimStartOffset, float AnimEndOffset, float AnimPlayRate, float StartTime) { if (AnimSeq.SequenceLength == 0) { return; } FbxTime ExportedStartTime = new FbxTime(); FbxTime ExportedStopTime = new FbxTime(); if (IsNearlyEqual(AnimSeq.FrameRate, DEFAULT_SAMPLERATE, 1.0f)) { FbxTime.SetGlobalTimeMode(FbxTime.EMode.eFrames30); //FbxTime.SetGlobalTimeMode(FbxTime.EMode.eFrames30); } else { FbxTime.SetGlobalTimeMode(FbxTime.EMode.eCustom, AnimSeq.FrameRate); //ExportedStopTime.SetGlobalTimeMode(FbxTime::eCustom, FrameRate); } ExportedStartTime.SetSecondDouble(0.0f); ExportedStopTime.SetSecondDouble(AnimSeq.SequenceLength); FbxTimeSpan ExportedTimeSpan = new FbxTimeSpan(); ExportedTimeSpan.Set(ExportedStartTime, ExportedStopTime); AnimStack.SetLocalTimeSpan(ExportedTimeSpan); // Add the animation data to the bone nodes for (int BoneIndex = 0; BoneIndex < BoneNodes.Count; ++BoneIndex) { FbxNode CurrentBoneNode = BoneNodes[BoneIndex]; // Create the AnimCurves int NumberOfCurves = 9; FbxAnimCurve[] Curves = new FbxAnimCurve[NumberOfCurves]; Curves[0] = CurrentBoneNode.LclTranslation.GetCurve(InAnimLayer, "X", true); Curves[1] = CurrentBoneNode.LclTranslation.GetCurve(InAnimLayer, "Y", true); Curves[2] = CurrentBoneNode.LclTranslation.GetCurve(InAnimLayer, "Z", true); Curves[3] = CurrentBoneNode.LclRotation.GetCurve(InAnimLayer, "X", true); Curves[4] = CurrentBoneNode.LclRotation.GetCurve(InAnimLayer, "Y", true); Curves[5] = CurrentBoneNode.LclRotation.GetCurve(InAnimLayer, "Z", true); Curves[6] = CurrentBoneNode.LclScaling.GetCurve(InAnimLayer, "X", true); Curves[7] = CurrentBoneNode.LclScaling.GetCurve(InAnimLayer, "Y", true); Curves[8] = CurrentBoneNode.LclScaling.GetCurve(InAnimLayer, "Z", true); float AnimTime = AnimStartOffset; float AnimEndTime = (AnimSeq.SequenceLength - AnimEndOffset); // Subtracts 1 because NumFrames includes an initial pose for 0.0 second float TimePerKey = AnimSeq.StepFrame; float AnimTimeIncrement = TimePerKey * AnimPlayRate; FbxTime ExportTime = new FbxTime(); ExportTime.SetSecondDouble(StartTime); FbxTime ExportTimeIncrement = new FbxTime(); ExportTimeIncrement.SetSecondDouble(TimePerKey); //int BoneTreeIndex = Skeleton.GetSkeletonBoneIndexFromMeshBoneIndex(SkelMesh, BoneIndex); //int BoneTrackIndex = Skeleton.GetAnimationTrackIndex(BoneTreeIndex, AnimSeq, true); //if (BoneTrackIndex == INDEX_NONE) //{ // // If this sequence does not have a track for the current bone, then skip it // continue; //} foreach (FbxAnimCurve Curve in Curves) { Curve.KeyModifyBegin(); } bool bLastKey = false; int FrameIndex = 0; foreach (var v in AnimSeq.animationFames) { int lKeyIndex; FrameIndex++; if (FrameIndex == AnimSeq.animationFames.Count) { bLastKey = true; } BoneTransformInfo BoneAtom = v[BoneIndex].BoneTransform; FbxDouble3 Translation = FbxDataConverter.ConvertToFbxPos(BoneAtom.position); FbxDouble3 Rotation = FbxDataConverter.ConvertToFbxRot(BoneAtom.rotation); FbxDouble3 Scale = FbxDataConverter.ConvertToFbxScale(BoneAtom.scale); FbxDouble3 [] Vectors = new FbxDouble3[3] { Translation, Rotation, Scale }; // Loop over each curve and channel to set correct values for (int CurveIndex = 0; CurveIndex < 3; ++CurveIndex) { for (int ChannelIndex = 0; ChannelIndex < 3; ++ChannelIndex) { int OffsetCurveIndex = (CurveIndex * 3) + ChannelIndex; lKeyIndex = Curves[OffsetCurveIndex].KeyAdd(ExportTime); Curves[OffsetCurveIndex].KeySetValue(lKeyIndex, (float)Vectors[CurveIndex].getDataValue(ChannelIndex)); Curves[OffsetCurveIndex].KeySetInterpolation(lKeyIndex, bLastKey ? FbxAnimCurveDef.EInterpolationType.eInterpolationConstant : FbxAnimCurveDef.EInterpolationType.eInterpolationCubic); if (bLastKey) { Curves[OffsetCurveIndex].KeySetConstantMode(lKeyIndex, FbxAnimCurveDef.EConstantMode.eConstantStandard); } } } ExportTime = ExportTime.add(ExportTimeIncrement); } while (!bLastKey) { //FTransform BoneAtom; //AnimSeq.GetBoneTransform(BoneAtom, BoneTrackIndex, AnimTime, true); //FbxVector4 Translation = Converter.ConvertToFbxPos(BoneAtom.GetTranslation()); //FbxVector4 Rotation = Converter.ConvertToFbxRot(BoneAtom.GetRotation().Euler()); //FbxVector4 Scale = Converter.ConvertToFbxScale(BoneAtom.GetScale3D()); //FbxVector4 Vectors[3] = { Translation, Rotation, Scale }; //int lKeyIndex; //bLastKey = AnimTime >= AnimEndTime; //// Loop over each curve and channel to set correct values //for (uint CurveIndex = 0; CurveIndex < 3; ++CurveIndex) //{ // for (uint ChannelIndex = 0; ChannelIndex < 3; ++ChannelIndex) // { // uint OffsetCurveIndex = (CurveIndex * 3) + ChannelIndex; // lKeyIndex = Curves[OffsetCurveIndex].KeyAdd(ExportTime); // Curves[OffsetCurveIndex].KeySetValue(lKeyIndex, Vectors[CurveIndex][ChannelIndex]); // Curves[OffsetCurveIndex].KeySetInterpolation(lKeyIndex, bLastKey ? FbxAnimCurveDef::eInterpolationConstant : FbxAnimCurveDef::eInterpolationCubic); // if (bLastKey) // { // Curves[OffsetCurveIndex].KeySetConstantMode(lKeyIndex, FbxAnimCurveDef::eConstantStandard); // } // } //} //ExportTime += ExportTimeIncrement; //AnimTime += AnimTimeIncrement; } foreach (FbxAnimCurve Curve in Curves) { Curve.KeyModifyEnd(); } } }