/// <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); } }
public void TestSetCurrentAnimStack() { using (FbxScene newScene = FbxScene.Create(Manager, "")) { FbxAnimStack animStack = FbxAnimStack.Create(Manager, ""); newScene.SetCurrentAnimationStack(animStack); Assert.AreEqual(animStack, newScene.GetCurrentAnimationStack()); // test null Assert.That(() => { newScene.SetCurrentAnimationStack(null); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); // test invalid animStack.Destroy(); Assert.That(() => { newScene.SetCurrentAnimationStack(animStack); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); } }
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); }
public void CreateDocument() { Scene = FbxScene.Create(SdkManager, ""); FbxDocumentInfo SceneInfo = FbxDocumentInfo.Create(SdkManager, "SceneInfo"); SceneInfo.mTitle = new FbxString("Unreal FBX Exporter"); SceneInfo.mSubject = new FbxString("Export FBX meshes from Unreal"); SceneInfo.Original_ApplicationVendor.Set(new FbxString("Epic Games")); SceneInfo.Original_ApplicationName.Set(new FbxString("Unreal Engine")); SceneInfo.Original_ApplicationVersion.Set(new FbxString("4.18")); SceneInfo.LastSaved_ApplicationVendor.Set(new FbxString("Epic Games")); SceneInfo.LastSaved_ApplicationName.Set(new FbxString("Unreal Engine")); SceneInfo.LastSaved_ApplicationVersion.Set(new FbxString("4.18")); FbxAxisSystem.EFrontVector FrontVector = (FbxAxisSystem.EFrontVector)(0 - FbxAxisSystem.EFrontVector.eParityOdd); if (bForceFrontXAxis) { FrontVector = FbxAxisSystem.EFrontVector.eParityEven; } FbxAxisSystem UnrealZUp = new FbxAxisSystem(FbxAxisSystem.EUpVector.eZAxis, FrontVector, FbxAxisSystem.ECoordSystem.eRightHanded); //const FbxAxisSystem UnrealZUp(FbxAxisSystem.EUpVector, FrontVector, FbxAxisSystem::eRightHanded); Scene.GetGlobalSettings().SetAxisSystem(FbxAxisSystem.Max); Scene.GetGlobalSettings().SetOriginalUpAxis(FbxAxisSystem.Max); Scene.GetGlobalSettings().SetSystemUnit(FbxSystemUnit.cm); Scene.SetSceneInfo(SceneInfo); // setup anim stack AnimStack = FbxAnimStack.Create(Scene, "Unreal Take"); //KFbxSet<KTime>(AnimStack.LocalStart, KTIME_ONE_SECOND); AnimStack.Description.Set((new FbxString("Animation Take for Unreal."))); // this take contains one base layer. In fact having at least one layer is mandatory. AnimLayer = FbxAnimLayer.Create(Scene, "Base Layer"); AnimStack.AddMember(AnimLayer); //if(Mathf) }
// tests that should work for any subclass of FbxProperty public static void GenericPropertyTests <T>(T property, FbxObject parent, string propertyName, FbxDataType dataType) where T : FbxProperty { Assert.IsTrue(property.IsValid()); Assert.AreEqual(dataType, property.GetPropertyDataType()); Assert.AreEqual(propertyName, property.GetName()); Assert.AreEqual(propertyName, property.ToString()); Assert.AreEqual(propertyName, property.GetHierarchicalName()); Assert.AreEqual(propertyName, property.GetLabel(true)); property.SetLabel("label"); Assert.AreEqual("label", property.GetLabel()); Assert.AreEqual(parent, property.GetFbxObject()); Assert.AreEqual(property.GetFbxObject(), parent); // test it both ways just in case equals is busted // test the flags using the animatable flag property.ModifyFlag(FbxPropertyFlags.EFlags.eAnimatable, true); Assert.IsTrue(property.GetFlag(FbxPropertyFlags.EFlags.eAnimatable)); Assert.AreNotEqual(0, property.GetFlags() | FbxPropertyFlags.EFlags.eAnimatable); property.SetFlagInheritType(FbxPropertyFlags.EFlags.eAnimatable, FbxPropertyFlags.EInheritType.eInherit); Assert.AreEqual(FbxPropertyFlags.EInheritType.eInherit, property.GetFlagInheritType(FbxPropertyFlags.EFlags.eAnimatable)); // not clear when this ever returns true: whether we set animatable // to true or false it says it has the default value. Assert.IsFalse(property.ModifiedFlag(FbxPropertyFlags.EFlags.eAnimatable)); // Test setting the value with the generic float accessor. // The value may not round-trip: a bool property will go to 1.0 property.Set(5.0f); TestGetter(property.GetFloat()); TestGetter(property.GetBool()); TestGetter(property.GetDouble()); TestGetter(property.GetFbxColor()); TestGetter(property.GetFbxDouble3()); TestGetter(property.GetString()); TestGetter(property.GetInt()); // Test setting the value with color accessor property.Set(new FbxColor()); // test GetCurve(). Just make sure it doesn't crash. We can't // generically test actually getting curves, because the details // (channel names etc) depend on the type of property and its // flags. FbxAnimLayer layer = FbxAnimLayer.Create(parent, "layer"); property.GetCurve(layer); property.GetCurve(layer, true); property.GetCurve(layer, "asdf"); property.GetCurve(layer, "asdf", true); property.GetCurve(layer, "asdf", "hjkl", true); Assert.That(() => { property.GetCurve(null); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); // test GetCurveNode() (make sure it doesn't crash) FbxAnimCurveNode curveNode = property.GetCurveNode(); Assert.IsNull(curveNode); // didn't create one so should be null curveNode = property.GetCurveNode(true); // TODO: figure out why the curve node doesn't get created //Assert.IsNotNull (curveNode); property.GetCurveNode(FbxAnimStack.Create(parent, "anim stack")); property.GetCurveNode(FbxAnimStack.Create(parent, "anim stack"), true); property.GetCurveNode(FbxAnimLayer.Create(parent, "anim layer")); property.GetCurveNode(FbxAnimLayer.Create(parent, "anim layer"), true); Assert.That(() => { property.GetCurveNode((FbxAnimStack)null); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); Assert.That(() => { property.GetCurveNode((FbxAnimLayer)null); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); using (FbxManager manager = FbxManager.Create()) { // Test ConnectSrcObject functions FbxObject obj = FbxObject.Create(manager, "obj"); bool result = property.ConnectSrcObject(obj); Assert.IsTrue(result); Assert.IsTrue(property.IsConnectedSrcObject(obj)); Assert.AreEqual(1, property.GetSrcObjectCount()); Assert.AreEqual(obj, property.GetSrcObject()); Assert.AreEqual(obj, property.GetSrcObject(0)); Assert.AreEqual(obj, property.FindSrcObject("obj")); Assert.IsNull(property.FindSrcObject("obj", 1)); Assert.That(() => { property.FindSrcObject(null); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); Assert.IsTrue(property.DisconnectSrcObject(obj)); Assert.IsFalse(property.IsConnectedSrcObject(obj)); Assert.That(() => { property.ConnectSrcObject(null); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); Assert.IsTrue(property.ConnectSrcObject(obj, FbxConnection.EType.eData)); Assert.IsTrue(property.DisconnectAllSrcObject()); // Test ConnectDstObject functions result = property.ConnectDstObject(obj); Assert.IsTrue(result); Assert.IsTrue(property.IsConnectedDstObject(obj)); Assert.AreEqual(1, property.GetDstObjectCount()); Assert.AreEqual(obj, property.GetDstObject()); Assert.AreEqual(obj, property.GetDstObject(0)); Assert.AreEqual(obj, property.FindDstObject("obj")); Assert.IsNull(property.FindDstObject("obj", 1)); Assert.That(() => { property.FindDstObject(null); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); Assert.IsTrue(property.DisconnectDstObject(obj)); Assert.IsFalse(property.IsConnectedDstObject(obj)); Assert.That(() => { property.ConnectDstObject(null); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); Assert.IsTrue(property.ConnectDstObject(obj, FbxConnection.EType.eData)); Assert.IsTrue(property.DisconnectAllDstObject()); } // verify this in the future: will dispose destroy? property.Dispose(); }
/// <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); } }