/// <summary> /// Export an AnimationCurve. /// NOTE: This is not used for rotations, because we need to convert from /// quaternion to euler and various other stuff. /// </summary> protected void ExportAnimCurve(UnityEngine.Object unityObj, AnimationCurve unityAnimCurve, string unityPropertyName, FbxScene fbxScene, FbxAnimLayer fbxAnimLayer) { FbxPropertyChannelPair fbxPropertyChannelPair; if (!FbxPropertyChannelPair.TryGetValue(unityPropertyName, out fbxPropertyChannelPair)) { Debug.LogWarning(string.Format("no mapping from Unity '{0}' to fbx property", unityPropertyName)); return; } GameObject unityGo = GetGameObject(unityObj); if (unityGo == null) { Debug.LogError(string.Format("cannot find gameobject for {0}", unityObj.ToString())); return; } FbxNode fbxNode; if (!MapUnityObjectToFbxNode.TryGetValue(unityGo, out fbxNode)) { Debug.LogError(string.Format("no fbx node for {0}", unityGo.ToString())); return; } // map unity property name to fbx property var fbxProperty = fbxNode.FindProperty(fbxPropertyChannelPair.Property, false); if (!fbxProperty.IsValid()) { Debug.LogError(string.Format("no fbx property {0} found on {1} ", fbxPropertyChannelPair.Property, fbxNode.GetName())); return; } if (Verbose) { Debug.Log("Exporting animation for " + unityObj.ToString() + " (" + unityPropertyName + ")"); } // Create the AnimCurve on the channel FbxAnimCurve fbxAnimCurve = fbxProperty.GetCurve(fbxAnimLayer, fbxPropertyChannelPair.Channel, true); // copy Unity AnimCurve to FBX AnimCurve. fbxAnimCurve.KeyModifyBegin(); for (int keyIndex = 0, n = unityAnimCurve.length; keyIndex < n; ++keyIndex) { var key = unityAnimCurve [keyIndex]; var fbxTime = FbxTime.FromSecondDouble(key.time); fbxAnimCurve.KeyAdd(fbxTime); fbxAnimCurve.KeySet(keyIndex, fbxTime, key.value); } fbxAnimCurve.KeyModifyEnd(); }
public void TestBasics() { var scene = FbxScene.Create(Manager, "scene"); var node = FbxNode.Create(scene, "node"); /* Test all we can test with a non-composite curve node, namely one that points to * a lcl translation. */ var animNode = FbxAnimCurveNode.CreateTypedCurveNode(node.LclTranslation, scene); Assert.IsFalse(animNode.IsComposite()); Assert.AreEqual(3, animNode.GetChannelsCount()); Assert.AreEqual(0, animNode.GetChannelIndex(Globals.FBXSDK_CURVENODE_COMPONENT_X)); Assert.AreEqual(Globals.FBXSDK_CURVENODE_COMPONENT_Y, animNode.GetChannelName(1)); var xcurve = animNode.CreateCurve(animNode.GetName(), Globals.FBXSDK_CURVENODE_COMPONENT_X); Assert.IsNotNull(xcurve); var xcurve2 = animNode.CreateCurve(animNode.GetName()); Assert.IsNotNull(xcurve2); var ycurve = animNode.CreateCurve(animNode.GetName(), 1); Assert.IsNotNull(ycurve); animNode.SetChannelValue(Globals.FBXSDK_CURVENODE_COMPONENT_Z, 6); Assert.AreEqual(6, animNode.GetChannelValue(Globals.FBXSDK_CURVENODE_COMPONENT_Z, 0)); Assert.AreEqual(6, animNode.GetChannelValue(2, 0)); animNode.SetChannelValue(2, 0); Assert.AreEqual(2, animNode.GetCurveCount(0)); Assert.AreEqual(1, animNode.GetCurveCount(1, animNode.GetName())); Assert.AreEqual(xcurve, animNode.GetCurve(0)); Assert.AreEqual(xcurve2, animNode.GetCurve(0, 1)); Assert.AreEqual(xcurve2, animNode.GetCurve(0, 1, animNode.GetName())); Assert.IsNull(animNode.GetCurve(1, 1)); var key = xcurve.KeyAdd(FbxTime.FromSecondDouble(0)); xcurve.KeySet(key, FbxTime.FromSecondDouble(0), 5); key = xcurve.KeyAdd(FbxTime.FromSecondDouble(1)); xcurve.KeySet(key, FbxTime.FromSecondDouble(1), -5); Assert.IsTrue(animNode.IsAnimated()); /* TODO: build a composite anim node and test this for real. */ Assert.IsTrue(animNode.IsAnimated(true)); var timespan = new FbxTimeSpan(); Assert.IsTrue(animNode.GetAnimationInterval(timespan)); Assert.AreEqual(FbxTime.FromSecondDouble(0), timespan.GetStart()); Assert.AreEqual(FbxTime.FromSecondDouble(1), timespan.GetStop()); /* Get a property that isn't a Double3; add a channel for it. */ var boolNode = FbxAnimCurveNode.CreateTypedCurveNode(node.VisibilityInheritance, scene); Assert.IsFalse(boolNode.IsComposite()); Assert.IsFalse(boolNode.IsAnimated()); Assert.IsTrue(boolNode.AddChannel("vis", 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); } }
// Update is called once per frame void Update() { // If we need to re-import, do that. if (!m_Settings.Equals(m_OldSettings)) { // Try to import; only nuke the old stuff if we succeed. var newManager = FbxManager.Create(); var newScene = FbxScene.Create(newManager, ""); var newNodes = TryImport(m_Settings, newScene); if (newNodes != null) { // success; zero out the time, destroy what we previously // imported, save the settings so we can compare m_time = 0; m_OldSettings = m_Settings; if (m_fbxManager != null) { m_fbxManager.Dispose(); } m_fbxManager = newManager; foreach (var kvp in m_nodes) { DestroyImmediate(kvp.Key); } m_nodes = newNodes; } } // Update the time m_time += Time.deltaTime * m_timeScale; var fbxtime = FbxTime.FromSecondDouble(m_time); // Update the transforms (todo: update other animatable properties) if (m_animate) { foreach (var kvp in m_nodes) { var fbxNode = kvp.Value; var unityNode = kvp.Key.transform; var mx = fbxNode.EvaluateLocalTransform(fbxtime); unityNode.localPosition = V3(mx.GetT()); unityNode.localRotation = Q(mx.GetQ()); unityNode.localScale = V3(mx.GetS()); } } }
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); }
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); }
protected void CreateAnimCurves( FbxObject animObject, FbxAnimLayer animLayer, List <PropertyComponentPair> properties, System.Func <int, double> calcTime, // lambda function for calculating time based on index System.Func <int, float> calcValue, // lambda function for calculating value based on index FbxNodeAttribute animNodeAttr = null) { foreach (var pair in properties) { FbxProperty fbxProperty = animObject.FindProperty(pair.propertyName, false); if (animNodeAttr != null && (fbxProperty == null || !fbxProperty.IsValid())) { // backup method for finding the property if we can't find it on the node itself fbxProperty = animNodeAttr.FindProperty(pair.propertyName, false); } Assert.IsNotNull(fbxProperty); Assert.IsTrue(fbxProperty.IsValid()); Assert.That(fbxProperty.GetFlag(FbxPropertyFlags.EFlags.eAnimatable), Is.True); foreach (var component in pair.componentList) { // Create the AnimCurve on the channel FbxAnimCurve fbxAnimCurve = fbxProperty.GetCurve(animLayer, component, true); Assert.IsNotNull(fbxAnimCurve); fbxAnimCurve.KeyModifyBegin(); for (int keyIndex = 0; keyIndex < m_keyCount; ++keyIndex) { FbxTime fbxTime = FbxTime.FromSecondDouble(calcTime(keyIndex)); fbxAnimCurve.KeyAdd(fbxTime); fbxAnimCurve.KeySet(keyIndex, fbxTime, calcValue(keyIndex)); } fbxAnimCurve.KeyModifyEnd(); } } }
public void BasicTests() { using (var manager = FbxManager.Create()) { // FbxPropertyT<FbxBool> example: VisibilityInheritance on a node var node = FbxNode.Create(manager, "node"); GenericPropertyTests <FbxPropertyBool> (node.VisibilityInheritance, node, "Visibility Inheritance", Globals.FbxVisibilityInheritanceDT); var property = node.VisibilityInheritance; property.Set(false); Assert.AreEqual(false, property.Get()); Assert.AreEqual(false, property.EvaluateValue()); Assert.AreEqual(false, property.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(false, property.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT<FbxDouble> example: several of them on a Lambert shader var obj = FbxSurfaceLambert.Create(manager, "lambert"); GenericPropertyTests <FbxPropertyDouble> (obj.EmissiveFactor, obj, "EmissiveFactor", Globals.FbxDoubleDT); var property = obj.EmissiveFactor; property.Set(5.0); // bool Set<float> is not accessible here! Assert.AreEqual(5.0, property.Get()); Assert.AreEqual(5.0, property.EvaluateValue()); Assert.AreEqual(5.0, property.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(5.0, property.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT<Double3> example: the LclTranslation on a node var node = FbxNode.Create(manager, "node"); GenericPropertyTests <FbxPropertyDouble3> (node.LclTranslation, node, "Lcl Translation", Globals.FbxLocalTranslationDT); var property = node.LclTranslation; property.Set(new FbxDouble3(1, 2, 3)); Assert.AreEqual(new FbxDouble3(1, 2, 3), property.Get()); Assert.AreEqual(new FbxDouble3(1, 2, 3), property.EvaluateValue()); Assert.AreEqual(new FbxDouble3(1, 2, 3), property.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(new FbxDouble3(1, 2, 3), property.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT<float> example: the LeftBarnDoor on a light var light = FbxLight.Create(manager, "light"); GenericPropertyTests(light.LeftBarnDoor, light, "LeftBarnDoor", Globals.FbxFloatDT); var property = light.LeftBarnDoor; light.LeftBarnDoor.Set(5.0f); Assert.AreEqual(5.0f, light.LeftBarnDoor.Get()); Assert.AreEqual(5.0f, property.EvaluateValue()); Assert.AreEqual(5.0f, property.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(5.0f, property.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT<int> example: the WorldUpType on an aim constraint var constraint = FbxConstraintAim.Create(manager, "constraint"); GenericPropertyTests(constraint.WorldUpType, constraint, "WorldUpType", Globals.FbxEnumDT); var property = constraint.WorldUpType; int value = (int)FbxConstraintAim.EWorldUp.eAimAtObjectUp; constraint.WorldUpType.Set(value); Assert.That(constraint.WorldUpType.Get(), Is.EqualTo(value)); Assert.That(property.EvaluateValue(), Is.EqualTo(value)); Assert.That(property.EvaluateValue(FbxTime.FromSecondDouble(5)), Is.EqualTo(value)); Assert.That(property.EvaluateValue(FbxTime.FromSecondDouble(5), true), Is.EqualTo(value)); } using (var manager = FbxManager.Create()) { // FbxPropertyT<FbxString> example: the description of a shader implementation var impl = FbxImplementation.Create(manager, "name"); GenericPropertyTests <FbxPropertyString> (impl.RenderAPI, impl, "RenderAPI", Globals.FbxStringDT); var property = impl.RenderAPI; property.Set("a value"); Assert.AreEqual("a value", property.Get()); // animated strings come out as empty-string Assert.AreEqual("", property.EvaluateValue()); Assert.AreEqual("", property.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual("", property.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT for FbxTexture enums EBlendMode and EWrapMode var tex = FbxTexture.Create(manager, "tex"); FbxPropertyTest.GenericPropertyTests(tex.CurrentTextureBlendMode, tex, "CurrentTextureBlendMode", Globals.FbxEnumDT); tex.CurrentTextureBlendMode.Set(FbxTexture.EBlendMode.eAdditive); Assert.AreEqual(FbxTexture.EBlendMode.eAdditive, tex.CurrentTextureBlendMode.Get()); Assert.AreEqual(FbxTexture.EBlendMode.eAdditive, tex.CurrentTextureBlendMode.EvaluateValue()); Assert.AreEqual(FbxTexture.EBlendMode.eAdditive, tex.CurrentTextureBlendMode.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(FbxTexture.EBlendMode.eAdditive, tex.CurrentTextureBlendMode.EvaluateValue(FbxTime.FromSecondDouble(5), true)); FbxPropertyTest.GenericPropertyTests(tex.WrapModeU, tex, "WrapModeU", Globals.FbxEnumDT); tex.WrapModeU.Set(FbxTexture.EWrapMode.eClamp); Assert.AreEqual(FbxTexture.EWrapMode.eClamp, tex.WrapModeU.Get()); Assert.AreEqual(FbxTexture.EWrapMode.eClamp, tex.WrapModeU.EvaluateValue()); Assert.AreEqual(FbxTexture.EWrapMode.eClamp, tex.WrapModeU.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(FbxTexture.EWrapMode.eClamp, tex.WrapModeU.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT<FbxNull.ELook> var null1 = FbxNull.Create(manager, "null1"); FbxPropertyTest.GenericPropertyTests(null1.Look, null1, "Look", Globals.FbxEnumDT); null1.Look.Set(FbxNull.ELook.eCross); Assert.AreEqual(FbxNull.ELook.eCross, null1.Look.Get()); Assert.AreEqual(FbxNull.ELook.eCross, null1.Look.EvaluateValue()); Assert.AreEqual(FbxNull.ELook.eCross, null1.Look.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(FbxNull.ELook.eCross, null1.Look.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT<FbxMarker.ELook> var marker1 = FbxMarker.Create(manager, "marker1"); FbxPropertyTest.GenericPropertyTests(marker1.Look, marker1, "Look", Globals.FbxEnumDT); marker1.Look.Set(FbxMarker.ELook.eCapsule); Assert.AreEqual(FbxMarker.ELook.eCapsule, marker1.Look.Get()); Assert.AreEqual(FbxMarker.ELook.eCapsule, marker1.Look.EvaluateValue()); Assert.AreEqual(FbxMarker.ELook.eCapsule, marker1.Look.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(FbxMarker.ELook.eCapsule, marker1.Look.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT for FbxCamera enum EProjectionType var camera = FbxCamera.Create(manager, "camera"); FbxPropertyTest.GenericPropertyTests(camera.ProjectionType, camera, "CameraProjectionType", Globals.FbxEnumDT); camera.ProjectionType.Set(FbxCamera.EProjectionType.ePerspective); Assert.AreEqual(FbxCamera.EProjectionType.ePerspective, camera.ProjectionType.Get()); Assert.AreEqual(FbxCamera.EProjectionType.ePerspective, camera.ProjectionType.EvaluateValue()); Assert.AreEqual(FbxCamera.EProjectionType.ePerspective, camera.ProjectionType.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(FbxCamera.EProjectionType.ePerspective, camera.ProjectionType.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT for FbxCamera enum EGateFit var camera = FbxCamera.Create(manager, "camera"); FbxPropertyTest.GenericPropertyTests(camera.GateFit, camera, "GateFit", Globals.FbxEnumDT); camera.GateFit.Set(FbxCamera.EGateFit.eFitHorizontal); Assert.AreEqual(FbxCamera.EGateFit.eFitHorizontal, camera.GateFit.Get()); Assert.AreEqual(FbxCamera.EGateFit.eFitHorizontal, camera.GateFit.EvaluateValue()); Assert.AreEqual(FbxCamera.EGateFit.eFitHorizontal, camera.GateFit.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(FbxCamera.EGateFit.eFitHorizontal, camera.GateFit.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT<EInheritType> var node = FbxNode.Create(manager, "node"); FbxPropertyTest.GenericPropertyTests(node.InheritType, node, "InheritType", Globals.FbxEnumDT); node.InheritType.Set(FbxTransform.EInheritType.eInheritRSrs); Assert.AreEqual(FbxTransform.EInheritType.eInheritRSrs, node.InheritType.Get()); Assert.AreEqual(FbxTransform.EInheritType.eInheritRSrs, node.InheritType.EvaluateValue()); Assert.AreEqual(FbxTransform.EInheritType.eInheritRSrs, node.InheritType.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(FbxTransform.EInheritType.eInheritRSrs, node.InheritType.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // FbxPropertyT for FbxLight enums var light = FbxLight.Create(manager, "light"); FbxPropertyTest.GenericPropertyTests(light.LightType, light, "LightType", Globals.FbxEnumDT); light.LightType.Set(FbxLight.EType.eSpot); Assert.AreEqual(FbxLight.EType.eSpot, light.LightType.Get()); Assert.AreEqual(FbxLight.EType.eSpot, light.LightType.EvaluateValue()); Assert.AreEqual(FbxLight.EType.eSpot, light.LightType.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(FbxLight.EType.eSpot, light.LightType.EvaluateValue(FbxTime.FromSecondDouble(5), true)); FbxPropertyTest.GenericPropertyTests(light.AreaLightShape, light, "AreaLightShape", Globals.FbxEnumDT); light.AreaLightShape.Set(FbxLight.EAreaLightShape.eSphere); Assert.AreEqual(FbxLight.EAreaLightShape.eSphere, light.AreaLightShape.Get()); Assert.AreEqual(FbxLight.EAreaLightShape.eSphere, light.AreaLightShape.EvaluateValue()); Assert.AreEqual(FbxLight.EAreaLightShape.eSphere, light.AreaLightShape.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(FbxLight.EAreaLightShape.eSphere, light.AreaLightShape.EvaluateValue(FbxTime.FromSecondDouble(5), true)); FbxPropertyTest.GenericPropertyTests(light.DecayType, light, "DecayType", Globals.FbxEnumDT); light.DecayType.Set(FbxLight.EDecayType.eCubic); Assert.AreEqual(FbxLight.EDecayType.eCubic, light.DecayType.Get()); Assert.AreEqual(FbxLight.EDecayType.eCubic, light.DecayType.EvaluateValue()); Assert.AreEqual(FbxLight.EDecayType.eCubic, light.DecayType.EvaluateValue(FbxTime.FromSecondDouble(5))); Assert.AreEqual(FbxLight.EDecayType.eCubic, light.DecayType.EvaluateValue(FbxTime.FromSecondDouble(5), true)); } using (var manager = FbxManager.Create()) { // Test all the create and destroy operations FbxProperty root, child; var obj = FbxObject.Create(manager, "obj"); Assert.IsNotNull(FbxProperty.Create(obj, Globals.FbxStringDT, "a")); Assert.IsNotNull(FbxProperty.Create(obj, Globals.FbxStringDT, "b", "label")); Assert.IsNotNull(FbxProperty.Create(obj, Globals.FbxStringDT, "c", "label", false)); bool didFind; Assert.IsNotNull(FbxProperty.Create(obj, Globals.FbxStringDT, "c", "label", true, out didFind)); Assert.IsTrue(didFind); root = FbxProperty.Create(obj, Globals.FbxCompoundDT, "root"); child = FbxProperty.Create(root, Globals.FbxStringDT, "a"); Assert.IsNotNull(child); Assert.IsNotNull(FbxProperty.Create(root, Globals.FbxStringDT, "b", "label")); Assert.IsNotNull(FbxProperty.Create(root, Globals.FbxStringDT, "c", "label", false)); Assert.IsNotNull(FbxProperty.Create(root, Globals.FbxStringDT, "c", "label", true, out didFind)); Assert.IsTrue(didFind); child.Destroy(); root.DestroyChildren(); Assert.IsNotNull(FbxProperty.Create(root, Globals.FbxStringDT, "c", "label", true, out didFind)); Assert.IsFalse(didFind); root.DestroyRecursively(); } }
public void TestBasics() { // create a curve we can unroll. var fbxScene = FbxScene.Create(Manager, "scene"); var fbxNode = FbxNode.Create(fbxScene, "node"); var fbxAnimNode = FbxAnimCurveNode.CreateTypedCurveNode(fbxNode.LclRotation, fbxScene); FbxAnimCurve[] fbxAnimCurves = { fbxAnimNode.CreateCurve(fbxAnimNode.GetName(), Globals.FBXSDK_CURVENODE_COMPONENT_X), fbxAnimNode.CreateCurve(fbxAnimNode.GetName(), Globals.FBXSDK_CURVENODE_COMPONENT_Y), fbxAnimNode.CreateCurve(fbxAnimNode.GetName(), Globals.FBXSDK_CURVENODE_COMPONENT_Z) }; FbxAnimCurveFilterUnroll filter = new FbxAnimCurveFilterUnroll(); Assert.That(filter.NeedApply(fbxAnimNode), Is.False, "expected not to need to unroll curves"); Assert.That(filter.Apply(fbxAnimNode), Is.False, "expected to have nothing to do"); // ensure coverage for function that takes an FbxStatus Assert.That(filter.NeedApply(fbxAnimNode, new FbxStatus()), Is.False); Assert.That(filter.Apply(fbxAnimNode, new FbxStatus()), Is.False); // configure the unroll condition foreach (float[] keydata in KeyTimeValues) { double seconds = keydata[0]; foreach (var fbxAnimCurve in fbxAnimCurves) { fbxAnimCurve.KeyModifyBegin(); } using (var fbxTime = FbxTime.FromSecondDouble(seconds)) { for (int ci = 0; ci < fbxAnimCurves.Length; ci++) { int ki = fbxAnimCurves[ci].KeyAdd(fbxTime); fbxAnimCurves[ci].KeySet(ki, fbxTime, keydata[ci + 1]); } } foreach (var fbxAnimCurve in fbxAnimCurves) { fbxAnimCurve.KeyModifyEnd(); } } Assert.That(filter.NeedApply(fbxAnimNode), Is.True, "expected to need to unroll curves"); Assert.That(filter.Apply(fbxAnimNode), Is.True, "expected to have unroll"); IEnumerator origKeydata = KeyTimeValues.GetEnumerator(); for (int ki = 0; ki < fbxAnimCurves[0].KeyGetCount(); ki++) { List <float> result = new List <float>() { (float)fbxAnimCurves[0].KeyGetTime(ki).GetSecondDouble() }; result = result.Concat((from ac in fbxAnimCurves select ac.KeyGetValue(ki))).ToList(); origKeydata.MoveNext(); if (ki == 0 || ki == 3 || ki == 4) { Assert.That(result, Is.EqualTo(origKeydata.Current)); } else { Assert.That(result, Is.Not.EqualTo(origKeydata.Current)); } } filter.Reset(); filter.Dispose(); }
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); }
/// <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); } }
/// <summary> /// Export an AnimationCurve. /// /// This is not used for rotations, because we need to convert from /// quaternion to euler and various other stuff. /// </summary> protected void ExportAnimCurve(UnityEngine.Object unityObj, AnimationCurve unityAnimCurve, string unityPropertyName, FbxAnimLayer fbxAnimLayer) { FbxPropertyChannelPair fbxPair; if (!MapUnityPropertyNameToFbx.TryGetValue(unityPropertyName, out fbxPair)) { Debug.LogWarning(string.Format("no property-channel mapping found for {0}", unityPropertyName)); return; } GameObject unityGo = GetGameObject(unityObj); if (unityGo == null) { Debug.LogError(string.Format("cannot convert to GameObject from {0}", unityObj.ToString())); return; } FbxNode fbxNode; if (!MapUnityObjectToFbxNode.TryGetValue(unityGo, out fbxNode)) { Debug.LogError(string.Format("cannot find fbxNode for {0}", unityGo.ToString())); return; } FbxProperty fbxProperty = null; // try finding unity property name on node attribute FbxNodeAttribute fbxNodeAttribute = fbxNode.GetNodeAttribute(); if (fbxNodeAttribute != null) { fbxProperty = fbxNodeAttribute.FindProperty(fbxPair.Property, false); } // try finding unity property on the node if (fbxProperty == null || !fbxProperty.IsValid()) { fbxProperty = fbxNode.FindProperty(fbxPair.Property, false); } if (fbxProperty == null || !fbxProperty.IsValid()) { Debug.LogError(string.Format("cannot find fbxProperty {0} on {1}", fbxPair.Property, fbxNode.GetName())); return; } if (Verbose) { Debug.Log(string.Format("Exporting animation for {0} ({1})", unityObj.ToString(), fbxPair.Property)); } // Create the AnimCurve on the channel FbxAnimCurve fbxAnimCurve = (fbxPair.Channel != null) ? fbxProperty.GetCurve(fbxAnimLayer, fbxPair.Channel, true) : fbxProperty.GetCurve(fbxAnimLayer, true); // copy Unity AnimCurve to FBX AnimCurve. fbxAnimCurve.KeyModifyBegin(); for (int keyIndex = 0, n = unityAnimCurve.length; keyIndex < n; ++keyIndex) { var key = unityAnimCurve [keyIndex]; var fbxTime = FbxTime.FromSecondDouble(key.time); fbxAnimCurve.KeyAdd(fbxTime); fbxAnimCurve.KeySet(keyIndex, fbxTime, key.value); } fbxAnimCurve.KeyModifyEnd(); }
public void TestBasics() { var scene = FbxScene.Create(Manager, "scene"); using (FbxAnimCurve curve = FbxAnimCurve.Create(scene, "curve")) { // test KeyModifyBegin (make sure it doesn't crash) curve.KeyModifyBegin(); // test KeyAdd int last = 0; int index = curve.KeyAdd(FbxTime.FromFrame(5), ref last); Assert.GreaterOrEqual(index, 0); // test KeyAdd null FbxTime Assert.That(() => { curve.KeyAdd(null); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); Assert.That(() => { curve.KeyAdd(null, ref last); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); // test KeySet FbxTime keyTime = FbxTime.FromSecondDouble(3); curve.KeySet(index, keyTime, 5); // test KeyGetValue, KeyGetTime, KeyGetCount Assert.AreEqual(5, curve.KeyGetValue(index)); Assert.AreEqual(keyTime, curve.KeyGetTime(index)); Assert.AreEqual(1, curve.KeyGetCount()); // make sure none of the variations crash curve.KeySet(index, new FbxTime(), 5, FbxAnimCurveDef.EInterpolationType.eInterpolationConstant ); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto ); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 4 ); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 3); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 0, FbxAnimCurveDef.EWeightedMode.eWeightedAll); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 0, FbxAnimCurveDef.EWeightedMode.eWeightedAll, 0); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 0, FbxAnimCurveDef.EWeightedMode.eWeightedAll, 0, 0); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 0, FbxAnimCurveDef.EWeightedMode.eWeightedAll, 0, 0, 0); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 0, FbxAnimCurveDef.EWeightedMode.eWeightedAll, 0, 0, 0, 0); // test KeyModifyEnd (make sure it doesn't crash) curve.KeyModifyEnd(); } // Also test that the AnimCurveBase can't be created. Assert.That(() => FbxAnimCurveBase.Create(Manager, ""), Throws.Exception.TypeOf <System.NotImplementedException>()); Assert.That(() => FbxAnimCurveBase.Create(FbxObject.Create(Manager, ""), ""), Throws.Exception.TypeOf <System.NotImplementedException>()); }
public void TestBasics() { var scene = FbxScene.Create(Manager, "scene"); using (FbxAnimCurve curve = FbxAnimCurve.Create(scene, "curve")) { // test KeyModifyBegin (make sure it doesn't crash) curve.KeyModifyBegin(); // test KeyAdd int last = 0; int index = curve.KeyAdd(FbxTime.FromFrame(5), ref last); Assert.GreaterOrEqual(index, 0); // test KeyAdd null FbxTime Assert.That(() => { curve.KeyAdd(null); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); Assert.That(() => { curve.KeyAdd(null, ref last); }, Throws.Exception.TypeOf <System.ArgumentNullException>()); // test KeySet FbxTime keyTime = FbxTime.FromSecondDouble(3); curve.KeySet(index, keyTime, 5); // test KeyGetValue, KeyGetTime, KeyGetCount Assert.AreEqual(5, curve.KeyGetValue(index)); Assert.AreEqual(keyTime, curve.KeyGetTime(index)); Assert.AreEqual(1, curve.KeyGetCount()); // test don't crash FbxAnimCurveKey key = curve.KeyGet(index); Assert.That(key, Is.Not.Null); // make sure none of the variations crash curve.KeySet(index, new FbxTime(), 5, FbxAnimCurveDef.EInterpolationType.eInterpolationConstant ); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto ); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 4 ); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 3); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 0, FbxAnimCurveDef.EWeightedMode.eWeightedAll); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 0, FbxAnimCurveDef.EWeightedMode.eWeightedAll, 0); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 0, FbxAnimCurveDef.EWeightedMode.eWeightedAll, 0, 0); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 0, FbxAnimCurveDef.EWeightedMode.eWeightedAll, 0, 0, 0); curve.KeySet(index, new FbxTime(), 0, FbxAnimCurveDef.EInterpolationType.eInterpolationCubic, FbxAnimCurveDef.ETangentMode.eTangentAuto, 0, 0, FbxAnimCurveDef.EWeightedMode.eWeightedAll, 0, 0, 0, 0); // more setter test curve.KeySetTangentMode(index, FbxAnimCurveDef.ETangentMode.eTangentUser); Assert.That(curve.KeyGetTangentMode(index), Is.EqualTo(FbxAnimCurveDef.ETangentMode.eTangentUser)); curve.KeySetTangentMode(index, FbxAnimCurveDef.ETangentMode.eTangentGenericBreak); Assert.That(curve.KeyGetTangentMode(index), Is.EqualTo(FbxAnimCurveDef.ETangentMode.eTangentGenericBreak)); // test settings key parameters key.SetTangentMode(FbxAnimCurveDef.ETangentMode.eTangentUser); // Set break is only meaningful if tangent is eTangentAuto or eTangentUser key.SetBreak(true); Assert.True(key.GetBreak()); key.SetDataFloat(FbxAnimCurveDef.EDataIndex.eRightSlope, 1.0f); Assert.That(key.GetDataFloat(FbxAnimCurveDef.EDataIndex.eRightSlope), Is.EqualTo(1.0f).Within(float.Epsilon)); key.SetBreak(false); Assert.False(key.GetBreak()); // key.SetTangentWeightMode(FbxAnimCurveDef.EWeightedMode.eWeightedAll); key.SetTangentWeightMode(FbxAnimCurveDef.EWeightedMode.eWeightedAll, FbxAnimCurveDef.EWeightedMode.eWeightedAll); Assert.That(key.GetTangentWeightMode(), Is.EqualTo(FbxAnimCurveDef.EWeightedMode.eWeightedAll)); // key.SetBreak(true); key.SetTangentWeightAndAdjustTangent(FbxAnimCurveDef.EDataIndex.eRightSlope, 1.0); key.SetTangentWeightAndAdjustTangent(FbxAnimCurveDef.EDataIndex.eNextLeftSlope, 1.0); key.SetTangentWeightAndAdjustTangent(FbxAnimCurveDef.EDataIndex.eWeights, 1.0); key.SetTangentWeightAndAdjustTangent(FbxAnimCurveDef.EDataIndex.eRightWeight, 1.0); key.SetTangentWeightAndAdjustTangent(FbxAnimCurveDef.EDataIndex.eNextLeftWeight, 1.0); key.SetBreak(false); // key.SetTangentVelocityMode(FbxAnimCurveDef.EVelocityMode.eVelocityAll); key.SetTangentVelocityMode(FbxAnimCurveDef.EVelocityMode.eVelocityAll, FbxAnimCurveDef.EVelocityMode.eVelocityAll); Assert.That(key.GetTangentVelocityMode(), Is.EqualTo(FbxAnimCurveDef.EVelocityMode.eVelocityAll)); // key.SetTangentVisibility(FbxAnimCurveDef.ETangentVisibility.eTangentShowLeft); Assert.That(key.GetTangentVisibility(), Is.EqualTo(FbxAnimCurveDef.ETangentVisibility.eTangentShowLeft)); // test KeyModifyEnd (make sure it doesn't crash) curve.KeyModifyEnd(); } // Also test that the AnimCurveBase can't be created. Assert.That(() => FbxAnimCurveBase.Create(Manager, ""), Throws.Exception.TypeOf <System.NotImplementedException>()); Assert.That(() => FbxAnimCurveBase.Create(FbxObject.Create(Manager, ""), ""), Throws.Exception.TypeOf <System.NotImplementedException>()); }