protected override FbxQuaternion GetConvertedQuaternionRotation(float seconds, Quaternion restRotation)
        {
            var            eulerRest = restRotation.eulerAngles;
            AnimationCurve x = GetCurves()[0], y = GetCurves()[1], z = GetCurves()[2];

            // 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 unityFinalAnimation = new Vector3(
                (x == null) ? eulerRest [0] : x.Evaluate(seconds),
                (y == null) ? eulerRest [1] : y.Evaluate(seconds),
                (z == null) ? eulerRest [2] : z.Evaluate(seconds)
                );

            // convert the final animation to righthanded coords

            return(ModelExporter.EulerToQuaternionZXY(unityFinalAnimation));
        }
        public void TestBasics()
        {
            Assert.That(!string.IsNullOrEmpty(ModelExporter.GetVersionFromReadme()));

            // Test GetOrCreateLayer
            using (var fbxManager = FbxManager.Create()) {
                var fbxMesh = FbxMesh.Create(fbxManager, "name");
                var layer0  = ModelExporter.GetOrCreateLayer(fbxMesh);
                Assert.That(layer0, Is.Not.Null);
                Assert.That(ModelExporter.GetOrCreateLayer(fbxMesh), Is.EqualTo(layer0));
                var layer5 = ModelExporter.GetOrCreateLayer(fbxMesh, layer: 5);
                Assert.That(layer5, Is.Not.Null);
                Assert.That(layer5, Is.Not.EqualTo(layer0));
            }

            // Test axis conversion: a x b in left-handed is the same as b x a
            // in right-handed (that's why we need to flip the winding order).
            var a         = new Vector3(1, 0, 0);
            var b         = new Vector3(0, 0, 1);
            var crossLeft = Vector3.Cross(a, b);

            Assert.That(ModelExporter.DefaultMaterial);

            // Test non-static functions.
            using (var fbxManager = FbxManager.Create()) {
                var fbxScene = FbxScene.Create(fbxManager, "scene");
                var fbxNode  = FbxNode.Create(fbxScene, "node");
                var exporter = new ModelExporter();

                // Test ExportMaterial: it exports and it re-exports
                bool result = exporter.ExportMaterial(ModelExporter.DefaultMaterial, fbxScene, fbxNode);
                Assert.IsTrue(result);
                var fbxMaterial = fbxNode.GetMaterial(0);
                Assert.That(fbxMaterial, Is.Not.Null);

                result = exporter.ExportMaterial(ModelExporter.DefaultMaterial, fbxScene, fbxNode);
                var fbxMaterial2 = fbxNode.GetMaterial(1);
                Assert.AreEqual(fbxMaterial, fbxMaterial2);

                // Test ExportTexture: it finds the same texture for the default-material (it doesn't create a new one)
                var fbxMaterialNew = FbxSurfaceLambert.Create(fbxScene, "lambert");
                exporter.ExportTexture(ModelExporter.DefaultMaterial, "_MainTex",
                                       fbxMaterialNew, FbxSurfaceLambert.sBump);
                Assert.AreEqual(
                    fbxMaterial.FindProperty(FbxSurfaceLambert.sDiffuse).GetSrcObject(),
                    fbxMaterialNew.FindProperty(FbxSurfaceLambert.sBump).GetSrcObject()
                    );

                // Test ExportMesh: make sure we exported a mesh with welded vertices.
                var cube     = GameObject.CreatePrimitive(PrimitiveType.Cube);
                var cubeNode = FbxNode.Create(fbxScene, "cube");
                exporter.ExportMesh(cube.GetComponent <MeshFilter>().sharedMesh, cubeNode);
                Assert.That(cubeNode.GetMesh(), Is.Not.Null);
                Assert.That(cubeNode.GetMesh().GetControlPointsCount(), Is.EqualTo(8));
            }

            // Test exporting a skinned-mesh. Make sure it doesn't leak (it did at one point)
            {
                var cube      = GameObject.CreatePrimitive(PrimitiveType.Cube);
                var character = new GameObject();
                var smr       = character.AddComponent <SkinnedMeshRenderer>();
                smr.sharedMesh = cube.GetComponent <MeshFilter>().sharedMesh;
                var meshCount = Object.FindObjectsOfType <Mesh>().Length;
                ModelExporter.ExportObject(GetRandomFbxFilePath(), character);
                Assert.AreEqual(meshCount, Object.FindObjectsOfType <Mesh>().Length);
            }

            // Test euler to quaternion conversion
            {
                // EulerToQuaternionZXY
                var v         = new Vector3(50, 45, 190);
                var quat      = ModelExporter.EulerToQuaternionZXY(v);
                var unityQuat = Quaternion.Euler(v);
                Assert.That((float)quat.X, Is.EqualTo(unityQuat.x));
                Assert.That((float)quat.Y, Is.EqualTo(unityQuat.y));
                Assert.That((float)quat.Z, Is.EqualTo(unityQuat.z));
                Assert.That((float)quat.W, Is.EqualTo(unityQuat.w));

                // EulerToQuaternionXYZ
                var fbxV    = new FbxVector4(v.x, v.y, v.z);
                var xyzQuat = ModelExporter.EulerToQuaternionXYZ(fbxV);

                // get the vector from the quaternion
                FbxAMatrix m = new FbxAMatrix();
                m.SetR(fbxV);
                var actualQuat = m.GetQ();

                // since this quaternion is XYZ instead of ZXY, it should not match the quaternion
                // created with EulerToQuaternionZXY
                Assert.That(xyzQuat, Is.Not.EqualTo(quat));
                Assert.That(xyzQuat, Is.EqualTo(actualQuat));
            }
        }