示例#1
0
            /// <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));
        }
示例#3
0
            /// <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();
        }
示例#10
0
                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);
                }
示例#11
0
            /// <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);
                }
            }
示例#12
0
            /// <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();
            }
示例#13
0
        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>());
        }
示例#14
0
        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>());
        }