Ejemplo n.º 1
0
        public void LoadScene()
        {
            objects.Clear();
            materials.Clear();
            skeleton = new STSkeleton();

            processNode();

            var idenity = Matrix4x4.Identity;

            BuildSkeletonNodes(scene.RootNode, BoneNames, skeleton, ref idenity);
            skeleton.update();
            skeleton.reset();

            if (scene.HasMaterials)
            {
                foreach (Material mat in scene.Materials)
                {
                    materials.Add(CreateGenericMaterial(mat));
                }
            }
            foreach (Assimp.Animation animation in scene.Animations)
            {
            }
            foreach (var tex in scene.Textures)
            {
            }
        }
Ejemplo n.º 2
0
        public STBone(STSkeleton skl)
        {
            skeletonParent   = skl;
            ImageKey         = "bone";
            SelectedImageKey = "bone";

            Checked = true;
        }
Ejemplo n.º 3
0
        private void SaveBones(Node parentBone, STBone bone, STSkeleton skeleton)
        {
            Node boneNode = new Node(bone.Text);

            parentBone.Children.Add(boneNode);

            boneNode.Transform = AssimpHelper.GetBoneMatrix(bone);

            foreach (STBone child in bone.GetChildren())
            {
                SaveBones(boneNode, child, skeleton);
            }
        }
Ejemplo n.º 4
0
        private void BuildSkeletonNodes(Node node, List <string> boneNames, STSkeleton skeleton, ref Matrix4x4 rootTransform)
        {
            Matrix4x4 trafo   = node.Transform;
            Matrix4x4 world   = trafo * rootTransform;
            Matrix4   worldTK = AssimpHelper.TKMatrix(world);

            string Name = node.Name;
            string ParentArmatureName = node.Parent != null ? node.Parent.Name : "";

            if (DaeHelper.IDMapToName.ContainsKey(node.Name))
            {
                Name = DaeHelper.IDMapToName[node.Name];
            }

            if (ParentArmatureName != string.Empty && DaeHelper.IDMapToName.ContainsKey(ParentArmatureName))
            {
                ParentArmatureName = DaeHelper.IDMapToName[ParentArmatureName];
            }

            bool IsBone = boneNames.Contains(Name) && !boneNames.Contains(ParentArmatureName) ||
                          Name.Contains("Skl_Root") || Name.Contains("nw4f_root") ||
                          Name.Contains("skl_root") || Name.Contains("_root") ||
                          Name.Contains("skeleton_root");

            short SmoothIndex = 0;
            short RigidIndex  = -1;

            //Loop through all the bones. If the parent is not in the bone list, then it's Parent is the root
            if (IsBone)
            {
                var idenity = Matrix4x4.Identity;

                var Root = node;
                if (node.Parent != null)
                {
                    Root = node.Parent;
                }

                CreateByNode(node, skeleton, ParentArmatureName, SmoothIndex, RigidIndex, true, ref idenity);
            }
            else
            {
                if (node.HasChildren)
                {
                    foreach (Node child in node.Children)
                    {
                        BuildSkeletonNodes(child, boneNames, skeleton, ref world);
                    }
                }
            }
        }
Ejemplo n.º 5
0
        private void SaveMeshes(Scene scene, STGenericModel model, STSkeleton skeleton, string FileName, List <int> NodeArray)
        {
            int MeshIndex = 0;

            foreach (var obj in model.Nodes[0].Nodes)
            {
                var mesh = SaveMesh((STGenericObject)obj, MeshIndex, skeleton, NodeArray);
                scene.Meshes.Add(mesh);
                MeshIndex++;
            }
            Node geomNode = new Node(Path.GetFileNameWithoutExtension(FileName), scene.RootNode);

            for (int ob = 0; ob < scene.MeshCount; ob++)
            {
                geomNode.MeshIndices.Add(ob);
            }
            scene.RootNode.Children.Add(geomNode);
        }
Ejemplo n.º 6
0
        private void SaveSkeleton(STSkeleton skeleton, Node parentNode)
        {
            Node root = new Node("skeleton_root");

            parentNode.Children.Add(root);

            if (skeleton.bones.Count > 0)
            {
                Node boneNode = new Node(skeleton.bones[0].Text);
                boneNode.Transform = AssimpHelper.GetBoneMatrix(skeleton.bones[0]);
                root.Children.Add(boneNode);

                foreach (STBone child in skeleton.bones[0].GetChildren())
                {
                    SaveBones(boneNode, child, skeleton);
                }
            }
        }
Ejemplo n.º 7
0
        private void CreateByNode(Node node, STSkeleton skeleton, short SmoothIndex, short RigidIndex, bool IsRoot, ref Assimp.Matrix4x4 rootTransform)
        {
            Matrix4x4 trafo        = node.Transform;
            Matrix4x4 world        = trafo * rootTransform;
            var       transformMat = AssimpHelper.TKMatrix(world);

            int matchedBoneIndex = skeleton.bones.FindIndex(item => item.Name == node.Name);

            if (matchedBoneIndex < 0)
            {
                tempBoneNodes.Add(node);

                STBone bone = new STBone();
                bone.skeletonParent = skeleton;
                skeleton.bones.Add(bone);

                bone.Text = node.Name;
                bone.SmoothMatrixIndex = (short)skeleton.bones.IndexOf(bone);
                bone.RigidMatrixIndex  = -1; //Todo calculate these

                STConsole.WriteLine($"-".Repeat(30));
                STConsole.WriteLine($"Processing Bone {bone.Text}");
                STConsole.WriteLine($"SmoothMatrixIndex {bone.SmoothMatrixIndex}");
                STConsole.WriteLine($"RigidMatrixIndex {bone.RigidMatrixIndex}");
                STConsole.WriteLine($"Transform Matrix {transformMat}");
                STConsole.WriteLine($"-".Repeat(30));

                if (IsRoot)
                {
                    bone.parentIndex = -1;
                    transformMat     = AssimpHelper.TKMatrix(world * Matrix4x4.FromRotationX(MathHelper.DegreesToRadians(BoneRotation)));
                }
                else
                {
                    if (tempBoneNodes.Contains(node.Parent))
                    {
                        bone.parentIndex = tempBoneNodes.IndexOf(node.Parent);
                    }
                }


                var scale    = transformMat.ExtractScale();
                var rotation = transformMat.ExtractRotation();
                var position = transformMat.ExtractTranslation();

                var rotEular = AssimpHelper.ToEular(rotation);

                bone.position = new float[] { position.X, position.Y, position.Z };
                bone.scale    = new float[] { scale.X, scale.Y, scale.Z };
                bone.rotation = new float[] { rotEular.X, rotEular.Y, rotEular.Z, 0 };
            }
            else
            {
                STConsole.WriteLine($"Duplicate node name found for bone {node.Name}!", Color.Red);
            }

            foreach (Node child in node.Children)
            {
                CreateByNode(child, skeleton, SmoothIndex, RigidIndex, false, ref rootTransform);
            }
        }
Ejemplo n.º 8
0
        public void SaveFromModel(STGenericModel model, string FileName, List <STGenericTexture> Textures, STSkeleton skeleton = null, List <int> NodeArray = null)
        {
            ExtractedTextures.Clear();

            Scene scene = new Scene();

            scene.RootNode = new Node("RootNode");

            progressBar               = new STProgressBar();
            progressBar.Task          = "Exorting Skeleton...";
            progressBar.Value         = 0;
            progressBar.StartPosition = FormStartPosition.CenterScreen;
            progressBar.Show();
            progressBar.Refresh();

            SaveSkeleton(skeleton, scene.RootNode);
            SaveMaterials(scene, model, FileName, Textures);

            progressBar.Task  = "Exorting Meshes...";
            progressBar.Value = 50;

            SaveMeshes(scene, model, skeleton, FileName, NodeArray);

            progressBar.Task  = "Saving File...";
            progressBar.Value = 80;

            SaveScene(FileName, scene, model.GetObjects());

            progressBar.Value = 100;
            progressBar.Close();
            progressBar.Dispose();
        }
Ejemplo n.º 9
0
        private Mesh SaveMesh(STGenericObject genericObj, int index, STSkeleton skeleton, List <int> NodeArray)
        {
            //Assimp is weird so use mesh_# for the name. We'll change it back after save
            Mesh mesh = new Mesh($"mesh_{ index }", PrimitiveType.Triangle);

            mesh.MaterialIndex = genericObj.MaterialIndex;

            List <Vector3D> textureCoords0 = new List <Vector3D>();
            List <Vector3D> textureCoords1 = new List <Vector3D>();
            List <Vector3D> textureCoords2 = new List <Vector3D>();
            List <Color4D>  vertexColors   = new List <Color4D>();

            int vertexID = 0;

            foreach (Vertex v in genericObj.vertices)
            {
                mesh.Vertices.Add(new Vector3D(v.pos.X, v.pos.Y, v.pos.Z));
                mesh.Normals.Add(new Vector3D(v.nrm.X, v.nrm.Y, v.nrm.Z));
                textureCoords0.Add(new Vector3D(v.uv0.X, v.uv0.Y, 0));
                textureCoords1.Add(new Vector3D(v.uv1.X, v.uv1.Y, 0));
                textureCoords2.Add(new Vector3D(v.uv2.X, v.uv2.Y, 0));
                vertexColors.Add(new Color4D(v.col.X, v.col.Y, v.col.Z, v.col.W));
                mesh.TextureCoordinateChannels[0] = textureCoords0;
                mesh.TextureCoordinateChannels[1] = textureCoords1;
                mesh.TextureCoordinateChannels[2] = textureCoords2;
                mesh.VertexColorChannels[0]       = vertexColors;

                if (skeleton != null)
                {
                    for (int j = 0; j < v.boneIds.Count; j++)
                    {
                        if (j < genericObj.VertexSkinCount)
                        {
                            //Get the bone via the node array and bone index from the vertex
                            STBone STbone = skeleton.bones[NodeArray[v.boneIds[j]]];

                            //Find the index of a bone. If it doesn't exist then we add it
                            int boneInd = mesh.Bones.FindIndex(x => x.Name == STbone.Text);

                            if (boneInd == -1)
                            {
                                var matrices = Switch_Toolbox.Library.IO.MatrixExenstion.CalculateInverseMatrix(STbone);

                                //Set the inverse matrix
                                Matrix4x4 transform = matrices.inverse.FromNumerics();

                                //Create a new assimp bone
                                Bone bone = new Bone();
                                bone.Name         = STbone.Text;
                                bone.OffsetMatrix = STbone.invert.ToMatrix4x4();
                                mesh.Bones.Add(bone);
                                BoneNames.Add(bone.Name);

                                boneInd = mesh.Bones.IndexOf(bone); //Set the index of the bone for the vertex weight
                            }

                            int MinWeightAmount = 0;

                            //Check if the max amount of weights is higher than the current bone id
                            if (v.boneWeights.Count > j && v.boneWeights[j] > MinWeightAmount)
                            {
                                if (v.boneWeights[j] <= 1)
                                {
                                    mesh.Bones[boneInd].VertexWeights.Add(new VertexWeight(vertexID, v.boneWeights[j]));
                                }
                                else
                                {
                                    mesh.Bones[boneInd].VertexWeights.Add(new VertexWeight(vertexID, 1));
                                }
                            }
                            else if (v.boneWeights.Count == 0 || v.boneWeights[j] > MinWeightAmount)
                            {
                                mesh.Bones[boneInd].VertexWeights.Add(new VertexWeight(vertexID, 1));
                            }
                        }
                    }
                }


                vertexID++;
            }
            List <int> faces = genericObj.lodMeshes[genericObj.DisplayLODIndex].faces;

            for (int f = 0; f < faces.Count; f++)
            {
                mesh.Faces.Add(new Face(new int[] { faces[f++], faces[f++], faces[f] }));
            }

            mesh.TextureCoordinateChannels.SetValue(textureCoords0, 0);

            return(mesh);
        }
Ejemplo n.º 10
0
        public void NextFrame(STSkeleton skeleton, bool isChild = false)
        {
            if (Frame >= FrameCount)
            {
                return;
            }

            if (Frame == 0 && !isChild)
            {
                skeleton.reset();
            }


            foreach (object child in Children)
            {
                if (child is Animation)
                {
                    ((Animation)child).SetFrame(Frame);
                    ((Animation)child).NextFrame(skeleton, isChild: true);
                }
            }

            bool Updated = false; // no need to update skeleton of animations that didn't change

            foreach (KeyNode node in Bones)
            {
                // Get Skeleton Node
                STBone b = null;
                b = skeleton.getBone(node.Text);
                if (b == null)
                {
                    continue;
                }
                Updated = true;

                b.pos.X = node.XPOS.GetValue(Frame);
                b.pos.Y = node.YPOS.GetValue(Frame);
                b.pos.Z = node.ZPOS.GetValue(Frame);

                if (node.XSCA.HasAnimation())
                {
                    b.sca.X = node.XSCA.GetValue(Frame);
                }
                else
                {
                    b.sca.X = 1;
                }
                if (node.YSCA.HasAnimation())
                {
                    b.sca.Y = node.YSCA.GetValue(Frame);
                }
                else
                {
                    b.sca.Y = 1;
                }
                if (node.ZSCA.HasAnimation())
                {
                    b.sca.Z = node.ZSCA.GetValue(Frame);
                }
                else
                {
                    b.sca.Z = 1;
                }


                if (node.XROT.HasAnimation() || node.YROT.HasAnimation() || node.ZROT.HasAnimation())
                {
                    if (node.RotType == RotationType.QUATERNION)
                    {
                        KeyFrame[] x  = node.XROT.GetFrame(Frame);
                        KeyFrame[] y  = node.YROT.GetFrame(Frame);
                        KeyFrame[] z  = node.ZROT.GetFrame(Frame);
                        KeyFrame[] w  = node.WROT.GetFrame(Frame);
                        Quaternion q1 = new Quaternion(x[0].Value, y[0].Value, z[0].Value, w[0].Value);
                        Quaternion q2 = new Quaternion(x[1].Value, y[1].Value, z[1].Value, w[1].Value);
                        if (x[0].Frame == Frame)
                        {
                            b.rot = q1;
                        }
                        else
                        if (x[1].Frame == Frame)
                        {
                            b.rot = q2;
                        }
                        else
                        {
                            b.rot = Quaternion.Slerp(q1, q2, (Frame - x[0].Frame) / (x[1].Frame - x[0].Frame));
                        }
                    }
                    else
                    if (node.RotType == RotationType.EULER)
                    {
                        float x = node.XROT.HasAnimation() ? node.XROT.GetValue(Frame) : b.rotation[0];
                        float y = node.YROT.HasAnimation() ? node.YROT.GetValue(Frame) : b.rotation[1];
                        float z = node.ZROT.HasAnimation() ? node.ZROT.GetValue(Frame) : b.rotation[2];
                        b.rot = EulerToQuat(z, y, x);
                    }
                }
            }
            Frame += 1f;
            if (Frame >= FrameCount)
            {
                Frame = 0;
            }

            if (!isChild && Updated)
            {
                skeleton.update();
            }
        }
Ejemplo n.º 11
0
        private void SaveMeshes(Scene scene, STGenericModel model, STSkeleton skeleton, string FileName, List <int> NodeArray)
        {
            int MeshIndex = 0;

            foreach (var obj in model.Nodes[0].Nodes)
            {
                var genericObj = (STGenericObject)obj;

                Mesh mesh = new Mesh(genericObj.Text, PrimitiveType.Triangle);
                mesh.MaterialIndex = genericObj.MaterialIndex;

                List <Vector3D> textureCoords0 = new List <Vector3D>();
                List <Vector3D> textureCoords1 = new List <Vector3D>();
                List <Vector3D> textureCoords2 = new List <Vector3D>();
                List <Color4D>  vertexColors   = new List <Color4D>();

                int vertexID = 0;
                foreach (Vertex v in genericObj.vertices)
                {
                    mesh.Vertices.Add(new Vector3D(v.pos.X, v.pos.Y, v.pos.Z));
                    mesh.Normals.Add(new Vector3D(v.nrm.X, v.nrm.Y, v.nrm.Z));
                    textureCoords0.Add(new Vector3D(v.uv0.X, v.uv0.Y, 0));
                    textureCoords1.Add(new Vector3D(v.uv1.X, v.uv1.Y, 0));
                    textureCoords2.Add(new Vector3D(v.uv2.X, v.uv2.Y, 0));
                    vertexColors.Add(new Color4D(v.col.X, v.col.Y, v.col.Z, v.col.W));
                    mesh.TextureCoordinateChannels[0] = textureCoords0;
                    mesh.TextureCoordinateChannels[1] = textureCoords1;
                    mesh.TextureCoordinateChannels[2] = textureCoords2;
                    mesh.VertexColorChannels[0]       = vertexColors;

                    for (int j = 0; j < v.boneIds.Count; j++)
                    {
                        if (j < genericObj.VertexSkinCount)
                        {
                            //Get the bone via the node array and bone index from the vertex
                            STBone STbone = skeleton.bones[NodeArray[v.boneIds[j]]];

                            //Find the index of a bone. If it doesn't exist then we add it
                            int boneInd = mesh.Bones.FindIndex(x => x.Name == STbone.Text);

                            if (boneInd == -1)
                            {
                                var matrices = Switch_Toolbox.Library.IO.MatrixExenstion.CalculateInverseMatrix(STbone);

                                //Set the inverse matrix
                                Matrix4x4 transform = matrices.inverse.FromNumerics();

                                //Create a new assimp bone
                                Bone bone = new Bone();
                                bone.Name         = STbone.Text;
                                bone.OffsetMatrix = transform;

                                mesh.Bones.Add(bone);
                                BoneNames.Add(bone.Name);

                                boneInd = mesh.Bones.IndexOf(bone); //Set the index of the bone for the vertex weight
                            }

                            //Check if the max amount of weights is higher than the current bone id
                            if (v.boneWeights.Count > j)
                            {
                                if (v.boneWeights[j] <= 1)
                                {
                                    mesh.Bones[boneInd].VertexWeights.Add(new VertexWeight(vertexID, v.boneWeights[j]));
                                }
                                else
                                {
                                    mesh.Bones[boneInd].VertexWeights.Add(new VertexWeight(vertexID, 1));
                                }
                            }
                            else
                            {
                                mesh.Bones[boneInd].VertexWeights.Add(new VertexWeight(vertexID, 1));
                            }
                        }
                    }
                    vertexID++;
                }
                List <int> faces = genericObj.lodMeshes[genericObj.DisplayLODIndex].faces;
                for (int f = 0; f < faces.Count; f++)
                {
                    mesh.Faces.Add(new Face(new int[] { faces[f++], faces[f++], faces[f] }));
                }

                mesh.TextureCoordinateChannels.SetValue(textureCoords0, 0);

                scene.Meshes.Add(mesh);

                MeshIndex++;
            }
            Node geomNode = new Node(Path.GetFileNameWithoutExtension(FileName), scene.RootNode);

            for (int ob = 0; ob < scene.MeshCount; ob++)
            {
                geomNode.MeshIndices.Add(ob);
            }
            scene.RootNode.Children.Add(geomNode);
        }
Ejemplo n.º 12
0
        public void SaveFromModel(STGenericModel model, string FileName, List <STGenericTexture> Textures, STSkeleton skeleton = null, List <int> NodeArray = null)
        {
            Scene scene = new Scene();

            scene.RootNode = new Node("RootNode");

            SaveSkeleton(skeleton, scene.RootNode);
            SaveMaterials(scene, model, FileName, Textures);
            SaveMeshes(scene, model, skeleton, FileName, NodeArray);

            using (var v = new AssimpContext())
            {
                string ext = System.IO.Path.GetExtension(FileName);

                string formatID = "collada";
                if (ext == ".obj")
                {
                    formatID = "obj";
                }
                if (ext == ".3ds")
                {
                    formatID = "3ds";
                }
                if (ext == ".dae")
                {
                    formatID = "collada";
                }
                if (ext == ".ply")
                {
                    formatID = "ply";
                }

                if (v.ExportFile(scene, FileName, formatID, PostProcessSteps.ValidateDataStructure))
                {
                    MessageBox.Show($"Exported {FileName} Successfuly!");
                }
                else
                {
                    MessageBox.Show($"Failed to export {FileName}!");
                }
            }
        }
Ejemplo n.º 13
0
        public static Animation read(string filename, STSkeleton vbn)
        {
            StreamReader reader = File.OpenText(filename);
            string       line;

            bool isHeader = true;

            string          angularUnit, linearUnit, timeUnit;
            int             startTime = 0;
            int             endTime   = 0;
            List <AnimBone> bones     = new List <AnimBone>();

            Animation.KeyNode  current = null;
            Animation.KeyFrame att     = new Animation.KeyFrame();
            bool   inKeys = false;
            string type   = "";

            Animation a = new Animation(filename);

            while ((line = reader.ReadLine()) != null)
            {
                string[] args = line.Replace(";", "").TrimStart().Split(' ');

                if (isHeader)
                {
                    if (args [0].Equals("anim"))
                    {
                        isHeader = false;
                    }
                    else if (args [0].Equals("angularUnit"))
                    {
                        angularUnit = args [1];
                    }
                    else if (args [0].Equals("endTime"))
                    {
                        endTime = (int)Math.Ceiling(float.Parse(args [1]));
                    }
                    else if (args [0].Equals("startTime"))
                    {
                        startTime = (int)Math.Ceiling(float.Parse(args [1]));
                    }
                }

                if (!isHeader)
                {
                    if (inKeys)
                    {
                        if (args[0].Equals("}"))
                        {
                            inKeys = false;
                            continue;
                        }
                        Animation.KeyFrame k = new Animation.KeyFrame();
                        //att.keys.Add (k);
                        if (type.Contains("translate"))
                        {
                            if (type.Contains("X"))
                            {
                                current.XPOS.Keys.Add(k);
                            }
                            if (type.Contains("Y"))
                            {
                                current.YPOS.Keys.Add(k);
                            }
                            if (type.Contains("Z"))
                            {
                                current.ZPOS.Keys.Add(k);
                            }
                        }
                        if (type.Contains("rotate"))
                        {
                            if (type.Contains("X"))
                            {
                                current.XROT.Keys.Add(k);
                            }
                            if (type.Contains("Y"))
                            {
                                current.YROT.Keys.Add(k);
                            }
                            if (type.Contains("Z"))
                            {
                                current.ZROT.Keys.Add(k);
                            }
                        }
                        if (type.Contains("scale"))
                        {
                            if (type.Contains("X"))
                            {
                                current.XSCA.Keys.Add(k);
                            }
                            if (type.Contains("Y"))
                            {
                                current.YSCA.Keys.Add(k);
                            }
                            if (type.Contains("Z"))
                            {
                                current.ZSCA.Keys.Add(k);
                            }
                        }
                        k.Frame = float.Parse(args [0]) - 1;
                        k.Value = float.Parse(args [1]);
                        if (type.Contains("rotate"))
                        {
                            k.Value *= (float)(Math.PI / 180f);
                        }
                        //k.intan = (args [2]);
                        //k.outtan = (args [3]);
                        if (args.Length > 7 && att.Weighted)
                        {
                            k.In  = float.Parse(args[7]) * (float)(Math.PI / 180f);
                            k.Out = float.Parse(args[8]) * (float)(Math.PI / 180f);
                        }
                    }

                    if (args [0].Equals("anim"))
                    {
                        inKeys = false;
                        if (args.Length == 5)
                        {
                            //TODO: finish this type
                            // can be name of attribute
                        }
                        if (args.Length == 7)
                        {
                            // see of the bone of this attribute exists
                            current = null;
                            foreach (Animation.KeyNode b in a.Bones)
                            {
                                if (b.Text.Equals(args [3]))
                                {
                                    current = b;
                                    break;
                                }
                            }
                            if (current == null)
                            {
                                current         = new Animation.KeyNode(args[3]);
                                current.RotType = Animation.RotationType.EULER;
                                a.Bones.Add(current);
                            }
                            current.Text = args [3];

                            att           = new Animation.KeyFrame();
                            att.InterType = Animation.InterpolationType.HERMITE;
                            type          = args [2];
                            //current.Nodes.Add (att);

                            // row child attribute aren't needed here
                        }
                    }

                    /*if (args [0].Equals ("input"))
                     *                          att.input = args [1];
                     *                  if (args [0].Equals ("output"))
                     *                          att.output = args [1];
                     *                  if (args [0].Equals ("preInfinity"))
                     *                          att.preInfinity = args [1];
                     *                  if (args [0].Equals ("postInfinity"))
                     *                          att.postInfinity = args [1];*/
                    if (args[0].Equals("weighted"))
                    {
                        att.Weighted = args[1].Equals("1");
                    }


                    // begining keys section
                    if (args [0].Contains("keys"))
                    {
                        inKeys = true;
                    }
                }
            }

            a.FrameCount = endTime - 1;

            reader.Close();
            return(a);
        }
Ejemplo n.º 14
0
        public static void CreateANIM(string fname, Animation a, STSkeleton vbn)
        {
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@fname))
            {
                file.WriteLine("animVersion 1.1;");
                file.WriteLine("mayaVersion 2014 x64;\ntimeUnit ntscf;\nlinearUnit cm;\nangularUnit deg;\nstartTime 1;\nendTime " + (a.FrameCount + 1) + ";");

                a.SetFrame(a.FrameCount - 1);             //from last frame
                for (int li = 0; li < a.FrameCount; ++li) //go through each frame with nextFrame
                {
                    a.NextFrame(vbn);
                }
                a.NextFrame(vbn);  //go on first frame

                int i = 0;

                // writing node attributes
                foreach (STBone b in vbn.getBoneTreeOrder())
                {
                    i = vbn.boneIndex(b.Text);

                    if (a.HasBone(b.Text))
                    {
                        // write the bone attributes
                        // count the attributes
                        Animation.KeyNode n = a.GetBone(b.Text);
                        int ac = 0;


                        if (n.XPOS.HasAnimation())
                        {
                            file.WriteLine("anim translate.translateX translateX " + b.Text + " 0 0 " + (ac++) + ";");
                            writeKey(file, n.XPOS, n, a.Size(), "translateX");
                            file.WriteLine("}");
                        }
                        if (n.YPOS.HasAnimation())
                        {
                            file.WriteLine("anim translate.translateY translateY " + b.Text + " 0 0 " + (ac++) + ";");
                            writeKey(file, n.YPOS, n, a.Size(), "translateY");
                            file.WriteLine("}");
                        }
                        if (n.ZPOS.HasAnimation())
                        {
                            file.WriteLine("anim translate.translateZ translateZ " + b.Text + " 0 0 " + (ac++) + ";");
                            writeKey(file, n.ZPOS, n, a.Size(), "translateZ");
                            file.WriteLine("}");
                        }
                        if (n.XROT.HasAnimation())
                        {
                            file.WriteLine("anim rotate.rotateX rotateX " + b.Text + " 0 0 " + (ac++) + ";");
                            writeKey(file, n.XROT, n, a.Size(), "rotateX");
                            file.WriteLine("}");
                        }
                        if (n.YROT.HasAnimation())
                        {
                            file.WriteLine("anim rotate.rotateY rotateY " + b.Text + " 0 0 " + (ac++) + ";");
                            writeKey(file, n.YROT, n, a.Size(), "rotateY");
                            file.WriteLine("}");
                        }
                        if (n.ZROT.HasAnimation())
                        {
                            file.WriteLine("anim rotate.rotateZ rotateZ " + b.Text + " 0 0 " + (ac++) + ";");
                            writeKey(file, n.ZROT, n, a.Size(), "rotateZ");
                            file.WriteLine("}");
                        }

                        if (n.XSCA.HasAnimation())
                        {
                            file.WriteLine("anim scale.scaleX scaleX " + b.Text + " 0 0 " + (ac++) + ";");
                            writeKey(file, n.XSCA, n, a.Size(), "scaleX");
                            file.WriteLine("}");
                        }
                        if (n.YSCA.HasAnimation())
                        {
                            file.WriteLine("anim scale.scaleY scaleY " + b.Text + " 0 0 " + (ac++) + ";");
                            writeKey(file, n.YSCA, n, a.Size(), "scaleY");
                            file.WriteLine("}");
                        }
                        if (n.ZSCA.HasAnimation())
                        {
                            file.WriteLine("anim scale.scaleZ scaleZ " + b.Text + " 0 0 " + (ac++) + ";");
                            writeKey(file, n.ZSCA, n, a.Size(), "scaleZ");
                            file.WriteLine("}");
                        }
                    }
                    else
                    {
                        file.WriteLine("anim " + b.Text + " 0 0 0;");
                    }
                }
            }
        }