protected override FbxScene CreateScene(FbxManager manager) { // Create a scene with a single node that has an animation clip // attached to it FbxScene scene = FbxScene.Create(manager, "myScene"); FbxNode animNode = FbxNode.Create(scene, "animNode"); // setup anim stack FbxAnimStack fbxAnimStack = CreateAnimStack(scene); // add an animation layer FbxAnimLayer fbxAnimLayer = FbxAnimLayer.Create(scene, "animBaseLayer"); fbxAnimStack.AddMember(fbxAnimLayer); // set up the translation CreateAnimCurves( animNode, fbxAnimLayer, PropertyComponentList, (index) => { return(index * 2.0); }, (index) => { return(index * 3.0f - 1); } ); // TODO: avoid needing to this by creating typemaps for // FbxObject::GetSrcObjectCount and FbxCast. // Not trivial to do as both fbxobject.i and fbxemitter.i // have to be moved up before the ignore all statement // to allow use of templates. scene.SetCurrentAnimationStack(fbxAnimStack); scene.GetRootNode().AddChild(animNode); return(scene); }
protected override FbxAnimStack GenerateFbx() { HavokAnimationData anim = Souls.dsAnimation; IDictionary <int, FrameData> frameDatas = ExtractAnimationData(anim); var animStack = FbxAnimStack.Create(Scene, anim.Name + "_AnimStack"); animStack.SetLocalTimeSpan(new FbxTimeSpan(FbxTime.FromFrame(0), FbxTime.FromFrame(anim.FrameCount))); FbxAnimLayer animLayer = FbxAnimLayer.Create(animStack, "Layer0"); animStack.AddMember(animLayer); IDictionary <int, AnimExportHelper> boneHelpers = new Dictionary <int, AnimExportHelper>(); foreach (DsBoneData boneData in Souls.skeleton.boneDatas) { boneData.exportData.FbxNode.LclTranslation.GetCurveNode(animLayer, true); boneData.exportData.FbxNode.LclRotation.GetCurveNode(animLayer, true); boneData.exportData.FbxNode.LclScaling.GetCurveNode(animLayer, true); boneHelpers.Add(boneData.exportData.SoulsData.HkxBoneIndex, new AnimExportHelper( animLayer, boneData.exportData.FbxNode.LclTranslation, boneData.exportData.FbxNode.LclRotation, boneData.exportData.FbxNode.LclScaling ) ); } foreach (var frameData in frameDatas) { FbxTime time = FbxTime.FromFrame(frameData.Key); foreach (var boneData in frameData.Value.boneDatas) { int hkxBoneIndex = boneData.hkxBoneIndex; var newBlendableTransform = boneData.transform; AnimExportHelper animExportHelper = boneHelpers[hkxBoneIndex]; var euler = new Quaternion(newBlendableTransform.Rotation.X, newBlendableTransform.Rotation.Y, newBlendableTransform.Rotation.Z, newBlendableTransform.Rotation.W).QuaternionToEuler(); animExportHelper.translation.AddPoint(time, newBlendableTransform.Translation); animExportHelper.rotation.AddPoint(time, euler); animExportHelper.scale.AddPoint(time, newBlendableTransform.Scale); } } foreach (AnimExportHelper helper in boneHelpers.Values) { helper.translation.Finish(); helper.rotation.Finish(); helper.scale.Finish(); } return(null); }
public static void GenericTests<T>(T fbxCollection, FbxManager manager) where T : FbxCollection { // TODO: FbxScene has a member count of 3 instead of one (even after clearing), is this normal? int initialMemberCount = fbxCollection.GetMemberCount (); // test AddMember FbxObject obj = FbxObject.Create (manager, ""); bool result = fbxCollection.AddMember (obj); Assert.IsTrue (result); Assert.AreEqual(initialMemberCount+1, fbxCollection.GetMemberCount()); // test Clear fbxCollection.Clear (); Assert.AreEqual (initialMemberCount, fbxCollection.GetMemberCount()); // test GetAnimLayerMember() fbxCollection.AddMember(FbxAnimLayer.Create(manager, "animLayer")); var animLayer = fbxCollection.GetAnimLayerMember (); Assert.IsInstanceOf<FbxAnimLayer> (animLayer); var animLayer2 = fbxCollection.GetAnimLayerMember (0); Assert.AreEqual (animLayer, animLayer2); // check invalid Assert.IsNull(fbxCollection.GetAnimLayerMember (1)); Assert.IsNull(fbxCollection.GetAnimLayerMember (-1)); }
/// <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 override FbxScene CreateScene(FbxManager manager) { // Create a scene with a single node that has an animation clip // attached to it FbxScene scene = FbxScene.Create(manager, "myScene"); FbxNode sourceNode = FbxNode.Create(scene, "source"); FbxNode constrainedNode = FbxNode.Create(scene, "constrained"); scene.GetRootNode().AddChild(sourceNode); scene.GetRootNode().AddChild(constrainedNode); FbxConstraint posConstraint = CreatePositionConstraint(scene, sourceNode, constrainedNode); Assert.That(posConstraint, Is.Not.Null); bool result = posConstraint.ConnectDstObject(scene); Assert.That(result, Is.True); // animate weight + active // setup anim stack FbxAnimStack fbxAnimStack = CreateAnimStack(scene); // add an animation layer FbxAnimLayer fbxAnimLayer = FbxAnimLayer.Create(scene, "animBaseLayer"); fbxAnimStack.AddMember(fbxAnimLayer); // set up the translation CreateAnimCurves( posConstraint, fbxAnimLayer, PropertyComponentList, (index) => { return(index * 2.0); }, (index) => { return(index * 3 - 2); } ); // TODO: avoid needing to do this by creating typemaps for // FbxObject::GetSrcObjectCount and FbxCast. // Not trivial to do as both fbxobject.i and fbxemitter.i // have to be moved up before the ignore all statement // to allow use of templates. scene.SetCurrentAnimationStack(fbxAnimStack); return(scene); }
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); } }