Example #1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="FileName"></param>
        /// <param name="animation"></param>
        /// <param name="skeleton"></param>
        public void ExportSBAnimation(string FileName, SBAnimation animation, SBSkeleton skeleton)
        {
            var jointTable = GetJointTable(Settings.JVCPath).ToList();

            Anim a = new Anim();

            a.EndTime   = animation.FrameCount / Settings.FrameScale;
            a.PlaySpeed = 0.01f;

            a.Joints.Add(new Joint()
            {
                BoneID  = -1,
                Flag3   = 0x40,
                MaxTime = a.EndTime,
            });

            List <double> AllKeys = new List <double>();

            foreach (var v in animation.TransformNodes)
            {
                if (skeleton[v.Name] == null)
                {
                    continue;
                }

                var sb = skeleton[v.Name];
                if (sb == null)
                {
                    continue;
                }

                var index = (short)jointTable.IndexOf((short)skeleton.IndexOfBone(sb));

                Console.WriteLine(v.Name + " " + index);

                if (index == -1)
                {
                    continue;
                }

                Joint Position = null;
                Joint Rotation = null;
                Joint Scale    = null;

                if (v.HasTranslation)
                {
                    Position = new Joint();
                    var j = Position;
                    j.Flag1   = 0x02;
                    j.Flag2   = 0x02;
                    j.Flag3   = 0x21;
                    j.BoneID  = index;
                    j.MaxTime = a.EndTime;
                }
                if (v.HasRotation)
                {
                    Rotation = new Joint();
                    var j = Rotation;
                    j.Flag1   = 0x02;
                    j.Flag2   = 0x02;
                    j.Flag3   = 0x28;
                    j.BoneID  = index;
                    j.MaxTime = a.EndTime;
                }
                if (v.HasScale)
                {
                    Scale = new Joint();
                    var j = Scale;
                    j.Flag1   = 0x02;
                    j.Flag2   = 0x02;
                    j.Flag3   = 0x22;
                    j.BoneID  = index;
                    j.MaxTime = a.EndTime;
                }

                // gather baked nodes
                List <Vector3> Positions = new List <Vector3>();
                List <Vector4> Rotations = new List <Vector4>();
                List <Vector3> Scales    = new List <Vector3>();
                for (int i = 0; i <= animation.FrameCount; i++)
                {
                    var t = v.GetTransformAt(i, skeleton);

                    if (v.HasTranslation)
                    {
                        Positions.Add(t.ExtractTranslation() - sb.Translation);
                    }

                    if (v.HasScale)
                    {
                        Positions.Add(t.ExtractScale() - sb.Scale);
                    }

                    if (v.HasRotation)
                    {
                        var quat = (t.ExtractRotation().Inverted() * sb.RotationQuaternion).Inverted();

                        var inv = new Quaternion(quat.X, 0, 0, quat.W).Normalized().Inverted();

                        var dir   = Vector3.TransformNormal(Vector3.UnitX, Matrix4.CreateFromQuaternion(quat * inv));
                        var angle = Tools.CrossMath.ToEulerAngles(inv).X * 180 / (float)Math.PI;

                        Rotations.Add(new Vector4(dir, angle));
                    }
                }

                // now we convert the bake nodes into linear tracks in the new time scale range
                if (Positions.Count > 0)
                {
                    var X = SimplifyLines(Positions.Select(e => e.X));
                    var Y = SimplifyLines(Positions.Select(e => e.Y));
                    var Z = SimplifyLines(Positions.Select(e => e.Z));

                    var frames = X.Select(e => e.Item1).Union(Y.Select(e => e.Item1).Union(Z.Select(e => e.Item1))).ToList();
                    frames.Sort();
                    if (frames[frames.Count - 1] != animation.FrameCount)
                    {
                        frames.Add(animation.FrameCount);
                    }

                    AllKeys = AllKeys.Union(frames).ToList();

                    foreach (var f in frames)
                    {
                        Position.Keys.Add(new Key()
                        {
                            Time = (float)f / Settings.FrameScale,
                            X    = Positions[(int)f].X,
                            Y    = Positions[(int)f].Y,
                            Z    = Positions[(int)f].Z,
                            W    = 0
                        });
                    }
                }
                if (Rotations.Count > 0)
                {
                    var X = SimplifyLines(Rotations.Select(e => e.X));
                    var Y = SimplifyLines(Rotations.Select(e => e.Y));
                    var Z = SimplifyLines(Rotations.Select(e => e.Z));
                    var W = SimplifyLines(Rotations.Select(e => e.W));

                    var frames = X.Select(e => e.Item1).Union(Y.Select(e => e.Item1).Union(Z.Select(e => e.Item1))).Union(W.Select(e => e.Item1)).ToList();
                    frames.Sort();
                    if (frames[frames.Count - 1] != animation.FrameCount)
                    {
                        frames.Add(animation.FrameCount);
                    }

                    AllKeys = AllKeys.Union(frames).ToList();

                    foreach (var f in frames)
                    {
                        Rotation.Keys.Add(new Key()
                        {
                            Time = (float)f / Settings.FrameScale,
                            X    = Rotations[(int)f].X,
                            Y    = Rotations[(int)f].Y,
                            Z    = Rotations[(int)f].Z,
                            W    = Rotations[(int)f].W,
                        });
                    }
                }

                if (Position != null)
                {
                    a.Joints.Add(Position);
                }
                if (Rotation != null)
                {
                    a.Joints.Add(Rotation);
                }
                if (Scale != null)
                {
                    a.Joints.Add(Scale);
                }
            }

            AllKeys.Sort();

            foreach (var v in AllKeys)
            {
                a.Joints[0].Keys.Add(new Key()
                {
                    Time = (float)v / Settings.FrameScale
                });
            }

            a.Save(FileName);
        }
Example #2
0
        public IOModel ImportIOModel(string FileName)
        {
            IOModel    model    = new IOModel();
            SBSkeleton skeleton = new SBSkeleton();

            model.Skeleton = skeleton;

            var test = Fbx.FbxIO.ReadBinary(FileName);

            if (test.Version != Fbx.FbxVersion.v7_4)
            {
                throw new NotSupportedException($"Only FBX version 7.4 is currently supported: Imported version = {test.Version}");
            }

            // global settings
            float Scale = 1;

            // read global settings
            var settings = test.GetNodesByName("GlobalSettings");

            if (settings.Length != 0)
            {
                var prop70 = settings[0].GetNodesByName("Properties70");
                if (prop70.Length != 0)
                {
                    foreach (var property in prop70[0].Nodes)
                    {
                        if (property == null)
                        {
                            continue;
                        }
                        if (property.Properties.Count > 0 && property.Name == "P")
                        {
                            //TODO: this is inaccurate...
                            //if (property.Properties[0].Equals("UnitScaleFactor"))
                            //    Scale = (float)(double)property.Properties[4];
                        }
                    }
                }
            }

            FbxAccessor accessor = new FbxAccessor(FileName);

            //Bones
            var limbs = accessor.GetLimbNodes();

            SBConsole.WriteLine($"Limb Node Count: {limbs.Length}");

            foreach (var limb in limbs)
            {
                skeleton.AddRoot(ConvertLimbToSBBone(limb));
            }
            foreach (var root in skeleton.Roots)
            {
                root.Scale *= Scale;
            }

            // Fast access to bone indices
            Dictionary <string, int> BoneNameToIndex = new Dictionary <string, int>();

            foreach (var b in skeleton.Bones)
            {
                BoneNameToIndex.Add(b.Name, skeleton.IndexOfBone(b));
            }

            // Mesh

            var models = accessor.GetModels();

            SBConsole.WriteLine($"Model Node Count: {models.Count}");

            int YupAxis = accessor.GetOriginalXAxis();

            SBConsole.WriteLine("Yup: " + YupAxis);
            foreach (var mod in models)
            {
                // rotation 90
                Matrix4 transform = (ImportSettings.Rotate90 ? Matrix4.CreateRotationX(-90 * DegToRag) : Matrix4.Identity) * GetModelTransform(mod);

                foreach (var geom in mod.Geometries)
                {
                    IOMesh mesh = new IOMesh();
                    mesh.Name = mod.Name;
                    model.Meshes.Add(mesh);

                    // Create Rigging information
                    Vector4[] BoneIndices = new Vector4[geom.Vertices.Length];
                    Vector4[] BoneWeights = new Vector4[geom.Vertices.Length];
                    foreach (var deformer in geom.Deformers)
                    {
                        //TODO: this shouldn't happen...
                        if (!BoneNameToIndex.ContainsKey(deformer.Name))
                        {
                            continue;
                        }
                        int index = BoneNameToIndex[deformer.Name];

                        for (int i = 0; i < deformer.Indices.Length; i++)
                        {
                            int vertexIndex = deformer.Indices[i];
                            for (int j = 0; j < 4; j++)
                            {
                                if (BoneWeights[vertexIndex][j] == 0)
                                {
                                    BoneIndices[vertexIndex][j] = index;
                                    BoneWeights[vertexIndex][j] = (float)deformer.Weights[i];
                                    break;
                                }
                            }
                        }
                        //SBConsole.WriteLine(deformer.Name + " " + deformer.Weights.Length + " " + deformer.Indices.Length + " " + index);
                    }

                    // Explanation:
                    // negative values are used to indicate a stopping point for the face
                    // so every 3rd index needed to be adjusted
                    for (int i = 0; i < geom.Indices.Length; i += 3)
                    {
                        mesh.Indices.Add((uint)i);
                        mesh.Indices.Add((uint)i + 1);
                        mesh.Indices.Add((uint)i + 2);
                        mesh.Vertices.Add(CreateVertex(transform, geom, i, BoneIndices, BoneWeights, Scale));
                        mesh.Vertices.Add(CreateVertex(transform, geom, i + 1, BoneIndices, BoneWeights, Scale));
                        mesh.Vertices.Add(CreateVertex(transform, geom, i + 2, BoneIndices, BoneWeights, Scale));
                    }

                    mesh.HasPositions = true;

                    //SBConsole.WriteLine(geom.Vertices.Length);
                    foreach (var layer in geom.Layers)
                    {
                        switch (layer.Name)
                        {
                        case "LayerElementNormal":
                        case "LayerElementUV":
                        case "LayerElementColor":
                            break;

                        default:
                            SBConsole.WriteLine(layer.Name + " " + layer.ReferenceInformationType + " " + layer.Data.Length + " " + (layer.ReferenceInformationType.Equals("IndexToDirect") ? layer.Indices.Length.ToString() : ""));
                            break;
                        }
                    }
                    mesh.Optimize();
                }
            }

            return(model);
        }