Exemplo n.º 1
0
        public Vitaboy.Bone NodeToBone(Node bone, Matrix4x4 worldMat)
        {
            AffineTransform worldTransform = AffineTransform.WorldToLocal(Matrix4x4.Identity, worldMat);
            var             vbone          = new Vitaboy.Bone();

            vbone.Name = bone.Name;
            var isroot = bone.Name == "ROOT";

            if (bone.VisualParent == null || isroot)
            {
                vbone.ParentName = "NULL";
            }
            else
            {
                vbone.ParentName = bone.VisualParent.Name;
            }
            vbone.HasProps     = false;
            vbone.Properties   = new List <Vitaboy.PropertyListItem>();
            vbone.CanBlend     = 1;
            vbone.CanRotate    = 1;
            vbone.CanTranslate = 1;
            var recursiveScale = RecursiveScale(bone.VisualParent);

            var transform = bone.LocalTransform;
            var baseQuat  = transform.Rotation;

            if (isroot)
            {
                baseQuat = RotateQ * worldTransform.Rotation * baseQuat;         //worldTransform.Rotation * baseQuat;
            }
            vbone.Rotation = QuatConvert(baseQuat);

            var baseTrans = transform.Translation;

            if (isroot)
            {
                baseTrans = Vector3.Transform(baseTrans, worldMat * RotateM); //worldMat * RotateM);
            }
            baseTrans        *= recursiveScale;
            vbone.Translation = Vec3Convert(baseTrans);
            return(vbone);
        }
Exemplo n.º 2
0
        public void Process(string filename)
        {
            var root = ModelRoot.Load(filename);

            Animations = new List <Vitaboy.Animation>();
            Meshes     = new List <ImportMeshGroup>();

            int   fps    = 36;
            float invFPS = 1f / fps;

            //var rootNode = root.LogicalNodes.FirstOrDefault(node => node.Name == "ROOT");
            var skin = root.LogicalSkins.FirstOrDefault();

            if (skin != null)
            {
                Skeleton      = new Vitaboy.Skeleton();
                Skeleton.Name = "custom";
                var invBind = skin.GetInverseBindMatricesAccessor().AsMatrix4x4Array();

                var orderedNodes = Enumerable.Range(0, skin.JointsCount).Select(i => {
                    var node = skin.GetJoint(i).Item1;
                    Matrix4x4 mat;
                    if (Matrix4x4.Invert(invBind[i], out mat))
                    {
                        node.WorldMatrix = mat;
                    }
                    return(node);
                }).ToList();
                var rootNode = orderedNodes.FirstOrDefault(x => x.Name == "ROOT");

                var worldMat = Matrix4x4.Identity;
                //find the first node above the root bone
                var animNode = rootNode.VisualParent;
                if (animNode != null)
                {
                    worldMat = animNode.WorldMatrix;
                }

                if (rootNode != null)
                {
                    //var orderedNodes = root.LogicalNodes.Where(x => x == rootNode || IsChildOf(x, rootNode));
                    var bones = new List <Vitaboy.Bone>();
                    var boneI = 0;
                    foreach (var node in orderedNodes)
                    {
                        var bone = NodeToBone(node, worldMat);
                        bone.Index = boneI++;
                        bones.Add(bone);
                    }
                    Skeleton.RootBone = bones.FirstOrDefault(x => x.ParentName == "NULL");
                    foreach (var bone in bones)
                    {
                        bone.Children = bones.Where(x => x.ParentName == bone.Name).ToArray();
                    }
                    Skeleton.Bones = bones.ToArray();
                    Skeleton.ComputeBonePositions(Skeleton.RootBone, Microsoft.Xna.Framework.Matrix.Identity);
                }
            }

            var models = root.LogicalMeshes;

            foreach (var model in models)
            {
                foreach (var prim in model.Primitives)
                {
                    var mat = prim.Material;
                    if (mat == null)
                    {
                        continue;              // must have a material
                    }
                    var mesh = new Vitaboy.Mesh();
                    mesh.SkinName = mat.Name;

                    var tris    = prim.GetTriangleIndices();
                    var indices = new List <int>();
                    foreach (var tri in tris)
                    {
                        indices.Add(tri.Item3);
                        indices.Add(tri.Item2);
                        indices.Add(tri.Item1);
                    }
                    var indBuffer = indices.ToArray();

                    var positions = prim.VertexAccessors["POSITION"].AsVector3Array();
                    var uvs       = prim.VertexAccessors["TEXCOORD_0"].AsVector2Array();
                    var normals   = prim.VertexAccessors["NORMAL"].AsVector3Array();
                    var joints    = prim.VertexAccessors["JOINTS_0"].AsVector4Array();
                    var weights   = prim.VertexAccessors["WEIGHTS_0"].AsVector4Array();

                    //we actually need to remap vertices.

                    var verts = new List <MeshVert>();

                    for (int i = 0; i < positions.Count; i++)
                    {
                        var pos    = positions[i];
                        var uv     = uvs[i];
                        var normal = normals[i];
                        var joint  = joints[i];
                        var weight = weights[i];

                        verts.Add(new MeshVert(i, pos, uv, normal, joint, weight));
                    }

                    // order by bone bindings, as bones and blend verts are bound using regions
                    // sorts by blend bone first, then real bone. Result looks like:
                    // (bone 0, no blend), (bone 1, blend 2), (bone 1, blend 3), (bone 2, no blend), ...
                    verts = verts.OrderBy(x => x.Joints.X).ToList(); //(x => (x.Weights.Y == 0) ? -1 : x.Joints.Y).ThenBy

                    // remap indices to point where the vertices are after sorting
                    for (int i = 0; i < indBuffer.Length; i++)
                    {
                        indBuffer[i] = verts.FindIndex(vert => vert.SourceIndex == indBuffer[i]);
                    }

                    mesh.IndexBuffer   = indBuffer;
                    mesh.NumPrimitives = indBuffer.Length / 3;
                    mesh.VertexBuffer  = verts.Select(vert => {
                        var pos    = Vec3Convert(vert.Position);
                        var normal = Vec3Convert(vert.Normal);
                        // position is already transformed. we need to invert the bind bone transform to get back to origin
                        // (do not scale)

                        //find main bone
                        var mainInd = (int)vert.Joints.X;
                        var main    = (mainInd < 0 || mainInd >= Skeleton.Bones.Length) ? null : Skeleton.Bones[mainInd];

                        //find blend bone
                        var blendInd = (int)vert.Joints.Y;
                        var blend    = (vert.Weights.Y == 0 || blendInd < 0 || blendInd >= Skeleton.Bones.Length) ? null : Skeleton.Bones[blendInd];

                        var bpos  = pos;
                        var bnorm = normal;

                        if (main != null)
                        {
                            var bonemat = RotateMX * Microsoft.Xna.Framework.Matrix.Invert(main.AbsoluteMatrix);
                            pos         = Microsoft.Xna.Framework.Vector3.Transform(pos, bonemat);
                            normal      = Microsoft.Xna.Framework.Vector3.TransformNormal(normal, bonemat);
                        }

                        if (blend != null)
                        {
                            var bonemat = RotateMX * Microsoft.Xna.Framework.Matrix.Invert(blend.AbsoluteMatrix);
                            bpos        = Microsoft.Xna.Framework.Vector3.Transform(bpos, bonemat);
                            bnorm       = Microsoft.Xna.Framework.Vector3.TransformNormal(bnorm, bonemat);
                        }
                        else
                        {
                            bpos  = pos;
                            bnorm = normal;
                        }

                        // only two weights are supported (one "real" and one "blend", so we will have to remove the smallest
                        // assume XYZW is sorted highest to lowest weight
                        var weight     = vert.Weights;
                        var multiplier = (weight.X + weight.Y + weight.Z + weight.W) / (weight.X + weight.Y);

                        return(new Vitaboy.Model.VitaboyVertex(
                                   pos,
                                   new Microsoft.Xna.Framework.Vector2(vert.UV.X, vert.UV.Y),
                                   bpos,
                                   new Microsoft.Xna.Framework.Vector3(vert.Joints.X, vert.Joints.Y, weight.Y * multiplier), //bone ind, blend ind, intensity
                                   normal,
                                   bnorm
                                   ));
                    }).ToArray();

                    //create bindings
                    var bindings                = new List <Vitaboy.BoneBinding>();
                    var blendData               = new List <Vitaboy.BlendData>();
                    var blendVerts              = new List <Microsoft.Xna.Framework.Vector3>();
                    var blendNormals            = new List <Microsoft.Xna.Framework.Vector3>();
                    Vitaboy.BoneBinding current = null;
                    var vertI  = 0;
                    var vcount = 0;

                    //real verts first
                    foreach (var vert in mesh.VertexBuffer)
                    {
                        if (current == null || current.BoneIndex != (int)vert.Parameters.X)
                        {
                            if (current != null)
                            {
                                current.RealVertexCount  = vcount;
                                current.BlendVertexCount = 0;
                            }
                            current = new Vitaboy.BoneBinding();
                            bindings.Add(current);
                            current.FirstRealVertex  = vertI;
                            current.FirstBlendVertex = -1;
                            current.BoneIndex        = (int)vert.Parameters.X;
                            current.BoneName         = (current.BoneIndex < 0 || current.BoneIndex >= Skeleton.Bones.Length) ? "NULL" : Skeleton.Bones[current.BoneIndex].Name;
                            vcount = 0;
                        }
                        vcount++;
                        vertI++;
                    }
                    if (current != null)
                    {
                        current.RealVertexCount  = vcount;
                        current.BlendVertexCount = 0;
                    }

                    // blend verts
                    // for each bone, find the verts blend affects
                    // then add them to the blend verts list, and update the binding
                    // O(n*m) but this is an import so whatever!
                    foreach (var binding in bindings)
                    {
                        var id         = binding.BoneIndex;
                        var startBlend = blendVerts.Count;
                        var blends     = mesh.VertexBuffer.Where(x => (int)x.Parameters.Y == id && x.Parameters.Z > 0).ToList();

                        vertI = 0;
                        foreach (var vert in mesh.VertexBuffer)
                        {
                            if ((int)vert.Parameters.Y == id && vert.Parameters.Z > 0)
                            {
                                blendData.Add(new Vitaboy.BlendData()
                                {
                                    OtherVertex = vertI, Weight = vert.Parameters.Z
                                });
                                blendVerts.Add(vert.BvPosition);
                                blendNormals.Add(vert.BvNormal);
                            }
                            vertI++;
                        }
                        if (blendVerts.Count != startBlend)
                        {
                            binding.FirstBlendVertex = startBlend;
                            binding.BlendVertexCount = blendVerts.Count - startBlend;
                        }
                    }

                    mesh.BlendData            = blendData.ToArray();
                    mesh.BlendVerts           = blendVerts.ToArray();
                    mesh.BlendNormals         = blendNormals.ToArray();
                    mesh.BoneBindings         = bindings.ToArray();
                    mesh.BlendVertBoneIndices = new int[mesh.BlendData.Length];

                    var tex     = mat.FindChannel("BaseColor");
                    var texData = tex?.Texture?.PrimaryImage?.GetImageContent();

                    Meshes.Add(new ImportMeshGroup()
                    {
                        Name = mat.Name, Mesh = mesh, TextureData = texData
                    });
                }
            }

            var nodes       = root.LogicalNodes;
            var sceneExtras = root.LogicalScenes.FirstOrDefault().TryUseExtrasAsDictionary(false);

            foreach (var animation in root.LogicalAnimations)
            {
                var vitaAnim = new Vitaboy.Animation();
                Animations.Add(vitaAnim);
                var vitaRot   = new List <Microsoft.Xna.Framework.Quaternion>();
                var vitaTrans = new List <Microsoft.Xna.Framework.Vector3>();

                vitaAnim.Name       = SanitizeName(animation.Name);
                vitaAnim.Duration   = animation.Duration;
                vitaAnim.XSkillName = vitaAnim.Name;

                uint frameDuration = (uint)Math.Max(1, Math.Ceiling(animation.Duration * fps));
                vitaAnim.NumFrames = (int)frameDuration;
                vitaAnim.UpdateFPS();
                var motions = new List <Vitaboy.AnimationMotion>();

                //find the first node above the root bone
                var transform = AffineTransform.Identity;
                var worldMat  = Matrix4x4.Identity;
                var animNode  = nodes.FirstOrDefault(x => animation.FindRotationSampler(x) != null || animation.FindTranslationSampler(x) != null);
                while (animNode != null && animNode.Name != "ROOT")
                {
                    animNode = animNode.VisualParent;
                }
                if (animNode != null)
                {
                    animNode = animNode.VisualParent;
                }
                if (animNode != null)
                {
                    worldMat  = animNode.WorldMatrix;
                    transform = AffineTransform.WorldToLocal(Matrix4x4.Identity, worldMat);
                }

                //check all nodes in the skeleton for matching samplers (rotation, translation).
                //if a sampler exists, add it to the animation
                foreach (var node in nodes)
                {
                    var recursiveScale = RecursiveScale(node.VisualParent);
                    var rotS           = animation.FindRotationSampler(node);
                    var transS         = animation.FindTranslationSampler(node);
                    var rotSampler     = rotS?.CreateCurveSampler();
                    var transSampler   = transS?.CreateCurveSampler();
                    var isroot         = node.Name == "ROOT";

                    if (rotSampler != null || transSampler != null)
                    {
                        //add this motion to the animation
                        var motion = new Vitaboy.AnimationMotion();
                        motions.Add(motion);
                        motion.BoneName       = node.Name;
                        motion.HasRotation    = rotSampler != null;
                        motion.HasTranslation = transSampler != null;
                        motion.FrameCount     = frameDuration;
                        motion.Duration       = animation.Duration;
                        motion.Properties     = new Vitaboy.PropertyList[0];

                        if (sceneExtras != null) //node.Extras != null)
                        {
                            //timeprops for this node
                            var timeprops = GetTimeProps(sceneExtras, vitaAnim.Name);
                            var list      = new Vitaboy.TimePropertyList();

                            var propDict = new Dictionary <int, Vitaboy.TimePropertyListItem>();
                            foreach (var tp in timeprops)
                            {
                                Vitaboy.TimePropertyListItem item;
                                if (!propDict.TryGetValue(tp.ID, out item))
                                {
                                    item                  = new Vitaboy.TimePropertyListItem();
                                    item.ID               = tp.ID;
                                    item.Properties       = new Vitaboy.PropertyList();
                                    item.Properties.Items = new Vitaboy.PropertyListItem[0];
                                    propDict[tp.ID]       = item;
                                }

                                Array.Resize(ref item.Properties.Items, item.Properties.Items.Length + 1);

                                var entry = new Vitaboy.PropertyListItem();
                                item.Properties.Items[item.Properties.Items.Length - 1] = entry;
                                entry.KeyPairs.Add(new KeyValuePair <string, string>(tp.Event, tp.Value));
                            }
                            list.Items            = propDict.Values.ToArray();
                            motion.TimeProperties = new Vitaboy.TimePropertyList[] { list };
                        }
                        else
                        {
                            motion.TimeProperties = new Vitaboy.TimePropertyList[0];
                        }

                        if (motion.HasRotation)
                        {
                            motion.FirstRotationIndex = (uint)vitaRot.Count;
                            float rotDuration;
                            if (rotS.InterpolationMode == AnimationInterpolationMode.CUBICSPLINE)
                            {
                                rotDuration = rotS.GetCubicKeys().LastOrDefault().Item1;
                            }
                            else
                            {
                                rotDuration = rotS.GetLinearKeys().LastOrDefault().Item1;
                            }

                            for (int i = 0; i < frameDuration; i++)
                            {
                                var baseQuat = rotSampler.GetPoint(Math.Min(rotDuration, i * invFPS));
                                if (isroot)
                                {
                                    baseQuat = RotateQ * transform.Rotation * baseQuat;
                                }
                                vitaRot.Add(QuatConvert(baseQuat));
                            }
                        }
                        else
                        {
                            motion.FirstRotationIndex = uint.MaxValue;
                        }

                        if (motion.HasTranslation)
                        {
                            motion.FirstTranslationIndex = (uint)vitaTrans.Count;
                            float transDuration;
                            if (transS.InterpolationMode == AnimationInterpolationMode.CUBICSPLINE)
                            {
                                transDuration = transS.GetCubicKeys().LastOrDefault().Item1;
                            }
                            else
                            {
                                transDuration = transS.GetLinearKeys().LastOrDefault().Item1;
                            }
                            for (int i = 0; i < frameDuration; i++)
                            {
                                var baseTrans = transSampler.GetPoint(Math.Min(transDuration, i * invFPS));
                                if (isroot)
                                {
                                    baseTrans = Vector3.Transform(baseTrans, worldMat * RotateM);
                                }
                                baseTrans *= recursiveScale;
                                vitaTrans.Add(Vec3Convert(baseTrans));
                            }
                        }
                        else
                        {
                            motion.FirstTranslationIndex = uint.MaxValue;
                        }
                    }
                }

                vitaAnim.Motions          = motions.ToArray();
                vitaAnim.Rotations        = vitaRot.ToArray();
                vitaAnim.Translations     = vitaTrans.ToArray();
                vitaAnim.RotationCount    = (uint)vitaAnim.Rotations.Length;
                vitaAnim.TranslationCount = (uint)vitaAnim.Translations.Length;

                vitaAnim.IsMoving = (byte)((motions.Count > 0) ? 1 : 0);
            }
        }